mirror of
https://github.com/postgres/postgres.git
synced 2025-08-24 09:27:52 +03:00
Restructure representation of aggregate functions so that they have pg_proc
entries, per pghackers discussion. This fixes aggregates to live in namespaces, and also simplifies/speeds up lookup in parse_func.c. Also, add a 'proimplicit' flag to pg_proc that controls whether a type coercion function may be invoked implicitly, or only explicitly. The current settings of these flags are more permissive than I would like, but we will need to debate and refine the behavior; for now, I avoided breaking regression tests as much as I could.
This commit is contained in:
@@ -8,24 +8,17 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.48 2002/04/09 20:35:52 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.49 2002/04/11 20:00:00 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_aggregate.h"
|
||||
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "parser/parse_agg.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "parser/parse_type.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -185,70 +178,3 @@ parseCheckAggregates(ParseState *pstate, Query *qry, Node *qual)
|
||||
/* Release the list storage (but not the pointed-to expressions!) */
|
||||
freeList(groupClauses);
|
||||
}
|
||||
|
||||
|
||||
Aggref *
|
||||
ParseAgg(ParseState *pstate, List *aggname, Oid basetype,
|
||||
List *args, bool agg_star, bool agg_distinct)
|
||||
{
|
||||
HeapTuple aggtuple;
|
||||
Form_pg_aggregate aggform;
|
||||
Aggref *aggref;
|
||||
|
||||
aggtuple = SearchSysCache(AGGNAME,
|
||||
PointerGetDatum(strVal(llast(aggname))),
|
||||
ObjectIdGetDatum(basetype),
|
||||
0, 0);
|
||||
/* shouldn't happen --- caller should have checked already */
|
||||
if (!HeapTupleIsValid(aggtuple))
|
||||
agg_error("ParseAgg", aggname, basetype);
|
||||
aggform = (Form_pg_aggregate) GETSTRUCT(aggtuple);
|
||||
|
||||
/*
|
||||
* There used to be a really ugly hack for count(*) here.
|
||||
*
|
||||
* It's gone. Now, the grammar transforms count(*) into count(1), which
|
||||
* does the right thing. (It didn't use to do the right thing,
|
||||
* because the optimizer had the wrong ideas about semantics of
|
||||
* queries without explicit variables. Fixed as of Oct 1999 --- tgl.)
|
||||
*/
|
||||
|
||||
/*
|
||||
* We assume caller has already checked that given args are compatible
|
||||
* with the agg's basetype.
|
||||
*/
|
||||
|
||||
aggref = makeNode(Aggref);
|
||||
aggref->aggname = pstrdup(strVal(llast(aggname)));
|
||||
aggref->basetype = aggform->aggbasetype;
|
||||
aggref->aggtype = aggform->aggfinaltype;
|
||||
aggref->target = lfirst(args);
|
||||
aggref->aggstar = agg_star;
|
||||
aggref->aggdistinct = agg_distinct;
|
||||
|
||||
ReleaseSysCache(aggtuple);
|
||||
|
||||
pstate->p_hasAggs = true;
|
||||
|
||||
return aggref;
|
||||
}
|
||||
|
||||
/*
|
||||
* Error message when aggregate lookup fails that gives details of the
|
||||
* basetype
|
||||
*/
|
||||
void
|
||||
agg_error(const char *caller, List *aggname, Oid basetypeID)
|
||||
{
|
||||
/*
|
||||
* basetypeID that is Invalid (zero) means aggregate over all types.
|
||||
* (count)
|
||||
*/
|
||||
|
||||
if (basetypeID == InvalidOid)
|
||||
elog(ERROR, "%s: aggregate '%s' for all types does not exist",
|
||||
caller, NameListToString(aggname));
|
||||
else
|
||||
elog(ERROR, "%s: aggregate '%s' for type %s does not exist",
|
||||
caller, NameListToString(aggname), format_type_be(basetypeID));
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.69 2002/04/09 20:35:52 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.70 2002/04/11 20:00:00 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -32,7 +32,7 @@ Oid PromoteTypeToNext(Oid inType);
|
||||
static Oid PreferredType(CATEGORY category, Oid type);
|
||||
static Node *build_func_call(Oid funcid, Oid rettype, List *args);
|
||||
static Oid find_coercion_function(Oid targetTypeId, Oid inputTypeId,
|
||||
Oid secondArgType);
|
||||
Oid secondArgType, bool isExplicit);
|
||||
|
||||
|
||||
/* coerce_type()
|
||||
@@ -40,7 +40,7 @@ static Oid find_coercion_function(Oid targetTypeId, Oid inputTypeId,
|
||||
*/
|
||||
Node *
|
||||
coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
||||
Oid targetTypeId, int32 atttypmod)
|
||||
Oid targetTypeId, int32 atttypmod, bool isExplicit)
|
||||
{
|
||||
Node *result;
|
||||
|
||||
@@ -131,7 +131,8 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
||||
|
||||
funcId = find_coercion_function(baseTypeId,
|
||||
getBaseType(inputTypeId),
|
||||
InvalidOid);
|
||||
InvalidOid,
|
||||
isExplicit);
|
||||
if (!OidIsValid(funcId))
|
||||
elog(ERROR, "coerce_type: no conversion function from %s to %s",
|
||||
format_type_be(inputTypeId), format_type_be(targetTypeId));
|
||||
@@ -171,13 +172,18 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
||||
*
|
||||
* There are a few types which are known apriori to be convertible.
|
||||
* We will check for those cases first, and then look for possible
|
||||
* conversion functions.
|
||||
* conversion functions.
|
||||
*
|
||||
* We must be told whether this is an implicit or explicit coercion
|
||||
* (explicit being a CAST construct, explicit function call, etc).
|
||||
* We will accept a wider set of coercion cases for an explicit coercion.
|
||||
*
|
||||
* Notes:
|
||||
* This uses the same mechanism as the CAST() SQL construct in gram.y.
|
||||
*/
|
||||
bool
|
||||
can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids)
|
||||
can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids,
|
||||
bool isExplicit)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -230,7 +236,7 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Else, try for explicit conversion using functions: look for a
|
||||
* Else, try for run-time conversion using functions: look for a
|
||||
* single-argument function named with the target type name and
|
||||
* accepting the source type.
|
||||
*
|
||||
@@ -238,7 +244,8 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids)
|
||||
*/
|
||||
funcId = find_coercion_function(getBaseType(targetTypeId),
|
||||
getBaseType(inputTypeId),
|
||||
InvalidOid);
|
||||
InvalidOid,
|
||||
isExplicit);
|
||||
if (!OidIsValid(funcId))
|
||||
return false;
|
||||
}
|
||||
@@ -279,7 +286,8 @@ coerce_type_typmod(ParseState *pstate, Node *node,
|
||||
/* If given type is a domain, use base type instead */
|
||||
baseTypeId = getBaseType(targetTypeId);
|
||||
|
||||
funcId = find_coercion_function(baseTypeId, baseTypeId, INT4OID);
|
||||
/* Note this is always implicit coercion */
|
||||
funcId = find_coercion_function(baseTypeId, baseTypeId, INT4OID, false);
|
||||
|
||||
if (OidIsValid(funcId))
|
||||
{
|
||||
@@ -321,9 +329,10 @@ coerce_to_boolean(ParseState *pstate, Node **pnode)
|
||||
if (inputTypeId == BOOLOID)
|
||||
return true; /* no work */
|
||||
targetTypeId = BOOLOID;
|
||||
if (!can_coerce_type(1, &inputTypeId, &targetTypeId))
|
||||
if (!can_coerce_type(1, &inputTypeId, &targetTypeId, false))
|
||||
return false; /* fail, but let caller choose error msg */
|
||||
*pnode = coerce_type(pstate, *pnode, inputTypeId, targetTypeId, -1);
|
||||
*pnode = coerce_type(pstate, *pnode, inputTypeId, targetTypeId, -1,
|
||||
false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -378,7 +387,7 @@ select_common_type(List *typeids, const char *context)
|
||||
}
|
||||
else if (IsPreferredType(pcategory, ntype)
|
||||
&& !IsPreferredType(pcategory, ptype)
|
||||
&& can_coerce_type(1, &ptype, &ntype))
|
||||
&& can_coerce_type(1, &ptype, &ntype, false))
|
||||
{
|
||||
/*
|
||||
* new one is preferred and can convert? then take it...
|
||||
@@ -424,8 +433,9 @@ coerce_to_common_type(ParseState *pstate, Node *node,
|
||||
|
||||
if (inputTypeId == targetTypeId)
|
||||
return node; /* no work */
|
||||
if (can_coerce_type(1, &inputTypeId, &targetTypeId))
|
||||
node = coerce_type(pstate, node, inputTypeId, targetTypeId, -1);
|
||||
if (can_coerce_type(1, &inputTypeId, &targetTypeId, false))
|
||||
node = coerce_type(pstate, node, inputTypeId, targetTypeId, -1,
|
||||
false);
|
||||
else
|
||||
{
|
||||
elog(ERROR, "%s unable to convert to type \"%s\"",
|
||||
@@ -659,6 +669,9 @@ PreferredType(CATEGORY category, Oid type)
|
||||
* A coercion function must be named after (the internal name of) its
|
||||
* result type, and must accept exactly the specified input type. We
|
||||
* also require it to be defined in the same namespace as its result type.
|
||||
* Furthermore, unless we are doing explicit coercion the function must
|
||||
* be marked as usable for implicit coercion --- this allows coercion
|
||||
* functions to be provided that aren't implicitly invokable.
|
||||
*
|
||||
* This routine is also used to look for length-coercion functions, which
|
||||
* are similar but accept a second argument. secondArgType is the type
|
||||
@@ -668,16 +681,16 @@ PreferredType(CATEGORY category, Oid type)
|
||||
* If a function is found, return its pg_proc OID; else return InvalidOid.
|
||||
*/
|
||||
static Oid
|
||||
find_coercion_function(Oid targetTypeId, Oid inputTypeId, Oid secondArgType)
|
||||
find_coercion_function(Oid targetTypeId, Oid inputTypeId, Oid secondArgType,
|
||||
bool isExplicit)
|
||||
{
|
||||
Oid funcid = InvalidOid;
|
||||
Type targetType;
|
||||
char *typname;
|
||||
Oid typnamespace;
|
||||
Oid oid_array[FUNC_MAX_ARGS];
|
||||
int nargs;
|
||||
HeapTuple ftup;
|
||||
Form_pg_proc pform;
|
||||
Oid funcid;
|
||||
|
||||
targetType = typeidType(targetTypeId);
|
||||
typname = NameStr(((Form_pg_type) GETSTRUCT(targetType))->typname);
|
||||
@@ -698,21 +711,24 @@ find_coercion_function(Oid targetTypeId, Oid inputTypeId, Oid secondArgType)
|
||||
Int16GetDatum(nargs),
|
||||
PointerGetDatum(oid_array),
|
||||
ObjectIdGetDatum(typnamespace));
|
||||
if (!HeapTupleIsValid(ftup))
|
||||
{
|
||||
ReleaseSysCache(targetType);
|
||||
return InvalidOid;
|
||||
}
|
||||
/* Make sure the function's result type is as expected, too */
|
||||
pform = (Form_pg_proc) GETSTRUCT(ftup);
|
||||
if (pform->prorettype != targetTypeId)
|
||||
if (HeapTupleIsValid(ftup))
|
||||
{
|
||||
Form_pg_proc pform = (Form_pg_proc) GETSTRUCT(ftup);
|
||||
|
||||
/* Make sure the function's result type is as expected */
|
||||
if (pform->prorettype == targetTypeId && !pform->proretset &&
|
||||
!pform->proisagg)
|
||||
{
|
||||
/* If needed, make sure it can be invoked implicitly */
|
||||
if (isExplicit || pform->proimplicit)
|
||||
{
|
||||
/* Okay to use it */
|
||||
funcid = ftup->t_data->t_oid;
|
||||
}
|
||||
}
|
||||
ReleaseSysCache(ftup);
|
||||
ReleaseSysCache(targetType);
|
||||
return InvalidOid;
|
||||
}
|
||||
funcid = ftup->t_data->t_oid;
|
||||
ReleaseSysCache(ftup);
|
||||
|
||||
ReleaseSysCache(targetType);
|
||||
return funcid;
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.113 2002/04/09 20:35:52 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.114 2002/04/11 20:00:00 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1174,7 +1174,8 @@ parser_typecast_expression(ParseState *pstate,
|
||||
if (inputType != targetType)
|
||||
{
|
||||
expr = CoerceTargetExpr(pstate, expr, inputType,
|
||||
targetType, typename->typmod);
|
||||
targetType, typename->typmod,
|
||||
true); /* explicit coercion */
|
||||
if (expr == NULL)
|
||||
elog(ERROR, "Cannot cast type '%s' to '%s'",
|
||||
format_type_be(inputType),
|
||||
|
@@ -8,23 +8,18 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.125 2002/04/09 20:35:53 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.126 2002/04/11 20:00:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/genam.h"
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/catname.h"
|
||||
#include "catalog/indexing.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_aggregate.h"
|
||||
#include "catalog/pg_inherits.h"
|
||||
#include "catalog/pg_namespace.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "parser/parse_agg.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_func.h"
|
||||
@@ -35,6 +30,7 @@
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
static Node *ParseComplexProjection(ParseState *pstate,
|
||||
char *funcname,
|
||||
Node *first_arg);
|
||||
@@ -54,9 +50,6 @@ static int match_argtypes(int nargs,
|
||||
static FieldSelect *setup_field_select(Node *input, char *attname, Oid relid);
|
||||
static FuncCandidateList func_select_candidate(int nargs, Oid *input_typeids,
|
||||
FuncCandidateList candidates);
|
||||
static int agg_get_candidates(List *aggname, Oid typeId,
|
||||
FuncCandidateList *candidates);
|
||||
static Oid agg_select_candidate(Oid typeid, FuncCandidateList candidates);
|
||||
|
||||
|
||||
/*
|
||||
@@ -89,14 +82,10 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
char *refname;
|
||||
int nargs = length(fargs);
|
||||
int argn;
|
||||
Func *funcnode;
|
||||
Oid oid_array[FUNC_MAX_ARGS];
|
||||
Oid *true_oid_array;
|
||||
Node *retval;
|
||||
bool retset;
|
||||
bool must_be_agg = agg_star || agg_distinct;
|
||||
bool could_be_agg;
|
||||
Expr *expr;
|
||||
FuncDetailCode fdresult;
|
||||
|
||||
/*
|
||||
@@ -123,7 +112,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
* then the "function call" could be a projection. We also check
|
||||
* that there wasn't any aggregate decoration.
|
||||
*/
|
||||
if (nargs == 1 && !must_be_agg && length(funcname) == 1)
|
||||
if (nargs == 1 && !agg_star && !agg_distinct && length(funcname) == 1)
|
||||
{
|
||||
char *cname = strVal(lfirst(funcname));
|
||||
|
||||
@@ -151,84 +140,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* See if it's an aggregate.
|
||||
*/
|
||||
if (must_be_agg)
|
||||
{
|
||||
/* We don't presently cope with, eg, foo(DISTINCT x,y) */
|
||||
if (nargs != 1)
|
||||
elog(ERROR, "Aggregate functions may only have one parameter");
|
||||
/* Agg's argument can't be a relation name, either */
|
||||
if (IsA(first_arg, RangeVar))
|
||||
elog(ERROR, "Aggregate functions cannot be applied to relation names");
|
||||
could_be_agg = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Try to parse as an aggregate if above-mentioned checks are OK */
|
||||
could_be_agg = (nargs == 1) && !(IsA(first_arg, RangeVar));
|
||||
}
|
||||
|
||||
if (could_be_agg)
|
||||
{
|
||||
Oid basetype = exprType(lfirst(fargs));
|
||||
int ncandidates;
|
||||
FuncCandidateList candidates;
|
||||
|
||||
/* try for exact match first... */
|
||||
if (SearchSysCacheExists(AGGNAME,
|
||||
PointerGetDatum(strVal(llast(funcname))),
|
||||
ObjectIdGetDatum(basetype),
|
||||
0, 0))
|
||||
return (Node *) ParseAgg(pstate, funcname, basetype,
|
||||
fargs, agg_star, agg_distinct);
|
||||
|
||||
/* check for aggregate-that-accepts-any-type (eg, COUNT) */
|
||||
if (SearchSysCacheExists(AGGNAME,
|
||||
PointerGetDatum(strVal(llast(funcname))),
|
||||
ObjectIdGetDatum(0),
|
||||
0, 0))
|
||||
return (Node *) ParseAgg(pstate, funcname, 0,
|
||||
fargs, agg_star, agg_distinct);
|
||||
|
||||
/*
|
||||
* No exact match yet, so see if there is another entry in the
|
||||
* aggregate table that is compatible. - thomas 1998-12-05
|
||||
*/
|
||||
ncandidates = agg_get_candidates(funcname, basetype, &candidates);
|
||||
if (ncandidates > 0)
|
||||
{
|
||||
Oid type;
|
||||
|
||||
type = agg_select_candidate(basetype, candidates);
|
||||
if (OidIsValid(type))
|
||||
{
|
||||
lfirst(fargs) = coerce_type(pstate, lfirst(fargs),
|
||||
basetype, type, -1);
|
||||
basetype = type;
|
||||
return (Node *) ParseAgg(pstate, funcname, basetype,
|
||||
fargs, agg_star, agg_distinct);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Multiple possible matches --- give up */
|
||||
elog(ERROR, "Unable to select an aggregate function %s(%s)",
|
||||
NameListToString(funcname), format_type_be(basetype));
|
||||
}
|
||||
}
|
||||
|
||||
if (must_be_agg)
|
||||
{
|
||||
/*
|
||||
* No matching agg, but we had '*' or DISTINCT, so a plain
|
||||
* function could not have been meant.
|
||||
*/
|
||||
elog(ERROR, "There is no aggregate function %s(%s)",
|
||||
NameListToString(funcname), format_type_be(basetype));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Okay, it's not a column projection, so it must really be a function.
|
||||
* Extract arg type info and transform RangeVar arguments into varnodes
|
||||
@@ -321,9 +232,22 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
* these cases, so why duplicate code...
|
||||
*/
|
||||
return coerce_type(pstate, lfirst(fargs),
|
||||
oid_array[0], rettype, -1);
|
||||
oid_array[0], rettype, -1, true);
|
||||
}
|
||||
if (fdresult != FUNCDETAIL_NORMAL)
|
||||
else if (fdresult == FUNCDETAIL_NORMAL)
|
||||
{
|
||||
/*
|
||||
* Normal function found; was there anything indicating it must be
|
||||
* an aggregate?
|
||||
*/
|
||||
if (agg_star)
|
||||
elog(ERROR, "%s(*) specified, but %s is not an aggregate function",
|
||||
NameListToString(funcname), NameListToString(funcname));
|
||||
if (agg_distinct)
|
||||
elog(ERROR, "DISTINCT specified, but %s is not an aggregate function",
|
||||
NameListToString(funcname));
|
||||
}
|
||||
else if (fdresult != FUNCDETAIL_AGGREGATE)
|
||||
{
|
||||
/*
|
||||
* Oops. Time to die.
|
||||
@@ -341,167 +265,64 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
"\n\tYou may need to add explicit typecasts");
|
||||
}
|
||||
|
||||
/* got it */
|
||||
funcnode = makeNode(Func);
|
||||
funcnode->funcid = funcid;
|
||||
funcnode->functype = rettype;
|
||||
funcnode->func_fcache = NULL;
|
||||
|
||||
/* perform the necessary typecasting of arguments */
|
||||
make_arguments(pstate, nargs, fargs, oid_array, true_oid_array);
|
||||
|
||||
expr = makeNode(Expr);
|
||||
expr->typeOid = rettype;
|
||||
expr->opType = FUNC_EXPR;
|
||||
expr->oper = (Node *) funcnode;
|
||||
expr->args = fargs;
|
||||
retval = (Node *) expr;
|
||||
|
||||
/*
|
||||
* if the function returns a set of values, then we need to iterate
|
||||
* over all the returned values in the executor, so we stick an iter
|
||||
* node here. if it returns a singleton, then we don't need the iter
|
||||
* node.
|
||||
*/
|
||||
if (retset)
|
||||
/* build the appropriate output structure */
|
||||
if (fdresult == FUNCDETAIL_NORMAL)
|
||||
{
|
||||
Iter *iter = makeNode(Iter);
|
||||
Expr *expr = makeNode(Expr);
|
||||
Func *funcnode = makeNode(Func);
|
||||
|
||||
iter->itertype = rettype;
|
||||
iter->iterexpr = retval;
|
||||
retval = (Node *) iter;
|
||||
funcnode->funcid = funcid;
|
||||
funcnode->functype = rettype;
|
||||
funcnode->func_fcache = NULL;
|
||||
|
||||
expr->typeOid = rettype;
|
||||
expr->opType = FUNC_EXPR;
|
||||
expr->oper = (Node *) funcnode;
|
||||
expr->args = fargs;
|
||||
|
||||
retval = (Node *) expr;
|
||||
|
||||
/*
|
||||
* if the function returns a set of values, then we need to iterate
|
||||
* over all the returned values in the executor, so we stick an iter
|
||||
* node here. if it returns a singleton, then we don't need the iter
|
||||
* node.
|
||||
*/
|
||||
if (retset)
|
||||
{
|
||||
Iter *iter = makeNode(Iter);
|
||||
|
||||
iter->itertype = rettype;
|
||||
iter->iterexpr = retval;
|
||||
retval = (Node *) iter;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* aggregate function */
|
||||
Aggref *aggref = makeNode(Aggref);
|
||||
|
||||
aggref->aggfnoid = funcid;
|
||||
aggref->aggtype = rettype;
|
||||
aggref->target = lfirst(fargs);
|
||||
aggref->aggstar = agg_star;
|
||||
aggref->aggdistinct = agg_distinct;
|
||||
|
||||
retval = (Node *) aggref;
|
||||
|
||||
if (retset)
|
||||
elog(ERROR, "Aggregates may not return sets");
|
||||
|
||||
pstate->p_hasAggs = true;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
agg_get_candidates(List *aggname,
|
||||
Oid typeId,
|
||||
FuncCandidateList *candidates)
|
||||
{
|
||||
Relation pg_aggregate_desc;
|
||||
SysScanDesc pg_aggregate_scan;
|
||||
HeapTuple tup;
|
||||
int ncandidates = 0;
|
||||
ScanKeyData aggKey[1];
|
||||
|
||||
*candidates = NULL;
|
||||
|
||||
ScanKeyEntryInitialize(&aggKey[0], 0,
|
||||
Anum_pg_aggregate_aggname,
|
||||
F_NAMEEQ,
|
||||
NameGetDatum(strVal(llast(aggname))));
|
||||
|
||||
pg_aggregate_desc = heap_openr(AggregateRelationName, AccessShareLock);
|
||||
pg_aggregate_scan = systable_beginscan(pg_aggregate_desc,
|
||||
AggregateNameTypeIndex, true,
|
||||
SnapshotNow,
|
||||
1, aggKey);
|
||||
|
||||
while (HeapTupleIsValid(tup = systable_getnext(pg_aggregate_scan)))
|
||||
{
|
||||
Form_pg_aggregate agg = (Form_pg_aggregate) GETSTRUCT(tup);
|
||||
FuncCandidateList current_candidate;
|
||||
|
||||
current_candidate = (FuncCandidateList)
|
||||
palloc(sizeof(struct _FuncCandidateList));
|
||||
current_candidate->args[0] = agg->aggbasetype;
|
||||
current_candidate->next = *candidates;
|
||||
*candidates = current_candidate;
|
||||
ncandidates++;
|
||||
}
|
||||
|
||||
systable_endscan(pg_aggregate_scan);
|
||||
heap_close(pg_aggregate_desc, AccessShareLock);
|
||||
|
||||
return ncandidates;
|
||||
} /* agg_get_candidates() */
|
||||
|
||||
/* agg_select_candidate()
|
||||
*
|
||||
* Try to choose only one candidate aggregate function from a list of
|
||||
* possible matches. Return value is Oid of input type of aggregate
|
||||
* if successful, else InvalidOid.
|
||||
*/
|
||||
static Oid
|
||||
agg_select_candidate(Oid typeid, FuncCandidateList candidates)
|
||||
{
|
||||
FuncCandidateList current_candidate;
|
||||
FuncCandidateList last_candidate;
|
||||
Oid current_typeid;
|
||||
int ncandidates;
|
||||
CATEGORY category,
|
||||
current_category;
|
||||
|
||||
/*
|
||||
* First look for exact matches or binary compatible matches. (Of
|
||||
* course exact matches shouldn't even get here, but anyway.)
|
||||
*/
|
||||
ncandidates = 0;
|
||||
last_candidate = NULL;
|
||||
for (current_candidate = candidates;
|
||||
current_candidate != NULL;
|
||||
current_candidate = current_candidate->next)
|
||||
{
|
||||
current_typeid = current_candidate->args[0];
|
||||
|
||||
if (IsBinaryCompatible(current_typeid, typeid))
|
||||
{
|
||||
last_candidate = current_candidate;
|
||||
ncandidates++;
|
||||
}
|
||||
}
|
||||
if (ncandidates == 1)
|
||||
return last_candidate->args[0];
|
||||
|
||||
/*
|
||||
* If no luck that way, look for candidates which allow coercion and
|
||||
* have a preferred type. Keep all candidates if none match.
|
||||
*/
|
||||
category = TypeCategory(typeid);
|
||||
ncandidates = 0;
|
||||
last_candidate = NULL;
|
||||
for (current_candidate = candidates;
|
||||
current_candidate != NULL;
|
||||
current_candidate = current_candidate->next)
|
||||
{
|
||||
current_typeid = current_candidate->args[0];
|
||||
current_category = TypeCategory(current_typeid);
|
||||
|
||||
if (current_category == category
|
||||
&& IsPreferredType(current_category, current_typeid)
|
||||
&& can_coerce_type(1, &typeid, ¤t_typeid))
|
||||
{
|
||||
/* only one so far? then keep it... */
|
||||
if (last_candidate == NULL)
|
||||
{
|
||||
candidates = current_candidate;
|
||||
last_candidate = current_candidate;
|
||||
ncandidates = 1;
|
||||
}
|
||||
/* otherwise, keep this one too... */
|
||||
else
|
||||
{
|
||||
last_candidate->next = current_candidate;
|
||||
last_candidate = current_candidate;
|
||||
ncandidates++;
|
||||
}
|
||||
}
|
||||
/* otherwise, don't bother keeping this one around... */
|
||||
}
|
||||
|
||||
if (last_candidate) /* terminate rebuilt list */
|
||||
last_candidate->next = NULL;
|
||||
|
||||
if (ncandidates == 1)
|
||||
return candidates->args[0];
|
||||
|
||||
return InvalidOid;
|
||||
} /* agg_select_candidate() */
|
||||
|
||||
|
||||
/* match_argtypes()
|
||||
*
|
||||
* Given a list of possible typeid arrays to a function and an array of
|
||||
@@ -529,7 +350,8 @@ match_argtypes(int nargs,
|
||||
current_candidate = next_candidate)
|
||||
{
|
||||
next_candidate = current_candidate->next;
|
||||
if (can_coerce_type(nargs, input_typeids, current_candidate->args))
|
||||
if (can_coerce_type(nargs, input_typeids, current_candidate->args,
|
||||
false))
|
||||
{
|
||||
current_candidate->next = *candidates;
|
||||
*candidates = current_candidate;
|
||||
@@ -1014,6 +836,7 @@ func_get_detail(List *funcname,
|
||||
{
|
||||
HeapTuple ftup;
|
||||
Form_pg_proc pform;
|
||||
FuncDetailCode result;
|
||||
|
||||
*funcid = best_candidate->oid;
|
||||
*true_typeids = best_candidate->args;
|
||||
@@ -1026,8 +849,9 @@ func_get_detail(List *funcname,
|
||||
pform = (Form_pg_proc) GETSTRUCT(ftup);
|
||||
*rettype = pform->prorettype;
|
||||
*retset = pform->proretset;
|
||||
result = pform->proisagg ? FUNCDETAIL_AGGREGATE : FUNCDETAIL_NORMAL;
|
||||
ReleaseSysCache(ftup);
|
||||
return FUNCDETAIL_NORMAL;
|
||||
return result;
|
||||
}
|
||||
|
||||
return FUNCDETAIL_NOTFOUND;
|
||||
@@ -1294,7 +1118,8 @@ make_arguments(ParseState *pstate,
|
||||
lfirst(current_fargs) = coerce_type(pstate,
|
||||
lfirst(current_fargs),
|
||||
input_typeids[i],
|
||||
function_typeids[i], -1);
|
||||
function_typeids[i], -1,
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1450,6 +1275,58 @@ func_error(const char *caller, List *funcname,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* find_aggregate_func
|
||||
* Convenience routine to check that a function exists and is an
|
||||
* aggregate.
|
||||
*
|
||||
* Note: basetype is InvalidOid if we are looking for an aggregate on
|
||||
* all types.
|
||||
*/
|
||||
Oid
|
||||
find_aggregate_func(const char *caller, List *aggname, Oid basetype)
|
||||
{
|
||||
Oid oid;
|
||||
HeapTuple ftup;
|
||||
Form_pg_proc pform;
|
||||
|
||||
oid = LookupFuncName(aggname, 1, &basetype);
|
||||
|
||||
if (!OidIsValid(oid))
|
||||
{
|
||||
if (basetype == InvalidOid)
|
||||
elog(ERROR, "%s: aggregate '%s' for all types does not exist",
|
||||
caller, NameListToString(aggname));
|
||||
else
|
||||
elog(ERROR, "%s: aggregate '%s' for type %s does not exist",
|
||||
caller, NameListToString(aggname),
|
||||
format_type_be(basetype));
|
||||
}
|
||||
|
||||
/* Make sure it's an aggregate */
|
||||
ftup = SearchSysCache(PROCOID,
|
||||
ObjectIdGetDatum(oid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(ftup)) /* should not happen */
|
||||
elog(ERROR, "function %u not found", oid);
|
||||
pform = (Form_pg_proc) GETSTRUCT(ftup);
|
||||
|
||||
if (!pform->proisagg)
|
||||
{
|
||||
if (basetype == InvalidOid)
|
||||
elog(ERROR, "%s: function %s(*) is not an aggregate",
|
||||
caller, NameListToString(aggname));
|
||||
else
|
||||
elog(ERROR, "%s: function %s(%s) is not an aggregate",
|
||||
caller, NameListToString(aggname),
|
||||
format_type_be(basetype));
|
||||
}
|
||||
|
||||
ReleaseSysCache(ftup);
|
||||
|
||||
return oid;
|
||||
}
|
||||
|
||||
/*
|
||||
* LookupFuncName
|
||||
* Given a possibly-qualified function name and a set of argument types,
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.60 2002/03/21 16:01:07 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.61 2002/04/11 20:00:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -73,7 +73,8 @@ make_operand(char *opname,
|
||||
{
|
||||
/* must coerce? */
|
||||
if (target_typeId != orig_typeId)
|
||||
result = coerce_type(NULL, tree, orig_typeId, target_typeId, -1);
|
||||
result = coerce_type(NULL, tree, orig_typeId, target_typeId, -1,
|
||||
false);
|
||||
else
|
||||
result = tree;
|
||||
}
|
||||
@@ -288,7 +289,7 @@ transformArraySubscripts(ParseState *pstate,
|
||||
subexpr = transformExpr(pstate, ai->lidx);
|
||||
/* If it's not int4 already, try to coerce */
|
||||
subexpr = CoerceTargetExpr(pstate, subexpr, exprType(subexpr),
|
||||
INT4OID, -1);
|
||||
INT4OID, -1, false);
|
||||
if (subexpr == NULL)
|
||||
elog(ERROR, "array index expressions must be integers");
|
||||
}
|
||||
@@ -308,7 +309,7 @@ transformArraySubscripts(ParseState *pstate,
|
||||
subexpr = transformExpr(pstate, ai->uidx);
|
||||
/* If it's not int4 already, try to coerce */
|
||||
subexpr = CoerceTargetExpr(pstate, subexpr, exprType(subexpr),
|
||||
INT4OID, -1);
|
||||
INT4OID, -1, false);
|
||||
if (subexpr == NULL)
|
||||
elog(ERROR, "array index expressions must be integers");
|
||||
upperIndexpr = lappend(upperIndexpr, subexpr);
|
||||
@@ -329,7 +330,7 @@ transformArraySubscripts(ParseState *pstate,
|
||||
/* XXX fixme: need to get the array's atttypmod? */
|
||||
assignFrom = CoerceTargetExpr(pstate, assignFrom,
|
||||
typesource, typeneeded,
|
||||
-1);
|
||||
-1, false);
|
||||
if (assignFrom == NULL)
|
||||
elog(ERROR, "Array assignment requires type '%s'"
|
||||
" but expression is of type '%s'"
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.53 2002/03/20 19:44:31 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.54 2002/04/11 20:00:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -265,7 +265,8 @@ oper_select_candidate(int nargs,
|
||||
current_candidate != NULL;
|
||||
current_candidate = current_candidate->next)
|
||||
{
|
||||
if (can_coerce_type(nargs, input_typeids, current_candidate->args))
|
||||
if (can_coerce_type(nargs, input_typeids, current_candidate->args,
|
||||
false))
|
||||
{
|
||||
if (last_candidate == NULL)
|
||||
{
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.83 2002/04/09 20:35:53 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.84 2002/04/11 20:00:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -290,7 +290,8 @@ updateTargetListEntry(ParseState *pstate,
|
||||
if (type_id != attrtype)
|
||||
{
|
||||
tle->expr = CoerceTargetExpr(pstate, tle->expr, type_id,
|
||||
attrtype, attrtypmod);
|
||||
attrtype, attrtypmod,
|
||||
false);
|
||||
if (tle->expr == NULL)
|
||||
elog(ERROR, "column \"%s\" is of type '%s'"
|
||||
" but expression is of type '%s'"
|
||||
@@ -327,10 +328,12 @@ CoerceTargetExpr(ParseState *pstate,
|
||||
Node *expr,
|
||||
Oid type_id,
|
||||
Oid attrtype,
|
||||
int32 attrtypmod)
|
||||
int32 attrtypmod,
|
||||
bool isExplicit)
|
||||
{
|
||||
if (can_coerce_type(1, &type_id, &attrtype))
|
||||
expr = coerce_type(pstate, expr, type_id, attrtype, attrtypmod);
|
||||
if (can_coerce_type(1, &type_id, &attrtype, isExplicit))
|
||||
expr = coerce_type(pstate, expr, type_id, attrtype, attrtypmod,
|
||||
isExplicit);
|
||||
|
||||
#ifndef DISABLE_STRING_HACKS
|
||||
|
||||
@@ -345,8 +348,9 @@ CoerceTargetExpr(ParseState *pstate,
|
||||
if (type_id == TEXTOID)
|
||||
{
|
||||
}
|
||||
else if (can_coerce_type(1, &type_id, &text_id))
|
||||
expr = coerce_type(pstate, expr, type_id, text_id, attrtypmod);
|
||||
else if (can_coerce_type(1, &type_id, &text_id, isExplicit))
|
||||
expr = coerce_type(pstate, expr, type_id, text_id, attrtypmod,
|
||||
isExplicit);
|
||||
else
|
||||
expr = NULL;
|
||||
}
|
||||
|
Reference in New Issue
Block a user