From 67bf7b919eddf9bd9ddc9e2d8f2dc7ca3bbfd3a2 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 26 Aug 2007 21:44:25 +0000 Subject: [PATCH] Make ARRAY(SELECT ...) return an empty array, rather than a NULL, when the 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 | 77 +++++++++++++++----------- src/backend/optimizer/plan/subselect.c | 10 ++-- src/include/nodes/primnodes.h | 6 +- 3 files changed, 52 insertions(+), 41 deletions(-) diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index ac91ed1be6..f12d0143a3 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -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); } diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 6a41138d3b..8b739e16d4 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -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) diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index cdcd4d5caa..01e94bc5f0 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -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