Skip to content

Commit 2178cbf

Browse files
committed
Modernize result-tuple construction in pltcl_trigger_handler().
Use Tcl_ListObjGetElements instead of Tcl_SplitList. Aside from being possibly more efficient in its own right, this means we are no longer responsible for freeing a malloc'd result array, so we can get rid of a PG_TRY/PG_CATCH block. Use heap_form_tuple instead of SPI_modifytuple. We don't need the extra generality of the latter, since we're always replacing all columns. Nor do we need its memory-context-munging, since at this point we're already out of the SPI environment. Per comparison of this code to tuple-building code submitted by Jim Nasby. I've abandoned the thought of merging the two cases into a single routine, but we may as well make the older code simpler and faster where we can.
1 parent fd2664d commit 2178cbf

File tree

1 file changed

+73
-94
lines changed

1 file changed

+73
-94
lines changed

src/pl/tcl/pltcl.c

+73-94
Original file line numberDiff line numberDiff line change
@@ -870,12 +870,11 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
870870
Tcl_Obj *tcl_newtup;
871871
int tcl_rc;
872872
int i;
873-
int *modattrs;
874-
Datum *modvalues;
875-
char *modnulls;
876-
int ret_numvals;
877873
const char *result;
878-
const char **ret_values;
874+
int result_Objc;
875+
Tcl_Obj **result_Objv;
876+
Datum *values;
877+
bool *nulls;
879878

880879
/* Connect to SPI manager */
881880
if (SPI_connect() != SPI_OK_CONNECT)
@@ -1065,13 +1064,16 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
10651064
throw_tcl_error(interp, prodesc->user_proname);
10661065

10671066
/************************************************************
1068-
* The return value from the procedure might be one of
1069-
* the magic strings OK or SKIP or a list from array get.
1070-
* We can check for OK or SKIP without worrying about encoding.
1067+
* Exit SPI environment.
10711068
************************************************************/
10721069
if (SPI_finish() != SPI_OK_FINISH)
10731070
elog(ERROR, "SPI_finish() failed");
10741071

1072+
/************************************************************
1073+
* The return value from the procedure might be one of
1074+
* the magic strings OK or SKIP, or a list from array get.
1075+
* We can check for OK or SKIP without worrying about encoding.
1076+
************************************************************/
10751077
result = Tcl_GetStringResult(interp);
10761078

10771079
if (strcmp(result, "OK") == 0)
@@ -1080,108 +1082,85 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
10801082
return (HeapTuple) NULL;
10811083

10821084
/************************************************************
1083-
* Convert the result value from the Tcl interpreter
1084-
* and setup structures for SPI_modifytuple();
1085+
* Otherwise, the return value should be a column name/value list
1086+
* specifying the modified tuple to return.
10851087
************************************************************/
1086-
if (Tcl_SplitList(interp, result,
1087-
&ret_numvals, &ret_values) != TCL_OK)
1088+
if (Tcl_ListObjGetElements(interp, Tcl_GetObjResult(interp),
1089+
&result_Objc, &result_Objv) != TCL_OK)
10881090
ereport(ERROR,
10891091
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
10901092
errmsg("could not split return value from trigger: %s",
10911093
utf_u2e(Tcl_GetStringResult(interp)))));
10921094

1093-
/* Use a TRY to ensure ret_values will get freed */
1094-
PG_TRY();
1095-
{
1096-
if (ret_numvals % 2 != 0)
1097-
ereport(ERROR,
1098-
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
1099-
errmsg("trigger's return list must have even number of elements")));
1095+
if (result_Objc % 2 != 0)
1096+
ereport(ERROR,
1097+
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
1098+
errmsg("trigger's return list must have even number of elements")));
11001099

1101-
modattrs = (int *) palloc(tupdesc->natts * sizeof(int));
1102-
modvalues = (Datum *) palloc(tupdesc->natts * sizeof(Datum));
1103-
for (i = 0; i < tupdesc->natts; i++)
1104-
{
1105-
modattrs[i] = i + 1;
1106-
modvalues[i] = (Datum) NULL;
1107-
}
1100+
values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
1101+
nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
1102+
memset(nulls, true, tupdesc->natts * sizeof(bool));
11081103

