Modernize result-tuple construction in pltcl_trigger_handler().
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 6 Nov 2016 21:09:57 +0000 (16:09 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 6 Nov 2016 21:09:57 +0000 (16:09 -0500)
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.

src/pl/tcl/pltcl.c

index 44fcf68054f718a37010284c032a8bda62e5dbab..97d1f7ef7d3746936c8e68f91ebc5b2f3f5aa60c 100644 (file)
@@ -870,12 +870,11 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
        Tcl_Obj    *tcl_newtup;
        int                     tcl_rc;
        int                     i;
-       int                *modattrs;
-       Datum      *modvalues;
-       char       *modnulls;
-       int                     ret_numvals;
        const char *result;
-       const char **ret_values;
+       int                     result_Objc;
+       Tcl_Obj   **result_Objv;
+       Datum      *values;
+       bool       *nulls;
 
        /* Connect to SPI manager */
        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);
 
        /************************************************************
-        * 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.
+        * Exit SPI environment.
         ************************************************************/
        if (SPI_finish() != SPI_OK_FINISH)
                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);
 
        if (strcmp(result, "OK") == 0)
@@ -1080,108 +1082,85 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
                return (HeapTuple) NULL;
 
        /************************************************************
-        * Convert the result value from the Tcl interpreter
-        * and setup structures for SPI_modifytuple();
+        * Otherwise, the return value should be a column name/value list
+        * specifying the modified tuple to return.
         ************************************************************/
-       if (Tcl_SplitList(interp, result,
-                                         &ret_numvals, &ret_values) != TCL_OK)
+       if (Tcl_ListObjGetElements(interp, Tcl_GetObjResult(interp),
+                                                          &result_Objc, &result_Objv) != TCL_OK)
                ereport(ERROR,
                                (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
                                 errmsg("could not split return value from trigger: %s",
                                                utf_u2e(Tcl_GetStringResult(interp)))));
 
-       /* Use a TRY to ensure ret_values will get freed */
-       PG_TRY();
-       {
-               if (ret_numvals % 2 != 0)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
-                                        errmsg("trigger's return list must have even number of elements")));
+       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")));
 
-               modattrs = (int *) palloc(tupdesc->natts * sizeof(int));
-               modvalues = (Datum *) palloc(tupdesc->natts * sizeof(Datum));
-               for (i = 0; i < tupdesc->natts; i++)
-               {
-                       modattrs[i] = i + 1;
-                       modvalues[i] = (Datum) NULL;
-               }
+       values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
+       nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
+       memset(nulls, true, tupdesc->natts * sizeof(bool));
 
-               modnulls = palloc(tupdesc->natts);
-               memset(modnulls, 'n', tupdesc->natts);
+       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;
 
-               for (i = 0; i < ret_numvals; i += 2)
+               /************************************************************
+                * 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)
                {
-                       char       *ret_name = utf_u2e(ret_values[i]);
-                       char       *ret_value = utf_u2e(ret_values[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)));
-
-                       /************************************************************
-                        * Ignore dropped columns
-                        ************************************************************/
-                       if (tupdesc->attrs[attnum - 1]->attisdropped)
+                       if (strcmp(ret_name, ".tupno") == 0)
                                continue;
-
-                       /************************************************************
-                        * Lookup the attribute type in the syscache
-                        * for the input function
-                        ************************************************************/
-                       getTypeInputInfo(tupdesc->attrs[attnum - 1]->atttypid,
-                                                        &typinput, &typioparam);
-                       fmgr_info(typinput, &finfo);
-
-                       /************************************************************
-                        * Set the attribute to NOT NULL and convert the contents
-                        ************************************************************/
-                       modvalues[attnum - 1] = InputFunctionCall(&finfo,
-                                                                                                         ret_value,
-                                                                                                         typioparam,
-                                                                         tupdesc->attrs[attnum - 1]->atttypmod);
-                       modnulls[attnum - 1] = ' ';
+                       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)));
 
-               rettup = SPI_modifytuple(trigdata->tg_relation, rettup, tupdesc->natts,
-                                                                modattrs, modvalues, modnulls);
+               /************************************************************
+                * Ignore dropped columns
+                ************************************************************/
+               if (tupdesc->attrs[attnum - 1]->attisdropped)
+                       continue;
 
-               pfree(modattrs);
-               pfree(modvalues);
-               pfree(modnulls);
+               /************************************************************
+                * Lookup the attribute type's input function
+                ************************************************************/
+               getTypeInputInfo(tupdesc->attrs[attnum - 1]->atttypid,
+                                                &typinput, &typioparam);
+               fmgr_info(typinput, &finfo);
 
-               if (rettup == NULL)
-                       elog(ERROR, "SPI_modifytuple() failed - RC = %d", SPI_result);
-       }
-       PG_CATCH();
-       {
-               ckfree((char *) ret_values);
-               PG_RE_THROW();
+               /************************************************************
+                * 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;
        }
-       PG_END_TRY();
-       ckfree((char *) ret_values);
+
+       /* Build the modified tuple to return */
+       rettup = heap_form_tuple(tupdesc, values, nulls);
+
+       pfree(values);
+       pfree(nulls);
 
        return rettup;
 }