| -- these triggers are dedicated to HPHC of RI who |
| -- decided that my kid's name was william not willem, and |
| -- vigorously resisted all efforts at correction. they have |
| -- since gone bankrupt... |
| CREATE FUNCTION users_insert() returns trigger |
| AS |
| 'if TD["new"]["fname"] == None or TD["new"]["lname"] == None: |
| return "SKIP" |
| if TD["new"]["username"] == None: |
| TD["new"]["username"] = TD["new"]["fname"][:1] + "_" + TD["new"]["lname"] |
| rv = "MODIFY" |
| else: |
| rv = None |
| if TD["new"]["fname"] == "william": |
| TD["new"]["fname"] = TD["args"][0] |
| rv = "MODIFY" |
| return rv' |
| LANGUAGE plpythonu; |
| CREATE FUNCTION users_update() returns trigger |
| AS |
| 'if TD["event"] == "UPDATE": |
| if TD["old"]["fname"] != TD["new"]["fname"] and TD["old"]["fname"] == TD["args"][0]: |
| return "SKIP" |
| return None' |
| LANGUAGE plpythonu; |
| CREATE FUNCTION users_delete() RETURNS trigger |
| AS |
| 'if TD["old"]["fname"] == TD["args"][0]: |
| return "SKIP" |
| return None' |
| LANGUAGE plpythonu; |
| CREATE TRIGGER users_insert_trig BEFORE INSERT ON users FOR EACH ROW |
| EXECUTE PROCEDURE users_insert ('willem'); |
| CREATE TRIGGER users_update_trig BEFORE UPDATE ON users FOR EACH ROW |
| EXECUTE PROCEDURE users_update ('willem'); |
| CREATE TRIGGER users_delete_trig BEFORE DELETE ON users FOR EACH ROW |
| EXECUTE PROCEDURE users_delete ('willem'); |
| -- quick peek at the table |
| -- |
| SELECT * FROM users; |
| fname | lname | username | userid |
| --------+-------+----------+-------- |
| jane | doe | j_doe | 1 |
| john | doe | johnd | 2 |
| willem | doe | w_doe | 3 |
| rick | smith | slash | 4 |
| (4 rows) |
| |
| -- should fail |
| -- |
| UPDATE users SET fname = 'william' WHERE fname = 'willem'; |
| -- should modify william to willem and create username |
| -- |
| INSERT INTO users (fname, lname) VALUES ('william', 'smith'); |
| INSERT INTO users (fname, lname, username) VALUES ('charles', 'darwin', 'beagle'); |
| SELECT * FROM users; |
| fname | lname | username | userid |
| ---------+--------+----------+-------- |
| jane | doe | j_doe | 1 |
| john | doe | johnd | 2 |
| willem | doe | w_doe | 3 |
| rick | smith | slash | 4 |
| willem | smith | w_smith | 5 |
| charles | darwin | beagle | 6 |
| (6 rows) |
| |
| -- dump trigger data |
| CREATE TABLE trigger_test |
| (i int, v text ); |
| CREATE TABLE trigger_test_generated ( |
| i int, |
| j int GENERATED ALWAYS AS (i * 2) STORED |
| ); |
| CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpythonu AS $$ |
| |
| if 'relid' in TD: |
| TD['relid'] = "bogus:12345" |
| |
| skeys = list(TD.keys()) |
| skeys.sort() |
| for key in skeys: |
| val = TD[key] |
| if not isinstance(val, dict): |
| plpy.notice("TD[" + key + "] => " + str(val)) |
| else: |
| # print dicts the hard way because otherwise the order is implementation-dependent |
| valkeys = list(val.keys()) |
| valkeys.sort() |
| plpy.notice("TD[" + key + "] => " + '{' + ', '.join([repr(k) + ': ' + repr(val[k]) for k in valkeys]) + '}') |
| |
| return None |
| |
| $$; |
| CREATE TRIGGER show_trigger_data_trig_before |
| BEFORE INSERT OR UPDATE OR DELETE ON trigger_test |
| FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo'); |
| CREATE TRIGGER show_trigger_data_trig_after |
| AFTER INSERT OR UPDATE OR DELETE ON trigger_test |
| FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo'); |
| CREATE TRIGGER show_trigger_data_trig_stmt |
| BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE ON trigger_test |
| FOR EACH STATEMENT EXECUTE PROCEDURE trigger_data(23,'skidoo'); |
| ERROR: Triggers for statements are not yet supported |
| insert into trigger_test values(1,'insert'); |
| NOTICE: TD[args] => ['23', 'skidoo'] |
| NOTICE: TD[event] => INSERT |
| NOTICE: TD[level] => ROW |
| NOTICE: TD[name] => show_trigger_data_trig_before |
| NOTICE: TD[new] => {'i': 1, 'v': 'insert'} |
| NOTICE: TD[old] => None |
| NOTICE: TD[relid] => bogus:12345 |
| NOTICE: TD[table_name] => trigger_test |
| NOTICE: TD[table_schema] => public |
| NOTICE: TD[when] => BEFORE |
| NOTICE: TD[args] => ['23', 'skidoo'] |
| NOTICE: TD[event] => INSERT |
| NOTICE: TD[level] => ROW |
| NOTICE: TD[name] => show_trigger_data_trig_after |
| NOTICE: TD[new] => {'i': 1, 'v': 'insert'} |
| NOTICE: TD[old] => None |
| NOTICE: TD[relid] => bogus:12345 |
| NOTICE: TD[table_name] => trigger_test |
| NOTICE: TD[table_schema] => public |
| NOTICE: TD[when] => AFTER |
| update trigger_test set v = 'update' where i = 1; |
| NOTICE: TD[args] => ['23', 'skidoo'] |
| NOTICE: TD[event] => UPDATE |
| NOTICE: TD[level] => ROW |
| NOTICE: TD[name] => show_trigger_data_trig_before |
| NOTICE: TD[new] => {'i': 1, 'v': 'update'} |
| NOTICE: TD[old] => {'i': 1, 'v': 'insert'} |
| NOTICE: TD[relid] => bogus:12345 |
| NOTICE: TD[table_name] => trigger_test |
| NOTICE: TD[table_schema] => public |
| NOTICE: TD[when] => BEFORE |
| NOTICE: TD[args] => ['23', 'skidoo'] |
| NOTICE: TD[event] => UPDATE |
| NOTICE: TD[level] => ROW |
| NOTICE: TD[name] => show_trigger_data_trig_after |
| NOTICE: TD[new] => {'i': 1, 'v': 'update'} |
| NOTICE: TD[old] => {'i': 1, 'v': 'insert'} |
| NOTICE: TD[relid] => bogus:12345 |
| NOTICE: TD[table_name] => trigger_test |
| NOTICE: TD[table_schema] => public |
| NOTICE: TD[when] => AFTER |
| delete from trigger_test; |
| NOTICE: TD[args] => ['23', 'skidoo'] |
| NOTICE: TD[event] => DELETE |
| NOTICE: TD[level] => ROW |
| NOTICE: TD[name] => show_trigger_data_trig_before |
| NOTICE: TD[new] => None |
| NOTICE: TD[old] => {'i': 1, 'v': 'update'} |
| NOTICE: TD[relid] => bogus:12345 |
| NOTICE: TD[table_name] => trigger_test |
| NOTICE: TD[table_schema] => public |
| NOTICE: TD[when] => BEFORE |
| NOTICE: TD[args] => ['23', 'skidoo'] |
| NOTICE: TD[event] => DELETE |
| NOTICE: TD[level] => ROW |
| NOTICE: TD[name] => show_trigger_data_trig_after |
| NOTICE: TD[new] => None |
| NOTICE: TD[old] => {'i': 1, 'v': 'update'} |
| NOTICE: TD[relid] => bogus:12345 |
| NOTICE: TD[table_name] => trigger_test |
| NOTICE: TD[table_schema] => public |
| NOTICE: TD[when] => AFTER |
| truncate table trigger_test; |
| DROP TRIGGER show_trigger_data_trig_stmt on trigger_test; |
| ERROR: trigger "show_trigger_data_trig_stmt" for table "trigger_test" does not exist |
| DROP TRIGGER show_trigger_data_trig_before on trigger_test; |
| DROP TRIGGER show_trigger_data_trig_after on trigger_test; |
| CREATE TRIGGER show_trigger_data_trig_before |
| BEFORE INSERT OR UPDATE OR DELETE ON trigger_test_generated |
| FOR EACH ROW EXECUTE PROCEDURE trigger_data(); |
| CREATE TRIGGER show_trigger_data_trig_after |
| AFTER INSERT OR UPDATE OR DELETE ON trigger_test_generated |
| FOR EACH ROW EXECUTE PROCEDURE trigger_data(); |
| insert into trigger_test_generated (i) values (1); |
| NOTICE: TD[args] => None |
| NOTICE: TD[event] => INSERT |
| NOTICE: TD[level] => ROW |
| NOTICE: TD[name] => show_trigger_data_trig_before |
| NOTICE: TD[new] => {'i': 1} |
| NOTICE: TD[old] => None |
| NOTICE: TD[relid] => bogus:12345 |
| NOTICE: TD[table_name] => trigger_test_generated |
| NOTICE: TD[table_schema] => public |
| NOTICE: TD[when] => BEFORE |
| NOTICE: TD[args] => None |
| NOTICE: TD[event] => INSERT |
| NOTICE: TD[level] => ROW |
| NOTICE: TD[name] => show_trigger_data_trig_after |
| NOTICE: TD[new] => {'i': 1, 'j': 2} |
| NOTICE: TD[old] => None |
| NOTICE: TD[relid] => bogus:12345 |
| NOTICE: TD[table_name] => trigger_test_generated |
| NOTICE: TD[table_schema] => public |
| NOTICE: TD[when] => AFTER |
| -- GPDB: Fails, updating the distribution key with triggers is not allowed. |
| update trigger_test_generated set i = 11 where i = 1; |
| ERROR: UPDATE on distributed key column not allowed on relation with update triggers |
| -- try this instead: |
| update trigger_test_generated set j = default where i = 1; |
| NOTICE: TD[args] => None |
| NOTICE: TD[event] => UPDATE |
| NOTICE: TD[level] => ROW |
| NOTICE: TD[name] => show_trigger_data_trig_before |
| NOTICE: TD[new] => {'i': 1} |
| NOTICE: TD[old] => {'i': 1, 'j': 2} |
| NOTICE: TD[relid] => bogus:12345 |
| NOTICE: TD[table_name] => trigger_test_generated |
| NOTICE: TD[table_schema] => public |
| NOTICE: TD[when] => BEFORE |
| NOTICE: TD[args] => None |
| NOTICE: TD[event] => UPDATE |
| NOTICE: TD[level] => ROW |
| NOTICE: TD[name] => show_trigger_data_trig_after |
| NOTICE: TD[new] => {'i': 1, 'j': 2} |
| NOTICE: TD[old] => {'i': 1, 'j': 2} |
| NOTICE: TD[relid] => bogus:12345 |
| NOTICE: TD[table_name] => trigger_test_generated |
| NOTICE: TD[table_schema] => public |
| NOTICE: TD[when] => AFTER |
| delete from trigger_test_generated; |
| NOTICE: TD[args] => None |
| NOTICE: TD[event] => DELETE |
| NOTICE: TD[level] => ROW |
| NOTICE: TD[name] => show_trigger_data_trig_before |
| NOTICE: TD[new] => None |
| NOTICE: TD[old] => {'i': 1, 'j': 2} |
| NOTICE: TD[relid] => bogus:12345 |
| NOTICE: TD[table_name] => trigger_test_generated |
| NOTICE: TD[table_schema] => public |
| NOTICE: TD[when] => BEFORE |
| NOTICE: TD[args] => None |
| NOTICE: TD[event] => DELETE |
| NOTICE: TD[level] => ROW |
| NOTICE: TD[name] => show_trigger_data_trig_after |
| NOTICE: TD[new] => None |
| NOTICE: TD[old] => {'i': 1, 'j': 2} |
| NOTICE: TD[relid] => bogus:12345 |
| NOTICE: TD[table_name] => trigger_test_generated |
| NOTICE: TD[table_schema] => public |
| NOTICE: TD[when] => AFTER |
| DROP TRIGGER show_trigger_data_trig_before ON trigger_test_generated; |
| DROP TRIGGER show_trigger_data_trig_after ON trigger_test_generated; |
| insert into trigger_test values(1,'insert'); |
| CREATE VIEW trigger_test_view AS SELECT * FROM trigger_test; |
| --start_ignore |
| -- INSTEAD OF triggers are not yet supported in Cloudberry |
| CREATE TRIGGER show_trigger_data_trig |
| INSTEAD OF INSERT OR UPDATE OR DELETE ON trigger_test_view |
| FOR EACH ROW EXECUTE PROCEDURE trigger_data(24,'skidoo view'); |
| insert into trigger_test_view values(2,'insert'); |
| NOTICE: TD[args] => ['24', 'skidoo view'] |
| NOTICE: TD[event] => INSERT |
| NOTICE: TD[level] => ROW |
| NOTICE: TD[name] => show_trigger_data_trig |
| NOTICE: TD[new] => {'i': 2, 'v': 'insert'} |
| NOTICE: TD[old] => None |
| NOTICE: TD[relid] => bogus:12345 |
| NOTICE: TD[table_name] => trigger_test_view |
| NOTICE: TD[table_schema] => public |
| NOTICE: TD[when] => INSTEAD OF |
| update trigger_test_view set v = 'update' where i = 1; |
| NOTICE: TD[args] => ['24', 'skidoo view'] |
| NOTICE: TD[event] => UPDATE |
| NOTICE: TD[level] => ROW |
| NOTICE: TD[name] => show_trigger_data_trig |
| NOTICE: TD[new] => {'i': 1, 'v': 'update'} |
| NOTICE: TD[old] => {'i': 1, 'v': 'insert'} |
| NOTICE: TD[relid] => bogus:12345 |
| NOTICE: TD[table_name] => trigger_test_view |
| NOTICE: TD[table_schema] => public |
| NOTICE: TD[when] => INSTEAD OF |
| delete from trigger_test_view; |
| NOTICE: TD[args] => ['24', 'skidoo view'] |
| NOTICE: TD[event] => DELETE |
| NOTICE: TD[level] => ROW |
| NOTICE: TD[name] => show_trigger_data_trig |
| NOTICE: TD[new] => None |
| NOTICE: TD[old] => {'i': 1, 'v': 'insert'} |
| NOTICE: TD[relid] => bogus:12345 |
| NOTICE: TD[table_name] => trigger_test_view |
| NOTICE: TD[table_schema] => public |
| NOTICE: TD[when] => INSTEAD OF |
| DROP FUNCTION trigger_data() CASCADE; |
| NOTICE: drop cascades to trigger show_trigger_data_trig on view trigger_test_view |
| --end_ignore |
| DROP VIEW trigger_test_view; |
| delete from trigger_test; |
| -- |
| -- trigger error handling |
| -- |
| INSERT INTO trigger_test VALUES (0, 'zero'); |
| -- returning non-string from trigger function |
| CREATE FUNCTION stupid1() RETURNS trigger |
| AS $$ |
| return 37 |
| $$ LANGUAGE plpythonu; |
| CREATE TRIGGER stupid_trigger1 |
| BEFORE INSERT ON trigger_test |
| FOR EACH ROW EXECUTE PROCEDURE stupid1(); |
| INSERT INTO trigger_test VALUES (1, 'one'); |
| ERROR: unexpected return value from trigger procedure |
| DETAIL: Expected None or a string. |
| CONTEXT: PL/Python function "stupid1" |
| DROP TRIGGER stupid_trigger1 ON trigger_test; |
| -- returning MODIFY from DELETE trigger |
| CREATE FUNCTION stupid2() RETURNS trigger |
| AS $$ |
| return "MODIFY" |
| $$ LANGUAGE plpythonu; |
| CREATE TRIGGER stupid_trigger2 |
| BEFORE DELETE ON trigger_test |
| FOR EACH ROW EXECUTE PROCEDURE stupid2(); |
| DELETE FROM trigger_test WHERE i = 0; |
| WARNING: PL/Python trigger function returned "MODIFY" in a DELETE trigger -- ignored |
| DROP TRIGGER stupid_trigger2 ON trigger_test; |
| INSERT INTO trigger_test VALUES (0, 'zero'); |
| -- returning unrecognized string from trigger function |
| CREATE FUNCTION stupid3() RETURNS trigger |
| AS $$ |
| return "foo" |
| $$ LANGUAGE plpythonu; |
| CREATE TRIGGER stupid_trigger3 |
| BEFORE UPDATE ON trigger_test |
| FOR EACH ROW EXECUTE PROCEDURE stupid3(); |
| UPDATE trigger_test SET v = 'null' WHERE i = 0; |
| ERROR: unexpected return value from trigger procedure |
| DETAIL: Expected None, "OK", "SKIP", or "MODIFY". |
| CONTEXT: PL/Python function "stupid3" |
| DROP TRIGGER stupid_trigger3 ON trigger_test; |
| -- Unicode variant |
| CREATE FUNCTION stupid3u() RETURNS trigger |
| AS $$ |
| return u"foo" |
| $$ LANGUAGE plpythonu; |
| CREATE TRIGGER stupid_trigger3 |
| BEFORE UPDATE ON trigger_test |
| FOR EACH ROW EXECUTE PROCEDURE stupid3u(); |
| UPDATE trigger_test SET v = 'null' WHERE i = 0; |
| ERROR: unexpected return value from trigger procedure |
| DETAIL: Expected None, "OK", "SKIP", or "MODIFY". |
| CONTEXT: PL/Python function "stupid3u" |
| DROP TRIGGER stupid_trigger3 ON trigger_test; |
| -- deleting the TD dictionary |
| CREATE FUNCTION stupid4() RETURNS trigger |
| AS $$ |
| del TD["new"] |
| return "MODIFY"; |
| $$ LANGUAGE plpythonu; |
| CREATE TRIGGER stupid_trigger4 |
| BEFORE UPDATE ON trigger_test |
| FOR EACH ROW EXECUTE PROCEDURE stupid4(); |
| UPDATE trigger_test SET v = 'null' WHERE i = 0; |
| ERROR: TD["new"] deleted, cannot modify row |
| CONTEXT: while modifying trigger row |
| PL/Python function "stupid4" |
| DROP TRIGGER stupid_trigger4 ON trigger_test; |
| -- TD not a dictionary |
| CREATE FUNCTION stupid5() RETURNS trigger |
| AS $$ |
| TD["new"] = ['foo', 'bar'] |
| return "MODIFY"; |
| $$ LANGUAGE plpythonu; |
| CREATE TRIGGER stupid_trigger5 |
| BEFORE UPDATE ON trigger_test |
| FOR EACH ROW EXECUTE PROCEDURE stupid5(); |
| UPDATE trigger_test SET v = 'null' WHERE i = 0; |
| ERROR: TD["new"] is not a dictionary |
| CONTEXT: while modifying trigger row |
| PL/Python function "stupid5" |
| DROP TRIGGER stupid_trigger5 ON trigger_test; |
| -- TD not having string keys |
| CREATE FUNCTION stupid6() RETURNS trigger |
| AS $$ |
| TD["new"] = {1: 'foo', 2: 'bar'} |
| return "MODIFY"; |
| $$ LANGUAGE plpythonu; |
| CREATE TRIGGER stupid_trigger6 |
| BEFORE UPDATE ON trigger_test |
| FOR EACH ROW EXECUTE PROCEDURE stupid6(); |
| UPDATE trigger_test SET v = 'null' WHERE i = 0; |
| ERROR: TD["new"] dictionary key at ordinal position 0 is not a string |
| CONTEXT: while modifying trigger row |
| PL/Python function "stupid6" |
| DROP TRIGGER stupid_trigger6 ON trigger_test; |
| -- TD keys not corresponding to row columns |
| CREATE FUNCTION stupid7() RETURNS trigger |
| AS $$ |
| TD["new"] = {'v': 'foo', 'a': 'bar'} |
| return "MODIFY"; |
| $$ LANGUAGE plpythonu; |
| CREATE TRIGGER stupid_trigger7 |
| BEFORE UPDATE ON trigger_test |
| FOR EACH ROW EXECUTE PROCEDURE stupid7(); |
| UPDATE trigger_test SET v = 'null' WHERE i = 0; |
| ERROR: key "a" found in TD["new"] does not exist as a column in the triggering row |
| CONTEXT: while modifying trigger row |
| PL/Python function "stupid7" |
| DROP TRIGGER stupid_trigger7 ON trigger_test; |
| -- Unicode variant |
| CREATE FUNCTION stupid7u() RETURNS trigger |
| AS $$ |
| TD["new"] = {u'v': 'foo', u'a': 'bar'} |
| return "MODIFY" |
| $$ LANGUAGE plpythonu; |
| CREATE TRIGGER stupid_trigger7 |
| BEFORE UPDATE ON trigger_test |
| FOR EACH ROW EXECUTE PROCEDURE stupid7u(); |
| UPDATE trigger_test SET v = 'null' WHERE i = 0; |
| ERROR: key "a" found in TD["new"] does not exist as a column in the triggering row |
| CONTEXT: while modifying trigger row |
| PL/Python function "stupid7u" |
| DROP TRIGGER stupid_trigger7 ON trigger_test; |
| -- calling a trigger function directly |
| SELECT stupid7(); |
| ERROR: trigger functions can only be called as triggers |
| -- |
| -- Null values |
| -- |
| SELECT * FROM trigger_test; |
| i | v |
| ---+------ |
| 0 | zero |
| (1 row) |
| |
| CREATE FUNCTION test_null() RETURNS trigger |
| AS $$ |
| TD["new"]['v'] = None |
| return "MODIFY" |
| $$ LANGUAGE plpythonu; |
| CREATE TRIGGER test_null_trigger |
| BEFORE UPDATE ON trigger_test |
| FOR EACH ROW EXECUTE PROCEDURE test_null(); |
| UPDATE trigger_test SET v = 'null' WHERE i = 0; |
| DROP TRIGGER test_null_trigger ON trigger_test; |
| SELECT * FROM trigger_test; |
| i | v |
| ---+--- |
| 0 | |
| (1 row) |
| |
| -- |
| -- Test that triggers honor typmod when assigning to tuple fields, |
| -- as per an early 9.0 bug report |
| -- |
| SET DateStyle = 'ISO'; |
| CREATE FUNCTION set_modif_time() RETURNS trigger AS $$ |
| TD['new']['modif_time'] = '2010-10-13 21:57:28.930486' |
| return 'MODIFY' |
| $$ LANGUAGE plpythonu; |
| -- Add 'DISTRIBUTED RANDOMLY' to avoid "ERROR: Cannot parallelize an UPDATE statement that updates the distribution columns" |
| CREATE TABLE pb (a TEXT, modif_time TIMESTAMP(0) WITHOUT TIME ZONE) DISTRIBUTED RANDOMLY; |
| CREATE TRIGGER set_modif_time BEFORE UPDATE ON pb |
| FOR EACH ROW EXECUTE PROCEDURE set_modif_time(); |
| INSERT INTO pb VALUES ('a', '2010-10-09 21:57:33.930486'); |
| SELECT * FROM pb; |
| a | modif_time |
| ---+--------------------- |
| a | 2010-10-09 21:57:34 |
| (1 row) |
| |
| UPDATE pb SET a = 'b'; |
| SELECT * FROM pb; |
| a | modif_time |
| ---+--------------------- |
| b | 2010-10-13 21:57:29 |
| (1 row) |
| |
| -- triggers for tables with composite types |
| CREATE TABLE comp1 (i integer, j boolean); |
| CREATE TYPE comp2 AS (k integer, l boolean); |
| CREATE TABLE composite_trigger_test (f1 comp1, f2 comp2); |
| CREATE FUNCTION composite_trigger_f() RETURNS trigger AS $$ |
| TD['new']['f1'] = (3, False) |
| TD['new']['f2'] = {'k': 7, 'l': 'yes', 'ignored': 10} |
| return 'MODIFY' |
| $$ LANGUAGE plpythonu; |
| CREATE TRIGGER composite_trigger BEFORE INSERT ON composite_trigger_test |
| FOR EACH ROW EXECUTE PROCEDURE composite_trigger_f(); |
| INSERT INTO composite_trigger_test VALUES (NULL, NULL); |
| SELECT * FROM composite_trigger_test; |
| f1 | f2 |
| -------+------- |
| (3,f) | (7,t) |
| (1 row) |
| |
| -- triggers with composite type columns (bug #6559) |
| CREATE TABLE composite_trigger_noop_test (f1 comp1, f2 comp2); |
| CREATE FUNCTION composite_trigger_noop_f() RETURNS trigger AS $$ |
| return 'MODIFY' |
| $$ LANGUAGE plpythonu; |
| CREATE TRIGGER composite_trigger_noop BEFORE INSERT ON composite_trigger_noop_test |
| FOR EACH ROW EXECUTE PROCEDURE composite_trigger_noop_f(); |
| INSERT INTO composite_trigger_noop_test VALUES (NULL, NULL); |
| INSERT INTO composite_trigger_noop_test VALUES (ROW(1, 'f'), NULL); |
| INSERT INTO composite_trigger_noop_test VALUES (ROW(NULL, 't'), ROW(1, 'f')); |
| SELECT * FROM composite_trigger_noop_test; |
| f1 | f2 |
| -------+------- |
| | |
| (1,f) | |
| (,t) | (1,f) |
| (3 rows) |
| |
| -- nested composite types |
| CREATE TYPE comp3 AS (c1 comp1, c2 comp2, m integer); |
| CREATE TABLE composite_trigger_nested_test(c comp3); |
| CREATE FUNCTION composite_trigger_nested_f() RETURNS trigger AS $$ |
| return 'MODIFY' |
| $$ LANGUAGE plpythonu; |
| CREATE TRIGGER composite_trigger_nested BEFORE INSERT ON composite_trigger_nested_test |
| FOR EACH ROW EXECUTE PROCEDURE composite_trigger_nested_f(); |
| INSERT INTO composite_trigger_nested_test VALUES (NULL); |
| INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(1, 'f'), NULL, 3)); |
| INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(NULL, 't'), ROW(1, 'f'), NULL)); |
| SELECT * FROM composite_trigger_nested_test; |
| c |
| ------------------- |
| |
| ("(1,f)",,3) |
| ("(,t)","(1,f)",) |
| (3 rows) |
| |
| -- check that using a function as a trigger over two tables works correctly |
| CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpythonu AS $$ |
| TD["new"]["data"] = '1234' |
| return 'MODIFY' |
| $$; |
| CREATE TABLE a(data text); |
| CREATE TABLE b(data int); -- different type conversion |
| CREATE TRIGGER a_t BEFORE INSERT ON a FOR EACH ROW EXECUTE PROCEDURE trig1234(); |
| CREATE TRIGGER b_t BEFORE INSERT ON b FOR EACH ROW EXECUTE PROCEDURE trig1234(); |
| INSERT INTO a DEFAULT VALUES; |
| SELECT * FROM a; |
| data |
| ------ |
| 1234 |
| (1 row) |
| |
| DROP TABLE a; |
| INSERT INTO b DEFAULT VALUES; |
| SELECT * FROM b; |
| data |
| ------ |
| 1234 |
| (1 row) |
| |
| -- check that SQL run in trigger code can see transition tables |
| CREATE TABLE transition_table_test (id int, name text); |
| INSERT INTO transition_table_test VALUES (1, 'a'); |
| CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpythonu AS |
| $$ |
| rv = plpy.execute("SELECT * FROM old_table") |
| assert(rv.nrows() == 1) |
| plpy.info("old: " + str(rv[0]["id"]) + " -> " + rv[0]["name"]) |
| rv = plpy.execute("SELECT * FROM new_table") |
| assert(rv.nrows() == 1) |
| plpy.info("new: " + str(rv[0]["id"]) + " -> " + rv[0]["name"]) |
| return None |
| $$; |
| -- GPDB: this test doesn't work properly on GPDB, because statement triggers |
| -- are not fired. |
| CREATE TRIGGER a_t AFTER UPDATE ON transition_table_test |
| REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table |
| FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_test_f(); |
| ERROR: Triggers for statements are not yet supported |
| UPDATE transition_table_test SET name = 'b'; |
| DROP TABLE transition_table_test; |
| DROP FUNCTION transition_table_test_f(); |
| -- dealing with generated columns |
| CREATE FUNCTION generated_test_func1() RETURNS trigger |
| LANGUAGE plpythonu |
| AS $$ |
| TD['new']['j'] = 5 # not allowed |
| return 'MODIFY' |
| $$; |
| CREATE TRIGGER generated_test_trigger1 BEFORE INSERT ON trigger_test_generated |
| FOR EACH ROW EXECUTE PROCEDURE generated_test_func1(); |
| TRUNCATE trigger_test_generated; |
| INSERT INTO trigger_test_generated (i) VALUES (1); |
| ERROR: cannot set generated column "j" |
| CONTEXT: while modifying trigger row |
| PL/Python function "generated_test_func1" |
| SELECT * FROM trigger_test_generated; |
| i | j |
| ---+--- |
| (0 rows) |
| |
| -- recursive call of a trigger mustn't corrupt TD (bug #18456) |
| CREATE TABLE recursive_trigger_test (a int, b int); |
| CREATE FUNCTION recursive_trigger_func() RETURNS trigger |
| LANGUAGE plpythonu |
| AS $$ |
| if TD["event"] == "UPDATE": |
| plpy.execute("INSERT INTO recursive_trigger_test VALUES (1, 2)") |
| plpy.notice("TD[event] => " + str(TD["event"]) + ", expecting UPDATE"); |
| else: |
| plpy.notice("TD[event] => " + str(TD["event"]) + ", expecting INSERT"); |
| return None |
| $$; |
| CREATE TRIGGER recursive_trigger_trig |
| AFTER INSERT OR UPDATE ON recursive_trigger_test |
| FOR EACH ROW EXECUTE PROCEDURE recursive_trigger_func(); |
| INSERT INTO recursive_trigger_test VALUES (0, 0); |
| NOTICE: TD[event] => INSERT, expecting INSERT |
| UPDATE recursive_trigger_test SET a = 11 WHERE b = 0; |
| ERROR: UPDATE on distributed key column not allowed on relation with update triggers |
| SELECT * FROM recursive_trigger_test; |
| a | b |
| ---+--- |
| 0 | 0 |
| (1 row) |
| |