Files
postgresql/src/pl/plpython/expected/plpython_trigger.out
Peter Eisentraut 4ab6ebf3f4 Add Unicode support in PL/Python
PL/Python now accepts Unicode objects where it previously only accepted string
objects (for example, as return value).  Unicode objects are converted to the
PostgreSQL server encoding as necessary.

This change is also necessary for future Python 3 support, which treats all
strings as Unicode objects.

Since this removes the error conditions that the plpython_unicode test file
tested for, the alternative result files are no longer necessary.
2009-09-12 22:13:12 +00:00

456 lines
16 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 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 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]
plpy.notice("TD[" + key + "] => " + str(val))
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']",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[event] => INSERT',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[level] => STATEMENT',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[name] => show_trigger_data_trig_stmt',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[new] => None',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[old] => None',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[relid] => bogus:12345',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_name] => trigger_test',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_schema] => public',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[when] => BEFORE',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[args] => ['23', 'skidoo']",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[event] => INSERT',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[level] => ROW',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[name] => show_trigger_data_trig_before',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[new] => {'i': 1, 'v': 'insert'}",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[old] => None',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[relid] => bogus:12345',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_name] => trigger_test',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_schema] => public',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[when] => BEFORE',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[args] => ['23', 'skidoo']",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[event] => INSERT',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[level] => ROW',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[name] => show_trigger_data_trig_after',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[new] => {'i': 1, 'v': 'insert'}",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[old] => None',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[relid] => bogus:12345',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_name] => trigger_test',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_schema] => public',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[when] => AFTER',)
CONTEXT: PL/Python function "trigger_data"
update trigger_test set v = 'update' where i = 1;
NOTICE: ("TD[args] => ['23', 'skidoo']",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[event] => UPDATE',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[level] => STATEMENT',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[name] => show_trigger_data_trig_stmt',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[new] => None',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[old] => None',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[relid] => bogus:12345',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_name] => trigger_test',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_schema] => public',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[when] => BEFORE',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[args] => ['23', 'skidoo']",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[event] => UPDATE',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[level] => ROW',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[name] => show_trigger_data_trig_before',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[new] => {'i': 1, 'v': 'update'}",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[old] => {'i': 1, 'v': 'insert'}",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[relid] => bogus:12345',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_name] => trigger_test',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_schema] => public',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[when] => BEFORE',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[args] => ['23', 'skidoo']",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[event] => UPDATE',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[level] => ROW',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[name] => show_trigger_data_trig_after',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[new] => {'i': 1, 'v': 'update'}",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[old] => {'i': 1, 'v': 'insert'}",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[relid] => bogus:12345',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_name] => trigger_test',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_schema] => public',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[when] => AFTER',)
CONTEXT: PL/Python function "trigger_data"
delete from trigger_test;
NOTICE: ("TD[args] => ['23', 'skidoo']",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[event] => DELETE',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[level] => STATEMENT',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[name] => show_trigger_data_trig_stmt',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[new] => None',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[old] => None',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[relid] => bogus:12345',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_name] => trigger_test',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_schema] => public',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[when] => BEFORE',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[args] => ['23', 'skidoo']",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[event] => DELETE',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[level] => ROW',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[name] => show_trigger_data_trig_before',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[new] => None',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[old] => {'i': 1, 'v': 'update'}",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[relid] => bogus:12345',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_name] => trigger_test',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_schema] => public',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[when] => BEFORE',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[args] => ['23', 'skidoo']",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[event] => DELETE',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[level] => ROW',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[name] => show_trigger_data_trig_after',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[new] => None',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[old] => {'i': 1, 'v': 'update'}",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[relid] => bogus:12345',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_name] => trigger_test',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_schema] => public',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[when] => AFTER',)
CONTEXT: PL/Python function "trigger_data"
truncate table trigger_test;
NOTICE: ("TD[args] => ['23', 'skidoo']",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[event] => TRUNCATE',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[level] => STATEMENT',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[name] => show_trigger_data_trig_stmt',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[new] => None',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[old] => None',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[relid] => bogus:12345',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_name] => trigger_test',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_schema] => public',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[when] => BEFORE',)
CONTEXT: PL/Python function "trigger_data"
DROP FUNCTION trigger_data() CASCADE;
NOTICE: drop cascades to 3 other objects
DETAIL: drop cascades to trigger show_trigger_data_trig_before on table trigger_test
drop cascades to trigger show_trigger_data_trig_after on table trigger_test
drop cascades to trigger show_trigger_data_trig_stmt on table 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
CONTEXT: PL/Python function "stupid2"
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"] = {'a': 'foo', 'b': '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'a': 'foo', u'b': '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)