1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-15 03:41:20 +03:00

Array mega-patch.

Joe Conway
This commit is contained in:
Bruce Momjian
2003-06-24 23:14:49 +00:00
parent 50e53236af
commit 46bf651480
42 changed files with 2617 additions and 678 deletions

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.56 2002/09/18 21:35:20 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.57 2003/06/24 23:14:42 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -50,10 +50,16 @@ AggregateCreate(const char *aggName,
Oid finalfn = InvalidOid; /* can be omitted */
Oid finaltype;
Oid fnArgs[FUNC_MAX_ARGS];
int nargs;
int nargs_transfn;
int nargs_finalfn;
Oid procOid;
TupleDesc tupDesc;
int i;
Oid rettype;
Oid *true_oid_array_transfn;
Oid *true_oid_array_finalfn;
bool retset;
FuncDetailCode fdresult;
ObjectAddress myself,
referenced;
@@ -68,24 +74,49 @@ AggregateCreate(const char *aggName,
MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
fnArgs[0] = aggTransType;
if (aggBaseType == ANYOID)
nargs = 1;
nargs_transfn = 1;
else
{
fnArgs[1] = aggBaseType;
nargs = 2;
nargs_transfn = 2;
}
transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
/*
* func_get_detail looks up the function in the catalogs, does
* disambiguation for polymorphic functions, handles inheritance, and
* returns the funcid and type and set or singleton status of the
* function's return value. it also returns the true argument types
* to the function.
*/
fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs,
&transfn, &rettype, &retset,
&true_oid_array_transfn);
/* only valid case is a normal function */
if (fdresult != FUNCDETAIL_NORMAL)
func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
if (!OidIsValid(transfn))
func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
/*
* enforce consistency with ANYARRAY and ANYELEMENT argument
* and return types, possibly modifying return type along the way
*/
rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn,
nargs_transfn, rettype);
if (rettype != aggTransType)
elog(ERROR, "return type of transition function %s is not %s",
NameListToString(aggtransfnName), format_type_be(aggTransType));
tup = SearchSysCache(PROCOID,
ObjectIdGetDatum(transfn),
0, 0, 0);
if (!HeapTupleIsValid(tup))
func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
func_error("AggregateCreate", aggtransfnName,
nargs_transfn, fnArgs, NULL);
proc = (Form_pg_proc) GETSTRUCT(tup);
if (proc->prorettype != aggTransType)
elog(ERROR, "return type of transition function %s is not %s",
NameListToString(aggtransfnName), format_type_be(aggTransType));
/*
* If the transfn is strict and the initval is NULL, make sure input
@@ -105,17 +136,26 @@ AggregateCreate(const char *aggName,
{
MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
fnArgs[0] = aggTransType;
finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
nargs_finalfn = 1;
fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs,
&finalfn, &rettype, &retset,
&true_oid_array_finalfn);
/* only valid case is a normal function */
if (fdresult != FUNCDETAIL_NORMAL)
func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
if (!OidIsValid(finalfn))
func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
tup = SearchSysCache(PROCOID,
ObjectIdGetDatum(finalfn),
0, 0, 0);
if (!HeapTupleIsValid(tup))
func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
proc = (Form_pg_proc) GETSTRUCT(tup);
finaltype = proc->prorettype;
ReleaseSysCache(tup);
/*
* enforce consistency with ANYARRAY and ANYELEMENT argument
* and return types, possibly modifying return type along the way
*/
finaltype = enforce_generic_type_consistency(fnArgs,
true_oid_array_finalfn,
nargs_finalfn, rettype);
}
else
{
@@ -126,6 +166,27 @@ AggregateCreate(const char *aggName,
}
Assert(OidIsValid(finaltype));
/*
* special disallowed cases:
* 1) if finaltype is polymorphic, basetype cannot be ANY
* 2) if finaltype is polymorphic, both args to transfn must be
* polymorphic
*/
if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
{
if (aggBaseType == ANYOID)
elog(ERROR, "aggregate with base type ANY must have a " \
"non-polymorphic return type");
if (nargs_transfn > 1 && (
(true_oid_array_transfn[0] != ANYARRAYOID &&
true_oid_array_transfn[0] != ANYELEMENTOID) ||
(true_oid_array_transfn[1] != ANYARRAYOID &&
true_oid_array_transfn[1] != ANYELEMENTOID)))
elog(ERROR, "aggregate with polymorphic return type requires " \
"state function with both arguments polymorphic");
}
/*
* Everything looks okay. Try to create the pg_proc entry for the
* aggregate. (This could fail if there's already a conflicting

View File

@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.5 2002/09/04 20:31:14 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.6 2003/06/24 23:14:43 momjian Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@@ -119,7 +119,9 @@ DefineAggregate(List *names, List *parameters)
baseTypeId = typenameTypeId(baseType);
transTypeId = typenameTypeId(transType);
if (get_typtype(transTypeId) == 'p')
if (get_typtype(transTypeId) == 'p' &&
transTypeId != ANYARRAYOID &&
transTypeId != ANYELEMENTOID)
elog(ERROR, "Aggregate transition datatype cannot be %s",
format_type_be(transTypeId));

View File

@@ -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");
}

View File

@@ -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;
}

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.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);
}

View File

@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.252 2003/06/06 15:04:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.253 2003/06/24 23:14:43 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -728,6 +728,7 @@ _copyAggref(Aggref *from)
COPY_SCALAR_FIELD(agglevelsup);
COPY_SCALAR_FIELD(aggstar);
COPY_SCALAR_FIELD(aggdistinct);
COPY_NODE_FIELD(args);
return newnode;
}
@@ -826,6 +827,7 @@ _copySubLink(SubLink *from)
COPY_SCALAR_FIELD(subLinkType);
COPY_SCALAR_FIELD(useOr);
COPY_SCALAR_FIELD(isExpr);
COPY_NODE_FIELD(lefthand);
COPY_NODE_FIELD(operName);
COPY_OIDLIST_FIELD(operOids);
@@ -844,6 +846,12 @@ _copySubPlan(SubPlan *from)
COPY_SCALAR_FIELD(subLinkType);
COPY_SCALAR_FIELD(useOr);
COPY_SCALAR_FIELD(isExpr);
COPY_SCALAR_FIELD(exprtype);
COPY_SCALAR_FIELD(elemtype);
COPY_SCALAR_FIELD(elmlen);
COPY_SCALAR_FIELD(elmbyval);
COPY_SCALAR_FIELD(elmalign);
COPY_NODE_FIELD(exprs);
COPY_INTLIST_FIELD(paramIds);
COPY_NODE_FIELD(plan);

View File

@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.195 2003/06/06 15:04:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.196 2003/06/24 23:14:43 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -205,6 +205,7 @@ _equalAggref(Aggref *a, Aggref *b)
COMPARE_SCALAR_FIELD(agglevelsup);
COMPARE_SCALAR_FIELD(aggstar);
COMPARE_SCALAR_FIELD(aggdistinct);
COMPARE_NODE_FIELD(args);
return true;
}
@@ -301,6 +302,7 @@ _equalSubLink(SubLink *a, SubLink *b)
{
COMPARE_SCALAR_FIELD(subLinkType);
COMPARE_SCALAR_FIELD(useOr);
COMPARE_SCALAR_FIELD(isExpr);
COMPARE_NODE_FIELD(lefthand);
COMPARE_NODE_FIELD(operName);
COMPARE_OIDLIST_FIELD(operOids);
@@ -314,6 +316,12 @@ _equalSubPlan(SubPlan *a, SubPlan *b)
{
COMPARE_SCALAR_FIELD(subLinkType);
COMPARE_SCALAR_FIELD(useOr);
COMPARE_SCALAR_FIELD(isExpr);
COMPARE_SCALAR_FIELD(exprtype);
COMPARE_SCALAR_FIELD(elemtype);
COMPARE_SCALAR_FIELD(elmlen);
COMPARE_SCALAR_FIELD(elmbyval);
COMPARE_SCALAR_FIELD(elmalign);
COMPARE_NODE_FIELD(exprs);
COMPARE_INTLIST_FIELD(paramIds);
/* should compare plans, but have to settle for comparing plan IDs */

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.208 2003/06/15 22:51:45 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.209 2003/06/24 23:14:43 momjian Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
@@ -616,6 +616,7 @@ _outAggref(StringInfo str, Aggref *node)
WRITE_UINT_FIELD(agglevelsup);
WRITE_BOOL_FIELD(aggstar);
WRITE_BOOL_FIELD(aggdistinct);
WRITE_NODE_FIELD(args);
}
static void
@@ -701,6 +702,7 @@ _outSubLink(StringInfo str, SubLink *node)
WRITE_ENUM_FIELD(subLinkType, SubLinkType);
WRITE_BOOL_FIELD(useOr);
WRITE_BOOL_FIELD(isExpr);
WRITE_NODE_FIELD(lefthand);
WRITE_NODE_FIELD(operName);
WRITE_OIDLIST_FIELD(operOids);
@@ -714,6 +716,12 @@ _outSubPlan(StringInfo str, SubPlan *node)
WRITE_ENUM_FIELD(subLinkType, SubLinkType);
WRITE_BOOL_FIELD(useOr);
WRITE_BOOL_FIELD(isExpr);
WRITE_OID_FIELD(exprtype);
WRITE_OID_FIELD(elemtype);
WRITE_INT_FIELD(elmlen);
WRITE_BOOL_FIELD(elmbyval);
WRITE_CHAR_FIELD(elmalign);
WRITE_NODE_FIELD(exprs);
WRITE_INTLIST_FIELD(paramIds);
WRITE_NODE_FIELD(plan);

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.154 2003/06/06 15:04:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.155 2003/06/24 23:14:43 momjian Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
@@ -416,6 +416,7 @@ _readAggref(void)
READ_UINT_FIELD(agglevelsup);
READ_BOOL_FIELD(aggstar);
READ_BOOL_FIELD(aggdistinct);
READ_NODE_FIELD(args);
READ_DONE();
}
@@ -545,6 +546,7 @@ _readSubLink(void)
READ_ENUM_FIELD(subLinkType, SubLinkType);
READ_BOOL_FIELD(useOr);
READ_BOOL_FIELD(isExpr);
READ_NODE_FIELD(lefthand);
READ_NODE_FIELD(operName);
READ_OIDLIST_FIELD(operOids);

