summaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2003-04-08 23:20:04 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2003-04-08 23:20:04 +0000
commit730840c9b649a48604083270d48792915ca89233 (patch)
treecf3ccc25e61cdfc07061ebec63393d77b3a2f643 /src/backend/executor
parent6fb5115850be766c42177cebf672c68c7d8e3ddd (diff)
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.
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/execQual.c225
-rw-r--r--src/backend/executor/nodeSubplan.c200
2 files changed, 396 insertions, 29 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 7e084f28302..8c6a7c04b66 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.127 2003/03/27 16:51:27 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.128 2003/04/08 23:20:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -75,6 +75,9 @@ static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
bool *isNull);
static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalArray(ArrayExprState *astate,
+ ExprContext *econtext,
+ bool *isNull);
static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
ExprContext *econtext,
bool *isNull);
@@ -246,38 +249,38 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
resultArray = array_set(array_source, i,
upper.indx,
sourceData,
- arrayRef->refattrlength,
- arrayRef->refelemlength,
- arrayRef->refelembyval,
- arrayRef->refelemalign,
+ astate->refattrlength,
+ astate->refelemlength,
+ astate->refelembyval,
+ astate->refelemalign,
isNull);
else
resultArray = array_set_slice(array_source, i,
upper.indx, lower.indx,
(ArrayType *) DatumGetPointer(sourceData),
- arrayRef->refattrlength,
- arrayRef->refelemlength,
- arrayRef->refelembyval,
- arrayRef->refelemalign,
+ astate->refattrlength,
+ astate->refelemlength,
+ astate->refelembyval,
+ astate->refelemalign,
isNull);
return PointerGetDatum(resultArray);
}
if (lIndex == NULL)
return array_ref(array_source, i, upper.indx,
- arrayRef->refattrlength,
- arrayRef->refelemlength,
- arrayRef->refelembyval,
- arrayRef->refelemalign,
+ astate->refattrlength,
+ astate->refelemlength,
+ astate->refelembyval,
+ astate->refelemalign,
isNull);
else
{
resultArray = array_get_slice(array_source, i,
upper.indx, lower.indx,
- arrayRef->refattrlength,
- arrayRef->refelemlength,
- arrayRef->refelembyval,
- arrayRef->refelemalign,
+ astate->refattrlength,
+ astate->refelemlength,
+ astate->refelembyval,
+ astate->refelemalign,
isNull);
return PointerGetDatum(resultArray);
}
@@ -613,6 +616,7 @@ init_fcache(Oid foid, FuncExprState *fcache, MemoryContext fcacheCxt)
/* Initialize additional info */
fcache->setArgsValid = false;
+ fcache->func.fn_expr = (Node *) fcache->xprstate.expr;
}
/*
@@ -1427,6 +1431,158 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
}
/* ----------------------------------------------------------------
+ * ExecEvalArray - ARRAY[] expressions
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
+ bool *isNull)
+{
+ ArrayExpr *arrayExpr = (ArrayExpr *) astate->xprstate.expr;
+ ArrayType *result;
+ List *element;
+ Oid element_type = arrayExpr->element_typeid;
+ int ndims = arrayExpr->ndims;
+ int dims[MAXDIM];
+ int lbs[MAXDIM];
+
+ if (ndims == 1)
+ {
+ int nelems;
+ Datum *dvalues;
+ int i = 0;
+
+ nelems = length(astate->elements);
+
+ /* Shouldn't happen here, but if length is 0, return NULL */
+ if (nelems == 0)
+ {
+ *isNull = true;
+ return (Datum) 0;
+ }
+
+ dvalues = (Datum *) palloc(nelems * sizeof(Datum));
+
+ /* loop through and build array of datums */
+ foreach(element, astate->elements)
+ {
+ ExprState *e = (ExprState *) lfirst(element);
+ bool eisnull;
+
+ dvalues[i++] = ExecEvalExpr(e, econtext, &eisnull, NULL);
+ if (eisnull)
+ elog(ERROR, "Arrays cannot have NULL elements");
+ }
+
+ /* setup for 1-D array of the given length */
+ dims[0] = nelems;
+ lbs[0] = 1;
+
+ result = construct_md_array(dvalues, ndims, dims, lbs,
+ element_type,
+ astate->elemlength,
+ astate->elembyval,
+ astate->elemalign);
+ }
+ else
+ {
+ char *dat = NULL;
+ Size ndatabytes = 0;
+ int nbytes;
+ int outer_nelems = length(astate->elements);
+ int elem_ndims = 0;
+ int *elem_dims = NULL;
+ int *elem_lbs = NULL;
+ bool firstone = true;
+ int i;
+
+ if (ndims <= 0 || ndims > MAXDIM)
+ elog(ERROR, "Arrays cannot have more than %d dimensions", MAXDIM);
+
+ /* loop through and get data area from each element */
+ foreach(element, astate->elements)
+ {
+ ExprState *e = (ExprState *) lfirst(element);
+ bool eisnull;
+ Datum arraydatum;
+ ArrayType *array;
+ int elem_ndatabytes;
+
+ arraydatum = ExecEvalExpr(e, econtext, &eisnull, NULL);
+ if (eisnull)
+ elog(ERROR, "Arrays cannot have NULL elements");
+
+ array = DatumGetArrayTypeP(arraydatum);
+
+ if (firstone)
+ {
+ /* Get sub-array details from first member */
+ elem_ndims = ARR_NDIM(array);
+ elem_dims = (int *) palloc(elem_ndims * sizeof(int));
+ memcpy(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int));
+ elem_lbs = (int *) palloc(elem_ndims * sizeof(int));
+ memcpy(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int));
+ firstone = false;
+ }
+ else
+ {
+ /* Check other sub-arrays are compatible */
+ if (elem_ndims != ARR_NDIM(array))
+ elog(ERROR, "Multiple dimension arrays must have array "
+ "expressions with matching number of dimensions");
+
+ if (memcmp(elem_dims, ARR_DIMS(array),
+ elem_ndims * sizeof(int)) != 0)
+ elog(ERROR, "Multiple dimension arrays must have array "
+ "expressions with matching dimensions");
+
+ if (memcmp(elem_lbs, ARR_LBOUND(array),
+ elem_ndims * sizeof(int)) != 0)
+ elog(ERROR, "Multiple dimension arrays must have array "
+ "expressions with matching dimensions");
+ }
+
+ elem_ndatabytes = ARR_SIZE(array) - ARR_OVERHEAD(elem_ndims);
+ ndatabytes += elem_ndatabytes;
+ if (dat == NULL)
+ dat = (char *) palloc(ndatabytes);
+ else
+ dat = (char *) repalloc(dat, ndatabytes);
+
+ memcpy(dat + (ndatabytes - elem_ndatabytes),
+ ARR_DATA_PTR(array),
+ elem_ndatabytes);
+ }
+
+ /* setup for multi-D array */
+ dims[0] = outer_nelems;
+ lbs[0] = 1;
+ for (i = 1; i < ndims; i++)
+ {
+ dims[i] = elem_dims[i - 1];
+ lbs[i] = elem_lbs[i - 1];
+ }
+
+ nbytes = ndatabytes + ARR_OVERHEAD(ndims);
+ result = (ArrayType *) palloc(nbytes);
+
+ result->size = nbytes;
+ result->ndim = ndims;
+ result->flags = 0;
+ result->elemtype = element_type;
+ memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
+ memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
+ if (ndatabytes > 0)
+ memcpy(ARR_DATA_PTR(result), dat, ndatabytes);
+
+ if (dat != NULL)
+ pfree(dat);
+ }
+
+ return PointerGetDatum(result);
+}
+
+/* ----------------------------------------------------------------
* ExecEvalCoalesce
* ----------------------------------------------------------------
*/
@@ -1908,6 +2064,11 @@ ExecEvalExpr(ExprState *expression,
isNull,
isDone);
break;
+ case T_ArrayExpr:
+ retDatum = ExecEvalArray((ArrayExprState *) expression,
+ econtext,
+ isNull);
+ break;
case T_CoalesceExpr:
retDatum = ExecEvalCoalesce((CoalesceExprState *) expression,
econtext,
@@ -2060,6 +2221,12 @@ ExecInitExpr(Expr *node, PlanState *parent)
astate->refexpr = ExecInitExpr(aref->refexpr, parent);
astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr,
parent);
+ /* do one-time catalog lookups for type info */
+ astate->refattrlength = get_typlen(aref->refarraytype);
+ get_typlenbyvalalign(aref->refelemtype,
+ &astate->refelemlength,
+ &astate->refelembyval,
+ &astate->refelemalign);
state = (ExprState *) astate;
}
break;
@@ -2174,6 +2341,30 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) cstate;
}
break;
+ case T_ArrayExpr:
+ {
+ ArrayExpr *arrayexpr = (ArrayExpr *) node;
+ ArrayExprState *astate = makeNode(ArrayExprState);
+ List *outlist = NIL;
+ List *inlist;
+
+ foreach(inlist, arrayexpr->elements)
+ {
+ Expr *e = (Expr *) lfirst(inlist);
+ ExprState *estate;
+
+ estate = ExecInitExpr(e, parent);
+ outlist = lappend(outlist, estate);
+ }
+ astate->elements = outlist;
+ /* do one-time catalog lookup for type info */
+ get_typlenbyvalalign(arrayexpr->element_typeid,
+ &astate->elemlength,
+ &astate->elembyval,
+ &astate->elemalign);
+ state = (ExprState *) astate;
+ }
+ break;
case T_CoalesceExpr:
{
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 4fd8af2ae4d..5d9bdc84241 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
- * $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);
+}