1create table T_comp1 (
2    tkey	char(10),
3    ref1	int4,
4    ref2	char(20)
5);
6
7create function tcl_composite_arg_ref1(T_comp1) returns int as '
8    return $1(ref1)
9' language pltcl;
10
11create function tcl_composite_arg_ref2(T_comp1) returns text as '
12    return $1(ref2)
13' language pltcl;
14
15create function tcl_argisnull(text) returns bool as '
16    argisnull 1
17' language pltcl;
18
19
20create function tcl_int4add(int4,int4) returns int4 as '
21    return [expr $1 + $2]
22' language pltcl;
23
24-- We use split(n) as a quick-and-dirty way of parsing the input array
25-- value, which comes in as a string like '{1,2}'.  There are better ways...
26
27create function tcl_int4_accum(int4[], int4) returns int4[] as '
28    set state [split $1 "{,}"]
29    set newsum [expr {[lindex $state 1] + $2}]
30    set newcnt [expr {[lindex $state 2] + 1}]
31    return "{$newsum,$newcnt}"
32' language pltcl;
33
34create function tcl_int4_avg(int4[]) returns int4 as '
35    set state [split $1 "{,}"]
36    if {[lindex $state 2] == 0} { return_null }
37    return [expr {[lindex $state 1] / [lindex $state 2]}]
38' language pltcl;
39
40create aggregate tcl_avg (
41		sfunc = tcl_int4_accum,
42		basetype = int4,
43		stype = int4[],
44		finalfunc = tcl_int4_avg,
45		initcond = '{0,0}'
46	);
47
48create aggregate tcl_sum (
49		sfunc = tcl_int4add,
50		basetype = int4,
51		stype = int4,
52		initcond1 = 0
53	);
54
55create function tcl_int4lt(int4,int4) returns bool as '
56    if {$1 < $2} {
57        return t
58    }
59    return f
60' language pltcl;
61
62create function tcl_int4le(int4,int4) returns bool as '
63    if {$1 <= $2} {
64        return t
65    }
66    return f
67' language pltcl;
68
69create function tcl_int4eq(int4,int4) returns bool as '
70    if {$1 == $2} {
71        return t
72    }
73    return f
74' language pltcl;
75
76create function tcl_int4ge(int4,int4) returns bool as '
77    if {$1 >= $2} {
78        return t
79    }
80    return f
81' language pltcl;
82
83create function tcl_int4gt(int4,int4) returns bool as '
84    if {$1 > $2} {
85        return t
86    }
87    return f
88' language pltcl;
89
90create operator @< (
91		leftarg = int4,
92		rightarg = int4,
93		procedure = tcl_int4lt
94	);
95
96create operator @<= (
97		leftarg = int4,
98		rightarg = int4,
99		procedure = tcl_int4le
100	);
101
102create operator @= (
103		leftarg = int4,
104		rightarg = int4,
105		procedure = tcl_int4eq
106	);
107
108create operator @>= (
109		leftarg = int4,
110		rightarg = int4,
111		procedure = tcl_int4ge
112	);
113
114create operator @> (
115		leftarg = int4,
116		rightarg = int4,
117		procedure = tcl_int4gt
118	);
119
120create function tcl_int4cmp(int4,int4) returns int4 as '
121    if {$1 < $2} {
122        return -1
123    }
124    if {$1 > $2} {
125        return 1
126    }
127    return 0
128' language pltcl;
129
130CREATE OPERATOR CLASS tcl_int4_ops
131	FOR TYPE int4 USING btree AS
132	OPERATOR 1  @<,
133	OPERATOR 2  @<=,
134	OPERATOR 3  @=,
135	OPERATOR 4  @>=,
136	OPERATOR 5  @>,
137	FUNCTION 1  tcl_int4cmp(int4,int4) ;
138
139--
140-- Test usage of Tcl's "clock" command.  In recent Tcl versions this
141-- command fails without working "unknown" support, so it's a good canary
142-- for initialization problems.
143--
144create function tcl_date_week(int4,int4,int4) returns text as $$
145    return [clock format [clock scan "$2/$3/$1"] -format "%U"]
146$$ language pltcl immutable;
147
148select tcl_date_week(2010,1,26);
149select tcl_date_week(2001,10,24);
150
151-- test pltcl event triggers
152create function tclsnitch() returns event_trigger language pltcl as $$
153  elog NOTICE "tclsnitch: $TG_event $TG_tag"
154$$;
155
156create event trigger tcl_a_snitch on ddl_command_start execute procedure tclsnitch();
157create event trigger tcl_b_snitch on ddl_command_end execute procedure tclsnitch();
158
159create function foobar() returns int language sql as $$select 1;$$;
160alter function foobar() cost 77;
161drop function foobar();
162
163create table foo();
164drop table foo;
165
166drop event trigger tcl_a_snitch;
167drop event trigger tcl_b_snitch;
168
169create function tcl_test_cube_squared(in int, out squared int, out cubed int) as $$
170    return [list squared [expr {$1 * $1}] cubed [expr {$1 * $1 * $1}]]
171$$ language pltcl;
172
173create function tcl_test_squared_rows(int,int) returns table (x int, y int) as $$
174    for {set i $1} {$i < $2} {incr i} {
175        return_next [list y [expr {$i * $i}] x $i]
176    }
177$$ language pltcl;
178
179create function tcl_test_sequence(int,int) returns setof int as $$
180    for {set i $1} {$i < $2} {incr i} {
181        return_next $i
182    }
183$$ language pltcl;
184
185create function tcl_eval(string text) returns text as $$
186    eval $1
187$$ language pltcl;
188
189-- test use of errorCode in error handling
190create function tcl_error_handling_test(text) returns text
191language pltcl
192as $function$
193    if {[catch $1 err]} {
194        # If not a Postgres error, just return the basic error message
195        if {[lindex $::errorCode 0] != "POSTGRES"} {
196            return $err
197        }
198
199        # Get rid of keys that can't be expected to remain constant
200        array set myArray $::errorCode
201        unset myArray(POSTGRES)
202        unset -nocomplain myArray(funcname)
203        unset -nocomplain myArray(filename)
204        unset -nocomplain myArray(lineno)
205
206        # Format into something nicer
207        set vals []
208        foreach {key} [lsort [array names myArray]] {
209            set value [string map {"\n" "\n\t"} $myArray($key)]
210            lappend vals "$key: $value"
211        }
212        return [join $vals "\n"]
213    } else {
214        return "no error"
215    }
216$function$;
217
218-- test spi_exec and spi_execp with -array
219create function tcl_spi_exec(
220    prepare boolean,
221    action text
222)
223returns void language pltcl AS $function$
224set query "select * from (values (1,'foo'),(2,'bar'),(3,'baz')) v(col1,col2)"
225if {$1 == "t"} {
226    set prep [spi_prepare $query {}]
227    spi_execp -array A $prep {
228        elog NOTICE "col1 $A(col1), col2 $A(col2)"
229
230        switch $A(col1) {
231            2 {
232                elog NOTICE "action: $2"
233                switch $2 {
234                    break {
235                        break
236                    }
237                    continue {
238                        continue
239                    }
240                    return {
241                        return
242                    }
243                    error {
244                        error "error message"
245                    }
246                }
247                error "should not get here"
248            }
249        }
250    }
251} else {
252    spi_exec -array A $query {
253        elog NOTICE "col1 $A(col1), col2 $A(col2)"
254
255        switch $A(col1) {
256            2 {
257                elog NOTICE "action: $2"
258                switch $2 {
259                    break {
260                        break
261                    }
262                    continue {
263                        continue
264                    }
265                    return {
266                        return
267                    }
268                    error {
269                        error "error message"
270                    }
271                }
272                error "should not get here"
273            }
274        }
275    }
276}
277elog NOTICE "end of function"
278$function$;
279