mirror of
https://github.com/postgres/postgres.git
synced 2025-07-02 09:02:37 +03:00
Array mega-patch.
Joe Conway
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.130 2003/05/28 22:32:49 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.131 2003/06/24 23:14:43 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -1528,17 +1528,17 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
|
||||
{
|
||||
/* Check other sub-arrays are compatible */
|
||||
if (elem_ndims != ARR_NDIM(array))
|
||||
elog(ERROR, "Multiple dimension arrays must have array "
|
||||
elog(ERROR, "Multidimensional 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 "
|
||||
elog(ERROR, "Multidimensional 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 "
|
||||
elog(ERROR, "Multidimensional arrays must have array "
|
||||
"expressions with matching dimensions");
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.107 2003/06/22 22:04:54 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.108 2003/06/24 23:14:43 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -58,6 +58,7 @@
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeAgg.h"
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_expr.h"
|
||||
@ -212,7 +213,7 @@ static TupleTableSlot *agg_retrieve_direct(AggState *aggstate);
|
||||
static void agg_fill_hash_table(AggState *aggstate);
|
||||
static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
|
||||
static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
|
||||
|
||||
static Oid resolve_type(Oid type_to_resolve, Oid context_type);
|
||||
|
||||
/*
|
||||
* Initialize all aggregates for a new group of input values.
|
||||
@ -351,14 +352,12 @@ advance_transition_function(AggState *aggstate,
|
||||
fcinfo.context = NULL;
|
||||
fcinfo.resultinfo = NULL;
|
||||
fcinfo.isnull = false;
|
||||
|
||||
fcinfo.flinfo = &peraggstate->transfn;
|
||||
fcinfo.nargs = 2;
|
||||
fcinfo.arg[0] = pergroupstate->transValue;
|
||||
fcinfo.argnull[0] = pergroupstate->transValueIsNull;
|
||||
fcinfo.arg[1] = newVal;
|
||||
fcinfo.argnull[1] = isNull;
|
||||
|
||||
newVal = FunctionCallInvoke(&fcinfo);
|
||||
|
||||
/*
|
||||
@ -1187,7 +1186,21 @@ ExecInitAgg(Agg *node, EState *estate)
|
||||
AclResult aclresult;
|
||||
Oid transfn_oid,
|
||||
finalfn_oid;
|
||||
FuncExpr *transfnexpr,
|
||||
*finalfnexpr;
|
||||
Datum textInitVal;
|
||||
List *fargs;
|
||||
Oid agg_rt_type;
|
||||
Oid *transfn_arg_types;
|
||||
List *transfn_args = NIL;
|
||||
int transfn_nargs;
|
||||
Oid transfn_ret_type;
|
||||
Oid *finalfn_arg_types = NULL;
|
||||
List *finalfn_args = NIL;
|
||||
Oid finalfn_ret_type = InvalidOid;
|
||||
int finalfn_nargs = 0;
|
||||
Node *arg0;
|
||||
Node *arg1;
|
||||
int i;
|
||||
|
||||
/* Planner should have assigned aggregate to correct level */
|
||||
@ -1238,6 +1251,166 @@ ExecInitAgg(Agg *node, EState *estate)
|
||||
&peraggstate->transtypeLen,
|
||||
&peraggstate->transtypeByVal);
|
||||
|
||||
peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
|
||||
peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
|
||||
|
||||
/* get the runtime aggregate argument type */
|
||||
fargs = aggref->args;
|
||||
agg_rt_type = exprType((Node *) nth(0, fargs));
|
||||
|
||||
/* get the transition function argument and return types */
|
||||
transfn_ret_type = get_func_rettype(transfn_oid);
|
||||
transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
|
||||
|
||||
/* resolve any polymorphic types */
|
||||
if (transfn_nargs == 2)
|
||||
/* base type was not ANY */
|
||||
{
|
||||
if (transfn_arg_types[1] == ANYARRAYOID ||
|
||||
transfn_arg_types[1] == ANYELEMENTOID)
|
||||
transfn_arg_types[1] = agg_rt_type;
|
||||
|
||||
transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
|
||||
agg_rt_type);
|
||||
|
||||
/*
|
||||
* Build arg list to use on the transfn FuncExpr node. We really
|
||||
* only care that the node type is correct so that the transfn
|
||||
* can discover the actual argument types at runtime using
|
||||
* get_fn_expr_argtype()
|
||||
*/
|
||||
arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
|
||||
-1, COERCE_DONTCARE);
|
||||
arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1],
|
||||
-1, COERCE_DONTCARE);
|
||||
transfn_args = makeList2(arg0, arg1);
|
||||
|
||||
/*
|
||||
* the state transition function always returns the same type
|
||||
* as its first argument
|
||||
*/
|
||||
if (transfn_ret_type == ANYARRAYOID ||
|
||||
transfn_ret_type == ANYELEMENTOID)
|
||||
transfn_ret_type = transfn_arg_types[0];
|
||||
}
|
||||
else if (transfn_nargs == 1)
|
||||
/*
|
||||
* base type was ANY, therefore the aggregate return type should
|
||||
* be non-polymorphic
|
||||
*/
|
||||
{
|
||||
Oid finaltype = get_func_rettype(aggref->aggfnoid);
|
||||
|
||||
/*
|
||||
* this should have been prevented in AggregateCreate,
|
||||
* but check anyway
|
||||
*/
|
||||
if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
|
||||
elog(ERROR, "aggregate with base type ANY must have a " \
|
||||
"non-polymorphic return type");
|
||||
|
||||
/* see if we have a final function */
|
||||
if (OidIsValid(finalfn_oid))
|
||||
{
|
||||
finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
|
||||
if (finalfn_nargs != 1)
|
||||
elog(ERROR, "final function takes unexpected number " \
|
||||
"of arguments: %d", finalfn_nargs);
|
||||
|
||||
/*
|
||||
* final function argument is always the same as the state
|
||||
* function return type
|
||||
*/
|
||||
if (finalfn_arg_types[0] != ANYARRAYOID &&
|
||||
finalfn_arg_types[0] != ANYELEMENTOID)
|
||||
{
|
||||
/* if it is not ambiguous, use it */
|
||||
transfn_ret_type = finalfn_arg_types[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if it is ambiguous, try to derive it */
|
||||
finalfn_ret_type = finaltype;
|
||||
finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
|
||||
finalfn_ret_type);
|
||||
transfn_ret_type = finalfn_arg_types[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
transfn_ret_type = finaltype;
|
||||
|
||||
transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
|
||||
transfn_ret_type);
|
||||
|
||||
/*
|
||||
* Build arg list to use on the transfn FuncExpr node. We really
|
||||
* only care that the node type is correct so that the transfn
|
||||
* can discover the actual argument types at runtime using
|
||||
* get_fn_expr_argtype()
|
||||
*/
|
||||
arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
|
||||
-1, COERCE_DONTCARE);
|
||||
transfn_args = makeList1(arg0);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "state transition function takes unexpected number " \
|
||||
"of arguments: %d", transfn_nargs);
|
||||
|
||||
if (OidIsValid(finalfn_oid))
|
||||
{
|
||||
/* get the final function argument and return types */
|
||||
if (finalfn_ret_type == InvalidOid)
|
||||
finalfn_ret_type = get_func_rettype(finalfn_oid);
|
||||
|
||||
if (!finalfn_arg_types)
|
||||
{
|
||||
finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
|
||||
if (finalfn_nargs != 1)
|
||||
elog(ERROR, "final function takes unexpected number " \
|
||||
"of arguments: %d", finalfn_nargs);
|
||||
}
|
||||
|
||||
/*
|
||||
* final function argument is always the same as the state
|
||||
* function return type, which by now should have been resolved
|
||||
*/
|
||||
if (finalfn_arg_types[0] == ANYARRAYOID ||
|
||||
finalfn_arg_types[0] == ANYELEMENTOID)
|
||||
finalfn_arg_types[0] = transfn_ret_type;
|
||||
|
||||
/*
|
||||
* Build arg list to use on the finalfn FuncExpr node. We really
|
||||
* only care that the node type is correct so that the finalfn
|
||||
* can discover the actual argument type at runtime using
|
||||
* get_fn_expr_argtype()
|
||||
*/
|
||||
arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0],
|
||||
-1, COERCE_DONTCARE);
|
||||
finalfn_args = makeList1(arg0);
|
||||
|
||||
finalfn_ret_type = resolve_type(finalfn_ret_type,
|
||||
finalfn_arg_types[0]);
|
||||
}
|
||||
|
||||
fmgr_info(transfn_oid, &peraggstate->transfn);
|
||||
transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
|
||||
transfn_ret_type,
|
||||
false, /* cannot be a set */
|
||||
COERCE_DONTCARE, /* to match any user expr */
|
||||
transfn_args);
|
||||
peraggstate->transfn.fn_expr = (Node *) transfnexpr;
|
||||
|
||||
if (OidIsValid(finalfn_oid))
|
||||
{
|
||||
fmgr_info(finalfn_oid, &peraggstate->finalfn);
|
||||
finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
|
||||
finalfn_ret_type,
|
||||
false, /* cannot be a set */
|
||||
COERCE_DONTCARE, /* to match any user expr */
|
||||
finalfn_args);
|
||||
peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
|
||||
}
|
||||
|
||||
/*
|
||||
* initval is potentially null, so don't try to access it as a
|
||||
* struct field. Must do it the hard way with SysCacheGetAttr.
|
||||
@ -1250,14 +1423,7 @@ ExecInitAgg(Agg *node, EState *estate)
|
||||
peraggstate->initValue = (Datum) 0;
|
||||
else
|
||||
peraggstate->initValue = GetAggInitVal(textInitVal,
|
||||
aggform->aggtranstype);
|
||||
|
||||
peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
|
||||
peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
|
||||
|
||||
fmgr_info(transfn_oid, &peraggstate->transfn);
|
||||
if (OidIsValid(finalfn_oid))
|
||||
fmgr_info(finalfn_oid, &peraggstate->finalfn);
|
||||
transfn_arg_types[0]);
|
||||
|
||||
/*
|
||||
* If the transfn is strict and the initval is NULL, make sure
|
||||
@ -1469,3 +1635,36 @@ aggregate_dummy(PG_FUNCTION_ARGS)
|
||||
fcinfo->flinfo->fn_oid);
|
||||
return (Datum) 0; /* keep compiler quiet */
|
||||
}
|
||||
|
||||
static Oid
|
||||
resolve_type(Oid type_to_resolve, Oid context_type)
|
||||
{
|
||||
Oid resolved_type;
|
||||
|
||||
if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
|
||||
resolved_type = type_to_resolve;
|
||||
else if (type_to_resolve == ANYARRAYOID)
|
||||
/* any array */
|
||||
{
|
||||
Oid context_type_arraytype = get_array_type(context_type);
|
||||
|
||||
if (context_type_arraytype != InvalidOid)
|
||||
resolved_type = context_type_arraytype;
|
||||
else
|
||||
resolved_type = context_type;
|
||||
}
|
||||
else if (type_to_resolve == ANYELEMENTOID)
|
||||
/* any element */
|
||||
{
|
||||
Oid context_type_elemtype = get_element_type(context_type);
|
||||
|
||||
if (context_type_elemtype != InvalidOid)
|
||||
resolved_type = context_type_elemtype;
|
||||
else
|
||||
resolved_type = context_type;
|
||||
}
|
||||
else
|
||||
resolved_type = type_to_resolve;
|
||||
|
||||
return resolved_type;
|
||||
}
|
||||
|
@ -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.47 2003/06/22 22:04:54 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.48 2003/06/24 23:14:43 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -28,23 +28,6 @@
|
||||
#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);
|
||||
@ -54,13 +37,6 @@ 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);
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecSubPlan
|
||||
@ -224,6 +200,7 @@ ExecScanSubPlan(SubPlanState *node,
|
||||
PlanState *planstate = node->planstate;
|
||||
SubLinkType subLinkType = subplan->subLinkType;
|
||||
bool useOr = subplan->useOr;
|
||||
bool isExpr = subplan->isExpr;
|
||||
MemoryContext oldcontext;
|
||||
TupleTableSlot *slot;
|
||||
Datum result;
|
||||
@ -294,6 +271,11 @@ ExecScanSubPlan(SubPlanState *node,
|
||||
bool rownull = false;
|
||||
int col = 1;
|
||||
List *plst;
|
||||
int numelems;
|
||||
int elemnum;
|
||||
Datum dvalue;
|
||||
Datum *dvalues = NULL;
|
||||
bool disnull;
|
||||
|
||||
if (subLinkType == EXISTS_SUBLINK)
|
||||
{
|
||||
@ -331,9 +313,6 @@ ExecScanSubPlan(SubPlanState *node,
|
||||
|
||||
if (subLinkType == ARRAY_SUBLINK)
|
||||
{
|
||||
Datum dvalue;
|
||||
bool disnull;
|
||||
|
||||
found = true;
|
||||
/* stash away current value */
|
||||
dvalue = heap_getattr(tup, 1, tdesc, &disnull);
|
||||
@ -351,99 +330,164 @@ ExecScanSubPlan(SubPlanState *node,
|
||||
found = true;
|
||||
|
||||
/*
|
||||
* For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
|
||||
* operators for columns of tuple.
|
||||
* When isExpr is true, we have either a scalar expression or an
|
||||
* array. In the former case, this is no different than the !isExpr
|
||||
* case. In the latter case, iterate over the elements as if they
|
||||
* were from multiple input tuples.
|
||||
*/
|
||||
plst = subplan->paramIds;
|
||||
foreach(lst, node->exprs)
|
||||
if (!isExpr)
|
||||
numelems = 1;
|
||||
else
|
||||
{
|
||||
ExprState *exprstate = (ExprState *) lfirst(lst);
|
||||
int paramid = lfirsti(plst);
|
||||
ParamExecData *prmdata;
|
||||
Datum expresult;
|
||||
bool expnull;
|
||||
Oid expr_typeid = tdesc->attrs[0]->atttypid;
|
||||
|
||||
/*
|
||||
* Load up the Param representing this column of the sub-select.
|
||||
*/
|
||||
prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
|
||||
Assert(prmdata->execPlan == NULL);
|
||||
prmdata->value = heap_getattr(tup, col, tdesc,
|
||||
&(prmdata->isnull));
|
||||
|
||||
/*
|
||||
* Now we can eval the combining operator for this column.
|
||||
*/
|
||||
expresult = ExecEvalExprSwitchContext(exprstate, econtext,
|
||||
&expnull, NULL);
|
||||
|
||||
/*
|
||||
* Combine the result into the row result as appropriate.
|
||||
*/
|
||||
if (col == 1)
|
||||
if (expr_typeid != subplan->exprtype)
|
||||
{
|
||||
rowresult = expresult;
|
||||
rownull = expnull;
|
||||
subplan->exprtype = expr_typeid;
|
||||
subplan->elemtype = get_element_type(expr_typeid);
|
||||
|
||||
if (subplan->elemtype != InvalidOid)
|
||||
get_typlenbyvalalign(subplan->elemtype,
|
||||
&subplan->elmlen,
|
||||
&subplan->elmbyval,
|
||||
&subplan->elmalign);
|
||||
}
|
||||
else if (useOr)
|
||||
|
||||
/* get current value */
|
||||
dvalue = heap_getattr(tup, 1, tdesc, &disnull);
|
||||
|
||||
/* XXX this will need work if/when arrays support NULL elements */
|
||||
if (!disnull)
|
||||
{
|
||||
/* combine within row per OR semantics */
|
||||
if (expnull)
|
||||
rownull = true;
|
||||
else if (DatumGetBool(expresult))
|
||||
if (subplan->elemtype != InvalidOid)
|
||||
{
|
||||
rowresult = BoolGetDatum(true);
|
||||
rownull = false;
|
||||
break; /* needn't look at any more columns */
|
||||
ArrayType *v = DatumGetArrayTypeP(dvalue);
|
||||
|
||||
deconstruct_array(v, subplan->elemtype, subplan->elmlen,
|
||||
subplan->elmbyval, subplan->elmalign,
|
||||
&dvalues, &numelems);
|
||||
}
|
||||
else
|
||||
{
|
||||
numelems = 1;
|
||||
dvalues = (Datum *) palloc(numelems * sizeof(Datum));
|
||||
dvalues[0] = dvalue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* combine within row per AND semantics */
|
||||
if (expnull)
|
||||
rownull = true;
|
||||
else if (!DatumGetBool(expresult))
|
||||
numelems = 1;
|
||||
dvalues = (Datum *) palloc(numelems * sizeof(Datum));
|
||||
dvalues[0] = (Datum) 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (elemnum = 0; elemnum < numelems; elemnum++)
|
||||
{
|
||||
/*
|
||||
* For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
|
||||
* operators for columns of tuple.
|
||||
*/
|
||||
col = 1;
|
||||
plst = subplan->paramIds;
|
||||
foreach(lst, node->exprs)
|
||||
{
|
||||
ExprState *exprstate = (ExprState *) lfirst(lst);
|
||||
int paramid = lfirsti(plst);
|
||||
ParamExecData *prmdata;
|
||||
Datum expresult;
|
||||
bool expnull;
|
||||
|
||||
/*
|
||||
* Load up the Param representing this column of the sub-select.
|
||||
*/
|
||||
prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
|
||||
Assert(prmdata->execPlan == NULL);
|
||||
|
||||
if (!isExpr)
|
||||
prmdata->value = heap_getattr(tup, col, tdesc,
|
||||
&(prmdata->isnull));
|
||||
else
|
||||
{
|
||||
rowresult = BoolGetDatum(false);
|
||||
rownull = false;
|
||||
break; /* needn't look at any more columns */
|
||||
prmdata->value = dvalues[elemnum];
|
||||
prmdata->isnull = disnull;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we can eval the combining operator for this column.
|
||||
*/
|
||||
expresult = ExecEvalExprSwitchContext(exprstate, econtext,
|
||||
&expnull, NULL);
|
||||
|
||||
/*
|
||||
* Combine the result into the row result as appropriate.
|
||||
*/
|
||||
if (col == 1)
|
||||
{
|
||||
rowresult = expresult;
|
||||
rownull = expnull;
|
||||
}
|
||||
else if (useOr)
|
||||
{
|
||||
/* combine within row per OR semantics */
|
||||
if (expnull)
|
||||
rownull = true;
|
||||
else if (DatumGetBool(expresult))
|
||||
{
|
||||
rowresult = BoolGetDatum(true);
|
||||
rownull = false;
|
||||
break; /* needn't look at any more columns */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* combine within row per AND semantics */
|
||||
if (expnull)
|
||||
rownull = true;
|
||||
else if (!DatumGetBool(expresult))
|
||||
{
|
||||
rowresult = BoolGetDatum(false);
|
||||
rownull = false;
|
||||
break; /* needn't look at any more columns */
|
||||
}
|
||||
}
|
||||
|
||||
plst = lnext(plst);
|
||||
col++;
|
||||
}
|
||||
|
||||
if (subLinkType == ANY_SUBLINK)
|
||||
{
|
||||
/* combine across rows per OR semantics */
|
||||
if (rownull)
|
||||
*isNull = true;
|
||||
else if (DatumGetBool(rowresult))
|
||||
{
|
||||
result = BoolGetDatum(true);
|
||||
*isNull = false;
|
||||
break; /* needn't look at any more rows */
|
||||
}
|
||||
}
|
||||
|
||||
plst = lnext(plst);
|
||||
col++;
|
||||
}
|
||||
|
||||
if (subLinkType == ANY_SUBLINK)
|
||||
{
|
||||
/* combine across rows per OR semantics */
|
||||
if (rownull)
|
||||
*isNull = true;
|
||||
else if (DatumGetBool(rowresult))
|
||||
else if (subLinkType == ALL_SUBLINK)
|
||||
{
|
||||
result = BoolGetDatum(true);
|
||||
*isNull = false;
|
||||
break; /* needn't look at any more rows */
|
||||
/* combine across rows per AND semantics */
|
||||
if (rownull)
|
||||
*isNull = true;
|
||||
else if (!DatumGetBool(rowresult))
|
||||
{
|
||||
result = BoolGetDatum(false);
|
||||
*isNull = false;
|
||||
break; /* needn't look at any more rows */
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (subLinkType == ALL_SUBLINK)
|
||||
{
|
||||
/* combine across rows per AND semantics */
|
||||
if (rownull)
|
||||
*isNull = true;
|
||||
else if (!DatumGetBool(rowresult))
|
||||
else
|
||||
{
|
||||
result = BoolGetDatum(false);
|
||||
*isNull = false;
|
||||
break; /* needn't look at any more rows */
|
||||
/* must be MULTIEXPR_SUBLINK */
|
||||
result = rowresult;
|
||||
*isNull = rownull;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* must be MULTIEXPR_SUBLINK */
|
||||
result = rowresult;
|
||||
*isNull = rownull;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
@ -480,6 +524,7 @@ static void
|
||||
buildSubPlanHash(SubPlanState *node)
|
||||
{
|
||||
SubPlan *subplan = (SubPlan *) node->xprstate.expr;
|
||||
bool isExpr = subplan->isExpr;
|
||||
PlanState *planstate = node->planstate;
|
||||
int ncols = length(node->exprs);
|
||||
ExprContext *innerecontext = node->innerecontext;
|
||||
@ -487,6 +532,7 @@ buildSubPlanHash(SubPlanState *node)
|
||||
MemoryContext oldcontext;
|
||||
int nbuckets;
|
||||
TupleTableSlot *slot;
|
||||
TupleTableSlot *arrslot = NULL;
|
||||
|
||||
Assert(subplan->subLinkType == ANY_SUBLINK);
|
||||
Assert(!subplan->useOr);
|
||||
@ -566,43 +612,139 @@ buildSubPlanHash(SubPlanState *node)
|
||||
{
|
||||
HeapTuple tup = slot->val;
|
||||
TupleDesc tdesc = slot->ttc_tupleDescriptor;
|
||||
int col = 1;
|
||||
TupleDesc arrtdesc = NULL;
|
||||
List *plst;
|
||||
bool isnew;
|
||||
int numelems;
|
||||
int elemnum;
|
||||
Datum dvalue;
|
||||
Datum *dvalues = NULL;
|
||||
bool disnull;
|
||||
|
||||
/*
|
||||
* Load up the Params representing the raw sub-select outputs,
|
||||
* then form the projection tuple to store in the hashtable.
|
||||
* When isExpr is true, we have either a scalar expression or an
|
||||
* array. In the former case, this is no different than the !isExpr
|
||||
* case. In the latter case, iterate over the elements as if they
|
||||
* were from multiple input tuples.
|
||||
*/
|
||||
foreach(plst, subplan->paramIds)
|
||||
if (!isExpr)
|
||||
numelems = 1;
|
||||
else
|
||||
{
|
||||
int paramid = lfirsti(plst);
|
||||
ParamExecData *prmdata;
|
||||
Oid expr_typeid = tdesc->attrs[0]->atttypid;
|
||||
|
||||
prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
|
||||
Assert(prmdata->execPlan == NULL);
|
||||
prmdata->value = heap_getattr(tup, col, tdesc,
|
||||
&(prmdata->isnull));
|
||||
col++;
|
||||
}
|
||||
slot = ExecProject(node->projRight, NULL);
|
||||
tup = slot->val;
|
||||
if (expr_typeid != subplan->exprtype)
|
||||
{
|
||||
subplan->exprtype = expr_typeid;
|
||||
subplan->elemtype = get_element_type(expr_typeid);
|
||||
|
||||
if (subplan->elemtype != InvalidOid)
|
||||
get_typlenbyvalalign(subplan->elemtype,
|
||||
&subplan->elmlen,
|
||||
&subplan->elmbyval,
|
||||
&subplan->elmalign);
|
||||
}
|
||||
|
||||
/* get current value */
|
||||
dvalue = heap_getattr(tup, 1, tdesc, &disnull);
|
||||
|
||||
if (subplan->elemtype != InvalidOid)
|
||||
{
|
||||
TupleTable tupleTable;
|
||||
ArrayType *v = DatumGetArrayTypeP(dvalue);
|
||||
|
||||
arrtdesc = CreateTemplateTupleDesc(1, false);
|
||||
TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype,
|
||||
-1, 0, false);
|
||||
|
||||
tupleTable = ExecCreateTupleTable(1);
|
||||
arrslot = ExecAllocTableSlot(tupleTable);
|
||||
ExecSetSlotDescriptor(arrslot, arrtdesc, true);
|
||||
|
||||
/* XXX this will need work if/when arrays support NULL elements */
|
||||
if (!disnull)
|
||||
{
|
||||
deconstruct_array(v, subplan->elemtype, subplan->elmlen,
|
||||
subplan->elmbyval, subplan->elmalign,
|
||||
&dvalues, &numelems);
|
||||
}
|
||||
else
|
||||
{
|
||||
numelems = 1;
|
||||
dvalues = (Datum *) palloc(numelems * sizeof(Datum));
|
||||
dvalues[0] = (Datum) 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
numelems = 1;
|
||||
dvalues = (Datum *) palloc(numelems * sizeof(Datum));
|
||||
dvalues[0] = dvalue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If result contains any nulls, store separately or not at all.
|
||||
* (Since we know the projection tuple has no junk columns, we
|
||||
* can just look at the overall hasnull info bit, instead of
|
||||
* groveling through the columns.)
|
||||
*/
|
||||
if (HeapTupleNoNulls(tup))
|
||||
{
|
||||
(void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
|
||||
node->havehashrows = true;
|
||||
}
|
||||
else if (node->hashnulls)
|
||||
|
||||
for (elemnum = 0; elemnum < numelems; elemnum++)
|
||||
{
|
||||
(void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
|
||||
node->havenullrows = true;
|
||||
int col = 1;
|
||||
|
||||
if (!isExpr || subplan->elemtype == InvalidOid)
|
||||
{
|
||||
/*
|
||||
* Load up the Params representing the raw sub-select outputs,
|
||||
* then form the projection tuple to store in the hashtable.
|
||||
*/
|
||||
foreach(plst, subplan->paramIds)
|
||||
{
|
||||
int paramid = lfirsti(plst);
|
||||
ParamExecData *prmdata;
|
||||
|
||||
prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
|
||||
Assert(prmdata->execPlan == NULL);
|
||||
|
||||
prmdata->value = heap_getattr(tup, col, tdesc,
|
||||
&(prmdata->isnull));
|
||||
|
||||
col++;
|
||||
}
|
||||
slot = ExecProject(node->projRight, NULL);
|
||||
tup = slot->val;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* For array type expressions, we need to build up our own
|
||||
* tuple and slot
|
||||
*/
|
||||
char nullflag;
|
||||
|
||||
nullflag = disnull ? 'n' : ' ';
|
||||
tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag);
|
||||
arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* If result contains any nulls, store separately or not at all.
|
||||
* (Since we know the projection tuple has no junk columns, we
|
||||
* can just look at the overall hasnull info bit, instead of
|
||||
* groveling through the columns.)
|
||||
*/
|
||||
if (HeapTupleNoNulls(tup))
|
||||
{
|
||||
if (!isExpr)
|
||||
(void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
|
||||
else
|
||||
(void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew);
|
||||
node->havehashrows = true;
|
||||
}
|
||||
else if (node->hashnulls)
|
||||
{
|
||||
if (!isExpr)
|
||||
(void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
|
||||
else
|
||||
(void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew);
|
||||
node->havenullrows = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -619,6 +761,8 @@ buildSubPlanHash(SubPlanState *node)
|
||||
* have the potential for a double free attempt.
|
||||
*/
|
||||
ExecClearTuple(node->projRight->pi_slot);
|
||||
if (arrslot)
|
||||
ExecClearTuple(arrslot);
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
@ -1099,101 +1243,3 @@ 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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user