Skip to content

Commit de5fed0

Browse files
committed
Merge two copies of tuple-building code in pltcl.c.
Make pltcl_trigger_handler() construct modified tuples using pltcl_build_tuple_result(), rather than its own copy of essentially the same logic. This results in slightly different message wording for the error cases, and in one case a different SQLSTATE, but it seems unlikely that any existing applications are depending on any of those details. While at it, fix a typo in commit 26abb50: pltcl_build_tuple_result was applying encoding conversion in the wrong direction. That would be a back-patchable bug fix, except the code hasn't shipped yet. Jim Nasby, reviewed by me Discussion: https://postgr.es/m/d2c6425a-d9e0-f034-f774-4a872c234d89@BlueTreble.com
1 parent d74ecbc commit de5fed0

File tree

1 file changed

+39
-74
lines changed

1 file changed

+39
-74
lines changed

src/pl/tcl/pltcl.c

Lines changed: 39 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ typedef struct pltcl_call_state
202202
/* Call info struct, or NULL in a trigger */
203203
FunctionCallInfo fcinfo;
204204

205+
/* Trigger data, if we're in a normal (not event) trigger; else NULL */
206+
TriggerData *trigdata;
207+
205208
/* Function we're executing (NULL if not yet identified) */
206209
pltcl_proc_desc *prodesc;
207210

@@ -1000,8 +1003,8 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
10001003
const char *result;
10011004
int result_Objc;
10021005
Tcl_Obj **result_Objv;
1003-
Datum *values;
1004-
bool *nulls;
1006+
1007+
call_state->trigdata = trigdata;
10051008

10061009
/* Connect to SPI manager */
10071010
if (SPI_connect() != SPI_OK_CONNECT)
@@ -1018,7 +1021,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
10181021

10191022
interp = prodesc->interp_desc->interp;
10201023

1021-
tupdesc = trigdata->tg_relation->rd_att;
1024+
tupdesc = RelationGetDescr(trigdata->tg_relation);
10221025

10231026
/************************************************************
10241027
* Create the tcl command to call the internal
@@ -1219,69 +1222,9 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
12191222
errmsg("could not split return value from trigger: %s",
12201223
utf_u2e(Tcl_GetStringResult(interp)))));
12211224

1222-
if (result_Objc % 2 != 0)
1223-
ereport(ERROR,
1224-
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
1225-
errmsg("trigger's return list must have even number of elements")));
1226-
1227-
values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
1228-
nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
1229-
memset(nulls, true, tupdesc->natts * sizeof(bool));
1230-
1231-
for (i = 0; i < result_Objc; i += 2)
1232-
{
1233-
char *ret_name = utf_u2e(Tcl_GetString(result_Objv[i]));
1234-
char *ret_value = utf_u2e(Tcl_GetString(result_Objv[i + 1]));
1235-
int attnum;
1236-
Oid typinput;
1237-
Oid typioparam;
1238-
FmgrInfo finfo;
1239-
1240-
/************************************************************
1241-
* Get the attribute number
1242-
*
1243-
* We silently ignore ".tupno", if it's present but doesn't match
1244-
* any actual output column. This allows direct use of a row
1245-
* returned by pltcl_set_tuple_values().
1246-
************************************************************/
1247-
attnum = SPI_fnumber(tupdesc, ret_name);
1248-
if (attnum == SPI_ERROR_NOATTRIBUTE)
1249-
{
1250-
if (strcmp(ret_name, ".tupno") == 0)
1251-
continue;
1252-
ereport(ERROR,
1253-
(errcode(ERRCODE_UNDEFINED_COLUMN),
1254-
errmsg("unrecognized attribute \"%s\"",
1255-
ret_name)));
1256-
}
1257-
if (attnum <= 0)
1258-
ereport(ERROR,
1259-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1260-
errmsg("cannot set system attribute \"%s\"",
1261-
ret_name)));
1262-
1263-
/************************************************************
1264-
* Lookup the attribute type's input function
1265-
************************************************************/
1266-
getTypeInputInfo(tupdesc->attrs[attnum - 1]->atttypid,
1267-
&typinput, &typioparam);
1268-
fmgr_info(typinput, &finfo);
1269-
1270-
/************************************************************
1271-
* Set the attribute to NOT NULL and convert the contents
1272-
************************************************************/
1273-
values[attnum - 1] = InputFunctionCall(&finfo,
1274-
ret_value,
1275-
typioparam,
1276-
tupdesc->attrs[attnum - 1]->atttypmod);
1277-
nulls[attnum - 1] = false;
1278-
}
1279-
1280-
/* Build the modified tuple to return */
1281-
rettup = heap_form_tuple(tupdesc, values, nulls);
1282-
1283-
pfree(values);
1284-
pfree(nulls);
1225+
/* Convert function result to tuple */
1226+
rettup = pltcl_build_tuple_result(interp, result_Objv, result_Objc,
1227+
call_state);
12851228

