Skip to content

Commit abdc839

Browse files
committed
Rationalize and document pltcl's handling of magic ".tupno" array element.
For a very long time, pltcl's spi_exec and spi_execp commands have had a behavior of storing the current row number as an element of output arrays, but this was never documented. Fix that. For an equally long time, pltcl_trigger_handler had a behavior of silently ignoring ".tupno" as an output column name, evidently so that the result of spi_exec could be used directly as a trigger result tuple. Not sure how useful that really is, but in any case it's bad that it would break attempts to use ".tupno" as an actual column name. We can fix it by not checking for ".tupno" until after we check for a column name match. This comports with the effective behavior of spi_exec[p] that ".tupno" is only magic when you don't have an actual column named that. In passing, wordsmith the description of returning modified tuples from a pltcl trigger. Noted while working on Jim Nasby's patch to support composite results from pltcl. The inability to return trigger tuples using ".tupno" as a column name is a bug, so back-patch to all supported branches.
1 parent 674877e commit abdc839

File tree

2 files changed

+50
-27
lines changed

2 files changed

+50
-27
lines changed

doc/src/sgml/pltcl.sgml

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -296,36 +296,38 @@ $$ LANGUAGE pltcl;
296296
If the command is a <command>SELECT</> statement, the values of the
297297
result columns are placed into Tcl variables named after the columns.
298298
If the <literal>-array</> option is given, the column values are
299-
instead stored into the named associative array, with the
300-
column names used as array indexes.
299+
instead stored into elements of the named associative array, with the
300+
column names used as array indexes. In addition, the current row
301+
number within the result (counting from zero) is stored into the array
302+
element named <quote><literal>.tupno</></quote>, unless that name is
303+
in use as a column name in the result.
301304
</para>
302305
<para>
303306
If the command is a <command>SELECT</> statement and no <replaceable>loop-body</>
304307
script is given, then only the first row of results are stored into
305-
Tcl variables; remaining rows, if any, are ignored. No storing occurs
306-
if the
307-
query returns no rows. (This case can be detected by checking the
308-
result of <function>spi_exec</function>.) For example:
308+
Tcl variables or array elements; remaining rows, if any, are ignored.
309+
No storing occurs if the query returns no rows. (This case can be
310+
detected by checking the result of <function>spi_exec</function>.)
311+
For example:
309312
<programlisting>
310313
spi_exec "SELECT count(*) AS cnt FROM pg_proc"
311314
</programlisting>
312-
313315
will set the Tcl variable <literal>$cnt</> to the number of rows in
314316
the <structname>pg_proc</> system catalog.
315317
</para>
316318
<para>
317319
If the optional <replaceable>loop-body</> argument is given, it is
318320
a piece of Tcl script that is executed once for each row in the
319321
query result. (<replaceable>loop-body</> is ignored if the given
320-
command is not a <command>SELECT</>.) The values of the current row's columns
321-
are stored into Tcl variables before each iteration. For example:
322-
322+
command is not a <command>SELECT</>.)
323+
The values of the current row's columns
324+
are stored into Tcl variables or array elements before each iteration.
325+
For example:
323326
<programlisting>
324327
spi_exec -array C "SELECT * FROM pg_class" {
325328
elog DEBUG "have table $C(relname)"
326329
}
327330
</programlisting>
328-
329331
will print a log message for every row of <literal>pg_class</>. This
330332
feature works similarly to other Tcl looping constructs; in
331333
particular <literal>continue</> and <literal>break</> work in the
@@ -666,21 +668,35 @@ SELECT 'doesn''t' AS ret
666668