View File

@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.76 2003/06/06 15:04:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.77 2003/06/24 23:14:43 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -83,7 +83,7 @@ typedef struct finalize_primnode_context
static List *convert_sublink_opers(List *lefthand, List *operOids,
List *targetlist, int rtindex,
List **righthandIds);
bool isExpr, List **righthandIds);
static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
static Node *replace_correlation_vars_mutator(Node *node, void *context);
static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
@@ -299,6 +299,12 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
*/
node->subLinkType = slink->subLinkType;
node->useOr = slink->useOr;
node->isExpr = slink->isExpr;
node->exprtype = InvalidOid;
node->elemtype = InvalidOid;
node->elmlen = 0;
node->elmbyval = false;
node->elmalign = '\0';
node->exprs = NIL;
node->paramIds = NIL;
node->useHashTable = false;
@@ -374,7 +380,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
exprs = convert_sublink_opers(lefthand,
slink->operOids,
plan->targetlist,
0,
0, node->isExpr,
&node->paramIds);
node->setParam = listCopy(node->paramIds);
PlannerInitPlan = lappend(PlannerInitPlan, node);
@@ -457,7 +463,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
node->exprs = convert_sublink_opers(lefthand,
slink->operOids,
plan->targetlist,
0,
0, node->isExpr,
&node->paramIds);
/*
@@ -499,7 +505,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
static List *
convert_sublink_opers(List *lefthand, List *operOids,
List *targetlist, int rtindex,
List **righthandIds)
bool isExpr, List **righthandIds)
{
List *result = NIL;
List *lst;
@@ -554,13 +560,38 @@ convert_sublink_opers(List *lefthand, List *operOids,
* are not expecting to have to resolve unknown Params, so
* it's okay to pass a null pstate.)
*/
result = lappend(result,
make_op_expr(NULL,
tup,
leftop,
rightop,
exprType(leftop),
te->resdom->restype));
if (!isExpr)
{
result = lappend(result,
make_op_expr(NULL,
tup,
leftop,
rightop,
exprType(leftop),
te->resdom->restype));
}
else
{
Oid exprtype = te->resdom->restype;
Oid elemtype = get_element_type(exprtype);
if (elemtype != InvalidOid)
result = lappend(result,
make_op_expr(NULL,
tup,
leftop,
rightop,
exprType(leftop),
elemtype));
else
result = lappend(result,
make_op_expr(NULL,
tup,
leftop,
rightop,
exprType(leftop),
exprtype));
}
ReleaseSysCache(tup);
@@ -671,13 +702,17 @@ convert_IN_to_join(Query *parse, SubLink *sublink)
/*
* The sublink type must be "= ANY" --- that is, an IN operator.
* (We require the operator name to be unqualified, which may be
* overly paranoid, or may not be.)
* overly paranoid, or may not be.) It must not be an Expression
* sublink.
*/
if (sublink->subLinkType != ANY_SUBLINK)
return NULL;
if (length(sublink->operName) != 1 ||
strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
return NULL;
if (sublink->isExpr)
return NULL;
/*
* The sub-select must not refer to any Vars of the parent query.
* (Vars of higher levels should be okay, though.)
@@ -730,7 +765,7 @@ convert_IN_to_join(Query *parse, SubLink *sublink)
exprs = convert_sublink_opers(sublink->lefthand,
sublink->operOids,
subselect->targetList,
rtindex,
rtindex, sublink->isExpr,
&ininfo->sub_targetlist);
return (Node *) make_ands_explicit(exprs);
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.139 2003/06/06 15:04:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.140 2003/06/24 23:14:43 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -132,6 +132,28 @@ get_rightop(Expr *clause)
return NULL;
}
/*****************************************************************************
* FUNCTION clause functions
*****************************************************************************/
/*
* make_funcclause
* Creates a function clause given its function info and argument list.
*/
Expr *
make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
CoercionForm funcformat, List *funcargs)
{
FuncExpr *expr = makeNode(FuncExpr);
expr->funcid = funcid;
expr->funcresulttype = funcresulttype;
expr->funcretset = funcretset;
expr->funcformat = funcformat;
expr->args = funcargs;
return (Expr *) expr;
}
/*****************************************************************************
* NOT clause functions
*****************************************************************************/

View File

