/* Call info struct, or NULL in a trigger */
FunctionCallInfo fcinfo;
+ /* Trigger data, if we're in a normal (not event) trigger; else NULL */
+ TriggerData *trigdata;
+
/* Function we're executing (NULL if not yet identified) */
pltcl_proc_desc *prodesc;
const char *result;
int result_Objc;
Tcl_Obj **result_Objv;
- Datum *values;
- bool *nulls;
+
+ call_state->trigdata = trigdata;
/* Connect to SPI manager */
if (SPI_connect() != SPI_OK_CONNECT)
interp = prodesc->interp_desc->interp;
- tupdesc = trigdata->tg_relation->rd_att;
+ tupdesc = RelationGetDescr(trigdata->tg_relation);
/************************************************************
* Create the tcl command to call the internal
errmsg("could not split return value from trigger: %s",
utf_u2e(Tcl_GetStringResult(interp)))));
- if (result_Objc % 2 != 0)
- ereport(ERROR,
- (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
- errmsg("trigger's return list must have even number of elements")));
-
- values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
- nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
- memset(nulls, true, tupdesc->natts * sizeof(bool));
-
- for (i = 0; i < result_Objc; i += 2)
- {
- char *ret_name = utf_u2e(Tcl_GetString(result_Objv[i]));
- char *ret_value = utf_u2e(Tcl_GetString(result_Objv[i + 1]));
- int attnum;
- Oid typinput;
- Oid typioparam;
- FmgrInfo finfo;
-
- /************************************************************
- * Get the attribute number
- *
- * We silently ignore ".tupno", if it's present but doesn't match
- * any actual output column. This allows direct use of a row
- * returned by pltcl_set_tuple_values().
- ************************************************************/
- attnum = SPI_fnumber(tupdesc, ret_name);
- if (attnum == SPI_ERROR_NOATTRIBUTE)
- {
- if (strcmp(ret_name, ".tupno") == 0)
- continue;
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
- errmsg("unrecognized attribute \"%s\"",
- ret_name)));
- }
- if (attnum <= 0)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot set system attribute \"%s\"",
- ret_name)));
-
- /************************************************************
- * Lookup the attribute type's input function
- ************************************************************/
- getTypeInputInfo(tupdesc->attrs[attnum - 1]->atttypid,
- &typinput, &typioparam);
- fmgr_info(typinput, &finfo);
-
- /************************************************************
- * Set the attribute to NOT NULL and convert the contents
- ************************************************************/
- values[attnum - 1] = InputFunctionCall(&finfo,
- ret_value,
- typioparam,
- tupdesc->attrs[attnum - 1]->atttypmod);
- nulls[attnum - 1] = false;
- }
-
- /* Build the modified tuple to return */
- rettup = heap_form_tuple(tupdesc, values, nulls);
-
- pfree(values);
- pfree(nulls);
+ /* Convert function result to tuple */
+ rettup = pltcl_build_tuple_result(interp, result_Objv, result_Objc,
+ call_state);
return rettup;
}
* pltcl_build_tuple_result() - Build a tuple of function's result rowtype
* from a Tcl list of column names and values
*
+ * In a trigger function, we build a tuple of the trigger table's rowtype.
+ *
* Note: this function leaks memory. Even if we made it clean up its own
* mess, there's no way to prevent the datatype input functions it calls
* from leaking. Run it in a short-lived context, unless we're about to
pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc,
pltcl_call_state *call_state)
{
+ TupleDesc tupdesc;
+ AttInMetadata *attinmeta;
char **values;
int i;
+ if (call_state->ret_tupdesc)
+ {
+ tupdesc = call_state->ret_tupdesc;
+ attinmeta = call_state->attinmeta;
+ }
+ else if (call_state->trigdata)
+ {
+ tupdesc = RelationGetDescr(call_state->trigdata->tg_relation);
+ attinmeta = TupleDescGetAttInMetadata(tupdesc);
+ }
+ else
+ {
+ elog(ERROR, "PL/Tcl function does not return a tuple");
+ tupdesc = NULL; /* keep compiler quiet */
+ attinmeta = NULL;
+ }
+
+ values = (char **) palloc0(tupdesc->natts * sizeof(char *));
+
if (kvObjc % 2 != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("column name/value list must have even number of elements")));
- values = (char **) palloc0(call_state->ret_tupdesc->natts * sizeof(char *));
-
for (i = 0; i < kvObjc; i += 2)
{
- char *fieldName = utf_e2u(Tcl_GetString(kvObjv[i]));
- int attn = SPI_fnumber(call_state->ret_tupdesc, fieldName);
+ char *fieldName = utf_u2e(Tcl_GetString(kvObjv[i]));
+ int attn = SPI_fnumber(tupdesc, fieldName);
/*
- * As in pltcl_trigger_handler, silently ignore ".tupno" if it's in
- * the list but doesn't match any column name.
+ * We silently ignore ".tupno", if it's present but doesn't match any
+ * actual output column. This allows direct use of a row returned by
+ * pltcl_set_tuple_values().
*/
if (attn == SPI_ERROR_NOATTRIBUTE)
{
errmsg("cannot set system attribute \"%s\"",
fieldName)));
- values[attn - 1] = utf_e2u(Tcl_GetString(kvObjv[i + 1]));
+ values[attn - 1] = utf_u2e(Tcl_GetString(kvObjv[i + 1]));
}
- return BuildTupleFromCStrings(call_state->attinmeta, values);
+ return BuildTupleFromCStrings(attinmeta, values);
}
/**********************************************************************