1109-
modnulls = palloc(tupdesc->natts);
1110-
memset(modnulls, 'n', tupdesc->natts);
1104+
for (i = 0; i < result_Objc; i += 2)
1105+
{
1106+
char *ret_name = utf_u2e(Tcl_GetString(result_Objv[i]));
1107+
char *ret_value = utf_u2e(Tcl_GetString(result_Objv[i + 1]));
1108+
int attnum;
1109+
Oid typinput;
1110+
Oid typioparam;
1111+
FmgrInfo finfo;
11111112

1112-
for (i = 0; i < ret_numvals; i += 2)
1113+
/************************************************************
1114+
* Get the attribute number
1115+
*
1116+
* We silently ignore ".tupno", if it's present but doesn't match
1117+
* any actual output column. This allows direct use of a row
1118+
* returned by pltcl_set_tuple_values().
1119+
************************************************************/
1120+
attnum = SPI_fnumber(tupdesc, ret_name);
1121+
if (attnum == SPI_ERROR_NOATTRIBUTE)
11131122
{
1114-
char *ret_name = utf_u2e(ret_values[i]);
1115-
char *ret_value = utf_u2e(ret_values[i + 1]);
1116-
int attnum;
1117-
Oid typinput;
1118-
Oid typioparam;
1119-
FmgrInfo finfo;
1120-
1121-
/************************************************************
1122-
* 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().
1127-
************************************************************/
1128-
attnum = SPI_fnumber(tupdesc, ret_name);
1129-
if (attnum == SPI_ERROR_NOATTRIBUTE)
1130-
{
1131-
if (strcmp(ret_name, ".tupno") == 0)
1132-
continue;
1133-
ereport(ERROR,
1134-
(errcode(ERRCODE_UNDEFINED_COLUMN),
1135-
errmsg("unrecognized attribute \"%s\"",
1136-
ret_name)));
1137-
}
1138-
if (attnum <= 0)
1139-
ereport(ERROR,
1140-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1141-
errmsg("cannot set system attribute \"%s\"",
1142-
ret_name)));
1143-
1144-
/************************************************************
1145-
* Ignore dropped columns
1146-
************************************************************/
1147-
if (tupdesc->attrs[attnum - 1]->attisdropped)
1123+
if (strcmp(ret_name, ".tupno") == 0)
11481124
continue;
1149-
1150-
/************************************************************
1151-
* Lookup the attribute type in the syscache
1152-
* for the input function
1153-
************************************************************/
1154-
getTypeInputInfo(tupdesc->attrs[attnum - 1]->atttypid,
1155-
&typinput, &typioparam);
1156-
fmgr_info(typinput, &finfo);
1157-
1158-
/************************************************************
1159-
* Set the attribute to NOT NULL and convert the contents
1160-
************************************************************/
1161-
modvalues[attnum - 1] = InputFunctionCall(&finfo,
1162-
ret_value,
1163-
typioparam,
1164-
tupdesc->attrs[attnum - 1]->atttypmod);
1165-
modnulls[attnum - 1] = ' ';
1125+
ereport(ERROR,
1126+
(errcode(ERRCODE_UNDEFINED_COLUMN),
1127+
errmsg("unrecognized attribute \"%s\"",
1128+
ret_name)));
11661129
}
1130+
if (attnum <= 0)
1131+
ereport(ERROR,
1132+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1133+
errmsg("cannot set system attribute \"%s\"",
1134+
ret_name)));
11671135

1168-
rettup = SPI_modifytuple(trigdata->tg_relation, rettup, tupdesc->natts,
1169-
modattrs, modvalues, modnulls);
1136+
/************************************************************
1137+
* Ignore dropped columns
1138+
************************************************************/
1139+
if (tupdesc->attrs[attnum - 1]->attisdropped)
1140+
continue;
11701141

1171-
pfree(modattrs);
1172-
pfree(modvalues);
1173-
pfree(modnulls);
1142+
/************************************************************
1143+
* Lookup the attribute type's input function
1144+
************************************************************/
1145+
getTypeInputInfo(tupdesc->attrs[attnum - 1]->atttypid,
1146+
&typinput, &typioparam);
1147+
fmgr_info(typinput, &finfo);
11741148

1175-
if (rettup == NULL)
1176-
elog(ERROR, "SPI_modifytuple() failed - RC = %d", SPI_result);
1177-
}
1178-
PG_CATCH();
1179-
{
1180-
ckfree((char *) ret_values);
1181-
PG_RE_THROW();
1149+
/************************************************************
1150+
* Set the attribute to NOT NULL and convert the contents
1151+
************************************************************/
1152+
values[attnum - 1] = InputFunctionCall(&finfo,
1153+
ret_value,
1154+
typioparam,
1155+
tupdesc->attrs[attnum - 1]->atttypmod);
1156+
nulls[attnum - 1] = false;
11821157
}
1183-
PG_END_TRY();
1184-
ckfree((char *) ret_values);
1158+
1159+
/* Build the modified tuple to return */
1160+
rettup = heap_form_tuple(tupdesc, values, nulls);
1161+
1162+
pfree(values);
1163+
pfree(nulls);
11851164

11861165
return rettup;
11871166
}

0 commit comments

Comments
 (0)