@@ -202,6 +202,9 @@ typedef struct pltcl_call_state
202202/* Call info struct, or NULL in a trigger */
203203FunctionCallInfo 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) */
206209pltcl_proc_desc * prodesc ;
207210
@@ -1000,8 +1003,8 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
10001003const char * result ;
10011004int result_Objc ;
10021005Tcl_Obj * * result_Objv ;
1003- Datum * values ;
1004- bool * nulls ;
1006+
1007+ call_state -> trigdata = trigdata ;
10051008
10061009/* Connect to SPI manager */
10071010if (SPI_connect ()!= SPI_OK_CONNECT )
@@ -1018,7 +1021,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
10181021
10191022interp = 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,
12191222errmsg ("could not split return value from trigger: %s" ,
12201223utf_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
12861229return 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 ,
30242969pltcl_call_state * call_state )
30252970{
2971+ TupleDesc tupdesc ;
2972+ AttInMetadata * attinmeta ;
30262973char * * values ;
30272974int 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+
30292995if (kvObjc %2 != 0 )
30302996ereport (ERROR ,
30312997(errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
30322998errmsg ("column name/value list must have even number of elements" )));
30332999
3034- values = (char * * )palloc0 (call_state -> ret_tupdesc -> natts * sizeof (char * ));
3035-
30363000for (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 */
30453010if (attn == SPI_ERROR_NOATTRIBUTE )
30463011{
@@ -3058,10 +3023,10 @@ pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc,
30583023errmsg ("cannot set system attribute \"%s\"" ,
30593024fieldName )));
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/**********************************************************************