667669
<para>
668670
The return value from a trigger procedure can be one of the strings
669-
<literal>OK</> or <literal>SKIP</>, or a list as returned by the
670-
<literal>array get</> Tcl command. If the return value is <literal>OK</>,
671-
the operation (<command>INSERT</>/<command>UPDATE</>/<command>DELETE</>) that fired the trigger will proceed
671+
<literal>OK</> or <literal>SKIP</>, or a list of column name/value pairs.
672+
If the return value is <literal>OK</>,
673+
the operation (<command>INSERT</>/<command>UPDATE</>/<command>DELETE</>)
674+
that fired the trigger will proceed
672675
normally. <literal>SKIP</> tells the trigger manager to silently suppress
673676
the operation for this row. If a list is returned, it tells PL/Tcl to
674-
return a modified row to the trigger manager. This is only meaningful
677+
return a modified row to the trigger manager; the contents of the
678+
modified row are specified by the column names and values in the list.
679+
Any columns not mentioned in the list are set to null.
680+
Returning a modified row is only meaningful
675681
for row-level <literal>BEFORE</> <command>INSERT</> or <command>UPDATE</>
676-
triggers for which the modified row will be inserted instead of the one
682+
triggers, for which the modified row will be inserted instead of the one
677683
given in <varname>$NEW</>; or for row-level <literal>INSTEAD OF</>
678684
<command>INSERT</> or <command>UPDATE</> triggers where the returned row
679-
is used to support <command>INSERT RETURNING</> and
680-
<command>UPDATE RETURNING</> commands. The return value is ignored for
681-
other types of triggers.
685+
is used as the source data for <command>INSERT RETURNING</> or
686+
<command>UPDATE RETURNING</> clauses.
687+
In row-level <literal>BEFORE</> <command>DELETE</> or <literal>INSTEAD
688+
OF</> <command>DELETE</> triggers, returning a modified row has the same
689+
effect as returning <literal>OK</>, that is the operation proceeds.
690+
The trigger return value is ignored for all other types of triggers.
682691
</para>
683692

693+
<tip>
694+
<para>
695+
The result list can be made from an array representation of the
696+
modified tuple with the <literal>array get</> Tcl command.
697+
</para>
698+
</tip>
699+
684700
<para>
685701
Here's a little example trigger procedure that forces an integer value
686702
in a table to keep track of the number of updates that are performed on the

src/pl/tcl/pltcl.c

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,21 +1078,23 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
10781078
Oid typioparam;
10791079
FmgrInfo finfo;
10801080

1081-
/************************************************************
1082-
* Ignore ".tupno" pseudo elements (see pltcl_set_tuple_values)
1083-
************************************************************/
1084-
if (strcmp(ret_name, ".tupno") == 0)
1085-
continue;
1086-
10871081
/************************************************************
10881082
* Get the attribute number
1083+
*
1084+
* We silently ignore ".tupno", if it's present but doesn't match
1085+
* any actual output column. This allows direct use of a row
1086+
* returned by pltcl_set_tuple_values().
10891087
************************************************************/
10901088
attnum = SPI_fnumber(tupdesc, ret_name);
10911089
if (attnum == SPI_ERROR_NOATTRIBUTE)
1090+
{
1091+
if (strcmp(ret_name, ".tupno") == 0)
1092+
continue;
10921093
ereport(ERROR,
10931094
(errcode(ERRCODE_UNDEFINED_COLUMN),
10941095
errmsg("unrecognized attribute \"%s\"",
10951096
ret_name)));
1097+
}
10961098
if (attnum <= 0)
10971099
ereport(ERROR,
10981100
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -2505,8 +2507,7 @@ pltcl_set_tuple_values(Tcl_Interp *interp, CONST84 char *arrayname,
25052507
CONST84 char *nullname = NULL;
25062508

25072509
/************************************************************
2508-
* Prepare pointers for Tcl_SetVar2() below and in array
2509-
* mode set the .tupno element
2510+
* Prepare pointers for Tcl_SetVar2() below
25102511
************************************************************/
25112512
if (arrayname == NULL)
25122513
{
@@ -2517,6 +2518,12 @@ pltcl_set_tuple_values(Tcl_Interp *interp, CONST84 char *arrayname,
25172518
{
25182519
arrptr = &arrayname;
25192520
nameptr = &attname;
2521+
2522+
/*
2523+
* When outputting to an array, fill the ".tupno" element with the
2524+
* current tuple number. This will be overridden below if ".tupno" is
2525+
* in use as an actual field name in the rowtype.
2526+
*/
25202527
snprintf(buf, sizeof(buf), "%d", tupno);
25212528
Tcl_SetVar2(interp, arrayname, ".tupno", buf, 0);
25222529
}

0 commit comments

Comments
 (0)