1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-30 11:03:19 +03:00

First phase of work on array improvements. ARRAY[x,y,z] constructor

expressions, ARRAY(sub-SELECT) expressions, some array functions.
Polymorphic functions using ANYARRAY/ANYELEMENT argument and return
types.  Some regression tests in place, documentation is lacking.
Joe Conway, with some kibitzing from Tom Lane.
This commit is contained in:
Tom Lane
2003-04-08 23:20:04 +00:00
parent 6fb5115850
commit 730840c9b6
47 changed files with 2597 additions and 479 deletions

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.44 2003/02/09 00:30:39 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.45 2003/04/08 23:20:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -24,9 +24,27 @@
#include "executor/nodeSubplan.h"
#include "nodes/makefuncs.h"
#include "parser/parse_expr.h"
#include "tcop/pquery.h"
#include "utils/array.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
typedef struct ArrayBuildState
{
MemoryContext mcontext; /* where all the temp stuff is kept */
Datum *dvalues; /* array of accumulated Datums */
/*
* The allocated size of dvalues[] is always a multiple of
* ARRAY_ELEMS_CHUNKSIZE
*/
#define ARRAY_ELEMS_CHUNKSIZE 64
int nelems; /* number of valid Datums in dvalues[] */
Oid element_type; /* data type of the Datums */
int16 typlen; /* needed info about datatype */
bool typbyval;
char typalign;
} ArrayBuildState;
static Datum ExecHashSubPlan(SubPlanState *node,
ExprContext *econtext,
bool *isNull);
@ -36,6 +54,12 @@ static Datum ExecScanSubPlan(SubPlanState *node,
static void buildSubPlanHash(SubPlanState *node);
static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
static bool tupleAllNulls(HeapTuple tuple);
static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
Datum dvalue, bool disnull,
Oid element_type,
MemoryContext rcontext);
static Datum makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext);
/* ----------------------------------------------------------------
@ -206,6 +230,7 @@ ExecScanSubPlan(SubPlanState *node,
bool found = false; /* TRUE if got at least one subplan tuple */
List *pvar;
List *lst;
ArrayBuildState *astate = NULL;
/*
* We are probably in a short-lived expression-evaluation context.
@ -236,11 +261,11 @@ ExecScanSubPlan(SubPlanState *node,
ExecReScan(planstate, NULL);
/*
* For all sublink types except EXPR_SUBLINK, the result is boolean as
* are the results of the combining operators. We combine results
* within a tuple (if there are multiple columns) using OR semantics
* if "useOr" is true, AND semantics if not. We then combine results
* across tuples (if the subplan produces more than one) using OR
* For all sublink types except EXPR_SUBLINK and ARRAY_SUBLINK, the result
* is boolean as are the results of the combining operators. We combine
* results within a tuple (if there are multiple columns) using OR
* semantics if "useOr" is true, AND semantics if not. We then combine
* results across tuples (if the subplan produces more than one) using OR
* semantics for ANY_SUBLINK or AND semantics for ALL_SUBLINK.
* (MULTIEXPR_SUBLINK doesn't allow multiple tuples from the subplan.)
* NULL results from the combining operators are handled according to
@ -249,9 +274,10 @@ ExecScanSubPlan(SubPlanState *node,
* MULTIEXPR_SUBLINK.
*
* For EXPR_SUBLINK we require the subplan to produce no more than one
* tuple, else an error is raised. If zero tuples are produced, we
* return NULL. Assuming we get a tuple, we just return its first
* column (there can be only one non-junk column in this case).
* 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).
*/
result = BoolGetDatum(subLinkType == ALL_SUBLINK);
*isNull = false;
@ -301,6 +327,21 @@ ExecScanSubPlan(SubPlanState *node,
continue;
}
if (subLinkType == ARRAY_SUBLINK)
{
Datum dvalue;
bool disnull;
found = true;
/* stash away current value */
dvalue = heap_getattr(tup, 1, tdesc, &disnull);
astate = accumArrayResult(astate, dvalue, disnull,
tdesc->attrs[0]->atttypid,
oldcontext);
/* keep scanning subplan to collect all values */
continue;
}
/* cannot allow multiple input tuples for MULTIEXPR sublink either */
if (subLinkType == MULTIEXPR_SUBLINK && found)
elog(ERROR, "More than one tuple returned by a subselect used as an expression.");
@ -407,15 +448,23 @@ ExecScanSubPlan(SubPlanState *node,
{
/*
* deal with empty subplan result. result/isNull were previously
* initialized correctly for all sublink types except EXPR and
* initialized correctly for all sublink types except EXPR, ARRAY, and
* MULTIEXPR; for those, return NULL.
*/
if (subLinkType == EXPR_SUBLINK || subLinkType == MULTIEXPR_SUBLINK)
if (subLinkType == EXPR_SUBLINK ||
subLinkType == ARRAY_SUBLINK ||
subLinkType == MULTIEXPR_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);
@ -797,6 +846,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
/* Lookup the combining function */
fmgr_info(opexpr->opfuncid, &node->eqfunctions[i-1]);
node->eqfunctions[i-1].fn_expr = (Node *) opexpr;
i++;
}
@ -857,6 +907,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
TupleTableSlot *slot;
List *lst;
bool found = false;
ArrayBuildState *astate = NULL;
/*
* Must switch to child query's per-query memory context.
@ -891,6 +942,21 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
break;
}
if (subLinkType == ARRAY_SUBLINK)
{
Datum dvalue;
bool disnull;
found = true;
/* stash away current value */
dvalue = heap_getattr(tup, 1, tdesc, &disnull);
astate = accumArrayResult(astate, dvalue, disnull,
tdesc->attrs[0]->atttypid,
oldcontext);
/* keep scanning subplan to collect all values */
continue;
}
if (found &&
(subLinkType == EXPR_SUBLINK ||
subLinkType == MULTIEXPR_SUBLINK))
@ -951,6 +1017,18 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
}
}
}
else if (subLinkType == ARRAY_SUBLINK)
{
/* There can be only one param... */
int paramid = lfirsti(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);
}
@ -1007,3 +1085,101 @@ ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
parent->chgParam = bms_add_member(parent->chgParam, paramid);
}
}
/*
* accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
*
* astate is working state (NULL on first call)
* rcontext is where to keep working state
*/
static ArrayBuildState *
accumArrayResult(ArrayBuildState *astate,
Datum dvalue, bool disnull,
Oid element_type,
MemoryContext rcontext)
{
MemoryContext arr_context,
oldcontext;
if (astate == NULL)
{
/* First time through --- initialize */
/* Make a temporary context to hold all the junk */
arr_context = AllocSetContextCreate(rcontext,
"ARRAY_SUBLINK Result",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldcontext = MemoryContextSwitchTo(arr_context);
astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
astate->mcontext = arr_context;
astate->dvalues = (Datum *)
palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
astate->nelems = 0;
astate->element_type = element_type;
get_typlenbyvalalign(element_type,
&astate->typlen,
&astate->typbyval,
&astate->typalign);
}
else
{
oldcontext = MemoryContextSwitchTo(astate->mcontext);
Assert(astate->element_type == element_type);
/* enlarge dvalues[] if needed */
if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
astate->dvalues = (Datum *)
repalloc(astate->dvalues,
(astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
}
if (disnull)
elog(ERROR, "NULL elements not allowed in Arrays");
/* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
astate->dvalues[astate->nelems++] =
datumCopy(dvalue, astate->typbyval, astate->typlen);
MemoryContextSwitchTo(oldcontext);
return astate;
}
/*
* makeArrayResult - produce final result of ARRAY_SUBLINK
*
* astate is working state (not NULL)
* rcontext is where to construct result
*/
static Datum
makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext)
{
ArrayType *result;
int dims[1];
int lbs[1];
MemoryContext oldcontext;
/* Build the final array result in rcontext */
oldcontext = MemoryContextSwitchTo(rcontext);
dims[0] = astate->nelems;
lbs[0] = 1;
result = construct_md_array(astate->dvalues,
1,
dims,
lbs,
astate->element_type,
astate->typlen,
astate->typbyval,
astate->typalign);
MemoryContextSwitchTo(oldcontext);
/* Clean up all the junk */
MemoryContextDelete(astate->mcontext);
return PointerGetDatum(result);
}