Skip to content

Commit 26abb50

Browse files
committed
Support PL/Tcl functions that return composite types and/or sets.
Jim Nasby, rather heavily editorialized by me Patch: <[email protected]>
1 parent 2178cbf commit 26abb50

File tree

6 files changed

+545
-81
lines changed

6 files changed

+545
-81
lines changed

doc/src/sgml/pltcl.sgml

+56-17
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,11 @@ $$ LANGUAGE pltcl;
9494

9595
<para>
9696
The body of the function is simply a piece of Tcl script.
97-
When the function is called, the argument values are passed as
98-
variables <literal>$1</literal> ... <literal>$<replaceable>n</replaceable></literal> to the
99-
Tcl script. The result is returned
100-
from the Tcl code in the usual way, with a <literal>return</literal>
101-
statement.
97+
When the function is called, the argument values are passed to the
98+
Tcl script as variables named <literal>1</literal>
99+
... <literal><replaceable>n</replaceable></literal>. The result is
100+
returned from the Tcl code in the usual way, with
101+
a <literal>return</literal> statement.
102102
</para>
103103

104104
<para>
@@ -173,17 +173,57 @@ $$ LANGUAGE pltcl;
173173
</para>
174174

175175
<para>
176-
There is currently no support for returning a composite-type
177-
result value, nor for returning sets.
176+
PL/Tcl functions can return composite-type results, too. To do this,
177+
the Tcl code must return a list of column name/value pairs matching
178+
the expected result type. Any column names omitted from the list
179+
are returned as nulls, and an error is raised if there are unexpected
180+
column names. Here is an example:
181+
182+
<programlisting>
183+
CREATE FUNCTION square_cube(in int, out squared int, out cubed int) AS $$
184+
return [list squared [expr {$1 * $1}] cubed [expr {$1 * $1 * $1}]]
185+
$$ LANGUAGE pltcl;
186+
</programlisting>
178187
</para>
179188

189+
<tip>
190+
<para>
191+
The result list can be made from an array representation of the
192+
desired tuple with the <literal>array get</> Tcl command. For example:
193+
194+
<programlisting>
195+
CREATE FUNCTION raise_pay(employee, delta int) RETURNS employee AS $$
196+
set 1(salary) [expr {$1(salary) + $2}]
197+
return [array get 1]
198+
$$ LANGUAGE pltcl;
199+
</programlisting>
200+
</para>
201+
</tip>
202+
180203
<para>
181-
<application>PL/Tcl</> does not currently have full support for
182-
domain types: it treats a domain the same as the underlying scalar
183-
type. This means that constraints associated with the domain will
184-
not be enforced. This is not an issue for function arguments, but
185-
it is a hazard if you declare a <application>PL/Tcl</> function
186-
as returning a domain type.
204+
PL/Tcl functions can return sets. To do this, the Tcl code should
205+
call <function>return_next</function> once per row to be returned,
206+
passing either the appropriate value when returning a scalar type,
207+
or a list of column name/value pairs when returning a composite type.
208+
Here is an example returning a scalar type:
209+
210+
<programlisting>
211+
CREATE FUNCTION sequence(int, int) RETURNS SETOF int AS $$
212+
for {set i $1} {$i &lt; $2} {incr i} {
213+
return_next $i
214+
}
215+
$$ LANGUAGE pltcl;
216+
</programlisting>
217+
218+
and here is one returning a composite type:
219+
220+
<programlisting>
221+
CREATE FUNCTION table_of_squares(int, int) RETURNS TABLE (x int, x2 int) AS $$
222+
for {set i $1} {$i &lt; $2} {incr i} {
223+
return_next [list x $i x2 [expr {$i * $i}]]
224+
}
225+
$$ LANGUAGE pltcl;
226+
</programlisting>
187227
</para>
188228

189229
</sect1>
@@ -195,10 +235,9 @@ $$ LANGUAGE pltcl;
195235
The argument values supplied to a PL/Tcl function's code are simply
196236
the input arguments converted to text form (just as if they had been
197237
displayed by a <command>SELECT</> statement). Conversely, the
198-
<literal>return</>
199-
command will accept any string that is acceptable input format for
200-
the function's declared return type. So, within the PL/Tcl function,
201-
all values are just text strings.
238+
<literal>return</> and <literal>return_next</> commands will accept
239+
any string that is acceptable input format for the function's declared
240+
result type, or for the specified column of a composite result type.
202241
</para>
203242