@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.417 2003/06/17 23:12:36 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.418 2003/06/24 23:14:43 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -5490,6 +5490,7 @@ r_expr: row IN_P select_with_parens
{
SubLink *n = makeNode(SubLink);
n->subLinkType = ANY_SUBLINK;
n->isExpr = false;
n->lefthand = $1;
n->operName = makeList1(makeString("="));
n->subselect = $3;
@@ -5500,6 +5501,7 @@ r_expr: row IN_P select_with_parens
/* Make an IN node */
SubLink *n = makeNode(SubLink);
n->subLinkType = ANY_SUBLINK;
n->isExpr = false;
n->lefthand = $1;
n->operName = makeList1(makeString("="));
n->subselect = $4;
@@ -5511,6 +5513,7 @@ r_expr: row IN_P select_with_parens
{
SubLink *n = makeNode(SubLink);
n->subLinkType = $3;
n->isExpr = false;
n->lefthand = $1;
n->operName = $2;
n->subselect = $4;
@@ -5521,6 +5524,7 @@ r_expr: row IN_P select_with_parens
{
SubLink *n = makeNode(SubLink);
n->subLinkType = MULTIEXPR_SUBLINK;
n->isExpr = false;
n->lefthand = $1;
n->operName = $2;
n->subselect = $3;
@@ -5904,6 +5908,7 @@ a_expr: c_expr { $$ = $1; }
{
SubLink *n = (SubLink *)$3;
n->subLinkType = ANY_SUBLINK;
n->isExpr = false;
n->lefthand = makeList1($1);
n->operName = makeList1(makeString("="));
$$ = (Node *)n;
@@ -5931,6 +5936,7 @@ a_expr: c_expr { $$ = $1; }
{
/* Make an IN node */
SubLink *n = (SubLink *)$4;
n->isExpr = false;
n->subLinkType = ANY_SUBLINK;
n->lefthand = makeList1($1);
n->operName = makeList1(makeString("="));
@@ -5957,11 +5963,38 @@ a_expr: c_expr { $$ = $1; }
{
SubLink *n = makeNode(SubLink);
n->subLinkType = $3;
n->isExpr = false;
n->lefthand = makeList1($1);
n->operName = $2;
n->subselect = $4;
$$ = (Node *)n;
}
| a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
{
SubLink *n = makeNode(SubLink);
SelectStmt *s = makeNode(SelectStmt);
ResTarget *r = makeNode(ResTarget);
r->name = NULL;
r->indirection = NIL;
r->val = (Node *)$5;
s->distinctClause = NIL;
s->targetList = makeList1(r);
s->into = NULL;
s->intoColNames = NIL;
s->fromClause = NIL;
s->whereClause = NULL;
s->groupClause = NIL;
s->havingClause = NULL;
n->subLinkType = $3;
n->isExpr = true;
n->lefthand = makeList1($1);
n->operName = $2;
n->subselect = (Node *) s;
$$ = (Node *)n;
}
| UNIQUE select_with_parens %prec Op
{
/* Not sure how to get rid of the parentheses
@@ -6538,6 +6571,7 @@ c_expr: columnref { $$ = (Node *) $1; }
{
SubLink *n = makeNode(SubLink);
n->subLinkType = EXPR_SUBLINK;
n->isExpr = false;
n->lefthand = NIL;
n->operName = NIL;
n->subselect = $1;
@@ -6547,6 +6581,7 @@ c_expr: columnref { $$ = (Node *) $1; }
{
SubLink *n = makeNode(SubLink);
n->subLinkType = EXISTS_SUBLINK;
n->isExpr = false;
n->lefthand = NIL;
n->operName = NIL;
n->subselect = $2;
@@ -6556,6 +6591,7 @@ c_expr: columnref { $$ = (Node *) $1; }
{
SubLink *n = makeNode(SubLink);
n->subLinkType = ARRAY_SUBLINK;
n->isExpr = false;
n->lefthand = NIL;
n->operName = NIL;
n->subselect = $2;
@@ -6730,6 +6766,7 @@ trim_list: a_expr FROM expr_list { $$ = lappend($3, $1); }
in_expr: select_with_parens
{
SubLink *n = makeNode(SubLink);
n->isExpr = false;
n->subselect = $1;
/* other fields will be filled later */
$$ = (Node *)n;

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.97 2003/05/26 00:11:27 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.98 2003/06/24 23:14:45 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -859,7 +859,11 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
/* Get the element type based on the array type, if we have one */
if (OidIsValid(array_typeid))
{
array_typelem = get_element_type(array_typeid);
if (array_typeid != ANYARRAYOID)
array_typelem = get_element_type(array_typeid);
else
array_typelem = ANYELEMENTOID;
if (!OidIsValid(array_typelem))
elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
format_type_be(array_typeid));
@@ -919,7 +923,11 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
{
if (!OidIsValid(array_typeid))
{
array_typeid = get_array_type(elem_typeid);
if (elem_typeid != ANYELEMENTOID)
array_typeid = get_array_type(elem_typeid);
else
array_typeid = ANYARRAYOID;
if (!OidIsValid(array_typeid))
elog(ERROR, "Cannot find array type for datatype %s",
format_type_be(elem_typeid));
@@ -1170,6 +1178,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (srctype == targettype)
return true;
/* Last of the fast-paths: check for matching polymorphic arrays */
if (targettype == ANYARRAYOID)
if (get_element_type(srctype) != InvalidOid)
return true;
/* Else look in pg_cast */
tuple = SearchSysCache(CASTSOURCETARGET,
ObjectIdGetDatum(srctype),

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.148 2003/04/29 22:13:10 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.149 2003/06/24 23:14:45 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -436,6 +436,7 @@ transformExpr(ParseState *pstate, Node *expr)
sublink->operName = NIL;
sublink->operOids = NIL;
sublink->useOr = FALSE;
sublink->isExpr = FALSE;
}
else if (sublink->subLinkType == EXPR_SUBLINK ||
sublink->subLinkType == ARRAY_SUBLINK)
@@ -463,6 +464,7 @@ transformExpr(ParseState *pstate, Node *expr)
sublink->operName = NIL;
sublink->operOids = NIL;
sublink->useOr = FALSE;
sublink->isExpr = FALSE;
}
else
{
@@ -538,10 +540,30 @@ transformExpr(ParseState *pstate, Node *expr)
* here, because make_subplan() will insert type
* coercion calls if needed.
*/
optup = oper(op,
exprType(lexpr),
exprType((Node *) tent->expr),
false);
if (!sublink->isExpr)
{
optup = oper(op,
exprType(lexpr),
exprType((Node *) tent->expr),
false);
}
else
{
Oid exprtype = exprType((Node *) tent->expr);
Oid elemtype = get_element_type(exprtype);
if (elemtype != InvalidOid)
optup = oper(op,
exprType(lexpr),
elemtype,
false);
else
optup = oper(op,
exprType(lexpr),
exprtype,
false);
}
opform = (Form_pg_operator) GETSTRUCT(optup);
if (opform->oprresult != BOOLOID)
@@ -743,7 +765,7 @@ transformExpr(ParseState *pstate, Node *expr)
ArrayExpr *e = (ArrayExpr *) lfirst(element);
if (!IsA(e, ArrayExpr))
elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions");
elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions");
if (ndims == 0)
ndims = e->ndims;
else if (e->ndims != ndims)

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.149 2003/06/06 15:04:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.150 2003/06/24 23:14:45 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -336,6 +336,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
aggref->target = lfirst(fargs);
aggref->aggstar = agg_star;
aggref->aggdistinct = agg_distinct;
aggref->args = fargs;
/* parse_agg.c does additional aggregate-specific processing */
transformAggregateCall(pstate, aggref);

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.64 2003/05/26 00:11:27 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.65 2003/06/24 23:14:45 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -137,6 +137,33 @@ Operator
equality_oper(Oid argtype, bool noError)
{
Operator optup;
Oid elem_type = get_element_type(argtype);
if (OidIsValid(elem_type))
{
bool found = false;
/*
* If the datatype is an array, look for an "=" operator for the
* element datatype. We require it to be an exact or binary-compatible
* match, since most callers are not prepared to cope with adding any
* run-time type coercion steps.
*/
optup = equality_oper(elem_type, true);
if (optup != NULL)
{
found = true;
ReleaseSysCache(optup);
}
if (!found)
{
if (!noError)
elog(ERROR, "Unable to identify an equality operator for " \
"array type's element type %s",
format_type_be(elem_type));
return NULL;
}
}
/*
* Look for an "=" operator for the datatype. We require it to be
@@ -175,6 +202,33 @@ Operator
ordering_oper(Oid argtype, bool noError)
{
Operator optup;
Oid elem_type = get_element_type(argtype);
if (OidIsValid(elem_type))
{
bool found = false;
/*
* If the datatype is an array, find the array element type's equality
* operator, and use its lsortop (it *must* be mergejoinable). We use
* this definition because for sorting and grouping purposes, it's
* important that the equality and ordering operators are consistent.
*/
optup = ordering_oper(elem_type, true);
if (optup != NULL)
{
found = true;
ReleaseSysCache(optup);
}
if (!found)
{
if (!noError)
elog(ERROR, "Unable to identify an ordering operator for " \
"array type's element type %s",
format_type_be(elem_type));
return NULL;
}
}
/*
* Find the type's equality operator, and use its lsortop (it *must*
@@ -220,6 +274,21 @@ equality_oper_funcid(Oid argtype)
return result;
}
/*
* ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
*/
Oid
ordering_oper_funcid(Oid argtype)
{
Operator optup;
Oid result;
optup = ordering_oper(argtype, false);
result = oprfuncid(optup);
ReleaseSysCache(optup);
return result;
}
/*
* ordering_oper_opid - convenience routine for oprid(ordering_oper())
*

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.88 2003/06/11 09:23:55 petere Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.89 2003/06/24 23:14:45 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -427,6 +427,15 @@ aclitemeq(const AclItem *a1, const AclItem *a2)
a1->ai_grantor == a2->ai_grantor;
}
/*
* user-facing version of aclitemeq() for use as the
* aclitem equality operator
*/
Datum
aclitem_eq(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(aclitemeq(PG_GETARG_ACLITEM_P(0), PG_GETARG_ACLITEM_P(1)));
}
/*
* acldefault() --- create an ACL describing default access permissions

View File

@@ -6,7 +6,7 @@
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.1 2003/04/08 23:20:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.2 2003/06/24 23:14:45 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -18,35 +18,6 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
/*-----------------------------------------------------------------------------
* singleton_array :
* Form a multi-dimensional array given one starting element.
*
* - first argument is the datum with which to build the array
* - second argument is the number of dimensions the array should have;
* defaults to 1 if no second argument is provided
*----------------------------------------------------------------------------
*/
Datum
singleton_array(PG_FUNCTION_ARGS)
{
Oid elem_type = get_fn_expr_argtype(fcinfo, 0);
int ndims;
if (elem_type == InvalidOid)
elog(ERROR, "Cannot determine input datatype");
if (PG_NARGS() == 2)
ndims = PG_GETARG_INT32(1);
else
ndims = 1;
PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
PG_GETARG_DATUM(0),
ndims));
}
/*-----------------------------------------------------------------------------
* array_push :
* push an element onto either end of a one-dimensional array
@@ -70,6 +41,7 @@ array_push(PG_FUNCTION_ARGS)
Oid arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
Oid arg0_elemid;
Oid arg1_elemid;
ArrayMetaState *my_extra;
if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
elog(ERROR, "array_push: cannot determine input data types");
@@ -95,28 +67,61 @@ array_push(PG_FUNCTION_ARGS)
PG_RETURN_NULL(); /* keep compiler quiet */
}
/* Sanity check: do we have a one-dimensional array */
if (ARR_NDIM(v) != 1)
elog(ERROR, "Arrays greater than one-dimension are not supported");
lb = ARR_LBOUND(v);
dimv = ARR_DIMS(v);
if (arg0_elemid != InvalidOid)
if (ARR_NDIM(v) == 1)
{
/* append newelem */
int ub = dimv[0] + lb[0] - 1;
indx = ub + 1;
lb = ARR_LBOUND(v);
dimv = ARR_DIMS(v);
if (arg0_elemid != InvalidOid)
{
/* append newelem */
int ub = dimv[0] + lb[0] - 1;
indx = ub + 1;
}
else
{
/* prepend newelem */
indx = lb[0] - 1;
}
}
else if (ARR_NDIM(v) == 0)
indx = 1;
else
elog(ERROR, "only empty and one-dimensional arrays are supported");
/*
* We arrange to look up info about element type only once per series
* of calls, assuming the element type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = InvalidOid;
}
if (my_extra->element_type != element_type)
{
/* Get info about element type */
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
my_extra->element_type = element_type;
my_extra->typlen = typlen;
my_extra->typbyval = typbyval;
my_extra->typalign = typalign;
}
else
{
/* prepend newelem */
indx = lb[0] - 1;
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
}
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
result = array_set(v, 1, &indx, newelem, -1,
typlen, typbyval, typalign, &isNull);
result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval,
typalign, &isNull);
PG_RETURN_ARRAYTYPE_P(result);
}
@@ -145,13 +150,28 @@ array_cat(PG_FUNCTION_ARGS)
/*
* We must have one of the following combinations of inputs:
* 1) two arrays with ndims1 == ndims2
* 2) ndims1 == ndims2 - 1
* 3) ndims1 == ndims2 + 1
* 1) one empty array, and one non-empty array
* 2) both arrays empty
* 3) two arrays with ndims1 == ndims2
* 4) ndims1 == ndims2 - 1
* 5) ndims1 == ndims2 + 1
*/
ndims1 = ARR_NDIM(v1);
ndims2 = ARR_NDIM(v2);
/*
* short circuit - if one input array is empty, and the other is not,
* we return the non-empty one as the result
*
* if both are empty, return the first one
*/
if (ndims1 == 0 && ndims2 > 0)
PG_RETURN_ARRAYTYPE_P(v2);
if (ndims2 == 0)
PG_RETURN_ARRAYTYPE_P(v1);
/* the rest fall into combo 2, 3, or 4 */
if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
"%d dimensions", ndims1, ndims2);
@@ -266,147 +286,15 @@ array_cat(PG_FUNCTION_ARGS)
PG_RETURN_ARRAYTYPE_P(result);
}
/*----------------------------------------------------------------------------
* array_accum :
* accumulator to build a 1-D array from input values -- this can be used
* to create custom aggregates.
*
* This function is not marked strict, so we have to be careful about nulls.
*----------------------------------------------------------------------------
*/
Datum
array_accum(PG_FUNCTION_ARGS)
{
/* return NULL if both arguments are NULL */
if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
PG_RETURN_NULL();
/* create a new 1-D array from the new element if the array is NULL */
if (PG_ARGISNULL(0))
{
Oid tgt_type = get_fn_expr_rettype(fcinfo);
Oid tgt_elem_type;
if (tgt_type == InvalidOid)
elog(ERROR, "Cannot determine target array type");
tgt_elem_type = get_element_type(tgt_type);
if (tgt_elem_type == InvalidOid)
elog(ERROR, "Target type is not an array");
PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
PG_GETARG_DATUM(1),
1));
}
/* return the array if the new element is NULL */
if (PG_ARGISNULL(1))
PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
/*
* Otherwise this is equivalent to array_push. We hack the call a little
* so that array_push can see the fn_expr information.
*/
return array_push(fcinfo);
}
/*-----------------------------------------------------------------------------
* array_assign :
* assign an element of an array to a new value and return the
* redefined array
*----------------------------------------------------------------------------
*/
Datum
array_assign(PG_FUNCTION_ARGS)
{
ArrayType *v;
int idx_to_chg;
Datum newelem;
int *dimv,
*lb, ub;
ArrayType *result;
bool isNull;
Oid element_type;
int16 typlen;
bool typbyval;
char typalign;
v = PG_GETARG_ARRAYTYPE_P(0);
idx_to_chg = PG_GETARG_INT32(1);
newelem = PG_GETARG_DATUM(2);
/* Sanity check: do we have a one-dimensional array */
if (ARR_NDIM(v) != 1)
elog(ERROR, "Arrays greater than one-dimension are not supported");
lb = ARR_LBOUND(v);
dimv = ARR_DIMS(v);
ub = dimv[0] + lb[0] - 1;
if (idx_to_chg < lb[0] || idx_to_chg > ub)
elog(ERROR, "Cannot alter nonexistent array element: %d", idx_to_chg);
element_type = ARR_ELEMTYPE(v);
/* Sanity check: do we have a non-zero element type */
if (element_type == 0)
elog(ERROR, "Invalid array element type: %u", element_type);
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
result = array_set(v, 1, &idx_to_chg, newelem, -1,
typlen, typbyval, typalign, &isNull);
PG_RETURN_ARRAYTYPE_P(result);
}
/*-----------------------------------------------------------------------------
* array_subscript :
* return specific element of an array
*----------------------------------------------------------------------------
*/
Datum
array_subscript(PG_FUNCTION_ARGS)
{
ArrayType *v;
int idx;
int *dimv,
*lb, ub;
Datum result;
bool isNull;
Oid element_type;
int16 typlen;
bool typbyval;
char typalign;
v = PG_GETARG_ARRAYTYPE_P(0);
idx = PG_GETARG_INT32(1);
/* Sanity check: do we have a one-dimensional array */
if (ARR_NDIM(v) != 1)
elog(ERROR, "Arrays greater than one-dimension are not supported");
lb = ARR_LBOUND(v);
dimv = ARR_DIMS(v);
ub = dimv[0] + lb[0] - 1;
if (idx < lb[0] || idx > ub)
elog(ERROR, "Cannot return nonexistent array element: %d", idx);
element_type = ARR_ELEMTYPE(v);
/* Sanity check: do we have a non-zero element type */
if (element_type == 0)
elog(ERROR, "Invalid array element type: %u", element_type);
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);
PG_RETURN_DATUM(result);
}
/*
* actually does the work for singleton_array(), and array_accum() if it is
* given a null input array.
* used by text_to_array() in varlena.c
*/
ArrayType *
create_singleton_array(Oid element_type, Datum element, int ndims)
create_singleton_array(FunctionCallInfo fcinfo,
Oid element_type,
Datum element,
int ndims)
{
Datum dvalues[1];
int16 typlen;
@@ -415,6 +303,7 @@ create_singleton_array(Oid element_type, Datum element, int ndims)
int dims[MAXDIM];
int lbs[MAXDIM];
int i;
ArrayMetaState *my_extra;
if (element_type == 0)
elog(ERROR, "Invalid array element type: %u", element_type);
@@ -429,7 +318,35 @@ create_singleton_array(Oid element_type, Datum element, int ndims)
lbs[i] = 1;
}
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
/*
* We arrange to look up info about element type only once per series
* of calls, assuming the element type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = InvalidOid;
}
if (my_extra->element_type != element_type)
{
/* Get info about element type */
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
my_extra->element_type = element_type;
my_extra->typlen = typlen;
my_extra->typbyval = typbyval;
my_extra->typalign = typalign;
}
else
{
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
}
return construct_md_array(dvalues, ndims, dims, lbs, element_type,
typlen, typbyval, typalign);

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.89 2003/05/09 23:01:45 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.90 2003/06/24 23:14:45 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -21,8 +21,10 @@
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "parser/parse_coerce.h"
#include "parser/parse_oper.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/memutils.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@@ -70,16 +72,6 @@
#define RETURN_NULL(type) do { *isNull = true; return (type) 0; } while (0)
/* I/O function selector for system_cache_lookup */
typedef enum IOFuncSelector
{
IOFunc_input,
IOFunc_output,
IOFunc_receive,
IOFunc_send
} IOFuncSelector;
static int ArrayCount(char *str, int *dim, char typdelim);
static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
FmgrInfo *inputproc, Oid typelem, int32 typmod,
@@ -93,10 +85,6 @@ static Datum *ReadArrayBinary(StringInfo buf, int nitems,
static void CopyArrayEls(char *p, Datum *values, int nitems,
int typlen, bool typbyval, char typalign,
bool freedata);
static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
int *typlen, bool *typbyval,
char *typdelim, Oid *typelem,
Oid *proc, char *typalign);
static Datum ArrayCast(char *value, bool byval, int len);
static int ArrayCastAndSet(Datum src,
int typlen, bool typbyval, char typalign,
@@ -119,7 +107,7 @@ static void array_insert_slice(int ndim, int *dim, int *lb,
char *destPtr,
int *st, int *endp, char *srcPtr,
int typlen, bool typbyval, char typalign);
static int array_cmp(FunctionCallInfo fcinfo);
/*---------------------------------------------------------------------
* array_in :
@@ -154,12 +142,49 @@ array_in(PG_FUNCTION_ARGS)
dim[MAXDIM],
lBound[MAXDIM];
char typalign;
ArrayMetaState *my_extra;
/* Get info about element type, including its input conversion proc */
system_cache_lookup(element_type, IOFunc_input,
&typlen, &typbyval, &typdelim,
&typelem, &typinput, &typalign);
fmgr_info(typinput, &inputproc);
/*
* We arrange to look up info about element type, including its input
* conversion proc only once per series of calls, assuming the element
* type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = InvalidOid;
}
if (my_extra->element_type != element_type)
{
/* Get info about element type, including its input conversion proc */
get_type_metadata(element_type, IOFunc_input,
&typlen, &typbyval, &typdelim,
&typelem, &typinput, &typalign);
fmgr_info(typinput, &inputproc);
my_extra->element_type = element_type;
my_extra->typlen = typlen;
my_extra->typbyval = typbyval;
my_extra->typdelim = typdelim;
my_extra->typelem = typelem;
my_extra->typiofunc = typinput;
my_extra->typalign = typalign;
my_extra->proc = inputproc;
}
else
{
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typdelim = my_extra->typdelim;
typelem = my_extra->typelem;
typinput = my_extra->typiofunc;
typalign = my_extra->typalign;
inputproc = my_extra->proc;
}
/* Make a modifiable copy of the input */
/* XXX why are we allocating an extra 2 bytes here? */
@@ -636,12 +661,51 @@ array_out(PG_FUNCTION_ARGS)
indx[MAXDIM];
int ndim,
*dim;
ArrayMetaState *my_extra;
element_type = ARR_ELEMTYPE(v);
system_cache_lookup(element_type, IOFunc_output,
&typlen, &typbyval, &typdelim,
&typelem, &typoutput, &typalign);
fmgr_info(typoutput, &outputproc);
/*
* We arrange to look up info about element type, including its input
* conversion proc only once per series of calls, assuming the element
* type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = InvalidOid;
}
if (my_extra->element_type != element_type)
{
/* Get info about element type, including its output conversion proc */
get_type_metadata(element_type, IOFunc_output,
&typlen, &typbyval, &typdelim,
&typelem, &typoutput, &typalign);
fmgr_info(typoutput, &outputproc);
my_extra->element_type = element_type;
my_extra->typlen = typlen;
my_extra->typbyval = typbyval;
my_extra->typdelim = typdelim;
my_extra->typelem = typelem;
my_extra->typiofunc = typoutput;
my_extra->typalign = typalign;
my_extra->proc = outputproc;
}
else
{
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typdelim = my_extra->typdelim;
typelem = my_extra->typelem;
typoutput = my_extra->typiofunc;
typalign = my_extra->typalign;
outputproc = my_extra->proc;
}
ndim = ARR_NDIM(v);
dim = ARR_DIMS(v);
@@ -800,6 +864,7 @@ array_recv(PG_FUNCTION_ARGS)
dim[MAXDIM],
lBound[MAXDIM];
char typalign;
ArrayMetaState *my_extra;
/* Get the array header information */
ndim = pq_getmsgint(buf, 4);
@@ -831,14 +896,50 @@ array_recv(PG_FUNCTION_ARGS)
PG_RETURN_ARRAYTYPE_P(retval);
}
/* Get info about element type, including its receive conversion proc */
system_cache_lookup(element_type, IOFunc_receive,
&typlen, &typbyval, &typdelim,
&typelem, &typreceive, &typalign);
if (!OidIsValid(typreceive))
elog(ERROR, "No binary input function available for type %s",
format_type_be(element_type));
fmgr_info(typreceive, &receiveproc);
/*
* We arrange to look up info about element type, including its receive
* conversion proc only once per series of calls, assuming the element
* type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = InvalidOid;
}
if (my_extra->element_type != element_type)
{
/* Get info about element type, including its receive conversion proc */
get_type_metadata(element_type, IOFunc_receive,
&typlen, &typbyval, &typdelim,
&typelem, &typreceive, &typalign);
if (!OidIsValid(typreceive))
elog(ERROR, "No binary input function available for type %s",
format_type_be(element_type));
fmgr_info(typreceive, &receiveproc);
my_extra->element_type = element_type;
my_extra->typlen = typlen;
my_extra->typbyval = typbyval;
my_extra->typdelim = typdelim;
my_extra->typelem = typelem;
my_extra->typiofunc = typreceive;
my_extra->typalign = typalign;
my_extra->proc = receiveproc;
}
else
{
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typdelim = my_extra->typdelim;
typelem = my_extra->typelem;
typreceive = my_extra->typiofunc;
typalign = my_extra->typalign;
receiveproc = my_extra->proc;
}
dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
typlen, typbyval, typalign,
@@ -976,15 +1077,54 @@ array_send(PG_FUNCTION_ARGS)
int ndim,
*dim;
StringInfoData buf;
ArrayMetaState *my_extra;
/* Get information about the element type and the array dimensions */
element_type = ARR_ELEMTYPE(v);
system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
&typdelim, &typelem, &typsend, &typalign);
if (!OidIsValid(typsend))
elog(ERROR, "No binary output function available for type %s",
format_type_be(element_type));
fmgr_info(typsend, &sendproc);
/*
* We arrange to look up info about element type, including its send
* proc only once per series of calls, assuming the element
* type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = InvalidOid;
}
if (my_extra->element_type != element_type)
{
/* Get info about element type, including its send proc */
get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval,
&typdelim, &typelem, &typsend, &typalign);
if (!OidIsValid(typsend))
elog(ERROR, "No binary output function available for type %s",
format_type_be(element_type));
fmgr_info(typsend, &sendproc);
my_extra->element_type = element_type;
my_extra->typlen = typlen;
my_extra->typbyval = typbyval;
my_extra->typdelim = typdelim;
my_extra->typelem = typelem;
my_extra->typiofunc = typsend;
my_extra->typalign = typalign;
my_extra->proc = sendproc;
}
else
{
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typdelim = my_extra->typdelim;
typelem = my_extra->typelem;
typsend = my_extra->typiofunc;
typalign = my_extra->typalign;
sendproc = my_extra->proc;
}
ndim = ARR_NDIM(v);
dim = ARR_DIMS(v);
@@ -1476,6 +1616,26 @@ array_set(ArrayType *array,
array = DatumGetArrayTypeP(PointerGetDatum(array));
ndim = ARR_NDIM(array);
/*
* if number of dims is zero, i.e. an empty array, create an array
* with nSubscripts dimensions, and set the lower bounds to the supplied
* subscripts
*/
if (ndim == 0)
{
Oid elmtype = ARR_ELEMTYPE(array);
for (i = 0; i < nSubscripts; i++)
{
dim[i] = 1;
lb[i] = indx[i];
}
return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype,
elmlen, elmbyval, elmalign);
}
if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
elog(ERROR, "Invalid array subscripts");
@@ -1632,6 +1792,31 @@ array_set_slice(ArrayType *array,
/* note: we assume srcArray contains no toasted elements */
ndim = ARR_NDIM(array);
/*
* if number of dims is zero, i.e. an empty array, create an array
* with nSubscripts dimensions, and set the upper and lower bounds
* to the supplied subscripts
*/
if (ndim == 0)
{
Datum *dvalues;
int nelems;
Oid elmtype = ARR_ELEMTYPE(array);
deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
&dvalues, &nelems);
for (i = 0; i < nSubscripts; i++)
{
dim[i] = 1 + upperIndx[i] - lowerIndx[i];
lb[i] = lowerIndx[i];
}
return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype,
elmlen, elmbyval, elmalign);
}
if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
elog(ERROR, "Invalid array subscripts");
@@ -1811,6 +1996,13 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
Oid typelem;
Oid proc;
char *s;
typedef struct {
ArrayMetaState *inp_extra;
ArrayMetaState *ret_extra;
} am_extra;
am_extra *my_extra;
ArrayMetaState *inp_extra;
ArrayMetaState *ret_extra;
/* Get input array */
if (fcinfo->nargs < 1)
@@ -1829,11 +2021,81 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
if (nitems <= 0)
PG_RETURN_ARRAYTYPE_P(v);
/* Lookup source and result types. Unneeded variables are reused. */
system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
&typdelim, &typelem, &proc, &inp_typalign);
system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
&typdelim, &typelem, &proc, &typalign);
/*
* We arrange to look up info about input and return element types only
* once per series of calls, assuming the element type doesn't change
* underneath us.
*/
my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(am_extra));
my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
inp_extra = my_extra->inp_extra;
inp_extra->element_type = InvalidOid;
my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
ret_extra = my_extra->ret_extra;
ret_extra->element_type = InvalidOid;
}
else
{
inp_extra = my_extra->inp_extra;
ret_extra = my_extra->ret_extra;
}
if (inp_extra->element_type != inpType)
{
/* Lookup source and result types. Unneeded variables are reused. */
get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
&typdelim, &typelem, &proc, &inp_typalign);
inp_extra->element_type = inpType;
inp_extra->typlen = inp_typlen;
inp_extra->typbyval = inp_typbyval;
inp_extra->typdelim = typdelim;
inp_extra->typelem = typelem;
inp_extra->typiofunc = proc;
inp_extra->typalign = inp_typalign;
}
else
{
inp_typlen = inp_extra->typlen;
inp_typbyval = inp_extra->typbyval;
typdelim = inp_extra->typdelim;
typelem = inp_extra->typelem;
proc = inp_extra->typiofunc;
inp_typalign = inp_extra->typalign;
}
if (ret_extra->element_type != retType)
{
/* Lookup source and result types. Unneeded variables are reused. */
get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
&typdelim, &typelem, &proc, &typalign);
ret_extra->element_type = retType;
ret_extra->typlen = typlen;
ret_extra->typbyval = typbyval;
ret_extra->typdelim = typdelim;
ret_extra->typelem = typelem;
ret_extra->typiofunc = proc;
ret_extra->typalign = typalign;
}
else
{
typlen = ret_extra->typlen;
typbyval = ret_extra->typbyval;
typdelim = ret_extra->typdelim;
typelem = ret_extra->typelem;
proc = ret_extra->typiofunc;
typalign = ret_extra->typalign;
}
/* Allocate temporary array for new values */
values = (Datum *) palloc(nitems * sizeof(Datum));
@@ -2049,8 +2311,6 @@ deconstruct_array(ArrayType *array,
* compares two arrays for equality
* result :
* returns true if the arrays are equal, false otherwise.
*
* XXX bitwise equality is pretty bogus ...
*-----------------------------------------------------------------------------
*/
Datum
@@ -2058,12 +2318,118 @@ array_eq(PG_FUNCTION_ARGS)
{
ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
char *p1 = (char *) ARR_DATA_PTR(array1);
char *p2 = (char *) ARR_DATA_PTR(array2);
int ndims1 = ARR_NDIM(array1);
int ndims2 = ARR_NDIM(array2);
int *dims1 = ARR_DIMS(array1);
int *dims2 = ARR_DIMS(array2);
int nitems1 = ArrayGetNItems(ndims1, dims1);
int nitems2 = ArrayGetNItems(ndims2, dims2);
Oid element_type = ARR_ELEMTYPE(array1);
FmgrInfo *ae_fmgr_info = fcinfo->flinfo;
bool result = true;
int typlen;
bool typbyval;
char typdelim;
Oid typelem;
char typalign;
Oid typiofunc;
int i;
ArrayMetaState *my_extra;
FunctionCallInfoData locfcinfo;
if (ARR_SIZE(array1) != ARR_SIZE(array2))
result = false;
else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
/* fast path if the arrays do not have the same number of elements */
if (nitems1 != nitems2)
result = false;
else
{
/*
* We arrange to look up the equality function only once per series of
* calls, assuming the element type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
if (my_extra == NULL)
{
ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
my_extra->element_type = InvalidOid;
}
if (my_extra->element_type != element_type)
{
Oid opfuncid = equality_oper_funcid(element_type);
if (OidIsValid(opfuncid))
fmgr_info_cxt(opfuncid, &my_extra->proc, ae_fmgr_info->fn_mcxt);
else
elog(ERROR,
"array_eq: cannot find equality operator for type: %u",
element_type);
get_type_metadata(element_type, IOFunc_output,
&typlen, &typbyval, &typdelim,
&typelem, &typiofunc, &typalign);
my_extra->element_type = element_type;
my_extra->typlen = typlen;
my_extra->typbyval = typbyval;
my_extra->typdelim = typdelim;
my_extra->typelem = typelem;
my_extra->typiofunc = typiofunc;
my_extra->typalign = typalign;
}
else
{
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typdelim = my_extra->typdelim;
typelem = my_extra->typelem;
typiofunc = my_extra->typiofunc;
typalign = my_extra->typalign;
}
/*
* apply the operator to each pair of array elements.
*/
MemSet(&locfcinfo, 0, sizeof(locfcinfo));
locfcinfo.flinfo = &my_extra->proc;
locfcinfo.nargs = 2;
/* Loop over source data */
for (i = 0; i < nitems1; i++)
{
Datum elt1;
Datum elt2;
bool oprresult;
/* Get element pair */
elt1 = fetch_att(p1, typbyval, typlen);
elt2 = fetch_att(p2, typbyval, typlen);
p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
p1 = (char *) att_align(p1, typalign);
p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
p2 = (char *) att_align(p2, typalign);
/*
* Apply the operator to the element pair
*/
locfcinfo.arg[0] = elt1;
locfcinfo.arg[1] = elt2;
locfcinfo.argnull[0] = false;
locfcinfo.argnull[1] = false;
locfcinfo.isnull = false;
oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
if (!oprresult)
{
result = false;
break;
}
}
}
/* Avoid leaking memory when handed toasted input. */
PG_FREE_IF_COPY(array1, 0);
@@ -2073,53 +2439,190 @@ array_eq(PG_FUNCTION_ARGS)
}
/*-----------------------------------------------------------------------------
* array-array bool operators:
* Given two arrays, iterate comparison operators
* over the array. Uses logic similar to text comparison
* functions, except element-by-element instead of
* character-by-character.
*----------------------------------------------------------------------------
*/
Datum
array_ne(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
}
Datum
array_lt(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
}
Datum
array_gt(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
}
Datum
array_le(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
}
Datum
array_ge(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
}
Datum
btarraycmp(PG_FUNCTION_ARGS)
{
PG_RETURN_INT32(array_cmp(fcinfo));
}
/*
* array_cmp()
* Internal comparison function for arrays.
*
* Returns -1, 0 or 1
*/
static int
array_cmp(FunctionCallInfo fcinfo)
{
ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
FmgrInfo *ac_fmgr_info = fcinfo->flinfo;
Datum opresult;
int result = 0;
Oid element_type = InvalidOid;
int typlen;
bool typbyval;
char typdelim;
Oid typelem;
char typalign;
Oid typiofunc;
Datum *dvalues1;
int nelems1;
Datum *dvalues2;
int nelems2;
int min_nelems;
int i;
typedef struct
{
Oid element_type;
int typlen;
bool typbyval;
char typdelim;
Oid typelem;
Oid typiofunc;
char typalign;
FmgrInfo eqproc;
FmgrInfo ordproc;
} ac_extra;
ac_extra *my_extra;
element_type = ARR_ELEMTYPE(array1);
/*
* We arrange to look up the element type operator function only once
* per series of calls, assuming the element type and opname don't
* change underneath us.
*/
my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
if (my_extra == NULL)
{
ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt,
sizeof(ac_extra));
my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
my_extra->element_type = InvalidOid;
}
if (my_extra->element_type != element_type)
{
Oid eqfuncid = equality_oper_funcid(element_type);
Oid ordfuncid = ordering_oper_funcid(element_type);
fmgr_info_cxt(eqfuncid, &my_extra->eqproc, ac_fmgr_info->fn_mcxt);
fmgr_info_cxt(ordfuncid, &my_extra->ordproc, ac_fmgr_info->fn_mcxt);
if (my_extra->eqproc.fn_nargs != 2)
elog(ERROR, "Equality operator does not take 2 arguments: %u",
eqfuncid);
if (my_extra->ordproc.fn_nargs != 2)
elog(ERROR, "Ordering operator does not take 2 arguments: %u",
ordfuncid);
get_type_metadata(element_type, IOFunc_output,
&typlen, &typbyval, &typdelim,
&typelem, &typiofunc, &typalign);
my_extra->element_type = element_type;
my_extra->typlen = typlen;
my_extra->typbyval = typbyval;
my_extra->typdelim = typdelim;
my_extra->typelem = typelem;
my_extra->typiofunc = InvalidOid;
my_extra->typalign = typalign;
}
else
{
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
}
/* extract a C array of arg array datums */
deconstruct_array(array1, element_type, typlen, typbyval, typalign,
&dvalues1, &nelems1);
deconstruct_array(array2, element_type, typlen, typbyval, typalign,
&dvalues2, &nelems2);
min_nelems = Min(nelems1, nelems2);
for (i = 0; i < min_nelems; i++)
{
/* are they equal */
opresult = FunctionCall2(&my_extra->eqproc,
dvalues1[i], dvalues2[i]);
if (!DatumGetBool(opresult))
{
/* nope, see if arg1 is less than arg2 */
opresult = FunctionCall2(&my_extra->ordproc,
dvalues1[i], dvalues2[i]);
if (DatumGetBool(opresult))
{
/* arg1 is less than arg2 */
result = -1;
break;
}
else
{
/* arg1 is greater than arg2 */
result = 1;
break;
}
}
}
if ((result == 0) && (nelems1 != nelems2))
result = (nelems1 < nelems2) ? -1 : 1;
/* Avoid leaking memory when handed toasted input. */
PG_FREE_IF_COPY(array1, 0);
PG_FREE_IF_COPY(array2, 1);
return result;
}
/***************************************************************************/
/******************| Support Routines |*****************/
/***************************************************************************/
static void
system_cache_lookup(Oid element_type,
IOFuncSelector which_func,
int *typlen,
bool *typbyval,
char *typdelim,
Oid *typelem,
Oid *proc,
char *typalign)
{
HeapTuple typeTuple;
Form_pg_type typeStruct;
typeTuple = SearchSysCache(TYPEOID,
ObjectIdGetDatum(element_type),
0, 0, 0);
if (!HeapTupleIsValid(typeTuple))
elog(ERROR, "cache lookup failed for type %u", element_type);
typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
*typlen = typeStruct->typlen;
*typbyval = typeStruct->typbyval;
*typdelim = typeStruct->typdelim;
*typelem = typeStruct->typelem;
*typalign = typeStruct->typalign;
switch (which_func)
{
case IOFunc_input:
*proc = typeStruct->typinput;
break;
case IOFunc_output:
*proc = typeStruct->typoutput;
break;
case IOFunc_receive:
*proc = typeStruct->typreceive;
break;
case IOFunc_send:
*proc = typeStruct->typsend;
break;
}
ReleaseSysCache(typeTuple);
}
/*
* Fetch array element at pointer, converted correctly to a Datum
*/
@@ -2423,6 +2926,18 @@ array_type_coerce(PG_FUNCTION_ARGS)
if (tgt_elem_type == InvalidOid)
elog(ERROR, "Target type is not an array");
/*
* We don't deal with domain constraints yet, so bail out.
* This isn't currently a problem, because we also don't
* support arrays of domain type elements either. But in the
* future we might. At that point consideration should be given
* to removing the check below and adding a domain constraints
* check to the coercion.
*/
if (getBaseType(tgt_elem_type) != tgt_elem_type)
elog(ERROR, "array coercion to domain type elements not " \
"currently supported");
if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
COERCION_EXPLICIT, &funcId))
{
@@ -2439,10 +2954,16 @@ array_type_coerce(PG_FUNCTION_ARGS)
}
/*
* If it's binary-compatible, return the array unmodified.
* If it's binary-compatible, modify the element type in the array header,
* but otherwise leave the array as we received it.
*/
if (my_extra->coerce_finfo.fn_oid == InvalidOid)
PG_RETURN_ARRAYTYPE_P(src);
{
ArrayType *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
ARR_ELEMTYPE(result) = my_extra->desttype;
PG_RETURN_ARRAYTYPE_P(result);
}
/*
* Use array_map to apply the function to each array element.
@@ -2454,3 +2975,118 @@ array_type_coerce(PG_FUNCTION_ARGS)
return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
}
/*
* accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
*
* astate is working state (NULL on first call)
* rcontext is where to keep working state
*/
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,
"accumArrayResult",
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 accumArrayResult
*
* astate is working state (not NULL)
* rcontext is where to construct result
*/
Datum
makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext)
{
int dims[1];
int lbs[1];
dims[0] = astate->nelems;
lbs[0] = 1;
return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
}
/*
* makeMdArrayResult - produce md final result of accumArrayResult
*
* astate is working state (not NULL)
* rcontext is where to construct result
*/
Datum
makeMdArrayResult(ArrayBuildState *astate,
int ndims,
int *dims,
int *lbs,
MemoryContext rcontext)
{
ArrayType *result;
MemoryContext oldcontext;
/* Build the final array result in rcontext */
oldcontext = MemoryContextSwitchTo(rcontext);
result = construct_md_array(astate->dvalues,
ndims,
dims,
lbs,
astate->element_type,
astate->typlen,
astate->typbyval,
astate->typalign);
MemoryContextSwitchTo(oldcontext);
/* Clean up all the junk */
MemoryContextDelete(astate->mcontext);
return PointerGetDatum(result);
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.98 2003/05/15 15:50:19 petere Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.99 2003/06/24 23:14:46 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -19,11 +19,14 @@
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
#include "lib/stringinfo.h"
#include "libpq/crypt.h"
#include "libpq/pqformat.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/pg_locale.h"
#include "utils/lsyscache.h"
typedef struct varlena unknown;
@@ -1983,8 +1986,7 @@ split_text(PG_FUNCTION_ARGS)
if (fldnum == 1) /* first field - just return the input
* string */
PG_RETURN_TEXT_P(inputstring);
else
/* otherwise return an empty string */
else /* otherwise return an empty string */
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
}
@@ -2004,8 +2006,7 @@ split_text(PG_FUNCTION_ARGS)
if (fldnum == 1) /* first field - just return the input
* string */
PG_RETURN_TEXT_P(inputstring);
else
/* otherwise return an empty string */
else /* otherwise return an empty string */
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
}
else if ((start_posn != 0) && (end_posn == 0))
@@ -2028,6 +2029,191 @@ split_text(PG_FUNCTION_ARGS)
}
}
/*
* text_to_array
* parse input string
* return text array of elements
* based on provided field separator
*/
Datum
text_to_array(PG_FUNCTION_ARGS)
{
text *inputstring = PG_GETARG_TEXT_P(0);
int inputstring_len = TEXTLEN(inputstring);
text *fldsep = PG_GETARG_TEXT_P(1);
int fldsep_len = TEXTLEN(fldsep);
int fldnum;
int start_posn = 0;
int end_posn = 0;
text *result_text = NULL;
ArrayBuildState *astate = NULL;
MemoryContext oldcontext = CurrentMemoryContext;
/* return NULL for empty input string */
if (inputstring_len < 1)
PG_RETURN_NULL();
/* empty field separator
* return one element, 1D, array using the input string */
if (fldsep_len < 1)
PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
CStringGetDatum(inputstring), 1));
/* start with end position holding the initial start position */
end_posn = 0;
for (fldnum=1;;fldnum++) /* field number is 1 based */
{
Datum dvalue;
bool disnull = false;
start_posn = end_posn;
end_posn = text_position(PointerGetDatum(inputstring),
PointerGetDatum(fldsep),
fldnum);
if ((start_posn == 0) && (end_posn == 0)) /* fldsep not found */
{
if (fldnum == 1)
{
/* first element
* return one element, 1D, array using the input string */
PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
CStringGetDatum(inputstring), 1));
}
else
{
/* otherwise create array and exit */
PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
}
}
else if ((start_posn != 0) && (end_posn == 0))
{
/* last field requested */
result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
}
else if ((start_posn == 0) && (end_posn != 0))
{
/* first field requested */
result_text = LEFT(inputstring, fldsep);
}
else
{
/* prior to last field requested */
result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn - fldsep_len, false);
}
/* stash away current value */
dvalue = PointerGetDatum(result_text);
astate = accumArrayResult(astate, dvalue,
disnull, TEXTOID, oldcontext);
}
/* never reached -- keep compiler quiet */
PG_RETURN_NULL();
}
/*
* array_to_text
* concatenate Cstring representation of input array elements
* using provided field separator
*/
Datum
array_to_text(PG_FUNCTION_ARGS)
{
ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
char *fldsep = PG_TEXTARG_GET_STR(1);
int nitems, *dims, ndims;
char *p;
Oid element_type;
int typlen;
bool typbyval;
char typdelim;
Oid typoutput,
typelem;
FmgrInfo outputproc;
char typalign;
StringInfo result_str = makeStringInfo();
int i;
ArrayMetaState *my_extra;
p = ARR_DATA_PTR(v);
ndims = ARR_NDIM(v);
dims = ARR_DIMS(v);
nitems = ArrayGetNItems(ndims, dims);
/* if there are no elements, return an empty string */
if (nitems == 0)
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
element_type = ARR_ELEMTYPE(v);
/*
* We arrange to look up info about element type, including its output
* conversion proc only once per series of calls, assuming the element
* type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = InvalidOid;
}
if (my_extra->element_type != element_type)
{
/* Get info about element type, including its output conversion proc */
get_type_metadata(element_type, IOFunc_output,
&typlen, &typbyval, &typdelim,
&typelem, &typoutput, &typalign);
fmgr_info(typoutput, &outputproc);
my_extra->element_type = element_type;
my_extra->typlen = typlen;
my_extra->typbyval = typbyval;
my_extra->typdelim = typdelim;
my_extra->typelem = typelem;
my_extra->typiofunc = typoutput;
my_extra->typalign = typalign;
my_extra->proc = outputproc;
}
else
{
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typdelim = my_extra->typdelim;
typelem = my_extra->typelem;
typoutput = my_extra->typiofunc;
typalign = my_extra->typalign;
outputproc = my_extra->proc;
}
for (i = 0; i < nitems; i++)
{
Datum itemvalue;
char *value;
itemvalue = fetch_att(p, typbyval, typlen);
value = DatumGetCString(FunctionCall3(&outputproc,
itemvalue,
ObjectIdGetDatum(typelem),
Int32GetDatum(-1)));
if (i > 0)
appendStringInfo(result_str, "%s%s", fldsep, value);
else
appendStringInfo(result_str, "%s", value);
p = att_addlength(p, typlen, PointerGetDatum(p));
p = (char *) att_align(p, typalign);
}
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
}
#define HEXBASE 16
/*
* Convert a int32 to a string containing a base 16 (hex) representation of

View File

@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.96 2003/06/22 22:04:54 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.97 2003/06/24 23:14:46 momjian Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@@ -718,6 +718,40 @@ get_func_rettype(Oid funcid)
return result;
}
/*
* get_func_argtypes
* Given procedure id, return the function's argument types.
* Also pass back the number of arguments.
*/
Oid *
get_func_argtypes(Oid funcid, int *nargs)
{
HeapTuple tp;
Form_pg_proc procstruct;
Oid *result = NULL;
int i;
tp = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcid),
0, 0, 0);
if (!HeapTupleIsValid(tp))
elog(ERROR, "Function OID %u does not exist", funcid);
procstruct = (Form_pg_proc) GETSTRUCT(tp);
*nargs = (int) procstruct->pronargs;
if (*nargs > 0)
{
result = (Oid *) palloc(*nargs * sizeof(Oid));
for (i = 0; i < *nargs; i++)
result[i] = procstruct->proargtypes[i];
}
ReleaseSysCache(tp);
return result;
}
/*
* get_func_retset
* Given procedure id, return the function's proretset flag.
@@ -1090,6 +1124,56 @@ get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
ReleaseSysCache(tp);
}
/*
* get_type_metadata
*
* A six-fer: given the type OID, return typlen, typbyval, typalign,
* typdelim, typelem, IO function Oid. The IO function
* returned is controlled by IOFuncSelector
*/
void
get_type_metadata(Oid element_type,
IOFuncSelector which_func,
int *typlen,
bool *typbyval,
char *typdelim,
Oid *typelem,
Oid *proc,
char *typalign)
{
HeapTuple typeTuple;
Form_pg_type typeStruct;
typeTuple = SearchSysCache(TYPEOID,
ObjectIdGetDatum(element_type),
0, 0, 0);
if (!HeapTupleIsValid(typeTuple))
elog(ERROR, "cache lookup failed for type %u", element_type);
typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
*typlen = typeStruct->typlen;
*typbyval = typeStruct->typbyval;
*typdelim = typeStruct->typdelim;
*typelem = typeStruct->typelem;
*typalign = typeStruct->typalign;
switch (which_func)
{
case IOFunc_input:
*proc = typeStruct->typinput;
break;
case IOFunc_output:
*proc = typeStruct->typoutput;
break;
case IOFunc_receive:
*proc = typeStruct->typreceive;
break;
case IOFunc_send:
*proc = typeStruct->typsend;
break;
}
ReleaseSysCache(typeTuple);
}
#ifdef NOT_USED
char
get_typalign(Oid typid)

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.68 2003/04/08 23:20:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.69 2003/06/24 23:14:46 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1673,3 +1673,29 @@ get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum)
return exprType((Node *) nth(argnum, args));
}
/*
* Get the OID of the function or operator
*
* Returns InvalidOid if information is not available
*/
Oid
get_fn_expr_functype(FunctionCallInfo fcinfo)
{
Node *expr;
/*
* can't return anything useful if we have no FmgrInfo or if
* its fn_expr node has not been initialized
*/
if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
return InvalidOid;
expr = fcinfo->flinfo->fn_expr;
if (IsA(expr, FuncExpr))
return ((FuncExpr *) expr)->funcid;
else if (IsA(expr, OpExpr))
return ((OpExpr *) expr)->opno;
else
return InvalidOid;
}