-- suppress CONTEXT so that function OIDs aren't in output \set VERBOSITY terse insert into T_pkey1 values (1, 'key1-1', 'test key'); insert into T_pkey1 values (1, 'key1-2', 'test key'); insert into T_pkey1 values (1, 'key1-3', 'test key'); insert into T_pkey1 values (2, 'key2-1', 'test key'); insert into T_pkey1 values (2, 'key2-2', 'test key'); insert into T_pkey1 values (2, 'key2-3', 'test key'); insert into T_pkey2 values (1, 'key1-1', 'test key'); insert into T_pkey2 values (1, 'key1-2', 'test key'); insert into T_pkey2 values (1, 'key1-3', 'test key'); insert into T_pkey2 values (2, 'key2-1', 'test key'); insert into T_pkey2 values (2, 'key2-2', 'test key'); insert into T_pkey2 values (2, 'key2-3', 'test key'); select * from T_pkey1; key1 | key2 | txt ------+----------------------+------------------------------------------ 1 | key1-1 | test key 1 | key1-2 | test key 1 | key1-3 | test key 2 | key2-1 | test key 2 | key2-2 | test key 2 | key2-3 | test key (6 rows) -- key2 in T_pkey2 should have upper case only select * from T_pkey2; key1 | key2 | txt ------+----------------------+------------------------------------------ 1 | KEY1-1 | test key 1 | KEY1-2 | test key 1 | KEY1-3 | test key 2 | KEY2-1 | test key 2 | KEY2-2 | test key 2 | KEY2-3 | test key (6 rows) insert into T_pkey1 values (1, 'KEY1-3', 'should work'); -- Due to the upper case translation in trigger this must fail insert into T_pkey2 values (1, 'KEY1-3', 'should fail'); ERROR: duplicate key '1', 'KEY1-3' for T_pkey2 insert into T_dta1 values ('trec 1', 1, 'key1-1'); insert into T_dta1 values ('trec 2', 1, 'key1-2'); insert into T_dta1 values ('trec 3', 1, 'key1-3'); -- Must fail due to unknown key in T_pkey1 insert into T_dta1 values ('trec 4', 1, 'key1-4'); ERROR: key for t_dta1 not in t_pkey1 insert into T_dta2 values ('trec 1', 1, 'KEY1-1'); insert into T_dta2 values ('trec 2', 1, 'KEY1-2'); insert into T_dta2 values ('trec 3', 1, 'KEY1-3'); -- Must fail due to unknown key in T_pkey2 insert into T_dta2 values ('trec 4', 1, 'KEY1-4'); ERROR: key for t_dta2 not in t_pkey2 select * from T_dta1; tkey | ref1 | ref2 ------------+------+---------------------- trec 1 | 1 | key1-1 trec 2 | 1 | key1-2 trec 3 | 1 | key1-3 (3 rows) select * from T_dta2; tkey | ref1 | ref2 ------------+------+---------------------- trec 1 | 1 | KEY1-1 trec 2 | 1 | KEY1-2 trec 3 | 1 | KEY1-3 (3 rows) update T_pkey1 set key2 = 'key2-9' where key1 = 2 and key2 = 'key2-1'; update T_pkey1 set key2 = 'key1-9' where key1 = 1 and key2 = 'key1-1'; ERROR: key '1', 'key1-1 ' referenced by T_dta1 delete from T_pkey1 where key1 = 2 and key2 = 'key2-2'; delete from T_pkey1 where key1 = 1 and key2 = 'key1-2'; ERROR: key '1', 'key1-2 ' referenced by T_dta1 update T_pkey2 set key2 = 'KEY2-9' where key1 = 2 and key2 = 'KEY2-1'; update T_pkey2 set key2 = 'KEY1-9' where key1 = 1 and key2 = 'KEY1-1'; NOTICE: updated 1 entries in T_dta2 for new key in T_pkey2 delete from T_pkey2 where key1 = 2 and key2 = 'KEY2-2'; delete from T_pkey2 where key1 = 1 and key2 = 'KEY1-2'; NOTICE: deleted 1 entries from T_dta2 select * from T_pkey1; key1 | key2 | txt ------+----------------------+------------------------------------------ 1 | key1-1 | test key 1 | key1-2 | test key 1 | key1-3 | test key 2 | key2-3 | test key 1 | KEY1-3 | should work 2 | key2-9 | test key (6 rows) select * from T_pkey2; key1 | key2 | txt ------+----------------------+------------------------------------------ 1 | KEY1-3 | test key 2 | KEY2-3 | test key 2 | KEY2-9 | test key 1 | KEY1-9 | test key (4 rows) select * from T_dta1; tkey | ref1 | ref2 ------------+------+---------------------- trec 1 | 1 | key1-1 trec 2 | 1 | key1-2 trec 3 | 1 | key1-3 (3 rows) select * from T_dta2; tkey | ref1 | ref2 ------------+------+---------------------- trec 3 | 1 | KEY1-3 trec 1 | 1 | KEY1-9 (2 rows) select tcl_avg(key1) from T_pkey1; tcl_avg --------- 1 (1 row) select tcl_sum(key1) from T_pkey1; tcl_sum --------- 8 (1 row) select tcl_avg(key1) from T_pkey2; tcl_avg --------- 1 (1 row) select tcl_sum(key1) from T_pkey2; tcl_sum --------- 6 (1 row) -- The following should return NULL instead of 0 select tcl_avg(key1) from T_pkey1 where key1 = 99; tcl_avg --------- (1 row) select tcl_sum(key1) from T_pkey1 where key1 = 99; tcl_sum --------- 0 (1 row) select 1 @< 2; ?column? ---------- t (1 row) select 100 @< 4; ?column? ---------- f (1 row) select * from T_pkey1 order by key1 using @<, key2 collate "C"; key1 | key2 | txt ------+----------------------+------------------------------------------ 1 | KEY1-3 | should work 1 | key1-1 | test key 1 | key1-2 | test key 1 | key1-3 | test key 2 | key2-3 | test key 2 | key2-9 | test key (6 rows) select * from T_pkey2 order by key1 using @<, key2 collate "C"; key1 | key2 | txt ------+----------------------+------------------------------------------ 1 | KEY1-3 | test key 1 | KEY1-9 | test key 2 | KEY2-3 | test key 2 | KEY2-9 | test key (4 rows) -- show dump of trigger data insert into trigger_test values(1,'insert'); NOTICE: NEW: {} NOTICE: OLD: {} NOTICE: TG_level: STATEMENT NOTICE: TG_name: statement_trigger NOTICE: TG_op: INSERT NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull} NOTICE: TG_relid: bogus:12345 NOTICE: TG_table_name: trigger_test NOTICE: TG_table_schema: public NOTICE: TG_when: BEFORE NOTICE: args: {42 {statement trigger}} NOTICE: NEW: {i: 1, test_argisnull: f, test_return_null: f, test_skip: f, v: insert} NOTICE: OLD: {} NOTICE: TG_level: ROW NOTICE: TG_name: show_trigger_data_trig NOTICE: TG_op: INSERT NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull} NOTICE: TG_relid: bogus:12345 NOTICE: TG_table_name: trigger_test NOTICE: TG_table_schema: public NOTICE: TG_when: BEFORE NOTICE: args: {23 skidoo} insert into trigger_test_view values(2,'insert'); NOTICE: NEW: {i: 2, v: insert} NOTICE: OLD: {} NOTICE: TG_level: ROW NOTICE: TG_name: show_trigger_data_view_trig NOTICE: TG_op: INSERT NOTICE: TG_relatts: {{} i v} NOTICE: TG_relid: bogus:12345 NOTICE: TG_table_name: trigger_test_view NOTICE: TG_table_schema: public NOTICE: TG_when: {INSTEAD OF} NOTICE: args: {24 {skidoo view}} update trigger_test_view set v = 'update' where i=1; NOTICE: NEW: {i: 1, v: update} NOTICE: OLD: {i: 1, v: insert} NOTICE: TG_level: ROW NOTICE: TG_name: show_trigger_data_view_trig NOTICE: TG_op: UPDATE NOTICE: TG_relatts: {{} i v} NOTICE: TG_relid: bogus:12345 NOTICE: TG_table_name: trigger_test_view NOTICE: TG_table_schema: public NOTICE: TG_when: {INSTEAD OF} NOTICE: args: {24 {skidoo view}} delete from trigger_test_view; NOTICE: NEW: {} NOTICE: OLD: {i: 1, v: insert} NOTICE: TG_level: ROW NOTICE: TG_name: show_trigger_data_view_trig NOTICE: TG_op: DELETE NOTICE: TG_relatts: {{} i v} NOTICE: TG_relid: bogus:12345 NOTICE: TG_table_name: trigger_test_view NOTICE: TG_table_schema: public NOTICE: TG_when: {INSTEAD OF} NOTICE: args: {24 {skidoo view}} update trigger_test set v = 'update', test_skip=true where i = 1; NOTICE: NEW: {} NOTICE: OLD: {} NOTICE: TG_level: STATEMENT NOTICE: TG_name: statement_trigger NOTICE: TG_op: UPDATE NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull} NOTICE: TG_relid: bogus:12345 NOTICE: TG_table_name: trigger_test NOTICE: TG_table_schema: public NOTICE: TG_when: BEFORE NOTICE: args: {42 {statement trigger}} NOTICE: SKIPPING OPERATION UPDATE update trigger_test set v = 'update' where i = 1; NOTICE: NEW: {} NOTICE: OLD: {} NOTICE: TG_level: STATEMENT NOTICE: TG_name: statement_trigger NOTICE: TG_op: UPDATE NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull} NOTICE: TG_relid: bogus:12345 NOTICE: TG_table_name: trigger_test NOTICE: TG_table_schema: public NOTICE: TG_when: BEFORE NOTICE: args: {42 {statement trigger}} NOTICE: NEW: {i: 1, test_argisnull: f, test_return_null: f, test_skip: f, v: update} NOTICE: OLD: {i: 1, test_argisnull: f, test_return_null: f, test_skip: f, v: insert} NOTICE: TG_level: ROW NOTICE: TG_name: show_trigger_data_trig NOTICE: TG_op: UPDATE NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull} NOTICE: TG_relid: bogus:12345 NOTICE: TG_table_name: trigger_test NOTICE: TG_table_schema: public NOTICE: TG_when: BEFORE NOTICE: args: {23 skidoo} delete from trigger_test; NOTICE: NEW: {} NOTICE: OLD: {} NOTICE: TG_level: STATEMENT NOTICE: TG_name: statement_trigger NOTICE: TG_op: DELETE NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull} NOTICE: TG_relid: bogus:12345 NOTICE: TG_table_name: trigger_test NOTICE: TG_table_schema: public NOTICE: TG_when: BEFORE NOTICE: args: {42 {statement trigger}} NOTICE: NEW: {} NOTICE: OLD: {i: 1, test_argisnull: f, test_return_null: f, test_skip: f, v: update} NOTICE: TG_level: ROW NOTICE: TG_name: show_trigger_data_trig NOTICE: TG_op: DELETE NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull} NOTICE: TG_relid: bogus:12345 NOTICE: TG_table_name: trigger_test NOTICE: TG_table_schema: public NOTICE: TG_when: BEFORE NOTICE: args: {23 skidoo} truncate trigger_test; NOTICE: NEW: {} NOTICE: OLD: {} NOTICE: TG_level: STATEMENT NOTICE: TG_name: statement_trigger NOTICE: TG_op: TRUNCATE NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull} NOTICE: TG_relid: bogus:12345 NOTICE: TG_table_name: trigger_test NOTICE: TG_table_schema: public NOTICE: TG_when: BEFORE NOTICE: args: {42 {statement trigger}} -- Test composite-type arguments select tcl_composite_arg_ref1(row('tkey', 42, 'ref2')); tcl_composite_arg_ref1 ------------------------ 42 (1 row) select tcl_composite_arg_ref2(row('tkey', 42, 'ref2')); tcl_composite_arg_ref2 ------------------------ ref2 (1 row) -- Test argisnull primitive select tcl_argisnull('foo'); tcl_argisnull --------------- f (1 row) select tcl_argisnull(''); tcl_argisnull --------------- f (1 row) select tcl_argisnull(null); tcl_argisnull --------------- t (1 row) -- should error insert into trigger_test(test_argisnull) values(true); NOTICE: NEW: {} NOTICE: OLD: {} NOTICE: TG_level: STATEMENT NOTICE: TG_name: statement_trigger NOTICE: TG_op: INSERT NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull} NOTICE: TG_relid: bogus:12345 NOTICE: TG_table_name: trigger_test NOTICE: TG_table_schema: public NOTICE: TG_when: BEFORE NOTICE: args: {42 {statement trigger}} ERROR: argisnull cannot be used in triggers select trigger_data(); ERROR: trigger functions can only be called as triggers -- Test spi_lastoid primitive create temp table t1 (f1 int); select tcl_lastoid('t1'); tcl_lastoid ------------- 0 (1 row) create temp table t2 (f1 int) with oids; select tcl_lastoid('t2') > 0; ?column? ---------- t (1 row) -- test some error cases create function tcl_error(out a int, out b int) as $$return {$$ language pltcl; select tcl_error(); ERROR: missing close-brace create function bad_record(out a text, out b text) as $$return [list a]$$ language pltcl; select bad_record(); ERROR: column name/value list must have even number of elements create function bad_field(out a text, out b text) as $$return [list a 1 b 2 cow 3]$$ language pltcl; select bad_field(); ERROR: column name/value list contains nonexistent column name "cow" -- test compound return select * from tcl_test_cube_squared(5); squared | cubed ---------+------- 25 | 125 (1 row) -- test SRF select * from tcl_test_squared_rows(0,5); x | y ---+---- 0 | 0 1 | 1 2 | 4 3 | 9 4 | 16 (5 rows) select * from tcl_test_sequence(0,5) as a; a --- 0 1 2 3 4 (5 rows) select 1, tcl_test_sequence(0,5); ?column? | tcl_test_sequence ----------+------------------- 1 | 0 1 | 1 1 | 2 1 | 3 1 | 4 (5 rows) create function non_srf() returns int as $$return_next 1$$ language pltcl; select non_srf(); ERROR: return_next cannot be used in non-set-returning functions create function bad_record_srf(out a text, out b text) returns setof record as $$ return_next [list a] $$ language pltcl; select bad_record_srf(); ERROR: column name/value list must have even number of elements create function bad_field_srf(out a text, out b text) returns setof record as $$ return_next [list a 1 b 2 cow 3] $$ language pltcl; select bad_field_srf(); ERROR: column name/value list contains nonexistent column name "cow" -- test quote select tcl_eval('quote foo bar'); ERROR: wrong # args: should be "quote string" select tcl_eval('quote [format %c 39]'); tcl_eval ---------- '' (1 row) select tcl_eval('quote [format %c 92]'); tcl_eval ---------- \\ (1 row) -- Test argisnull select tcl_eval('argisnull'); ERROR: wrong # args: should be "argisnull argno" select tcl_eval('argisnull 14'); ERROR: argno out of range select tcl_eval('argisnull abc'); ERROR: expected integer but got "abc" -- Test return_null select tcl_eval('return_null 14'); ERROR: wrong # args: should be "return_null " -- should error insert into trigger_test(test_return_null) values(true); NOTICE: NEW: {} NOTICE: OLD: {} NOTICE: TG_level: STATEMENT NOTICE: TG_name: statement_trigger NOTICE: TG_op: INSERT NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull} NOTICE: TG_relid: bogus:12345 NOTICE: TG_table_name: trigger_test NOTICE: TG_table_schema: public NOTICE: TG_when: BEFORE NOTICE: args: {42 {statement trigger}} ERROR: return_null cannot be used in triggers -- Test spi_exec select tcl_eval('spi_exec'); ERROR: wrong # args: should be "spi_exec ?-count n? ?-array name? query ?loop body?" select tcl_eval('spi_exec -count'); ERROR: missing argument to -count or -array select tcl_eval('spi_exec -array'); ERROR: missing argument to -count or -array select tcl_eval('spi_exec -count abc'); ERROR: expected integer but got "abc" select tcl_eval('spi_exec query loop body toomuch'); ERROR: wrong # args: should be "query ?loop body?" select tcl_eval('spi_exec "begin; rollback;"'); ERROR: pltcl: SPI_execute failed: SPI_ERROR_TRANSACTION -- Test spi_execp select tcl_eval('spi_execp'); ERROR: missing argument to -count or -array select tcl_eval('spi_execp -count'); ERROR: missing argument to -array, -count or -nulls select tcl_eval('spi_execp -array'); ERROR: missing argument to -array, -count or -nulls select tcl_eval('spi_execp -count abc'); ERROR: expected integer but got "abc" select tcl_eval('spi_execp -nulls'); ERROR: missing argument to -array, -count or -nulls select tcl_eval('spi_execp ""'); ERROR: invalid queryid '' -- test spi_prepare select tcl_eval('spi_prepare'); ERROR: wrong # args: should be "spi_prepare query argtypes" select tcl_eval('spi_prepare a b'); ERROR: type "b" does not exist select tcl_eval('spi_prepare a "b {"'); ERROR: unmatched open brace in list select tcl_error_handling_test($tcl$spi_prepare "select moo" []$tcl$); tcl_error_handling_test -------------------------------------- SQLSTATE: 42703 + condition: undefined_column + cursor_position: 8 + message: column "moo" does not exist+ statement: select moo (1 row) -- test full error text select tcl_error_handling_test($tcl$ spi_exec "DO $$ BEGIN RAISE 'my message' USING HINT = 'my hint' , DETAIL = 'my detail' , SCHEMA = 'my schema' , TABLE = 'my table' , COLUMN = 'my column' , CONSTRAINT = 'my constraint' , DATATYPE = 'my datatype' ; END$$;" $tcl$); tcl_error_handling_test -------------------------------------------------------------- SQLSTATE: P0001 + column: my column + condition: raise_exception + constraint: my constraint + context: PL/pgSQL function inline_code_block line 3 at RAISE+ SQL statement "DO $$ + BEGIN + RAISE 'my message' + USING HINT = 'my hint' + , DETAIL = 'my detail' + , SCHEMA = 'my schema' + , TABLE = 'my table' + , COLUMN = 'my column' + , CONSTRAINT = 'my constraint' + , DATATYPE = 'my datatype' + ; + END$$;" + datatype: my datatype + detail: my detail + hint: my hint + message: my message + schema: my schema + table: my table (1 row) -- verify tcl_error_handling_test() properly reports non-postgres errors select tcl_error_handling_test('moo'); tcl_error_handling_test ---------------------------- invalid command name "moo" (1 row) -- test elog select tcl_eval('elog'); ERROR: wrong # args: should be "elog level msg" select tcl_eval('elog foo bar'); ERROR: bad priority "foo": must be DEBUG, LOG, INFO, NOTICE, WARNING, ERROR, or FATAL -- test forced error select tcl_eval('error "forced error"'); ERROR: forced error -- test loop control in spi_exec[p] select tcl_spi_exec(true, 'break'); NOTICE: col1 1, col2 foo NOTICE: col1 2, col2 bar NOTICE: action: break NOTICE: end of function tcl_spi_exec -------------- (1 row) select tcl_spi_exec(true, 'continue'); NOTICE: col1 1, col2 foo NOTICE: col1 2, col2 bar NOTICE: action: continue NOTICE: col1 3, col2 baz NOTICE: end of function tcl_spi_exec -------------- (1 row) select tcl_spi_exec(true, 'error'); NOTICE: col1 1, col2 foo NOTICE: col1 2, col2 bar NOTICE: action: error ERROR: error message select tcl_spi_exec(true, 'return'); NOTICE: col1 1, col2 foo NOTICE: col1 2, col2 bar NOTICE: action: return tcl_spi_exec -------------- (1 row) select tcl_spi_exec(false, 'break'); NOTICE: col1 1, col2 foo NOTICE: col1 2, col2 bar NOTICE: action: break NOTICE: end of function tcl_spi_exec -------------- (1 row) select tcl_spi_exec(false, 'continue'); NOTICE: col1 1, col2 foo NOTICE: col1 2, col2 bar NOTICE: action: continue NOTICE: col1 3, col2 baz NOTICE: end of function tcl_spi_exec -------------- (1 row) select tcl_spi_exec(false, 'error'); NOTICE: col1 1, col2 foo NOTICE: col1 2, col2 bar NOTICE: action: error ERROR: error message select tcl_spi_exec(false, 'return'); NOTICE: col1 1, col2 foo NOTICE: col1 2, col2 bar NOTICE: action: return tcl_spi_exec -------------- (1 row) -- forcibly run the Tcl event loop for awhile, to check that we have not -- messed things up too badly by disabling the Tcl notifier subsystem select tcl_eval($$ unset -nocomplain ::tcl_vwait after 100 {set ::tcl_vwait 1} vwait ::tcl_vwait unset -nocomplain ::tcl_vwait$$); tcl_eval ---------- (1 row) -- test transition table visibility 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 pltcl as $$ spi_exec -array C "SELECT id, name FROM old_table" { elog INFO "old: $C(id) -> $C(name)" } spi_exec -array C "SELECT id, name FROM new_table" { elog INFO "new: $C(id) -> $C(name)" } return OK $$; 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();