Fix problems with ParamListInfo serialization mechanism.
authorRobert Haas <rhaas@postgresql.org>
Mon, 12 Oct 2015 15:46:40 +0000 (11:46 -0400)
committerRobert Haas <rhaas@postgresql.org>
Wed, 28 Oct 2015 13:43:45 +0000 (14:43 +0100)
Commit d1b7c1ffe72e86932b5395f29e006c3f503bc53d introduced a mechanism
for serializing a ParamListInfo structure to be passed to a parallel
worker.  However, this mechanism failed to handle external expanded
values, as pointed out by Noah Misch.  Moreover, plpgsql_param_fetch
requires adjustment because the serialization mechanism needs it to skip
evaluating unused parameters just as we would do when it is called from
copyParamList, but params == estate->paramLI in that case.  To fix, do
the relevant bms_is_member test unconditionally.

src/backend/utils/adt/datum.c
src/pl/plpgsql/src/pl_exec.c

index 3d9e35442dabd6d5a056ac360a32886d99d9f37e..0d619504552a113f1c09df91779d70f4c65166e5 100644 (file)
@@ -264,6 +264,11 @@ datumEstimateSpace(Datum value, bool isnull, bool typByVal, int typLen)
                /* no need to use add_size, can't overflow */
                if (typByVal)
                        sz += sizeof(Datum);
+               else if (VARATT_IS_EXTERNAL_EXPANDED(value))
+               {
+                       ExpandedObjectHeader *eoh = DatumGetEOHP(value);
+                       sz += EOH_get_flat_size(eoh);
+               }
                else
                        sz += datumGetSize(value, typByVal, typLen);
        }
@@ -292,6 +297,7 @@ void
 datumSerialize(Datum value, bool isnull, bool typByVal, int typLen,
                           char **start_address)
 {
+       ExpandedObjectHeader *eoh = NULL;
        int             header;
 
        /* Write header word. */
@@ -299,6 +305,11 @@ datumSerialize(Datum value, bool isnull, bool typByVal, int typLen,
                header = -2;
        else if (typByVal)
                header = -1;
+       else if (VARATT_IS_EXTERNAL_EXPANDED(value))
+       {
+               eoh = DatumGetEOHP(value);
+               header = EOH_get_flat_size(eoh);
+       }
        else
                header = datumGetSize(value, typByVal, typLen);
        memcpy(*start_address, &header, sizeof(int));
@@ -312,6 +323,11 @@ datumSerialize(Datum value, bool isnull, bool typByVal, int typLen,
                        memcpy(*start_address, &value, sizeof(Datum));
                        *start_address += sizeof(Datum);
                }
+               else if (eoh)
+               {
+                       EOH_flatten_into(eoh, (void *) *start_address, header);
+                       *start_address += header;
+               }
                else
                {
                        memcpy(*start_address, DatumGetPointer(value), header);
index c73f20b725a0ed65d5fe0c8c2a83c2bba8254755..346e8f851cd6491f15ef314f46a4f11941c04e43 100644 (file)
@@ -5696,21 +5696,17 @@ plpgsql_param_fetch(ParamListInfo params, int paramid)
        /* now we can access the target datum */
        datum = estate->datums[dno];
 
-       /* need to behave slightly differently for shared and unshared arrays */
-       if (params != estate->paramLI)
-       {
-               /*
-                * We're being called, presumably from copyParamList(), for cursor
-                * parameters.  Since copyParamList() will try to materialize every
-                * single parameter slot, it's important to do nothing when asked for
-                * a datum that's not supposed to be used by this SQL expression.
-                * Otherwise we risk failures in exec_eval_datum(), not to mention
-                * possibly copying a lot more data than the cursor actually uses.
-                */
-               if (!bms_is_member(dno, expr->paramnos))
-                       return;
-       }
-       else
+       /*
+        * Since copyParamList() and SerializeParamList() will try to materialize
+        * every single parameter slot, it's important to do nothing when asked for
+        * a datum that's not supposed to be used by this SQL expression.
+        * Otherwise we risk failures in exec_eval_datum(), not to mention
+        * possibly copying a lot more data than the cursor actually uses.
+        */
+       if (!bms_is_member(dno, expr->paramnos))
+               return;
+
+       if (params == estate->paramLI)
        {
                /*
                 * Normal evaluation cases.  We don't need to sanity-check dno, but we