12861229
return rettup;
12871230
}
@@ -3014,6 +2957,8 @@ pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc)
30142957
* pltcl_build_tuple_result() - Build a tuple of function's result rowtype
30152958
* from a Tcl list of column names and values
30162959
*
2960+
* In a trigger function, we build a tuple of the trigger table's rowtype.
2961+
*
30172962
* Note: this function leaks memory. Even if we made it clean up its own
30182963
* mess, there's no way to prevent the datatype input functions it calls
30192964
* from leaking. Run it in a short-lived context, unless we're about to
@@ -3023,24 +2968,44 @@ static HeapTuple
30232968
pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc,
30242969
pltcl_call_state *call_state)
30252970
{
2971+
TupleDesc tupdesc;
2972+
AttInMetadata *attinmeta;
30262973
char **values;
30272974
int i;
30282975

2976+
if (call_state->ret_tupdesc)
2977+
{
2978+
tupdesc = call_state->ret_tupdesc;
2979+
attinmeta = call_state->attinmeta;
2980+
}
2981+
else if (call_state->trigdata)
2982+
{
2983+
tupdesc = RelationGetDescr(call_state->trigdata->tg_relation);
2984+
attinmeta = TupleDescGetAttInMetadata(tupdesc);
2985+
}
2986+
else
2987+
{
2988+
elog(ERROR, "PL/Tcl function does not return a tuple");
2989+
tupdesc = NULL; /* keep compiler quiet */
2990+
attinmeta = NULL;
2991+
}
2992+
2993+
values = (char **) palloc0(tupdesc->natts * sizeof(char *));
2994+
30292995
if (kvObjc % 2 != 0)
30302996
ereport(ERROR,
30312997
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
30322998
errmsg("column name/value list must have even number of elements")));
30332999

3034-
values = (char **) palloc0(call_state->ret_tupdesc->natts * sizeof(char *));
3035-
30363000
for (i = 0; i < kvObjc; i += 2)
30373001
{
3038-
char *fieldName = utf_e2u(Tcl_GetString(kvObjv[i]));
3039-
int attn = SPI_fnumber(call_state->ret_tupdesc, fieldName);
3002+
char *fieldName = utf_u2e(Tcl_GetString(kvObjv[i]));
3003+
int attn = SPI_fnumber(tupdesc, fieldName);
30403004

30413005
/*
3042-
* As in pltcl_trigger_handler, silently ignore ".tupno" if it's in
3043-
* the list but doesn't match any column name.
3006+
* We silently ignore ".tupno", if it's present but doesn't match any
3007+
* actual output column. This allows direct use of a row returned by
3008+
* pltcl_set_tuple_values().
30443009
*/
30453010
if (attn == SPI_ERROR_NOATTRIBUTE)
30463011
{
@@ -3058,10 +3023,10 @@ pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc,
30583023
errmsg("cannot set system attribute \"%s\"",
30593024
fieldName)));
30603025

3061-
values[attn - 1] = utf_e2u(Tcl_GetString(kvObjv[i + 1]));
3026+
values[attn - 1] = utf_u2e(Tcl_GetString(kvObjv[i + 1]));
30623027
}
30633028

3064-
return BuildTupleFromCStrings(call_state->attinmeta, values);
3029+
return BuildTupleFromCStrings(attinmeta, values);
30653030
}
30663031

30673032
/**********************************************************************

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy