Commit 2178cbf4 authored by Tom Lane's avatar Tom Lane

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.
parent fd2664dc
...@@ -870,12 +870,11 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted) ...@@ -870,12 +870,11 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
Tcl_Obj *tcl_newtup; Tcl_Obj *tcl_newtup;
int tcl_rc; int tcl_rc;
int i; int i;
int *modattrs;
Datum *modvalues;
char *modnulls;
int ret_numvals;
const char *result; const char *result;
const char **ret_values; int result_Objc;
Tcl_Obj **result_Objv;
Datum *values;
bool *nulls;
/* Connect to SPI manager */ /* Connect to SPI manager */
if (SPI_connect() != SPI_OK_CONNECT) if (SPI_connect() != SPI_OK_CONNECT)
...@@ -1065,13 +1064,16 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted) ...@@ -1065,13 +1064,16 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
throw_tcl_error(interp, prodesc->user_proname); throw_tcl_error(interp, prodesc->user_proname);
/************************************************************ /************************************************************
* The return value from the procedure might be one of * Exit SPI environment.
* the magic strings OK or SKIP or a list from array get.
* We can check for OK or SKIP without worrying about encoding.
************************************************************/ ************************************************************/
if (SPI_finish() != SPI_OK_FINISH) if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish() failed"); elog(ERROR, "SPI_finish() failed");
/************************************************************
* The return value from the procedure might be one of
* the magic strings OK or SKIP, or a list from array get.
* We can check for OK or SKIP without worrying about encoding.
************************************************************/
result = Tcl_GetStringResult(interp); result = Tcl_GetStringResult(interp);
if (strcmp(result, "OK") == 0) if (strcmp(result, "OK") == 0)
...@@ -1080,39 +1082,29 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted) ...@@ -1080,39 +1082,29 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
return (HeapTuple) NULL; return (HeapTuple) NULL;
/************************************************************ /************************************************************
* Convert the result value from the Tcl interpreter * Otherwise, the return value should be a column name/value list
* and setup structures for SPI_modifytuple(); * specifying the modified tuple to return.
************************************************************/ ************************************************************/
if (Tcl_SplitList(interp, result, if (Tcl_ListObjGetElements(interp, Tcl_GetObjResult(interp),
&ret_numvals, &ret_values) != TCL_OK) &result_Objc, &result_Objv) != TCL_OK)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
errmsg("could not split return value from trigger: %s", errmsg("could not split return value from trigger: %s",
utf_u2e(Tcl_GetStringResult(interp))))); utf_u2e(Tcl_GetStringResult(interp)))));
/* Use a TRY to ensure ret_values will get freed */ if (result_Objc % 2 != 0)
PG_TRY();
{
if (ret_numvals % 2 != 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
errmsg("trigger's return list must have even number of elements"))); errmsg("trigger's return list must have even number of elements")));
modattrs = (int *) palloc(tupdesc->natts * sizeof(int)); values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
modvalues = (Datum *) palloc(tupdesc->natts * sizeof(Datum)); nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
for (i = 0; i < tupdesc->natts; i++) memset(nulls, true, tupdesc->natts * sizeof(bool));
{
modattrs[i] = i + 1;
modvalues[i] = (Datum) NULL;
}
modnulls = palloc(tupdesc->natts);
memset(modnulls, 'n', tupdesc->natts);
for (i = 0; i < ret_numvals; i += 2) for (i = 0; i < result_Objc; i += 2)
{ {
char *ret_name = utf_u2e(ret_values[i]); char *ret_name = utf_u2e(Tcl_GetString(result_Objv[i]));
char *ret_value = utf_u2e(ret_values[i + 1]); char *ret_value = utf_u2e(Tcl_GetString(result_Objv[i + 1]));
int attnum; int attnum;
Oid typinput; Oid typinput;
Oid typioparam; Oid typioparam;
...@@ -1148,8 +1140,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted) ...@@ -1148,8 +1140,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
continue; continue;
/************************************************************ /************************************************************
* Lookup the attribute type in the syscache * Lookup the attribute type's input function
* for the input function
************************************************************/ ************************************************************/
getTypeInputInfo(tupdesc->attrs[attnum - 1]->atttypid, getTypeInputInfo(tupdesc->attrs[attnum - 1]->atttypid,
&typinput, &typioparam); &typinput, &typioparam);
...@@ -1158,30 +1149,18 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted) ...@@ -1158,30 +1149,18 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
/************************************************************ /************************************************************
* Set the attribute to NOT NULL and convert the contents * Set the attribute to NOT NULL and convert the contents
************************************************************/ ************************************************************/
modvalues[attnum - 1] = InputFunctionCall(&finfo, values[attnum - 1] = InputFunctionCall(&finfo,
ret_value, ret_value,
typioparam, typioparam,
tupdesc->attrs[attnum - 1]->atttypmod); tupdesc->attrs[attnum - 1]->atttypmod);
modnulls[attnum - 1] = ' '; nulls[attnum - 1] = false;
} }
rettup = SPI_modifytuple(trigdata->tg_relation, rettup, tupdesc->natts, /* Build the modified tuple to return */
modattrs, modvalues, modnulls); rettup = heap_form_tuple(tupdesc, values, nulls);
pfree(modattrs);
pfree(modvalues);
pfree(modnulls);
if (rettup == NULL) pfree(values);
elog(ERROR, "SPI_modifytuple() failed - RC = %d", SPI_result); pfree(nulls);
}
PG_CATCH();
{
ckfree((char *) ret_values);
PG_RE_THROW();
}
PG_END_TRY();
ckfree((char *) ret_values);
return rettup; return rettup;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment