@@ -202,6 +202,9 @@ typedef struct pltcl_call_state
202202 /* Call info struct, or NULL in a trigger */
203203 FunctionCallInfo fcinfo ;
204204
205+ /* Trigger data, if we're in a normal (not event) trigger; else NULL */
206+ TriggerData * trigdata ;
207+
205208 /* Function we're executing (NULL if not yet identified) */
206209 pltcl_proc_desc * prodesc ;
207210
@@ -1000,8 +1003,8 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
10001003 const char * result ;
10011004 int result_Objc ;
10021005 Tcl_Obj * * result_Objv ;
1003- Datum * values ;
1004- bool * nulls ;
1006+
1007+ call_state -> trigdata = trigdata ;
10051008
10061009 /* Connect to SPI manager */
10071010 if (SPI_connect () != SPI_OK_CONNECT )
@@ -1018,7 +1021,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
10181021
10191022 interp = prodesc -> interp_desc -> interp ;
10201023
1021- tupdesc = trigdata -> tg_relation -> rd_att ;
1024+ tupdesc = RelationGetDescr ( trigdata -> tg_relation ) ;
10221025
10231026 /************************************************************
10241027 * Create the tcl command to call the internal
@@ -1219,69 +1222,9 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
12191222 errmsg ("could not split return value from trigger: %s" ,
12201223 utf_u2e (Tcl_GetStringResult (interp )))));
12211224
1222- if (result_Objc % 2 != 0 )
1223- ereport (ERROR ,
1224- (errcode (ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED ),
1225- errmsg ("trigger's return list must have even number of elements" )));
1226-
1227- values = (Datum * ) palloc0 (tupdesc -> natts * sizeof (Datum ));
1228- nulls = (bool * ) palloc (tupdesc -> natts * sizeof (bool ));
1229- memset (nulls , true, tupdesc -> natts * sizeof (bool ));
1230-
1231- for (i = 0 ; i < result_Objc ; i += 2 )
1232- {
1233- char * ret_name = utf_u2e (Tcl_GetString (result_Objv [i ]));
1234- char * ret_value = utf_u2e (Tcl_GetString (result_Objv [i + 1 ]));
1235- int attnum ;
1236- Oid typinput ;
1237- Oid typioparam ;
1238- FmgrInfo finfo ;
1239-
1240- /************************************************************
1241- * Get the attribute number
1242- *
1243- * We silently ignore ".tupno", if it's present but doesn't match
1244- * any actual output column. This allows direct use of a row
1245- * returned by pltcl_set_tuple_values().
1246- ************************************************************/
1247- attnum = SPI_fnumber (tupdesc , ret_name );
1248- if (attnum == SPI_ERROR_NOATTRIBUTE )
1249- {
1250- if (strcmp (ret_name , ".tupno" ) == 0 )
1251- continue ;
1252- ereport (ERROR ,
1253- (errcode (ERRCODE_UNDEFINED_COLUMN ),
1254- errmsg ("unrecognized attribute \"%s\"" ,
1255- ret_name )));
1256- }
1257- if (attnum <= 0 )
1258- ereport (ERROR ,
1259- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
1260- errmsg ("cannot set system attribute \"%s\"" ,
1261- ret_name )));
1262-
1263- /************************************************************
1264- * Lookup the attribute type's input function
1265- ************************************************************/
1266- getTypeInputInfo (tupdesc -> attrs [attnum - 1 ]-> atttypid ,
1267- & typinput , & typioparam );
1268- fmgr_info (typinput , & finfo );
1269-
1270- /************************************************************
1271- * Set the attribute to NOT NULL and convert the contents
1272- ************************************************************/
1273- values [attnum - 1 ] = InputFunctionCall (& finfo ,
1274- ret_value ,
1275- typioparam ,
1276- tupdesc -> attrs [attnum - 1 ]-> atttypmod );
1277- nulls [attnum - 1 ] = false;
1278- }
1279-
1280- /* Build the modified tuple to return */
1281- rettup = heap_form_tuple (tupdesc , values , nulls );
1282-
1283- pfree (values );
1284- pfree (nulls );
1225+ /* Convert function result to tuple */
1226+ rettup = pltcl_build_tuple_result (interp , result_Objv , result_Objc ,
1227+ call_state );
12851228
12861229 return rettup ;
12871230}
@@ -3014,6 +2957,8 @@ pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc)
30142957 * pltcl_build_tuple_result() - Build a tuple of function's result rowtype
30152958 * from a Tcl list of column names and values
30162959 *
2960+ * In a trigger function, we build a tuple of the trigger table's rowtype.
2961+ *
30172962 * Note: this function leaks memory. Even if we made it clean up its own
30182963 * mess, there's no way to prevent the datatype input functions it calls
30192964 * from leaking. Run it in a short-lived context, unless we're about to
@@ -3023,24 +2968,44 @@ static HeapTuple
30232968pltcl_build_tuple_result (Tcl_Interp * interp , Tcl_Obj * * kvObjv , int kvObjc ,
30242969 pltcl_call_state * call_state )
30252970{
2971+ TupleDesc tupdesc ;
2972+ AttInMetadata * attinmeta ;
30262973 char * * values ;
30272974 int i ;
30282975
2976+ if (call_state -> ret_tupdesc )
2977+ {
2978+ tupdesc = call_state -> ret_tupdesc ;
2979+ attinmeta = call_state -> attinmeta ;
2980+ }
2981+ else if (call_state -> trigdata )
2982+ {
2983+ tupdesc = RelationGetDescr (call_state -> trigdata -> tg_relation );
2984+ attinmeta = TupleDescGetAttInMetadata (tupdesc );
2985+ }
2986+ else
2987+ {
2988+ elog (ERROR , "PL/Tcl function does not return a tuple" );
2989+ tupdesc = NULL ; /* keep compiler quiet */
2990+ attinmeta = NULL ;
2991+ }
2992+
2993+ values = (char * * ) palloc0 (tupdesc -> natts * sizeof (char * ));
2994+
30292995 if (kvObjc % 2 != 0 )
30302996 ereport (ERROR ,
30312997 (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
30322998 errmsg ("column name/value list must have even number of elements" )));
30332999
3034- values = (char * * ) palloc0 (call_state -> ret_tupdesc -> natts * sizeof (char * ));
3035-
30363000 for (i = 0 ; i < kvObjc ; i += 2 )
30373001 {
3038- char * fieldName = utf_e2u (Tcl_GetString (kvObjv [i ]));
3039- int attn = SPI_fnumber (call_state -> ret_tupdesc , fieldName );
3002+ char * fieldName = utf_u2e (Tcl_GetString (kvObjv [i ]));
3003+ int attn = SPI_fnumber (tupdesc , fieldName );
30403004
30413005 /*
3042- * As in pltcl_trigger_handler, silently ignore ".tupno" if it's in
3043- * the list but doesn't match any column name.
3006+ * We silently ignore ".tupno", if it's present but doesn't match any
3007+ * actual output column. This allows direct use of a row returned by
3008+ * pltcl_set_tuple_values().
30443009 */
30453010 if (attn == SPI_ERROR_NOATTRIBUTE )
30463011 {
@@ -3058,10 +3023,10 @@ pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc,
30583023 errmsg ("cannot set system attribute \"%s\"" ,
30593024 fieldName )));
30603025
3061- values [attn - 1 ] = utf_e2u (Tcl_GetString (kvObjv [i + 1 ]));
3026+ values [attn - 1 ] = utf_u2e (Tcl_GetString (kvObjv [i + 1 ]));
30623027 }
30633028
3064- return BuildTupleFromCStrings (call_state -> attinmeta , values );
3029+ return BuildTupleFromCStrings (attinmeta , values );
30653030}
30663031
30673032/**********************************************************************
0 commit comments