204243
</sect1>

src/pl/tcl/expected/pltcl_queries.out

+61
Original file line numberDiff line numberDiff line change
@@ -303,3 +303,64 @@ select tcl_lastoid('t2') > 0;
303303
t
304304
(1 row)
305305

306+
-- test some error cases
307+
CREATE FUNCTION tcl_error(OUT a int, OUT b int) AS $$return {$$ LANGUAGE pltcl;
308+
SELECT tcl_error();
309+
ERROR: missing close-brace
310+
CREATE FUNCTION bad_record(OUT a text, OUT b text) AS $$return [list a]$$ LANGUAGE pltcl;
311+
SELECT bad_record();
312+
ERROR: column name/value list must have even number of elements
313+
CREATE FUNCTION bad_field(OUT a text, OUT b text) AS $$return [list a 1 b 2 cow 3]$$ LANGUAGE pltcl;
314+
SELECT bad_field();
315+
ERROR: column name/value list contains nonexistent column name "cow"
316+
-- test compound return
317+
select * from tcl_test_cube_squared(5);
318+
squared | cubed
319+
---------+-------
320+
25 | 125
321+
(1 row)
322+
323+
-- test SRF
324+
select * from tcl_test_squared_rows(0,5);
325+
x | y
326+
---+----
327+
0 | 0
328+
1 | 1
329+
2 | 4
330+
3 | 9
331+
4 | 16
332+
(5 rows)
333+
334+
select * from tcl_test_sequence(0,5) as a;
335+
a
336+
---
337+
0
338+
1
339+
2
340+
3
341+
4
342+
(5 rows)
343+
344+
select 1, tcl_test_sequence(0,5);
345+
?column? | tcl_test_sequence
346+
----------+-------------------
347+
1 | 0
348+
1 | 1
349+
1 | 2
350+
1 | 3
351+
1 | 4
352+
(5 rows)
353+
354+
CREATE FUNCTION non_srf() RETURNS int AS $$return_next 1$$ LANGUAGE pltcl;
355+
select non_srf();
356+
ERROR: return_next cannot be used in non-set-returning functions
357+
CREATE FUNCTION bad_record_srf(OUT a text, OUT b text) RETURNS SETOF record AS $$
358+
return_next [list a]
359+
$$ LANGUAGE pltcl;
360+
SELECT bad_record_srf();
361+
ERROR: column name/value list must have even number of elements
362+
CREATE FUNCTION bad_field_srf(OUT a text, OUT b text) RETURNS SETOF record AS $$
363+
return_next [list a 1 b 2 cow 3]
364+
$$ LANGUAGE pltcl;
365+
SELECT bad_field_srf();
366+
ERROR: column name/value list contains nonexistent column name "cow"

src/pl/tcl/expected/pltcl_setup.out

+13
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,19 @@ NOTICE: tclsnitch: ddl_command_start DROP TABLE
555555
NOTICE: tclsnitch: ddl_command_end DROP TABLE
556556
drop event trigger tcl_a_snitch;
557557
drop event trigger tcl_b_snitch;
558+
CREATE FUNCTION tcl_test_cube_squared(in int, out squared int, out cubed int) AS $$
559+
return [list squared [expr {$1 * $1}] cubed [expr {$1 * $1 * $1}]]
560+
$$ language pltcl;
561+
CREATE FUNCTION tcl_test_squared_rows(int,int) RETURNS TABLE (x int, y int) AS $$
562+
for {set i $1} {$i < $2} {incr i} {
563+
return_next [list y [expr {$i * $i}] x $i]
564+
}
565+
$$ language pltcl;
566+
CREATE FUNCTION tcl_test_sequence(int,int) RETURNS SETOF int AS $$
567+
for {set i $1} {$i < $2} {incr i} {
568+
return_next $i
569+
}
570+
$$ language pltcl;
558571
-- test use of errorCode in error handling
559572
create function tcl_error_handling_test() returns text as $$
560573
global errorCode

0 commit comments

Comments
 (0)