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.
This commit is contained in:
parent
fd2664dcb7
commit
2178cbf40d
@ -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)
|
|||||||
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)
|
|||||||
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);
|
for (i = 0; i < result_Objc; i += 2)
|
||||||
memset(modnulls, 'n', tupdesc->natts);
|
|
||||||
|
|
||||||
for (i = 0; i < ret_numvals; 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)
|
|||||||
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)
|
|||||||
/************************************************************
|
/************************************************************
|
||||||
* 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(values);
|
||||||
pfree(modvalues);
|
pfree(nulls);
|
||||||
pfree(modnulls);
|
|
||||||
|
|
||||||
if (rettup == NULL)
|
|
||||||
elog(ERROR, "SPI_modifytuple() failed - RC = %d", SPI_result);
|
|
||||||
}
|
|
||||||
PG_CATCH();
|
|
||||||
{
|
|
||||||
ckfree((char *) ret_values);
|
|
||||||
PG_RE_THROW();
|
|
||||||
}
|
|
||||||
PG_END_TRY();
|
|
||||||
ckfree((char *) ret_values);
|
|
||||||
|
|
||||||
return rettup;
|
return rettup;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user