621 lines
21 KiB
Plaintext
621 lines
21 KiB
Plaintext
-- 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 plpython3u;
|
|
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 plpython3u;
|
|
CREATE FUNCTION users_delete() RETURNS trigger
|
|
AS
|
|
'if TD["old"]["fname"] == TD["args"][0]:
|
|
return "SKIP"
|
|
return None'
|
|
LANGUAGE plpython3u;
|
|
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 plpython3u 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');
|
|
insert into trigger_test values(1,'insert');
|
|
NOTICE: TD[args] => ['23', 'skidoo']
|
|
NOTICE: TD[event] => INSERT
|
|
NOTICE: TD[level] => STATEMENT
|
|
NOTICE: TD[name] => show_trigger_data_trig_stmt
|
|
NOTICE: TD[new] => None
|
|
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_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] => STATEMENT
|
|
NOTICE: TD[name] => show_trigger_data_trig_stmt
|
|
NOTICE: TD[new] => None
|
|
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] => 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] => STATEMENT
|
|
NOTICE: TD[name] => show_trigger_data_trig_stmt
|
|
NOTICE: TD[new] => None
|
|
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] => 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;
|
|
NOTICE: TD[args] => ['23', 'skidoo']
|
|
NOTICE: TD[event] => TRUNCATE
|
|
NOTICE: TD[level] => STATEMENT
|
|
NOTICE: TD[name] => show_trigger_data_trig_stmt
|
|
NOTICE: TD[new] => None
|
|
NOTICE: TD[old] => None
|
|
NOTICE: TD[relid] => bogus:12345
|
|
NOTICE: TD[table_name] => trigger_test
|
|
NOTICE: TD[table_schema] => public
|
|
NOTICE: TD[when] => BEFORE
|
|
DROP TRIGGER show_trigger_data_trig_stmt on trigger_test;
|
|
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
|
|
update trigger_test_generated set i = 11 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': 11}
|
|
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': 11, 'j': 22}
|
|
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': 11, 'j': 22}
|
|
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': 11, 'j': 22}
|
|
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;
|
|
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
|
|
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 plpython3u;
|
|
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 plpython3u;
|
|
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 plpython3u;
|
|
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 "foo"
|
|
$$ LANGUAGE plpython3u;
|
|
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 plpython3u;
|
|
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 plpython3u;
|
|
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 plpython3u;
|
|
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 plpython3u;
|
|
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"] = {'v': 'foo', 'a': 'bar'}
|
|
return "MODIFY"
|
|
$$ LANGUAGE plpython3u;
|
|
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 plpython3u;
|
|
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 plpython3u;
|
|
CREATE TABLE pb (a TEXT, modif_time TIMESTAMP(0) WITHOUT TIME ZONE);
|
|
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 plpython3u;
|
|
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 plpython3u;
|
|
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 plpython3u;
|
|
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 plpython3u 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 plpython3u 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
|
|
$$;
|
|
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();
|
|
UPDATE transition_table_test SET name = 'b';
|
|
INFO: old: 1 -> a
|
|
INFO: new: 1 -> b
|
|
DROP TABLE transition_table_test;
|
|
DROP FUNCTION transition_table_test_f();
|
|
-- dealing with generated columns
|
|
CREATE FUNCTION generated_test_func1() RETURNS trigger
|
|
LANGUAGE plpython3u
|
|
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)
|
|
|