create table T_comp1 ( tkey char(10), ref1 int4, ref2 char(20) ); create function tcl_composite_arg_ref1(T_comp1) returns int as ' return $1(ref1) ' language pltcl; create function tcl_composite_arg_ref2(T_comp1) returns text as ' return $1(ref2) ' language pltcl; create function tcl_argisnull(text) returns bool as ' argisnull 1 ' language pltcl; create function tcl_int4add(int4,int4) returns int4 as ' return [expr $1 + $2] ' language pltcl; -- We use split(n) as a quick-and-dirty way of parsing the input array -- value, which comes in as a string like '{1,2}'. There are better ways... create function tcl_int4_accum(int4[], int4) returns int4[] as ' set state [split $1 "{,}"] set newsum [expr {[lindex $state 1] + $2}] set newcnt [expr {[lindex $state 2] + 1}] return "{$newsum,$newcnt}" ' language pltcl; create function tcl_int4_avg(int4[]) returns int4 as ' set state [split $1 "{,}"] if {[lindex $state 2] == 0} { return_null } return [expr {[lindex $state 1] / [lindex $state 2]}] ' language pltcl; create aggregate tcl_avg ( sfunc = tcl_int4_accum, basetype = int4, stype = int4[], finalfunc = tcl_int4_avg, initcond = '{0,0}' ); create aggregate tcl_sum ( sfunc = tcl_int4add, basetype = int4, stype = int4, initcond1 = 0 ); create function tcl_int4lt(int4,int4) returns bool as ' if {$1 < $2} { return t } return f ' language pltcl; create function tcl_int4le(int4,int4) returns bool as ' if {$1 <= $2} { return t } return f ' language pltcl; create function tcl_int4eq(int4,int4) returns bool as ' if {$1 == $2} { return t } return f ' language pltcl; create function tcl_int4ge(int4,int4) returns bool as ' if {$1 >= $2} { return t } return f ' language pltcl; create function tcl_int4gt(int4,int4) returns bool as ' if {$1 > $2} { return t } return f ' language pltcl; create operator @< ( leftarg = int4, rightarg = int4, procedure = tcl_int4lt ); create operator @<= ( leftarg = int4, rightarg = int4, procedure = tcl_int4le ); create operator @= ( leftarg = int4, rightarg = int4, procedure = tcl_int4eq ); create operator @>= ( leftarg = int4, rightarg = int4, procedure = tcl_int4ge ); create operator @> ( leftarg = int4, rightarg = int4, procedure = tcl_int4gt ); create function tcl_int4cmp(int4,int4) returns int4 as ' if {$1 < $2} { return -1 } if {$1 > $2} { return 1 } return 0 ' language pltcl; CREATE OPERATOR CLASS tcl_int4_ops FOR TYPE int4 USING btree AS OPERATOR 1 @<, OPERATOR 2 @<=, OPERATOR 3 @=, OPERATOR 4 @>=, OPERATOR 5 @>, FUNCTION 1 tcl_int4cmp(int4,int4) ; -- -- Test usage of Tcl's "clock" command. In recent Tcl versions this -- command fails without working "unknown" support, so it's a good canary -- for initialization problems. -- create function tcl_date_week(int4,int4,int4) returns text as $$ return [clock format [clock scan "$2/$3/$1" -gmt 1] -format "%U" -gmt 1] $$ language pltcl immutable; select tcl_date_week(2010,1,26); select tcl_date_week(2001,10,24); -- test pltcl event triggers create function tclsnitch() returns event_trigger language pltcl as $$ elog NOTICE "tclsnitch: $TG_event $TG_tag" $$; create event trigger tcl_a_snitch on ddl_command_start execute procedure tclsnitch(); create event trigger tcl_b_snitch on ddl_command_end execute procedure tclsnitch(); create function foobar() returns int language sql as $$select 1;$$; alter function foobar() cost 77; drop function foobar(); create table foo(); drop table foo; drop event trigger tcl_a_snitch; drop event trigger tcl_b_snitch; create function tcl_test_cube_squared(in int, out squared int, out cubed int) as $$ return [list squared [expr {$1 * $1}] cubed [expr {$1 * $1 * $1}]] $$ language pltcl; create function tcl_test_squared_rows(int,int) returns table (x int, y int) as $$ for {set i $1} {$i < $2} {incr i} { return_next [list y [expr {$i * $i}] x $i] } $$ language pltcl; create function tcl_test_sequence(int,int) returns setof int as $$ for {set i $1} {$i < $2} {incr i} { return_next $i } $$ language pltcl; create function tcl_eval(string text) returns text as $$ eval $1 $$ language pltcl; -- test use of errorCode in error handling create function tcl_error_handling_test(text) returns text language pltcl as $function$ if {[catch $1 err]} { # If not a Postgres error, just return the basic error message if {[lindex $::errorCode 0] != "POSTGRES"} { return $err } # Get rid of keys that can't be expected to remain constant array set myArray $::errorCode unset myArray(POSTGRES) unset -nocomplain myArray(funcname) unset -nocomplain myArray(filename) unset -nocomplain myArray(lineno) # 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" } $function$; -- test spi_exec and spi_execp with -array create function tcl_spi_exec( prepare boolean, action text ) returns void language pltcl AS $function$ set query "select * from (values (1,'foo'),(2,'bar'),(3,'baz')) v(col1,col2)" if {$1 == "t"} { set prep [spi_prepare $query {}] spi_execp -array A $prep { elog NOTICE "col1 $A(col1), col2 $A(col2)" switch $A(col1) { 2 { elog NOTICE "action: $2" switch $2 { break { break } continue { continue } return { return } error { error "error message" } } error "should not get here" } } } } else { spi_exec -array A $query { elog NOTICE "col1 $A(col1), col2 $A(col2)" switch $A(col1) { 2 { elog NOTICE "action: $2" switch $2 { break { break } continue { continue } return { return } error { error "error message" } } error "should not get here" } } } } elog NOTICE "end of function" $function$;