This patch increases test coverage for pltcl, from 70% to 83%. Aside
from that, the work on this uncovered 2 new bugs (the trigger return one
I just submitted, as well as a bug in the SRF/composite patch).
--
Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX
Experts in Analytics, Data Architecture and PostgreSQL
Data in Trouble? Get it in Treble! http://BlueTreble.com
855-TREBLE2 (855-873-2532) mobile: 512-569-9461
diff --git a/src/pl/tcl/expected/pltcl_queries.out
b/src/pl/tcl/expected/pltcl_queries.out
index 6cb1fdb..9e3a0dc 100644
--- a/src/pl/tcl/expected/pltcl_queries.out
+++ b/src/pl/tcl/expected/pltcl_queries.out
@@ -1,3 +1,7 @@
+BEGIN;
+SET LOCAL client_min_messages = WARNING;
+CREATE EXTENSION IF NOT EXISTS plpgsql;
+COMMIT;
-- suppress CONTEXT so that function OIDs aren't in output
\set VERBOSITY terse
insert into T_pkey1 values (1, 'key1-1', 'test key');
@@ -185,12 +189,23 @@ select * from T_pkey2 order by key1 using @<, key2
collate "C";
-- show dump of trigger data
insert into trigger_test values(1,'insert');
-NOTICE: NEW: {i: 1, v: 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}
+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
@@ -232,13 +247,37 @@ 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: {i: 1, v: update}
-NOTICE: OLD: {i: 1, v: insert}
+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}
+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
@@ -246,16 +285,39 @@ NOTICE: TG_when: BEFORE
NOTICE: args: {23 skidoo}
delete from trigger_test;
NOTICE: NEW: {}
-NOTICE: OLD: {i: 1, v: update}
+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}
+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
@@ -288,6 +350,20 @@ select tcl_argisnull(null);
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
-- Test spi_lastoid primitive
create temp table t1 (f1 int);
select tcl_lastoid('t1');
@@ -303,3 +379,216 @@ select tcl_lastoid('t2') > 0;
t
(1 row)
+-- 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: bad option "begin; rollback;": must be -array or -countpltcl:
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: bad option "": must be -array, -count, or -nullsinvalid 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 "moo" }$tcl$);
+ tcl_error_handling_test
+------------------------------
+ COMMAND: spi_prepare "moo" +
+ POSTGRES: 'POSTGRES' +
+ TCL: LOOKUP +
+ funcname: 'funcname' +
+ lineno: 'lineno'
+(1 row)
+
+-- test full error text
+select tcl_error_handling_test($tcl$
+spi_exec "DO $$
+BEGIN
+RAISE 'message'
+ USING HINT = 'hint'
+ , DETAIL = 'detail'
+ , SCHEMA = 'schema'
+ , TABLE = 'table'
+ , COLUMN = 'column'
+ , CONSTRAINT = 'constraint'
+ , DATATYPE = 'datatype'
+;
+END$$;"
+$tcl$);
+ tcl_error_handling_test
+--------------------------------------------------------------
+ POSTGRES: 'POSTGRES' +
+ SQLSTATE: P0001 +
+ column: column +
+ condition: raise_exception +
+ constraint: constraint +
+ context: PL/pgSQL function inline_code_block line 3 at RAISE+
+ SQL statement "DO $$ +
+ BEGIN +
+ RAISE 'message' +
+ USING HINT = 'hint' +
+ , DETAIL = 'detail' +
+ , SCHEMA = 'schema' +
+ , TABLE = 'table' +
+ , COLUMN = 'column' +
+ , CONSTRAINT = 'constraint' +
+ , DATATYPE = 'datatype' +
+ ; +
+ END$$;" +
+ datatype: datatype +
+ detail: detail +
+ filename: pl_exec.c +
+ funcname: 'funcname' +
+ hint: hint +
+ lineno: 'lineno' +
+ message: message +
+ schema: schema +
+ table: table
+(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
+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 loop control
+select tcl_spi_exec(true, 'break');
+NOTICE: attnum 1, attname relname
+NOTICE: attnum 2, attname relnamespace
+NOTICE: action: break
+NOTICE: end of function
+ tcl_spi_exec
+--------------
+
+(1 row)
+
+select tcl_spi_exec(true, 'continue');
+NOTICE: attnum 1, attname relname
+NOTICE: attnum 2, attname relnamespace
+NOTICE: action: continue
+NOTICE: attnum 3, attname reltype
+NOTICE: end of function
+ tcl_spi_exec
+--------------
+
+(1 row)
+
+select tcl_spi_exec(true, 'error');
+NOTICE: attnum 1, attname relname
+NOTICE: attnum 2, attname relnamespace
+NOTICE: action: error
+ERROR: error message
+select tcl_spi_exec(true, 'return');
+NOTICE: attnum 1, attname relname
+NOTICE: attnum 2, attname relnamespace
+NOTICE: action: return
+ tcl_spi_exec
+--------------
+
+(1 row)
+
+select tcl_spi_exec(false, 'break');
+NOTICE: attnum 1, attname relname
+NOTICE: attnum 2, attname relnamespace
+NOTICE: action: break
+NOTICE: end of function
+ tcl_spi_exec
+--------------
+
+(1 row)
+
+select tcl_spi_exec(false, 'continue');
+NOTICE: attnum 1, attname relname
+NOTICE: attnum 2, attname relnamespace
+NOTICE: action: continue
+NOTICE: attnum 3, attname reltype
+NOTICE: end of function
+ tcl_spi_exec
+--------------
+
+(1 row)
+
+select tcl_spi_exec(false, 'error');
+NOTICE: attnum 1, attname relname
+NOTICE: attnum 2, attname relnamespace
+NOTICE: action: error
+ERROR: error message
+select tcl_spi_exec(false, 'return');
+NOTICE: attnum 1, attname relname
+NOTICE: attnum 2, attname relnamespace
+NOTICE: action: return
+ tcl_spi_exec
+--------------
+
+(1 row)
+
diff --git a/src/pl/tcl/expected/pltcl_setup.out
b/src/pl/tcl/expected/pltcl_setup.out
index e65e9e3..3571550 100644
--- a/src/pl/tcl/expected/pltcl_setup.out
+++ b/src/pl/tcl/expected/pltcl_setup.out
@@ -49,10 +49,31 @@ create function check_pkey1_exists(int4, bpchar) returns
bool as E'
return "f"
' language pltcl;
-- dump trigger data
-CREATE TABLE trigger_test
- (i int, v text );
-CREATE VIEW trigger_test_view AS SELECT * FROM trigger_test;
+CREATE TABLE trigger_test (
+ i int,
+ v text,
+ dropme text,
+ test_skip boolean DEFAULT false,
+ test_return_null boolean DEFAULT false,
+ test_argisnull boolean DEFAULT false
+);
+-- Make certain dropped attributes are handled correctly
+ALTER TABLE trigger_test DROP dropme;
+CREATE VIEW trigger_test_view AS SELECT i, v FROM trigger_test;
CREATE FUNCTION trigger_data() returns trigger language pltcl as $_$
+ if {$TG_table_name eq "trigger_test" && $TG_level eq "ROW" && $TG_op ne
"DELETE"} {
+ # Special case tests
+ if {$NEW(test_return_null) eq "t" } {
+ return_null
+ }
+ if {$NEW(test_argisnull) eq "t" } {
+ set should_error [argisnull 1]
+ }
+ if {$NEW(test_skip) eq "t" } {
+ elog NOTICE "SKIPPING OPERATION $TG_op"
+ return SKIP
+ }
+ }
if { [info exists TG_relid] } {
set TG_relid "bogus:12345"
@@ -86,6 +107,9 @@ $_$;
CREATE TRIGGER show_trigger_data_trig
BEFORE INSERT OR UPDATE OR DELETE ON trigger_test
FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
+CREATE TRIGGER statement_trigger
+BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE ON trigger_test
+FOR EACH STATEMENT EXECUTE PROCEDURE trigger_data(42,'statement trigger');
CREATE TRIGGER show_trigger_data_view_trig
INSTEAD OF INSERT OR UPDATE OR DELETE ON trigger_test_view
FOR EACH ROW EXECUTE PROCEDURE trigger_data(24,'skidoo view');
@@ -555,31 +579,88 @@ NOTICE: tclsnitch: ddl_command_start DROP TABLE
NOTICE: tclsnitch: ddl_command_end DROP TABLE
drop event trigger tcl_a_snitch;
drop event trigger tcl_b_snitch;
+CREATE OR REPLACE FUNCTION tcl_eval(in string varchar) RETURNS text AS $$
+ eval $1
+$$ LANGUAGE 'pltcl';
-- test use of errorCode in error handling
-create function tcl_error_handling_test() returns text as $$
- global errorCode
- if {[catch { spi_exec "select no_such_column from foo;" }]} {
- array set errArray $errorCode
- if {$errArray(condition) == "undefined_table"} {
- return "expected error: $errArray(message)"
- } else {
- return "unexpected error: $errArray(condition) $errArray(message)"
- }
+CREATE FUNCTION public.tcl_error_handling_test(text)
+ RETURNS text
+ LANGUAGE pltcl
+AS $function$
+ if {[catch $1 err]} {
+ # Set keys that will change over time to fixed values
+ array set myArray $::errorCode
+ set myArray(funcname) "'funcname'"
+ set myArray(lineno) 'lineno'
+ set myArray(POSTGRES) 'POSTGRES'
+
+ # Format into something nicer
+ set vals []
+ foreach {key} [lsort [array names myArray]] {
+ set value [string map {"\n" "\n\t"} $myArray($key)]
+ lappend vals "$key: $value"
+ }
+ return [join $vals "\n"]
} else {
return "no error"
}
-$$ language pltcl;
-select tcl_error_handling_test();
- tcl_error_handling_test
------------------------------------------------
- expected error: relation "foo" does not exist
-(1 row)
-
-create temp table foo(f1 int);
-select tcl_error_handling_test();
- tcl_error_handling_test
----------------------------------------------------------------------------
- unexpected error: undefined_column column "no_such_column" does not exist
-(1 row)
-
-drop table foo;
+$function$
+;
+-- test use of arrays
+create function tcl_spi_exec(
+ prepare boolean,
+ action text
+)
+returns void language pltcl AS $function$
+# unnest would be easier but this way we get multiple fields
+set query "SELECT *
+ FROM pg_attribute
+ WHERE attrelid = 'pg_class'::regclass
+ AND attnum BETWEEN 1 AND 3
+ ORDER BY attnum"
+if {$1 == "t"} {
+ set prep [spi_prepare $query {}]
+ spi_execp -array A $prep {
+ elog NOTICE "attnum $A(attnum), attname $A(attname)"
+
+ switch $A(attnum) {
+ 2 {
+ elog NOTICE "action: $2"
+ switch $2 {
+ break {
+ break
+ }
+ return {
+ return
+ }
+ error {
+ error "error message"
+ }
+ }
+ }
+ }
+ }
+} else {
+ spi_exec -array A $query {
+ elog NOTICE "attnum $A(attnum), attname $A(attname)"
+
+ switch $A(attnum) {
+ 2 {
+ elog NOTICE "action: $2"
+ switch $2 {
+ break {
+ break
+ }
+ return {
+ return
+ }
+ error {
+ error "error message"
+ }
+ }
+ }
+ }
+ }
+}
+elog NOTICE "end of function"
+$function$;
diff --git a/src/pl/tcl/sql/pltcl_queries.sql b/src/pl/tcl/sql/pltcl_queries.sql
index a0a9619..5c584d8 100644
--- a/src/pl/tcl/sql/pltcl_queries.sql
+++ b/src/pl/tcl/sql/pltcl_queries.sql
@@ -1,3 +1,8 @@
+BEGIN;
+SET LOCAL client_min_messages = WARNING;
+CREATE EXTENSION IF NOT EXISTS plpgsql;
+COMMIT;
+
-- suppress CONTEXT so that function OIDs aren't in output
\set VERBOSITY terse
@@ -80,8 +85,10 @@ insert into trigger_test_view values(2,'insert');
update trigger_test_view set v = 'update' where i=1;
delete from trigger_test_view;
+update trigger_test set v = 'update', test_skip=true where i = 1;
update trigger_test set v = 'update' where i = 1;
delete from trigger_test;
+truncate trigger_test;
-- Test composite-type arguments
select tcl_composite_arg_ref1(row('tkey', 42, 'ref2'));
@@ -91,9 +98,84 @@ select tcl_composite_arg_ref2(row('tkey', 42, 'ref2'));
select tcl_argisnull('foo');
select tcl_argisnull('');
select tcl_argisnull(null);
+-- should error
+insert into trigger_test(test_argisnull) values(true);
-- Test spi_lastoid primitive
create temp table t1 (f1 int);
select tcl_lastoid('t1');
create temp table t2 (f1 int) with oids;
select tcl_lastoid('t2') > 0;
+
+-- Test quote
+select tcl_eval('quote foo bar');
+select tcl_eval('quote [format %c 39]');
+select tcl_eval('quote [format %c 92]');
+
+-- Test argisnull
+select tcl_eval('argisnull');
+select tcl_eval('argisnull 14');
+select tcl_eval('argisnull abc');
+
+-- Test return_null
+select tcl_eval('return_null 14');
+-- should error
+insert into trigger_test(test_return_null) values(true);
+
+-- Test spi_exec
+select tcl_eval('spi_exec');
+select tcl_eval('spi_exec -count');
+select tcl_eval('spi_exec -array');
+select tcl_eval('spi_exec -count abc');
+select tcl_eval('spi_exec query loop body toomuch');
+select tcl_eval('spi_exec "begin; rollback;"');
+
+-- Test spi_execp
+select tcl_eval('spi_execp');
+select tcl_eval('spi_execp -count');
+select tcl_eval('spi_execp -array');
+select tcl_eval('spi_execp -count abc');
+select tcl_eval('spi_execp -nulls');
+select tcl_eval('spi_execp ""');
+
+-- test spi_prepare
+select tcl_eval('spi_prepare');
+select tcl_eval('spi_prepare a b');
+select tcl_eval('spi_prepare a "b {"');
+select tcl_error_handling_test($tcl${ spi_prepare "moo" }$tcl$);
+
+-- test full error text
+select tcl_error_handling_test($tcl$
+spi_exec "DO $$
+BEGIN
+RAISE 'message'
+ USING HINT = 'hint'
+ , DETAIL = 'detail'
+ , SCHEMA = 'schema'
+ , TABLE = 'table'
+ , COLUMN = 'column'
+ , CONSTRAINT = 'constraint'
+ , DATATYPE = 'datatype'
+;
+END$$;"
+$tcl$);
+
+
+-- test elog
+select tcl_eval('elog');
+select tcl_eval('elog foo bar');
+
+-- test forced error
+select tcl_eval('error "forced error"');
+
+select tcl_eval('unset -nocomplain ::tcl_vwait; after 100 {set ::tcl_vwait 1};
vwait ::tcl_vwait; unset -nocomplain ::tcl_vwait');
+
+-- test loop control
+select tcl_spi_exec(true, 'break');
+select tcl_spi_exec(true, 'continue');
+select tcl_spi_exec(true, 'error');
+select tcl_spi_exec(true, 'return');
+select tcl_spi_exec(false, 'break');
+select tcl_spi_exec(false, 'continue');
+select tcl_spi_exec(false, 'error');
+select tcl_spi_exec(false, 'return');
diff --git a/src/pl/tcl/sql/pltcl_setup.sql b/src/pl/tcl/sql/pltcl_setup.sql
index 8df65a5..664f3db 100644
--- a/src/pl/tcl/sql/pltcl_setup.sql
+++ b/src/pl/tcl/sql/pltcl_setup.sql
@@ -57,12 +57,33 @@ create function check_pkey1_exists(int4, bpchar) returns
bool as E'
-- dump trigger data
-CREATE TABLE trigger_test
- (i int, v text );
+CREATE TABLE trigger_test (
+ i int,
+ v text,
+ dropme text,
+ test_skip boolean DEFAULT false,
+ test_return_null boolean DEFAULT false,
+ test_argisnull boolean DEFAULT false
+);
+-- Make certain dropped attributes are handled correctly
+ALTER TABLE trigger_test DROP dropme;
-CREATE VIEW trigger_test_view AS SELECT * FROM trigger_test;
+CREATE VIEW trigger_test_view AS SELECT i, v FROM trigger_test;
CREATE FUNCTION trigger_data() returns trigger language pltcl as $_$
+ if {$TG_table_name eq "trigger_test" && $TG_level eq "ROW" && $TG_op ne
"DELETE"} {
+ # Special case tests
+ if {$NEW(test_return_null) eq "t" } {
+ return_null
+ }
+ if {$NEW(test_argisnull) eq "t" } {
+ set should_error [argisnull 1]
+ }
+ if {$NEW(test_skip) eq "t" } {
+ elog NOTICE "SKIPPING OPERATION $TG_op"
+ return SKIP
+ }
+ }
if { [info exists TG_relid] } {
set TG_relid "bogus:12345"
@@ -97,6 +118,9 @@ $_$;
CREATE TRIGGER show_trigger_data_trig
BEFORE INSERT OR UPDATE OR DELETE ON trigger_test
FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
+CREATE TRIGGER statement_trigger
+BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE ON trigger_test
+FOR EACH STATEMENT EXECUTE PROCEDURE trigger_data(42,'statement trigger');
CREATE TRIGGER show_trigger_data_view_trig
INSTEAD OF INSERT OR UPDATE OR DELETE ON trigger_test_view
@@ -596,26 +620,92 @@ drop table foo;
drop event trigger tcl_a_snitch;
drop event trigger tcl_b_snitch;
+CREATE OR REPLACE FUNCTION tcl_eval(in string varchar) RETURNS text AS $$
+ eval $1
+$$ LANGUAGE 'pltcl';
+
+
-- test use of errorCode in error handling
-create function tcl_error_handling_test() returns text as $$
- global errorCode
- if {[catch { spi_exec "select no_such_column from foo;" }]} {
- array set errArray $errorCode
- if {$errArray(condition) == "undefined_table"} {
- return "expected error: $errArray(message)"
- } else {
- return "unexpected error: $errArray(condition) $errArray(message)"
- }
+CREATE FUNCTION public.tcl_error_handling_test(text)
+ RETURNS text
+ LANGUAGE pltcl
+AS $function$
+ if {[catch $1 err]} {
+ # Set keys that will change over time to fixed values
+ array set myArray $::errorCode
+ set myArray(funcname) "'funcname'"
+ set myArray(lineno) 'lineno'
+ set myArray(POSTGRES) 'POSTGRES'
+
+ # Format into something nicer
+ set vals []
+ foreach {key} [lsort [array names myArray]] {
+ set value [string map {"\n" "\n\t"} $myArray($key)]
+ lappend vals "$key: $value"
+ }
+ return [join $vals "\n"]
} else {
return "no error"
}
-$$ language pltcl;
-
-select tcl_error_handling_test();
-
-create temp table foo(f1 int);
-
-select tcl_error_handling_test();
-
-drop table foo;
+$function$
+;
+
+-- test use of arrays
+create function tcl_spi_exec(
+ prepare boolean,
+ action text
+)
+returns void language pltcl AS $function$
+# unnest would be easier but this way we get multiple fields
+set query "SELECT *
+ FROM pg_attribute
+ WHERE attrelid = 'pg_class'::regclass
+ AND attnum BETWEEN 1 AND 3
+ ORDER BY attnum"
+if {$1 == "t"} {
+ set prep [spi_prepare $query {}]
+ spi_execp -array A $prep {
+ elog NOTICE "attnum $A(attnum), attname $A(attname)"
+
+ switch $A(attnum) {
+ 2 {
+ elog NOTICE "action: $2"
+ switch $2 {
+ break {
+ break
+ }
+ return {
+ return
+ }
+ error {
+ error "error message"
+ }
+ }
+ }
+ }
+ }
+} else {
+ spi_exec -array A $query {
+ elog NOTICE "attnum $A(attnum), attname $A(attname)"
+
+ switch $A(attnum) {
+ 2 {
+ elog NOTICE "action: $2"
+ switch $2 {
+ break {
+ break
+ }
+ return {
+ return
+ }
+ error {
+ error "error message"
+ }
+ }
+ }
+ }
+ }
+}
+elog NOTICE "end of function"
+$function$;
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers