Skip to content

Commit fd2664d

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 fc8b81a commit fd2664d

File tree

2 files changed

+50
-27
lines changed

2 files changed

+50
-27
lines changed

doc/src/sgml/pltcl.sgml

+35-19
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
@@ -667,21 +669,35 @@ SELECT 'doesn''t' AS ret
667669

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

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

src/pl/tcl/pltcl.c

+15-8
Original file line numberDiff line numberDiff line change
@@ -1118,21 +1118,23 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
11181118
Oid typioparam;
11191119
FmgrInfo finfo;
11201120

1121-
/************************************************************
1122-
* Ignore ".tupno" pseudo elements (see pltcl_set_tuple_values)
1123-
************************************************************/
1124-
if (strcmp(ret_name, ".tupno") == 0)
1125-
continue;
1126-
11271121
/************************************************************
11281122
* Get the attribute number
1123+
*
1124+
* We silently ignore ".tupno", if it's present but doesn't match
1125+
* any actual output column. This allows direct use of a row
1126+
* returned by pltcl_set_tuple_values().
11291127
************************************************************/
11301128
attnum = SPI_fnumber(tupdesc, ret_name);
11311129
if (attnum == SPI_ERROR_NOATTRIBUTE)
1130+
{
1131+
if (strcmp(ret_name, ".tupno") == 0)
1132+
continue;
11321133
ereport(ERROR,
11331134
(errcode(ERRCODE_UNDEFINED_COLUMN),
11341135
errmsg("unrecognized attribute \"%s\"",
11351136
ret_name)));
1137+
}
11361138
if (attnum <= 0)
11371139
ereport(ERROR,
11381140
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -2703,8 +2705,7 @@ pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
27032705
const char *nullname = NULL;
27042706

27052707
/************************************************************
2706-
* Prepare pointers for Tcl_SetVar2() below and in array
2707-
* mode set the .tupno element
2708+
* Prepare pointers for Tcl_SetVar2() below
27082709
************************************************************/
27092710
if (arrayname == NULL)
27102711
{
@@ -2715,6 +2716,12 @@ pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
27152716
{
27162717
arrptr = &arrayname;
27172718
nameptr = &attname;
2719+
2720+
/*
2721+
* When outputting to an array, fill the ".tupno" element with the
2722+
* current tuple number. This will be overridden below if ".tupno" is
2723+
* in use as an actual field name in the rowtype.
2724+
*/
27182725
Tcl_SetVar2Ex(interp, arrayname, ".tupno", Tcl_NewWideIntObj(tupno), 0);
27192726
}
27202727

0 commit comments

Comments
 (0)