Don't corrupt plpython's "TD" dictionary in a recursive trigger call.
[pgsql.git] / src / pl / plpython / expected / plpython_trigger.out
blob4cb90cb520493639d45bc33e70f551c810e0d377
1 -- these triggers are dedicated to HPHC of RI who
2 -- decided that my kid's name was william not willem, and
3 -- vigorously resisted all efforts at correction.  they have
4 -- since gone bankrupt...
5 CREATE FUNCTION users_insert() returns trigger
6         AS
7 'if TD["new"]["fname"] == None or TD["new"]["lname"] == None:
8         return "SKIP"
9 if TD["new"]["username"] == None:
10         TD["new"]["username"] = TD["new"]["fname"][:1] + "_" + TD["new"]["lname"]
11         rv = "MODIFY"
12 else:
13         rv = None
14 if TD["new"]["fname"] == "william":
15         TD["new"]["fname"] = TD["args"][0]
16         rv = "MODIFY"
17 return rv'
18         LANGUAGE plpython3u;
19 CREATE FUNCTION users_update() returns trigger
20         AS
21 'if TD["event"] == "UPDATE":
22         if TD["old"]["fname"] != TD["new"]["fname"] and TD["old"]["fname"] == TD["args"][0]:
23                 return "SKIP"
24 return None'
25         LANGUAGE plpython3u;
26 CREATE FUNCTION users_delete() RETURNS trigger
27         AS
28 'if TD["old"]["fname"] == TD["args"][0]:
29         return "SKIP"
30 return None'
31         LANGUAGE plpython3u;
32 CREATE TRIGGER users_insert_trig BEFORE INSERT ON users FOR EACH ROW
33         EXECUTE PROCEDURE users_insert ('willem');
34 CREATE TRIGGER users_update_trig BEFORE UPDATE ON users FOR EACH ROW
35         EXECUTE PROCEDURE users_update ('willem');
36 CREATE TRIGGER users_delete_trig BEFORE DELETE ON users FOR EACH ROW
37         EXECUTE PROCEDURE users_delete ('willem');
38 -- quick peek at the table
40 SELECT * FROM users;
41  fname  | lname | username | userid 
42 --------+-------+----------+--------
43  jane   | doe   | j_doe    |      1
44  john   | doe   | johnd    |      2
45  willem | doe   | w_doe    |      3
46  rick   | smith | slash    |      4
47 (4 rows)
49 -- should fail
51 UPDATE users SET fname = 'william' WHERE fname = 'willem';
52 -- should modify william to willem and create username
54 INSERT INTO users (fname, lname) VALUES ('william', 'smith');
55 INSERT INTO users (fname, lname, username) VALUES ('charles', 'darwin', 'beagle');
56 SELECT * FROM users;
57   fname  | lname  | username | userid 
58 ---------+--------+----------+--------
59  jane    | doe    | j_doe    |      1
60  john    | doe    | johnd    |      2
61  willem  | doe    | w_doe    |      3
62  rick    | smith  | slash    |      4
63  willem  | smith  | w_smith  |      5
64  charles | darwin | beagle   |      6
65 (6 rows)
67 -- dump trigger data
68 CREATE TABLE trigger_test
69         (i int, v text );
70 CREATE TABLE trigger_test_generated (
71         i int,
72         j int GENERATED ALWAYS AS (i * 2) STORED
74 CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpython3u AS $$
76 if 'relid' in TD:
77         TD['relid'] = "bogus:12345"
79 skeys = list(TD.keys())
80 skeys.sort()
81 for key in skeys:
82     val = TD[key]
83     if not isinstance(val, dict):
84         plpy.notice("TD[" + key + "] => " + str(val))
85     else:
86         # print dicts the hard way because otherwise the order is implementation-dependent
87         valkeys = list(val.keys())
88         valkeys.sort()
89         plpy.notice("TD[" + key + "] => " + '{' + ', '.join([repr(k) + ': ' + repr(val[k]) for k in valkeys]) + '}')
91 return None
93 $$;
94 CREATE TRIGGER show_trigger_data_trig_before
95 BEFORE INSERT OR UPDATE OR DELETE ON trigger_test
96 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
97 CREATE TRIGGER show_trigger_data_trig_after
98 AFTER INSERT OR UPDATE OR DELETE ON trigger_test
99 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
100 CREATE TRIGGER show_trigger_data_trig_stmt
101 BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE ON trigger_test
102 FOR EACH STATEMENT EXECUTE PROCEDURE trigger_data(23,'skidoo');
103 insert into trigger_test values(1,'insert');
104 NOTICE:  TD[args] => ['23', 'skidoo']
105 NOTICE:  TD[event] => INSERT
106 NOTICE:  TD[level] => STATEMENT
107 NOTICE:  TD[name] => show_trigger_data_trig_stmt
108 NOTICE:  TD[new] => None
109 NOTICE:  TD[old] => None
110 NOTICE:  TD[relid] => bogus:12345
111 NOTICE:  TD[table_name] => trigger_test
112 NOTICE:  TD[table_schema] => public
113 NOTICE:  TD[when] => BEFORE
114 NOTICE:  TD[args] => ['23', 'skidoo']
115 NOTICE:  TD[event] => INSERT
116 NOTICE:  TD[level] => ROW
117 NOTICE:  TD[name] => show_trigger_data_trig_before
118 NOTICE:  TD[new] => {'i': 1, 'v': 'insert'}
119 NOTICE:  TD[old] => None
120 NOTICE:  TD[relid] => bogus:12345
121 NOTICE:  TD[table_name] => trigger_test
122 NOTICE:  TD[table_schema] => public
123 NOTICE:  TD[when] => BEFORE
124 NOTICE:  TD[args] => ['23', 'skidoo']
125 NOTICE:  TD[event] => INSERT
126 NOTICE:  TD[level] => ROW
127 NOTICE:  TD[name] => show_trigger_data_trig_after
128 NOTICE:  TD[new] => {'i': 1, 'v': 'insert'}
129 NOTICE:  TD[old] => None
130 NOTICE:  TD[relid] => bogus:12345
131 NOTICE:  TD[table_name] => trigger_test
132 NOTICE:  TD[table_schema] => public
133 NOTICE:  TD[when] => AFTER
134 update trigger_test set v = 'update' where i = 1;
135 NOTICE:  TD[args] => ['23', 'skidoo']
136 NOTICE:  TD[event] => UPDATE
137 NOTICE:  TD[level] => STATEMENT
138 NOTICE:  TD[name] => show_trigger_data_trig_stmt
139 NOTICE:  TD[new] => None
140 NOTICE:  TD[old] => None
141 NOTICE:  TD[relid] => bogus:12345
142 NOTICE:  TD[table_name] => trigger_test
143 NOTICE:  TD[table_schema] => public
144 NOTICE:  TD[when] => BEFORE
145 NOTICE:  TD[args] => ['23', 'skidoo']
146 NOTICE:  TD[event] => UPDATE
147 NOTICE:  TD[level] => ROW
148 NOTICE:  TD[name] => show_trigger_data_trig_before
149 NOTICE:  TD[new] => {'i': 1, 'v': 'update'}
150 NOTICE:  TD[old] => {'i': 1, 'v': 'insert'}
151 NOTICE:  TD[relid] => bogus:12345
152 NOTICE:  TD[table_name] => trigger_test
153 NOTICE:  TD[table_schema] => public
154 NOTICE:  TD[when] => BEFORE
155 NOTICE:  TD[args] => ['23', 'skidoo']
156 NOTICE:  TD[event] => UPDATE
157 NOTICE:  TD[level] => ROW
158 NOTICE:  TD[name] => show_trigger_data_trig_after
159 NOTICE:  TD[new] => {'i': 1, 'v': 'update'}
160 NOTICE:  TD[old] => {'i': 1, 'v': 'insert'}
161 NOTICE:  TD[relid] => bogus:12345
162 NOTICE:  TD[table_name] => trigger_test
163 NOTICE:  TD[table_schema] => public
164 NOTICE:  TD[when] => AFTER
165 delete from trigger_test;
166 NOTICE:  TD[args] => ['23', 'skidoo']
167 NOTICE:  TD[event] => DELETE
168 NOTICE:  TD[level] => STATEMENT
169 NOTICE:  TD[name] => show_trigger_data_trig_stmt
170 NOTICE:  TD[new] => None
171 NOTICE:  TD[old] => None
172 NOTICE:  TD[relid] => bogus:12345
173 NOTICE:  TD[table_name] => trigger_test
174 NOTICE:  TD[table_schema] => public
175 NOTICE:  TD[when] => BEFORE
176 NOTICE:  TD[args] => ['23', 'skidoo']
177 NOTICE:  TD[event] => DELETE
178 NOTICE:  TD[level] => ROW
179 NOTICE:  TD[name] => show_trigger_data_trig_before
180 NOTICE:  TD[new] => None
181 NOTICE:  TD[old] => {'i': 1, 'v': 'update'}
182 NOTICE:  TD[relid] => bogus:12345
183 NOTICE:  TD[table_name] => trigger_test
184 NOTICE:  TD[table_schema] => public
185 NOTICE:  TD[when] => BEFORE
186 NOTICE:  TD[args] => ['23', 'skidoo']
187 NOTICE:  TD[event] => DELETE
188 NOTICE:  TD[level] => ROW
189 NOTICE:  TD[name] => show_trigger_data_trig_after
190 NOTICE:  TD[new] => None
191 NOTICE:  TD[old] => {'i': 1, 'v': 'update'}
192 NOTICE:  TD[relid] => bogus:12345
193 NOTICE:  TD[table_name] => trigger_test
194 NOTICE:  TD[table_schema] => public
195 NOTICE:  TD[when] => AFTER
196 truncate table trigger_test;
197 NOTICE:  TD[args] => ['23', 'skidoo']
198 NOTICE:  TD[event] => TRUNCATE
199 NOTICE:  TD[level] => STATEMENT
200 NOTICE:  TD[name] => show_trigger_data_trig_stmt
201 NOTICE:  TD[new] => None
202 NOTICE:  TD[old] => None
203 NOTICE:  TD[relid] => bogus:12345
204 NOTICE:  TD[table_name] => trigger_test
205 NOTICE:  TD[table_schema] => public
206 NOTICE:  TD[when] => BEFORE
207 DROP TRIGGER show_trigger_data_trig_stmt on trigger_test;
208 DROP TRIGGER show_trigger_data_trig_before on trigger_test;
209 DROP TRIGGER show_trigger_data_trig_after on trigger_test;
210 CREATE TRIGGER show_trigger_data_trig_before
211 BEFORE INSERT OR UPDATE OR DELETE ON trigger_test_generated
212 FOR EACH ROW EXECUTE PROCEDURE trigger_data();
213 CREATE TRIGGER show_trigger_data_trig_after
214 AFTER INSERT OR UPDATE OR DELETE ON trigger_test_generated
215 FOR EACH ROW EXECUTE PROCEDURE trigger_data();
216 insert into trigger_test_generated (i) values (1);
217 NOTICE:  TD[args] => None
218 NOTICE:  TD[event] => INSERT
219 NOTICE:  TD[level] => ROW
220 NOTICE:  TD[name] => show_trigger_data_trig_before
221 NOTICE:  TD[new] => {'i': 1}
222 NOTICE:  TD[old] => None
223 NOTICE:  TD[relid] => bogus:12345
224 NOTICE:  TD[table_name] => trigger_test_generated
225 NOTICE:  TD[table_schema] => public
226 NOTICE:  TD[when] => BEFORE
227 NOTICE:  TD[args] => None
228 NOTICE:  TD[event] => INSERT
229 NOTICE:  TD[level] => ROW
230 NOTICE:  TD[name] => show_trigger_data_trig_after
231 NOTICE:  TD[new] => {'i': 1, 'j': 2}
232 NOTICE:  TD[old] => None
233 NOTICE:  TD[relid] => bogus:12345
234 NOTICE:  TD[table_name] => trigger_test_generated
235 NOTICE:  TD[table_schema] => public
236 NOTICE:  TD[when] => AFTER
237 update trigger_test_generated set i = 11 where i = 1;
238 NOTICE:  TD[args] => None
239 NOTICE:  TD[event] => UPDATE
240 NOTICE:  TD[level] => ROW
241 NOTICE:  TD[name] => show_trigger_data_trig_before
242 NOTICE:  TD[new] => {'i': 11}
243 NOTICE:  TD[old] => {'i': 1, 'j': 2}
244 NOTICE:  TD[relid] => bogus:12345
245 NOTICE:  TD[table_name] => trigger_test_generated
246 NOTICE:  TD[table_schema] => public
247 NOTICE:  TD[when] => BEFORE
248 NOTICE:  TD[args] => None
249 NOTICE:  TD[event] => UPDATE
250 NOTICE:  TD[level] => ROW
251 NOTICE:  TD[name] => show_trigger_data_trig_after
252 NOTICE:  TD[new] => {'i': 11, 'j': 22}
253 NOTICE:  TD[old] => {'i': 1, 'j': 2}
254 NOTICE:  TD[relid] => bogus:12345
255 NOTICE:  TD[table_name] => trigger_test_generated
256 NOTICE:  TD[table_schema] => public
257 NOTICE:  TD[when] => AFTER
258 delete from trigger_test_generated;
259 NOTICE:  TD[args] => None
260 NOTICE:  TD[event] => DELETE
261 NOTICE:  TD[level] => ROW
262 NOTICE:  TD[name] => show_trigger_data_trig_before
263 NOTICE:  TD[new] => None
264 NOTICE:  TD[old] => {'i': 11, 'j': 22}
265 NOTICE:  TD[relid] => bogus:12345
266 NOTICE:  TD[table_name] => trigger_test_generated
267 NOTICE:  TD[table_schema] => public
268 NOTICE:  TD[when] => BEFORE
269 NOTICE:  TD[args] => None
270 NOTICE:  TD[event] => DELETE
271 NOTICE:  TD[level] => ROW
272 NOTICE:  TD[name] => show_trigger_data_trig_after
273 NOTICE:  TD[new] => None
274 NOTICE:  TD[old] => {'i': 11, 'j': 22}
275 NOTICE:  TD[relid] => bogus:12345
276 NOTICE:  TD[table_name] => trigger_test_generated
277 NOTICE:  TD[table_schema] => public
278 NOTICE:  TD[when] => AFTER
279 DROP TRIGGER show_trigger_data_trig_before ON trigger_test_generated;
280 DROP TRIGGER show_trigger_data_trig_after ON trigger_test_generated;
281 insert into trigger_test values(1,'insert');
282 CREATE VIEW trigger_test_view AS SELECT * FROM trigger_test;
283 CREATE TRIGGER show_trigger_data_trig
284 INSTEAD OF INSERT OR UPDATE OR DELETE ON trigger_test_view
285 FOR EACH ROW EXECUTE PROCEDURE trigger_data(24,'skidoo view');
286 insert into trigger_test_view values(2,'insert');
287 NOTICE:  TD[args] => ['24', 'skidoo view']
288 NOTICE:  TD[event] => INSERT
289 NOTICE:  TD[level] => ROW
290 NOTICE:  TD[name] => show_trigger_data_trig
291 NOTICE:  TD[new] => {'i': 2, 'v': 'insert'}
292 NOTICE:  TD[old] => None
293 NOTICE:  TD[relid] => bogus:12345
294 NOTICE:  TD[table_name] => trigger_test_view
295 NOTICE:  TD[table_schema] => public
296 NOTICE:  TD[when] => INSTEAD OF
297 update trigger_test_view set v = 'update' where i = 1;
298 NOTICE:  TD[args] => ['24', 'skidoo view']
299 NOTICE:  TD[event] => UPDATE
300 NOTICE:  TD[level] => ROW
301 NOTICE:  TD[name] => show_trigger_data_trig
302 NOTICE:  TD[new] => {'i': 1, 'v': 'update'}
303 NOTICE:  TD[old] => {'i': 1, 'v': 'insert'}
304 NOTICE:  TD[relid] => bogus:12345
305 NOTICE:  TD[table_name] => trigger_test_view
306 NOTICE:  TD[table_schema] => public
307 NOTICE:  TD[when] => INSTEAD OF
308 delete from trigger_test_view;
309 NOTICE:  TD[args] => ['24', 'skidoo view']
310 NOTICE:  TD[event] => DELETE
311 NOTICE:  TD[level] => ROW
312 NOTICE:  TD[name] => show_trigger_data_trig
313 NOTICE:  TD[new] => None
314 NOTICE:  TD[old] => {'i': 1, 'v': 'insert'}
315 NOTICE:  TD[relid] => bogus:12345
316 NOTICE:  TD[table_name] => trigger_test_view
317 NOTICE:  TD[table_schema] => public
318 NOTICE:  TD[when] => INSTEAD OF
319 DROP FUNCTION trigger_data() CASCADE;
320 NOTICE:  drop cascades to trigger show_trigger_data_trig on view trigger_test_view
321 DROP VIEW trigger_test_view;
322 delete from trigger_test;
324 -- trigger error handling
326 INSERT INTO trigger_test VALUES (0, 'zero');
327 -- returning non-string from trigger function
328 CREATE FUNCTION stupid1() RETURNS trigger
329 AS $$
330     return 37
331 $$ LANGUAGE plpython3u;
332 CREATE TRIGGER stupid_trigger1
333 BEFORE INSERT ON trigger_test
334 FOR EACH ROW EXECUTE PROCEDURE stupid1();
335 INSERT INTO trigger_test VALUES (1, 'one');
336 ERROR:  unexpected return value from trigger procedure
337 DETAIL:  Expected None or a string.
338 CONTEXT:  PL/Python function "stupid1"
339 DROP TRIGGER stupid_trigger1 ON trigger_test;
340 -- returning MODIFY from DELETE trigger
341 CREATE FUNCTION stupid2() RETURNS trigger
342 AS $$
343     return "MODIFY"
344 $$ LANGUAGE plpython3u;
345 CREATE TRIGGER stupid_trigger2
346 BEFORE DELETE ON trigger_test
347 FOR EACH ROW EXECUTE PROCEDURE stupid2();
348 DELETE FROM trigger_test WHERE i = 0;
349 WARNING:  PL/Python trigger function returned "MODIFY" in a DELETE trigger -- ignored
350 DROP TRIGGER stupid_trigger2 ON trigger_test;
351 INSERT INTO trigger_test VALUES (0, 'zero');
352 -- returning unrecognized string from trigger function
353 CREATE FUNCTION stupid3() RETURNS trigger
354 AS $$
355     return "foo"
356 $$ LANGUAGE plpython3u;
357 CREATE TRIGGER stupid_trigger3
358 BEFORE UPDATE ON trigger_test
359 FOR EACH ROW EXECUTE PROCEDURE stupid3();
360 UPDATE trigger_test SET v = 'null' WHERE i = 0;
361 ERROR:  unexpected return value from trigger procedure
362 DETAIL:  Expected None, "OK", "SKIP", or "MODIFY".
363 CONTEXT:  PL/Python function "stupid3"
364 DROP TRIGGER stupid_trigger3 ON trigger_test;
365 -- Unicode variant
366 CREATE FUNCTION stupid3u() RETURNS trigger
367 AS $$
368     return "foo"
369 $$ LANGUAGE plpython3u;
370 CREATE TRIGGER stupid_trigger3
371 BEFORE UPDATE ON trigger_test
372 FOR EACH ROW EXECUTE PROCEDURE stupid3u();
373 UPDATE trigger_test SET v = 'null' WHERE i = 0;
374 ERROR:  unexpected return value from trigger procedure
375 DETAIL:  Expected None, "OK", "SKIP", or "MODIFY".
376 CONTEXT:  PL/Python function "stupid3u"
377 DROP TRIGGER stupid_trigger3 ON trigger_test;
378 -- deleting the TD dictionary
379 CREATE FUNCTION stupid4() RETURNS trigger
380 AS $$
381     del TD["new"]
382     return "MODIFY";
383 $$ LANGUAGE plpython3u;
384 CREATE TRIGGER stupid_trigger4
385 BEFORE UPDATE ON trigger_test
386 FOR EACH ROW EXECUTE PROCEDURE stupid4();
387 UPDATE trigger_test SET v = 'null' WHERE i = 0;
388 ERROR:  TD["new"] deleted, cannot modify row
389 CONTEXT:  while modifying trigger row
390 PL/Python function "stupid4"
391 DROP TRIGGER stupid_trigger4 ON trigger_test;
392 -- TD not a dictionary
393 CREATE FUNCTION stupid5() RETURNS trigger
394 AS $$
395     TD["new"] = ['foo', 'bar']
396     return "MODIFY";
397 $$ LANGUAGE plpython3u;
398 CREATE TRIGGER stupid_trigger5
399 BEFORE UPDATE ON trigger_test
400 FOR EACH ROW EXECUTE PROCEDURE stupid5();
401 UPDATE trigger_test SET v = 'null' WHERE i = 0;
402 ERROR:  TD["new"] is not a dictionary
403 CONTEXT:  while modifying trigger row
404 PL/Python function "stupid5"
405 DROP TRIGGER stupid_trigger5 ON trigger_test;
406 -- TD not having string keys
407 CREATE FUNCTION stupid6() RETURNS trigger
408 AS $$
409     TD["new"] = {1: 'foo', 2: 'bar'}
410     return "MODIFY";
411 $$ LANGUAGE plpython3u;
412 CREATE TRIGGER stupid_trigger6
413 BEFORE UPDATE ON trigger_test
414 FOR EACH ROW EXECUTE PROCEDURE stupid6();
415 UPDATE trigger_test SET v = 'null' WHERE i = 0;
416 ERROR:  TD["new"] dictionary key at ordinal position 0 is not a string
417 CONTEXT:  while modifying trigger row
418 PL/Python function "stupid6"
419 DROP TRIGGER stupid_trigger6 ON trigger_test;
420 -- TD keys not corresponding to row columns
421 CREATE FUNCTION stupid7() RETURNS trigger
422 AS $$
423     TD["new"] = {'v': 'foo', 'a': 'bar'}
424     return "MODIFY";
425 $$ LANGUAGE plpython3u;
426 CREATE TRIGGER stupid_trigger7
427 BEFORE UPDATE ON trigger_test
428 FOR EACH ROW EXECUTE PROCEDURE stupid7();
429 UPDATE trigger_test SET v = 'null' WHERE i = 0;
430 ERROR:  key "a" found in TD["new"] does not exist as a column in the triggering row
431 CONTEXT:  while modifying trigger row
432 PL/Python function "stupid7"
433 DROP TRIGGER stupid_trigger7 ON trigger_test;
434 -- Unicode variant
435 CREATE FUNCTION stupid7u() RETURNS trigger
436 AS $$
437     TD["new"] = {'v': 'foo', 'a': 'bar'}
438     return "MODIFY"
439 $$ LANGUAGE plpython3u;
440 CREATE TRIGGER stupid_trigger7
441 BEFORE UPDATE ON trigger_test
442 FOR EACH ROW EXECUTE PROCEDURE stupid7u();
443 UPDATE trigger_test SET v = 'null' WHERE i = 0;
444 ERROR:  key "a" found in TD["new"] does not exist as a column in the triggering row
445 CONTEXT:  while modifying trigger row
446 PL/Python function "stupid7u"
447 DROP TRIGGER stupid_trigger7 ON trigger_test;
448 -- calling a trigger function directly
449 SELECT stupid7();
450 ERROR:  trigger functions can only be called as triggers
452 -- Null values
454 SELECT * FROM trigger_test;
455  i |  v   
456 ---+------
457  0 | zero
458 (1 row)
460 CREATE FUNCTION test_null() RETURNS trigger
461 AS $$
462     TD["new"]['v'] = None
463     return "MODIFY"
464 $$ LANGUAGE plpython3u;
465 CREATE TRIGGER test_null_trigger
466 BEFORE UPDATE ON trigger_test
467 FOR EACH ROW EXECUTE PROCEDURE test_null();
468 UPDATE trigger_test SET v = 'null' WHERE i = 0;
469 DROP TRIGGER test_null_trigger ON trigger_test;
470 SELECT * FROM trigger_test;
471  i | v 
472 ---+---
473  0 | 
474 (1 row)
477 -- Test that triggers honor typmod when assigning to tuple fields,
478 -- as per an early 9.0 bug report
480 SET DateStyle = 'ISO';
481 CREATE FUNCTION set_modif_time() RETURNS trigger AS $$
482     TD['new']['modif_time'] = '2010-10-13 21:57:28.930486'
483     return 'MODIFY'
484 $$ LANGUAGE plpython3u;
485 CREATE TABLE pb (a TEXT, modif_time TIMESTAMP(0) WITHOUT TIME ZONE);
486 CREATE TRIGGER set_modif_time BEFORE UPDATE ON pb
487   FOR EACH ROW EXECUTE PROCEDURE set_modif_time();
488 INSERT INTO pb VALUES ('a', '2010-10-09 21:57:33.930486');
489 SELECT * FROM pb;
490  a |     modif_time      
491 ---+---------------------
492  a | 2010-10-09 21:57:34
493 (1 row)
495 UPDATE pb SET a = 'b';
496 SELECT * FROM pb;
497  a |     modif_time      
498 ---+---------------------
499  b | 2010-10-13 21:57:29
500 (1 row)
502 -- triggers for tables with composite types
503 CREATE TABLE comp1 (i integer, j boolean);
504 CREATE TYPE comp2 AS (k integer, l boolean);
505 CREATE TABLE composite_trigger_test (f1 comp1, f2 comp2);
506 CREATE FUNCTION composite_trigger_f() RETURNS trigger AS $$
507     TD['new']['f1'] = (3, False)
508     TD['new']['f2'] = {'k': 7, 'l': 'yes', 'ignored': 10}
509     return 'MODIFY'
510 $$ LANGUAGE plpython3u;
511 CREATE TRIGGER composite_trigger BEFORE INSERT ON composite_trigger_test
512   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_f();
513 INSERT INTO composite_trigger_test VALUES (NULL, NULL);
514 SELECT * FROM composite_trigger_test;
515   f1   |  f2   
516 -------+-------
517  (3,f) | (7,t)
518 (1 row)
520 -- triggers with composite type columns (bug #6559)
521 CREATE TABLE composite_trigger_noop_test (f1 comp1, f2 comp2);
522 CREATE FUNCTION composite_trigger_noop_f() RETURNS trigger AS $$
523     return 'MODIFY'
524 $$ LANGUAGE plpython3u;
525 CREATE TRIGGER composite_trigger_noop BEFORE INSERT ON composite_trigger_noop_test
526   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_noop_f();
527 INSERT INTO composite_trigger_noop_test VALUES (NULL, NULL);
528 INSERT INTO composite_trigger_noop_test VALUES (ROW(1, 'f'), NULL);
529 INSERT INTO composite_trigger_noop_test VALUES (ROW(NULL, 't'), ROW(1, 'f'));
530 SELECT * FROM composite_trigger_noop_test;
531   f1   |  f2   
532 -------+-------
533        | 
534  (1,f) | 
535  (,t)  | (1,f)
536 (3 rows)
538 -- nested composite types
539 CREATE TYPE comp3 AS (c1 comp1, c2 comp2, m integer);
540 CREATE TABLE composite_trigger_nested_test(c comp3);
541 CREATE FUNCTION composite_trigger_nested_f() RETURNS trigger AS $$
542     return 'MODIFY'
543 $$ LANGUAGE plpython3u;
544 CREATE TRIGGER composite_trigger_nested BEFORE INSERT ON composite_trigger_nested_test
545   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_nested_f();
546 INSERT INTO composite_trigger_nested_test VALUES (NULL);
547 INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(1, 'f'), NULL, 3));
548 INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(NULL, 't'), ROW(1, 'f'), NULL));
549 SELECT * FROM composite_trigger_nested_test;
550          c         
551 -------------------
553  ("(1,f)",,3)
554  ("(,t)","(1,f)",)
555 (3 rows)
557 -- check that using a function as a trigger over two tables works correctly
558 CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpython3u AS $$
559     TD["new"]["data"] = '1234'
560     return 'MODIFY'
562 CREATE TABLE a(data text);
563 CREATE TABLE b(data int); -- different type conversion
564 CREATE TRIGGER a_t BEFORE INSERT ON a FOR EACH ROW EXECUTE PROCEDURE trig1234();
565 CREATE TRIGGER b_t BEFORE INSERT ON b FOR EACH ROW EXECUTE PROCEDURE trig1234();
566 INSERT INTO a DEFAULT VALUES;
567 SELECT * FROM a;
568  data 
569 ------
570  1234
571 (1 row)
573 DROP TABLE a;
574 INSERT INTO b DEFAULT VALUES;
575 SELECT * FROM b;
576  data 
577 ------
578  1234
579 (1 row)
581 -- check that SQL run in trigger code can see transition tables
582 CREATE TABLE transition_table_test (id int, name text);
583 INSERT INTO transition_table_test VALUES (1, 'a');
584 CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpython3u AS
586     rv = plpy.execute("SELECT * FROM old_table")
587     assert(rv.nrows() == 1)
588     plpy.info("old: " + str(rv[0]["id"]) + " -> " + rv[0]["name"])
589     rv = plpy.execute("SELECT * FROM new_table")
590     assert(rv.nrows() == 1)
591     plpy.info("new: " + str(rv[0]["id"]) + " -> " + rv[0]["name"])
592     return None
594 CREATE TRIGGER a_t AFTER UPDATE ON transition_table_test
595   REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
596   FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_test_f();
597 UPDATE transition_table_test SET name = 'b';
598 INFO:  old: 1 -> a
599 INFO:  new: 1 -> b
600 DROP TABLE transition_table_test;
601 DROP FUNCTION transition_table_test_f();
602 -- dealing with generated columns
603 CREATE FUNCTION generated_test_func1() RETURNS trigger
604 LANGUAGE plpython3u
605 AS $$
606 TD['new']['j'] = 5  # not allowed
607 return 'MODIFY'
609 CREATE TRIGGER generated_test_trigger1 BEFORE INSERT ON trigger_test_generated
610 FOR EACH ROW EXECUTE PROCEDURE generated_test_func1();
611 TRUNCATE trigger_test_generated;
612 INSERT INTO trigger_test_generated (i) VALUES (1);
613 ERROR:  cannot set generated column "j"
614 CONTEXT:  while modifying trigger row
615 PL/Python function "generated_test_func1"
616 SELECT * FROM trigger_test_generated;
617  i | j 
618 ---+---
619 (0 rows)
621 -- recursive call of a trigger mustn't corrupt TD (bug #18456)
622 CREATE TABLE recursive_trigger_test (a int, b int);
623 CREATE FUNCTION recursive_trigger_func() RETURNS trigger
624 LANGUAGE plpython3u
625 AS $$
626 if TD["event"] == "UPDATE":
627     plpy.execute("INSERT INTO recursive_trigger_test VALUES (1, 2)")
628     plpy.notice("TD[event] => " + str(TD["event"]) + ", expecting UPDATE");
629 else:
630     plpy.notice("TD[event] => " + str(TD["event"]) + ", expecting INSERT");
631 return None
633 CREATE TRIGGER recursive_trigger_trig
634   AFTER INSERT OR UPDATE ON recursive_trigger_test
635   FOR EACH ROW EXECUTE PROCEDURE recursive_trigger_func();
636 INSERT INTO recursive_trigger_test VALUES (0, 0);
637 NOTICE:  TD[event] => INSERT, expecting INSERT
638 UPDATE recursive_trigger_test SET a = 11 WHERE b = 0;
639 NOTICE:  TD[event] => INSERT, expecting INSERT
640 NOTICE:  TD[event] => UPDATE, expecting UPDATE
641 SELECT * FROM recursive_trigger_test;
642  a  | b 
643 ----+---
644  11 | 0
645   1 | 2
646 (2 rows)