]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Make ARRAY(SELECT ...) return an empty array, rather than a NULL, when the
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 26 Aug 2007 21:44:25 +0000 (21:44 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 26 Aug 2007 21:44:25 +0000 (21:44 +0000)
sub-select returns zero rows.  Per complaint from Jens Schicke.  Since this
is more in the nature of a definition change than a bug, not back-patched.

src/backend/executor/nodeSubplan.c
src/backend/optimizer/plan/subselect.c
src/include/nodes/primnodes.h

index ac91ed1be64d752b6eafe06648fb1346de4f838a..f12d0143a38237679206276d8a1dc688f122466f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.89 2007/05/17 19:35:08 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.90 2007/08/26 21:44:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -259,10 +259,14 @@ ExecScanSubPlan(SubPlanState *node,
         * ROWCOMPARE_SUBLINK.
         *
         * For EXPR_SUBLINK we require the subplan to produce no more than one
-        * tuple, else an error is raised. For ARRAY_SUBLINK we allow the subplan
-        * to produce more than one tuple. In either case, if zero tuples are
-        * produced, we return NULL. Assuming we get a tuple, we just use its
-        * first column (there can be only one non-junk column in this case).
+        * tuple, else an error is raised.  If zero tuples are produced, we return
+        * NULL.  Assuming we get a tuple, we just use its first column (there can
+        * be only one non-junk column in this case).
+        *
+        * For ARRAY_SUBLINK we allow the subplan to produce any number of tuples,
+        * and form an array of the first column's values.  Note in particular
+        * that we produce a zero-element array if no tuples are produced (this
+        * is a change from pre-8.3 behavior of returning NULL).
         */
        result = BoolGetDatum(subLinkType == ALL_SUBLINK);
        *isNull = false;
@@ -317,10 +321,10 @@ ExecScanSubPlan(SubPlanState *node,
 
                        found = true;
                        /* stash away current value */
+                       Assert(subplan->firstColType == tdesc->attrs[0]->atttypid);
                        dvalue = slot_getattr(slot, 1, &disnull);
                        astate = accumArrayResult(astate, dvalue, disnull,
-                                                                         tdesc->attrs[0]->atttypid,
-                                                                         oldcontext);
+                                                                         subplan->firstColType, oldcontext);
                        /* keep scanning subplan to collect all values */
                        continue;
                }
@@ -385,29 +389,30 @@ ExecScanSubPlan(SubPlanState *node,
                }
        }
 
-       if (!found)
+       MemoryContextSwitchTo(oldcontext);
+
+       if (subLinkType == ARRAY_SUBLINK)
+       {
+               /* We return the result in the caller's context */
+               if (astate != NULL)
+                       result = makeArrayResult(astate, oldcontext);
+               else
+                       result = PointerGetDatum(construct_empty_array(subplan->firstColType));
+       }
+       else if (!found)
        {
                /*
                 * deal with empty subplan result.      result/isNull were previously
-                * initialized correctly for all sublink types except EXPR, ARRAY, and
+                * initialized correctly for all sublink types except EXPR and
                 * ROWCOMPARE; for those, return NULL.
                 */
                if (subLinkType == EXPR_SUBLINK ||
-                       subLinkType == ARRAY_SUBLINK ||
                        subLinkType == ROWCOMPARE_SUBLINK)
                {
                        result = (Datum) 0;
                        *isNull = true;
                }
        }
-       else if (subLinkType == ARRAY_SUBLINK)
-       {
-               Assert(astate != NULL);
-               /* We return the result in the caller's context */
-               result = makeArrayResult(astate, oldcontext);
-       }
-
-       MemoryContextSwitchTo(oldcontext);
 
        return result;
 }
@@ -938,10 +943,10 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 
                        found = true;
                        /* stash away current value */
+                       Assert(subplan->firstColType == tdesc->attrs[0]->atttypid);
                        dvalue = slot_getattr(slot, 1, &disnull);
                        astate = accumArrayResult(astate, dvalue, disnull,
-                                                                         tdesc->attrs[0]->atttypid,
-                                                                         oldcontext);
+                                                                         subplan->firstColType, oldcontext);
                        /* keep scanning subplan to collect all values */
                        continue;
                }
@@ -980,7 +985,25 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
                }
        }
 
-       if (!found)
+       if (subLinkType == ARRAY_SUBLINK)
+       {
+               /* There can be only one param... */
+               int                     paramid = linitial_int(subplan->setParam);
+               ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
+
+               prm->execPlan = NULL;
+               /* We build the result in query context so it won't disappear */
+               if (astate != NULL)
+                       prm->value = makeArrayResult(astate,
+                                                                                econtext->ecxt_per_query_memory);
+               else
+               {
+                       MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+                       prm->value = PointerGetDatum(construct_empty_array(subplan->firstColType));
+               }
+               prm->isnull = false;
+       }
+       else if (!found)
        {
                if (subLinkType == EXISTS_SUBLINK)
                {
@@ -1005,18 +1028,6 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
                        }
                }
        }
-       else if (subLinkType == ARRAY_SUBLINK)
-       {
-               /* There can be only one param... */
-               int                     paramid = linitial_int(subplan->setParam);
-               ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
-
-               Assert(astate != NULL);
-               prm->execPlan = NULL;
-               /* We build the result in query context so it won't disappear */
-               prm->value = makeArrayResult(astate, econtext->ecxt_per_query_memory);
-               prm->isnull = false;
-       }
 
        MemoryContextSwitchTo(oldcontext);
 }
index 6a41138d3b2464d6b30b3c87127b968b876f8fc2..8b739e16d4464b05146f0555c07882e967c78a78 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.123 2007/07/18 21:40:57 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.124 2007/08/26 21:44:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -208,10 +208,10 @@ generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod)
 /*
  * Get the datatype of the first column of the plan's output.
  *
- * This is a hack to support exprType(), which doesn't have any way to get
- * at the plan associated with a SubPlan node.  We really only need the value
- * for EXPR_SUBLINK and ARRAY_SUBLINK subplans, but for consistency we set
- * it always.
+ * This is stored for ARRAY_SUBLINK and for exprType(), which doesn't have any
+ * way to get at the plan associated with a SubPlan node.  We really only need
+ * the value for EXPR_SUBLINK and ARRAY_SUBLINK subplans, but for consistency
+ * we set it always.
  */
 static Oid
 get_first_col_type(Plan *plan)
index cdcd4d5caa5ad88c95404251a9b683ecfa9851b0..01e94bc5f0ebf218eeb42f94f2a6a3b1a42291e9 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.132 2007/06/11 22:22:42 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.133 2007/08/26 21:44:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -388,7 +388,7 @@ typedef struct BoolExpr
  * results.  ALL and ANY combine the per-row results using AND and OR
  * semantics respectively.
  * ARRAY requires just one target column, and creates an array of the target
- * column's type using one or more rows resulting from the subselect.
+ * column's type using any number of rows resulting from the subselect.
  *
  * SubLink is classed as an Expr node, but it is not actually executable;
  * it must be replaced in the expression tree by a SubPlan node during
@@ -468,7 +468,7 @@ typedef struct SubPlan
        List       *paramIds;           /* IDs of Params embedded in the above */
        /* Identification of the Plan tree to use: */
        int                     plan_id;                /* Index (from 1) in PlannedStmt.subplans */
-       /* Extra data saved for the convenience of exprType(): */
+       /* Extra data useful for determining subplan's output type: */
        Oid                     firstColType;   /* Type of first column of subplan result */
        /* Information about execution strategy: */
        bool            useHashTable;   /* TRUE to store subselect output in a hash