diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2003-04-08 23:20:04 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2003-04-08 23:20:04 +0000 |
commit | 730840c9b649a48604083270d48792915ca89233 (patch) | |
tree | cf3ccc25e61cdfc07061ebec63393d77b3a2f643 /src/backend/executor | |
parent | 6fb5115850be766c42177cebf672c68c7d8e3ddd (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.c | 225 | ||||
-rw-r--r-- | src/backend/executor/nodeSubplan.c | 200 |
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); +} |