diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c index b0d9e41..25d959e 100644 --- a/src/pl/tcl/pltcl.c +++ b/src/pl/tcl/pltcl.c @@ -301,7 +301,8 @@ static void pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname, static Tcl_Obj *pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc); static HeapTuple pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc, - pltcl_call_state *call_state); + pltcl_call_state *call_state, + TupleDesc tg_tupdesc); static void pltcl_init_tuple_store(pltcl_call_state *call_state); @@ -966,7 +967,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state, throw_tcl_error(interp, prodesc->user_proname); tup = pltcl_build_tuple_result(interp, resultObjv, resultObjc, - call_state); + call_state, NULL); retval = HeapTupleGetDatum(tup); } else @@ -1000,8 +1001,6 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state, const char *result; int result_Objc; Tcl_Obj **result_Objv; - Datum *values; - bool *nulls; /* Connect to SPI manager */ if (SPI_connect() != SPI_OK_CONNECT) @@ -1219,70 +1218,9 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state, 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, tupdesc); return rettup; } @@ -2183,7 +2121,7 @@ pltcl_returnnext(ClientData cdata, Tcl_Interp *interp, HeapTuple tuple; tuple = pltcl_build_tuple_result(interp, rowObjv, rowObjc, - call_state); + call_state, NULL); tuplestore_puttuple(call_state->tuple_store, tuple); } } @@ -3010,6 +2948,8 @@ pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc) * pltcl_build_tuple_result() - Build a tuple of function's result rowtype * from a Tcl list of column names and values * + * If not called from the trigger handler, tg_tupdesc must be NULL! + * * 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 @@ -3017,8 +2957,9 @@ pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc) **********************************************************************/ static HeapTuple pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc, - pltcl_call_state *call_state) + pltcl_call_state *call_state, TupleDesc tg_tupdesc) { + TupleDesc tupdesc; char **values; int i; @@ -3027,12 +2968,17 @@ pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc, (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 *)); + if (tg_tupdesc) + tupdesc = tg_tupdesc; + else + tupdesc = call_state->ret_tupdesc; + + values = (char **) palloc0(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); + int attn = SPI_fnumber(tupdesc, fieldName); /* * As in pltcl_trigger_handler, silently ignore ".tupno" if it's in @@ -3057,7 +3003,10 @@ pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc, values[attn - 1] = utf_e2u(Tcl_GetString(kvObjv[i + 1])); } - return BuildTupleFromCStrings(call_state->attinmeta, values); + if (tg_tupdesc) + return BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupdesc), values); + else + return BuildTupleFromCStrings(call_state->attinmeta, values); } /**********************************************************************