mirror of
https://github.com/postgres/postgres.git
synced 2025-11-13 16:22:44 +03:00
Aggregates can be polymorphic, using polymorphic implementation functions.
It also works to create a non-polymorphic aggregate from polymorphic functions, should you want to do that. Regression test added, docs still lacking. By Joe Conway, with some kibitzing from Tom Lane.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.58 2003/06/25 21:30:25 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.59 2003/07/01 19:10:52 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -29,6 +29,10 @@
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
|
||||
Oid *rettype);
|
||||
|
||||
|
||||
/*
|
||||
* AggregateCreate
|
||||
*/
|
||||
@@ -48,9 +52,10 @@ AggregateCreate(const char *aggName,
|
||||
Form_pg_proc proc;
|
||||
Oid transfn;
|
||||
Oid finalfn = InvalidOid; /* can be omitted */
|
||||
Oid rettype;
|
||||
Oid finaltype;
|
||||
Oid fnArgs[FUNC_MAX_ARGS];
|
||||
int nargs;
|
||||
int nargs_transfn;
|
||||
Oid procOid;
|
||||
TupleDesc tupDesc;
|
||||
int i;
|
||||
@@ -64,28 +69,49 @@ AggregateCreate(const char *aggName,
|
||||
if (!aggtransfnName)
|
||||
elog(ERROR, "aggregate must have a transition function");
|
||||
|
||||
/*
|
||||
* If transtype is polymorphic, basetype must be polymorphic also;
|
||||
* else we will have no way to deduce the actual transtype.
|
||||
*/
|
||||
if ((aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID) &&
|
||||
!(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID))
|
||||
elog(ERROR, "an aggregate using ANYARRAY or ANYELEMENT as trans type "
|
||||
"must also have one of them as its base type");
|
||||
|
||||
/* handle transfn */
|
||||
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);
|
||||
if (!OidIsValid(transfn))
|
||||
func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
|
||||
transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs,
|
||||
&rettype);
|
||||
|
||||
/*
|
||||
* Return type of transfn (possibly after refinement by
|
||||
* enforce_generic_type_consistency, if transtype isn't polymorphic)
|
||||
* must exactly match declared transtype.
|
||||
*
|
||||
* In the non-polymorphic-transtype case, it might be okay to allow
|
||||
* a rettype that's binary-coercible to transtype, but I'm not quite
|
||||
* convinced that it's either safe or useful. When transtype is
|
||||
* polymorphic we *must* demand exact equality.
|
||||
*/
|
||||
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 +131,8 @@ AggregateCreate(const char *aggName,
|
||||
{
|
||||
MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
||||
fnArgs[0] = aggTransType;
|
||||
finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
|
||||
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);
|
||||
finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs,
|
||||
&finaltype);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -126,6 +143,19 @@ AggregateCreate(const char *aggName,
|
||||
}
|
||||
Assert(OidIsValid(finaltype));
|
||||
|
||||
/*
|
||||
* If finaltype (i.e. aggregate return type) is polymorphic,
|
||||
* basetype must be polymorphic also, else parser will fail to deduce
|
||||
* result type. (Note: given the previous test on transtype and basetype,
|
||||
* this cannot happen, unless someone has snuck a finalfn definition
|
||||
* into the catalogs that itself violates the rule against polymorphic
|
||||
* result with no polymorphic input.)
|
||||
*/
|
||||
if ((finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID) &&
|
||||
!(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID))
|
||||
elog(ERROR, "an aggregate returning ANYARRAY or ANYELEMENT "
|
||||
"must also have one of them as its base type");
|
||||
|
||||
/*
|
||||
* Everything looks okay. Try to create the pg_proc entry for the
|
||||
* aggregate. (This could fail if there's already a conflicting
|
||||
@@ -207,3 +237,71 @@ AggregateCreate(const char *aggName,
|
||||
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* lookup_agg_function -- common code for finding both transfn and finalfn
|
||||
*/
|
||||
static Oid
|
||||
lookup_agg_function(List *fnName,
|
||||
int nargs,
|
||||
Oid *input_types,
|
||||
Oid *rettype)
|
||||
{
|
||||
Oid fnOid;
|
||||
bool retset;
|
||||
Oid *true_oid_array;
|
||||
FuncDetailCode fdresult;
|
||||
|
||||
/*
|
||||
* 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(fnName, NIL, nargs, input_types,
|
||||
&fnOid, rettype, &retset,
|
||||
&true_oid_array);
|
||||
|
||||
/* only valid case is a normal function not returning a set */
|
||||
if (fdresult != FUNCDETAIL_NORMAL ||
|
||||
!OidIsValid(fnOid) ||
|
||||
retset)
|
||||
func_error("AggregateCreate", fnName, nargs, input_types, NULL);
|
||||
|
||||
/*
|
||||
* If the given type(s) are all polymorphic, there's nothing we
|
||||
* can check. Otherwise, enforce consistency, and possibly refine
|
||||
* the result type.
|
||||
*/
|
||||
if ((input_types[0] == ANYARRAYOID || input_types[0] == ANYELEMENTOID) &&
|
||||
(nargs == 1 ||
|
||||
(input_types[1] == ANYARRAYOID || input_types[1] == ANYELEMENTOID)))
|
||||
{
|
||||
/* nothing to check here */
|
||||
}
|
||||
else
|
||||
{
|
||||
*rettype = enforce_generic_type_consistency(input_types,
|
||||
true_oid_array,
|
||||
nargs,
|
||||
*rettype);
|
||||
}
|
||||
|
||||
/*
|
||||
* func_get_detail will find functions requiring run-time argument type
|
||||
* coercion, but nodeAgg.c isn't prepared to deal with that
|
||||
*/
|
||||
if (true_oid_array[0] != ANYARRAYOID &&
|
||||
true_oid_array[0] != ANYELEMENTOID &&
|
||||
!IsBinaryCoercible(input_types[0], true_oid_array[0]))
|
||||
func_error("AggregateCreate", fnName, nargs, input_types, NULL);
|
||||
|
||||
if (nargs == 2 &&
|
||||
true_oid_array[1] != ANYARRAYOID &&
|
||||
true_oid_array[1] != ANYELEMENTOID &&
|
||||
!IsBinaryCoercible(input_types[1], true_oid_array[1]))
|
||||
func_error("AggregateCreate", fnName, nargs, input_types, NULL);
|
||||
|
||||
return fnOid;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.8 2003/06/27 14:45:27 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.9 2003/07/01 19:10:52 tgl Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The "DefineFoo" routines take the parse tree and pick out the
|
||||
@@ -120,7 +120,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));
|
||||
|
||||
|
||||
@@ -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.109 2003/06/25 21:30:28 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.110 2003/07/01 19:10:52 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -59,6 +59,7 @@
|
||||
#include "executor/nodeAgg.h"
|
||||
#include "miscadmin.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "parser/parse_agg.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_oper.h"
|
||||
@@ -1182,11 +1183,15 @@ ExecInitAgg(Agg *node, EState *estate)
|
||||
AggrefExprState *aggrefstate = (AggrefExprState *) lfirst(alist);
|
||||
Aggref *aggref = (Aggref *) aggrefstate->xprstate.expr;
|
||||
AggStatePerAgg peraggstate;
|
||||
Oid inputType;
|
||||
HeapTuple aggTuple;
|
||||
Form_pg_aggregate aggform;
|
||||
Oid aggtranstype;
|
||||
AclResult aclresult;
|
||||
Oid transfn_oid,
|
||||
finalfn_oid;
|
||||
Expr *transfnexpr,
|
||||
*finalfnexpr;
|
||||
Datum textInitVal;
|
||||
int i;
|
||||
|
||||
@@ -1217,6 +1222,13 @@ ExecInitAgg(Agg *node, EState *estate)
|
||||
peraggstate->aggrefstate = aggrefstate;
|
||||
peraggstate->aggref = aggref;
|
||||
|
||||
/*
|
||||
* Get actual datatype of the input. We need this because it may
|
||||
* be different from the agg's declared input type, when the agg
|
||||
* accepts ANY (eg, COUNT(*)) or ANYARRAY or ANYELEMENT.
|
||||
*/
|
||||
inputType = exprType((Node *) aggref->target);
|
||||
|
||||
aggTuple = SearchSysCache(AGGFNOID,
|
||||
ObjectIdGetDatum(aggref->aggfnoid),
|
||||
0, 0, 0);
|
||||
@@ -1231,10 +1243,47 @@ ExecInitAgg(Agg *node, EState *estate)
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
aclcheck_error(aclresult, get_func_name(aggref->aggfnoid));
|
||||
|
||||
peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
|
||||
peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
|
||||
|
||||
/* resolve actual type of transition state, if polymorphic */
|
||||
aggtranstype = aggform->aggtranstype;
|
||||
if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID)
|
||||
{
|
||||
/* have to fetch the agg's declared input type... */
|
||||
Oid agg_arg_types[FUNC_MAX_ARGS];
|
||||
int agg_nargs;
|
||||
|
||||
(void) get_func_signature(aggref->aggfnoid,
|
||||
agg_arg_types, &agg_nargs);
|
||||
Assert(agg_nargs == 1);
|
||||
aggtranstype = resolve_generic_type(aggtranstype,
|
||||
inputType,
|
||||
agg_arg_types[0]);
|
||||
}
|
||||
|
||||
/* build expression trees using actual argument & result types */
|
||||
build_aggregate_fnexprs(inputType,
|
||||
aggtranstype,
|
||||
aggref->aggtype,
|
||||
transfn_oid,
|
||||
finalfn_oid,
|
||||
&transfnexpr,
|
||||
&finalfnexpr);
|
||||
|
||||
fmgr_info(transfn_oid, &peraggstate->transfn);
|
||||
peraggstate->transfn.fn_expr = (Node *) transfnexpr;
|
||||
|
||||
if (OidIsValid(finalfn_oid))
|
||||
{
|
||||
fmgr_info(finalfn_oid, &peraggstate->finalfn);
|
||||
peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
|
||||
}
|
||||
|
||||
get_typlenbyval(aggref->aggtype,
|
||||
&peraggstate->resulttypeLen,
|
||||
&peraggstate->resulttypeByVal);
|
||||
get_typlenbyval(aggform->aggtranstype,
|
||||
get_typlenbyval(aggtranstype,
|
||||
&peraggstate->transtypeLen,
|
||||
&peraggstate->transtypeByVal);
|
||||
|
||||
@@ -1250,14 +1299,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);
|
||||
aggtranstype);
|
||||
|
||||
/*
|
||||
* If the transfn is strict and the initval is NULL, make sure
|
||||
@@ -1268,26 +1310,13 @@ ExecInitAgg(Agg *node, EState *estate)
|
||||
*/
|
||||
if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
|
||||
{
|
||||
/*
|
||||
* Note: use the type from the input expression here, not from
|
||||
* pg_proc.proargtypes, because the latter might be 0.
|
||||
* (Consider COUNT(*).)
|
||||
*/
|
||||
Oid inputType = exprType((Node *) aggref->target);
|
||||
|
||||
if (!IsBinaryCoercible(inputType, aggform->aggtranstype))
|
||||
if (!IsBinaryCoercible(inputType, aggtranstype))
|
||||
elog(ERROR, "Aggregate %u needs to have compatible input type and transition type",
|
||||
aggref->aggfnoid);
|
||||
}
|
||||
|
||||
if (aggref->aggdistinct)
|
||||
{
|
||||
/*
|
||||
* Note: use the type from the input expression here, not from
|
||||
* pg_proc.proargtypes, because the latter might be a pseudotype.
|
||||
* (Consider COUNT(*).)
|
||||
*/
|
||||
Oid inputType = exprType((Node *) aggref->target);
|
||||
Oid eq_function;
|
||||
|
||||
/* We don't implement DISTINCT aggs in the HASHED case */
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.39 2003/05/06 00:20:32 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.40 2003/07/01 19:10:52 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -251,3 +251,24 @@ makeTypeName(char *typnam)
|
||||
n->typmod = -1;
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* makeFuncExpr -
|
||||
* build an expression tree representing a function call.
|
||||
*
|
||||
* The argument expressions must have been transformed already.
|
||||
*/
|
||||
FuncExpr *
|
||||
makeFuncExpr(Oid funcid, Oid rettype, List *args, CoercionForm fformat)
|
||||
{
|
||||
FuncExpr *funcexpr;
|
||||
|
||||
funcexpr = makeNode(FuncExpr);
|
||||
funcexpr->funcid = funcid;
|
||||
funcexpr->funcresulttype = rettype;
|
||||
funcexpr->funcretset = false; /* only allowed case here */
|
||||
funcexpr->funcformat = fformat;
|
||||
funcexpr->args = args;
|
||||
|
||||
return funcexpr;
|
||||
}
|
||||
|
||||
@@ -8,18 +8,21 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.53 2003/06/06 15:04:02 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.54 2003/07/01 19:10:52 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/params.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "optimizer/var.h"
|
||||
#include "parser/parse_agg.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "rewrite/rewriteManip.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
|
||||
typedef struct
|
||||
@@ -312,3 +315,91 @@ check_ungrouped_columns_walker(Node *node,
|
||||
return expression_tree_walker(node, check_ungrouped_columns_walker,
|
||||
(void *) context);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create expression trees for the transition and final functions
|
||||
* of an aggregate. These are needed so that polymorphic functions
|
||||
* can be used within an aggregate --- without the expression trees,
|
||||
* such functions would not know the datatypes they are supposed to use.
|
||||
* (The trees will never actually be executed, however, so we can skimp
|
||||
* a bit on correctness.)
|
||||
*
|
||||
* agg_input_type, agg_state_type, agg_result_type identify the input,
|
||||
* transition, and result types of the aggregate. These should all be
|
||||
* resolved to actual types (ie, none should ever be ANYARRAY or ANYELEMENT).
|
||||
*
|
||||
* transfn_oid and finalfn_oid identify the funcs to be called; the latter
|
||||
* may be InvalidOid.
|
||||
*
|
||||
* Pointers to the constructed trees are returned into *transfnexpr and
|
||||
* *finalfnexpr. The latter is set to NULL if there's no finalfn.
|
||||
*/
|
||||
void
|
||||
build_aggregate_fnexprs(Oid agg_input_type,
|
||||
Oid agg_state_type,
|
||||
Oid agg_result_type,
|
||||
Oid transfn_oid,
|
||||
Oid finalfn_oid,
|
||||
Expr **transfnexpr,
|
||||
Expr **finalfnexpr)
|
||||
{
|
||||
Oid transfn_arg_types[FUNC_MAX_ARGS];
|
||||
int transfn_nargs;
|
||||
Param *arg0;
|
||||
Param *arg1;
|
||||
List *args;
|
||||
|
||||
/* get the transition function signature (only need nargs) */
|
||||
(void) get_func_signature(transfn_oid, transfn_arg_types, &transfn_nargs);
|
||||
|
||||
/*
|
||||
* Build arg list to use in the transfn FuncExpr node. We really
|
||||
* only care that transfn can discover the actual argument types
|
||||
* at runtime using get_fn_expr_argtype(), so it's okay to use
|
||||
* Param nodes that don't correspond to any real Param.
|
||||
*/
|
||||
arg0 = makeNode(Param);
|
||||
arg0->paramkind = PARAM_EXEC;
|
||||
arg0->paramid = -1;
|
||||
arg0->paramtype = agg_state_type;
|
||||
|
||||
if (transfn_nargs == 2)
|
||||
{
|
||||
arg1 = makeNode(Param);
|
||||
arg1->paramkind = PARAM_EXEC;
|
||||
arg1->paramid = -1;
|
||||
arg1->paramtype = agg_input_type;
|
||||
|
||||
args = makeList2(arg0, arg1);
|
||||
}
|
||||
else
|
||||
{
|
||||
args = makeList1(arg0);
|
||||
}
|
||||
|
||||
*transfnexpr = (Expr *) makeFuncExpr(transfn_oid,
|
||||
agg_state_type,
|
||||
args,
|
||||
COERCE_DONTCARE);
|
||||
|
||||
/* see if we have a final function */
|
||||
if (!OidIsValid(finalfn_oid))
|
||||
{
|
||||
*finalfnexpr = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build expr tree for final function
|
||||
*/
|
||||
arg0 = makeNode(Param);
|
||||
arg0->paramkind = PARAM_EXEC;
|
||||
arg0->paramid = -1;
|
||||
arg0->paramtype = agg_state_type;
|
||||
args = makeList1(arg0);
|
||||
|
||||
*finalfnexpr = (Expr *) makeFuncExpr(finalfn_oid,
|
||||
agg_result_type,
|
||||
args,
|
||||
COERCE_DONTCARE);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.101 2003/06/27 00:33:25 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.102 2003/07/01 19:10:53 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -32,8 +32,6 @@
|
||||
static Node *coerce_type_typmod(Node *node,
|
||||
Oid targetTypeId, int32 targetTypMod,
|
||||
CoercionForm cformat, bool isExplicit);
|
||||
static Node *build_func_call(Oid funcid, Oid rettype, List *args,
|
||||
CoercionForm fformat);
|
||||
|
||||
|
||||
/*
|
||||
@@ -275,8 +273,9 @@ coerce_type(ParseState *pstate, Node *node,
|
||||
*/
|
||||
Oid baseTypeId = getBaseType(targetTypeId);
|
||||
|
||||
result = build_func_call(funcId, baseTypeId, makeList1(node),
|
||||
cformat);
|
||||
result = (Node *) makeFuncExpr(funcId, baseTypeId,
|
||||
makeList1(node),
|
||||
cformat);
|
||||
|
||||
/*
|
||||
* If domain, coerce to the domain type and relabel with
|
||||
@@ -534,7 +533,7 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
|
||||
args = lappend(args, cons);
|
||||
}
|
||||
|
||||
node = build_func_call(funcId, targetTypeId, args, cformat);
|
||||
node = (Node *) makeFuncExpr(funcId, targetTypeId, args, cformat);
|
||||
}
|
||||
|
||||
return node;
|
||||
@@ -935,6 +934,76 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
|
||||
return rettype;
|
||||
}
|
||||
|
||||
/*
|
||||
* resolve_generic_type()
|
||||
* Deduce an individual actual datatype on the assumption that
|
||||
* the rules for ANYARRAY/ANYELEMENT are being followed.
|
||||
*
|
||||
* declared_type is the declared datatype we want to resolve.
|
||||
* context_actual_type is the actual input datatype to some argument
|
||||
* that has declared datatype context_declared_type.
|
||||
*
|
||||
* If declared_type isn't polymorphic, we just return it. Otherwise,
|
||||
* context_declared_type must be polymorphic, and we deduce the correct
|
||||
* return type based on the relationship of the two polymorphic types.
|
||||
*/
|
||||
Oid
|
||||
resolve_generic_type(Oid declared_type,
|
||||
Oid context_actual_type,
|
||||
Oid context_declared_type)
|
||||
{
|
||||
if (declared_type == ANYARRAYOID)
|
||||
{
|
||||
if (context_declared_type == ANYARRAYOID)
|
||||
{
|
||||
/* Use actual type, but it must be an array */
|
||||
Oid array_typelem = get_element_type(context_actual_type);
|
||||
|
||||
if (!OidIsValid(array_typelem))
|
||||
elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
|
||||
format_type_be(context_actual_type));
|
||||
return context_actual_type;
|
||||
}
|
||||
else if (context_declared_type == ANYELEMENTOID)
|
||||
{
|
||||
/* Use the array type corresponding to actual type */
|
||||
Oid array_typeid = get_array_type(context_actual_type);
|
||||
|
||||
if (!OidIsValid(array_typeid))
|
||||
elog(ERROR, "Cannot find array type for datatype %s",
|
||||
format_type_be(context_actual_type));
|
||||
return array_typeid;
|
||||
}
|
||||
}
|
||||
else if (declared_type == ANYELEMENTOID)
|
||||
{
|
||||
if (context_declared_type == ANYARRAYOID)
|
||||
{
|
||||
/* Use the element type corresponding to actual type */
|
||||
Oid array_typelem = get_element_type(context_actual_type);
|
||||
|
||||
if (!OidIsValid(array_typelem))
|
||||
elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
|
||||
format_type_be(context_actual_type));
|
||||
return array_typelem;
|
||||
}
|
||||
else if (context_declared_type == ANYELEMENTOID)
|
||||
{
|
||||
/* Use the actual type; it doesn't matter if array or not */
|
||||
return context_actual_type;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* declared_type isn't polymorphic, so return it as-is */
|
||||
return declared_type;
|
||||
}
|
||||
/* If we get here, declared_type is polymorphic and context isn't */
|
||||
/* NB: this is a calling-code logic error, not a user error */
|
||||
elog(ERROR, "Cannot determine ANYARRAY/ANYELEMENT type because context isn't polymorphic");
|
||||
return InvalidOid; /* keep compiler quiet */
|
||||
}
|
||||
|
||||
|
||||
/* TypeCategory()
|
||||
* Assign a category to the specified type OID.
|
||||
@@ -1417,23 +1486,3 @@ find_typmod_coercion_function(Oid typeId, int *nargs)
|
||||
|
||||
return funcid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build an expression tree representing a function call.
|
||||
*
|
||||
* The argument expressions must have been transformed already.
|
||||
*/
|
||||
static Node *
|
||||
build_func_call(Oid funcid, Oid rettype, List *args, CoercionForm fformat)
|
||||
{
|
||||
FuncExpr *funcexpr;
|
||||
|
||||
funcexpr = makeNode(FuncExpr);
|
||||
funcexpr->funcid = funcid;
|
||||
funcexpr->funcresulttype = rettype;
|
||||
funcexpr->funcretset = false; /* only possible case here */
|
||||
funcexpr->funcformat = fformat;
|
||||
funcexpr->args = args;
|
||||
|
||||
return (Node *) funcexpr;
|
||||
}
|
||||
|
||||
32
src/backend/utils/cache/lsyscache.c
vendored
32
src/backend/utils/cache/lsyscache.c
vendored
@@ -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.100 2003/06/27 00:33:25 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.101 2003/07/01 19:10:53 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Eventually, the index information should go through here, too.
|
||||
@@ -718,6 +718,36 @@ get_func_rettype(Oid funcid)
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_func_signature
|
||||
* Given procedure id, return the function's argument and result types.
|
||||
* (The return value is the result type.)
|
||||
*
|
||||
* argtypes must point to a vector of size FUNC_MAX_ARGS.
|
||||
*/
|
||||
Oid
|
||||
get_func_signature(Oid funcid, Oid *argtypes, int *nargs)
|
||||
{
|
||||
HeapTuple tp;
|
||||
Form_pg_proc procstruct;
|
||||
Oid result;
|
||||
|
||||
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);
|
||||
|
||||
result = procstruct->prorettype;
|
||||
memcpy(argtypes, procstruct->proargtypes, FUNC_MAX_ARGS * sizeof(Oid));
|
||||
*nargs = (int) procstruct->pronargs;
|
||||
|
||||
ReleaseSysCache(tp);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_func_retset
|
||||
* Given procedure id, return the function's proretset flag.
|
||||
|
||||
Reference in New Issue
Block a user