*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: outfuncs.c,v 1.98 1999/11/23 20:06:53 momjian Exp $
+ * $Id: outfuncs.c,v 1.99 1999/12/10 07:37:31 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
static void
_outFuncCall(StringInfo str, FuncCall *node)
{
- appendStringInfo(str, "FUNCTION %s :args ", stringStringInfo(node->funcname));
+ appendStringInfo(str, "FUNCTION %s :args ",
+ stringStringInfo(node->funcname));
_outNode(str, node->args);
+ appendStringInfo(str, " :agg_star %s :agg_distinct %s ",
+ node->agg_star ? "true" : "false",
+ node->agg_distinct ? "true" : "false");
}
static void
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: analyze.c,v 1.125 1999/12/06 18:02:42 wieck Exp $
+ * $Id: analyze.c,v 1.126 1999/12/10 07:37:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
funccallnode = makeNode(FuncCall);
funccallnode->funcname = "nextval";
funccallnode->args = lcons(snamenode, NIL);
+ funccallnode->agg_star = false;
+ funccallnode->agg_distinct = false;
constraint = makeNode(Constraint);
constraint->contype = CONSTR_DEFAULT;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.120 1999/12/10 05:17:13 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.121 1999/12/10 07:37:35 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
%type <str> TypeId
%type <node> TableConstraint
-%type <list> ColPrimaryKey, ColQualifier
+%type <list> ColPrimaryKey, ColConstraintList
%type <node> ColConstraint, ColConstraintElem
%type <ival> key_actions, key_action, key_reference
%type <str> key_match
| TableConstraint { $$ = $1; }
;
-columnDef: ColId Typename ColQualifier
+columnDef: ColId Typename ColConstraintList
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
}
;
-ColQualifier: ColQualifier ColConstraint
+ColConstraintList: ColConstraintList ColConstraint
{
if ($2 != NULL)
$$ = lappend($1, $2);
else
$$ = $1;
}
- | /*EMPTY*/ { $$ = NULL; }
+ | /*EMPTY*/
+ { $$ = NIL; }
;
ColPrimaryKey: PRIMARY KEY
FuncCall *n = makeNode(FuncCall);
n->funcname = $3->name;
n->args = lcons($1,NIL);
+ n->agg_star = false;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
}
FuncCall *n = makeNode(FuncCall);
n->funcname = $3->name;
n->args = lcons($1,NIL);
+ n->agg_star = false;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
}
FuncCall *n = makeNode(FuncCall);
n->funcname = $5->name;
n->args = lcons($3,NIL);
+ n->agg_star = false;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
}
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
n->args = NIL;
+ n->agg_star = false;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
| func_name '(' expr_list ')'
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
n->args = $3;
+ n->agg_star = false;
+ n->agg_distinct = false;
+ $$ = (Node *)n;
+ }
+ | func_name '(' DISTINCT expr_list ')'
+ {
+ FuncCall *n = makeNode(FuncCall);
+ n->funcname = $1;
+ n->args = $4;
+ n->agg_star = false;
+ n->agg_distinct = true;
$$ = (Node *)n;
}
| func_name '(' '*' ')'
* and there are no other aggregates in SQL92 that accept
* '*' as parameter.
*
- * XXX really, the '*' ought to be transformed to some
- * special construct that wouldn't be acceptable as the
- * input of a non-aggregate function, in case the given
- * func_name matches a plain function. This would also
- * support a possible extension to let user-defined
- * aggregates do something special with '*' as input.
+ * The FuncCall node is also marked agg_star = true,
+ * so that later processing can detect what the argument
+ * really was.
*/
FuncCall *n = makeNode(FuncCall);
A_Const *star = makeNode(A_Const);
star->val.val.ival = 1;
n->funcname = $1;
n->args = lcons(star, NIL);
+ n->agg_star = true;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
| CURRENT_DATE
n->funcname = xlateSqlType("date");
n->args = lcons(s, NIL);
+ n->agg_star = false;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
n->funcname = xlateSqlType("time");
n->args = lcons(s, NIL);
+ n->agg_star = false;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
n->funcname = xlateSqlType("time");
n->args = lcons(s, NIL);
+ n->agg_star = false;
+ n->agg_distinct = false;
if ($3 != 0)
elog(NOTICE,"CURRENT_TIME(%d) precision not implemented; zero used instead",$3);
n->funcname = xlateSqlType("timestamp");
n->args = lcons(s, NIL);
+ n->agg_star = false;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
n->funcname = xlateSqlType("timestamp");
n->args = lcons(s, NIL);
+ n->agg_star = false;
+ n->agg_distinct = false;
if ($3 != 0)
elog(NOTICE,"CURRENT_TIMESTAMP(%d) precision not implemented; zero used instead",$3);
FuncCall *n = makeNode(FuncCall);
n->funcname = "getpgusername";
n->args = NIL;
+ n->agg_star = false;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
| USER
FuncCall *n = makeNode(FuncCall);
n->funcname = "getpgusername";
n->args = NIL;
+ n->agg_star = false;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
| EXTRACT '(' extract_list ')'
FuncCall *n = makeNode(FuncCall);
n->funcname = "date_part";
n->args = $3;
+ n->agg_star = false;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
| POSITION '(' position_list ')'
FuncCall *n = makeNode(FuncCall);
n->funcname = "strpos";
n->args = $3;
+ n->agg_star = false;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
| SUBSTRING '(' substr_list ')'
FuncCall *n = makeNode(FuncCall);
n->funcname = "substr";
n->args = $3;
+ n->agg_star = false;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
/* various trim expressions are defined in SQL92 - thomas 1997-07-19 */
FuncCall *n = makeNode(FuncCall);
n->funcname = "btrim";
n->args = $4;
+ n->agg_star = false;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
| TRIM '(' LEADING trim_list ')'
FuncCall *n = makeNode(FuncCall);
n->funcname = "ltrim";
n->args = $4;
+ n->agg_star = false;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
| TRIM '(' TRAILING trim_list ')'
FuncCall *n = makeNode(FuncCall);
n->funcname = "rtrim";
n->args = $4;
+ n->agg_star = false;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
| TRIM '(' trim_list ')'
FuncCall *n = makeNode(FuncCall);
n->funcname = "btrim";
n->args = $3;
+ n->agg_star = false;
+ n->agg_distinct = false;
$$ = (Node *)n;
}
| '(' SubSelect ')'
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.30 1999/12/09 05:58:54 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.31 1999/12/10 07:37:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Aggref *
ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
- List *target, int precedence)
+ List *args, bool agg_star, bool agg_distinct,
+ int precedence)
{
HeapTuple theAggTuple;
Form_pg_aggregate aggform;
if (OidIsValid(xfn1))
{
basetype = aggform->aggbasetype;
- vartype = exprType(lfirst(target));
+ vartype = exprType(lfirst(args));
if ((basetype != vartype)
&& (!IS_BINARY_COMPATIBLE(basetype, vartype)))
{
aggref->aggname = pstrdup(aggname);
aggref->basetype = aggform->aggbasetype;
aggref->aggtype = fintype;
- aggref->target = lfirst(target);
+ aggref->target = lfirst(args);
aggref->usenulls = usenulls;
+ /*
+ * We should store agg_star and agg_distinct into the Aggref node,
+ * and let downstream processing deal with them. Currently, agg_star
+ * is ignored and agg_distinct is not implemented...
+ */
+ if (agg_distinct)
+ elog(ERROR, "aggregate(DISTINCT ...) is not implemented yet");
+
pstate->p_hasAggs = true;
return aggref;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.25 1999/11/22 17:56:20 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.26 1999/12/10 07:37:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
n->funcname = typeTypeName(targetType);
n->args = lcons(node, NIL);
+ n->agg_star = false;
+ n->agg_distinct = false;
result = transformExpr(pstate, (Node *) n, EXPR_COLUMN_FIRST);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.59 1999/11/15 02:00:10 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.60 1999/12/10 07:37:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
result = ParseFuncOrColumn(pstate,
- "nullvalue", lcons(lexpr, NIL),
- &pstate->p_last_resno,
+ "nullvalue",
+ lcons(lexpr, NIL),
+ false, false,
+ &pstate->p_last_resno,
precedence);
}
break;
Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
result = ParseFuncOrColumn(pstate,
- "nonnullvalue", lcons(lexpr, NIL),
- &pstate->p_last_resno,
+ "nonnullvalue",
+ lcons(lexpr, NIL),
+ false, false,
+ &pstate->p_last_resno,
precedence);
}
break;
result = ParseFuncOrColumn(pstate,
fn->funcname,
fn->args,
+ fn->agg_star,
+ fn->agg_distinct,
&pstate->p_last_resno,
precedence);
break;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.63 1999/12/07 04:09:39 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.64 1999/12/10 07:37:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
lcons(param, NIL),
+ false, false,
curr_resno,
precedence);
}
ident->isRel = TRUE;
retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
lcons(ident, NIL),
+ false, false,
curr_resno,
precedence);
}
{
retval = ParseFuncOrColumn(pstate, strVal(lfirst(mutator_iter)),
lcons(retval, NIL),
+ false, false,
curr_resno,
precedence);
}
*/
Node *
ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
+ bool agg_star, bool agg_distinct,
int *curr_resno, int precedence)
{
Oid rettype = InvalidOid;
char *refname = NULL;
Relation rd;
Oid relid;
- int nargs;
+ int nargs = length(fargs);
Func *funcnode;
Oid oid_array[MAXFARGS];
Oid *true_oid_array;
Node *retval;
bool retset;
+ bool must_be_agg = agg_star || agg_distinct;
bool attisset = false;
Oid toid = InvalidOid;
Expr *expr;
* that argument is a relation, param, or PQ function returning a
* complex * type, then the function could be a projection.
*/
- /* We only have one parameter */
- if (length(fargs) == 1)
+ /* We only have one parameter, and it's not got aggregate decoration */
+ if (nargs == 1 && !must_be_agg)
{
- /* Is is a plain Relation name from the parser? */
- if (nodeTag(first_arg) == T_Ident && ((Ident *) first_arg)->isRel)
+ /* Is it a plain Relation name from the parser? */
+ if (IsA(first_arg, Ident) && ((Ident *) first_arg)->isRel)
{
RangeTblEntry *rte;
Ident *ident = (Ident *) first_arg;
refname,
funcname);
}
- else
- {
- /* drop through - attr is a set */
- ;
- }
+ /* else drop through - attr is a set */
}
else if (ISCOMPLEX(exprType(first_arg)))
{
-
/*
* Attempt to handle projection of a complex argument. If
* ParseComplexProjection can't handle the projection, we have
argrelid = typeidTypeRelid(toid);
/*
- * A projection contains either an attribute name or the
- * "*".
+ * A projection contains either an attribute name or "*".
*/
if ((get_attnum(argrelid, funcname) == InvalidAttrNumber)
&& strcmp(funcname, "*"))
if (retval)
return retval;
}
- else
- {
+ }
- /*
- * Parsing aggregates.
- */
- Type tp;
- Oid basetype;
- int ncandidates;
- CandidateList candidates;
+ if (nargs == 1 || must_be_agg)
+ {
+ /*
+ * See if it's an aggregate.
+ */
+ Oid basetype;
+ int ncandidates;
+ CandidateList candidates;
- /*
- * the aggregate COUNT is a special case, ignore its base
- * type. Treat it as zero
- */
- if (strcmp(funcname, "count") == 0)
- basetype = 0;
- else
- basetype = exprType(lfirst(fargs));
+ /* We don't presently cope with, eg, foo(DISTINCT x,y) */
+ if (nargs != 1)
+ elog(ERROR, "Aggregate functions may only have one parameter");
- /* try for exact match first... */
- if (SearchSysCacheTuple(AGGNAME,
- PointerGetDatum(funcname),
- ObjectIdGetDatum(basetype),
- 0, 0))
- return (Node *) ParseAgg(pstate, funcname, basetype,
- fargs, precedence);
+ /*
+ * the aggregate COUNT is a special case, ignore its base
+ * type. Treat it as zero. XXX mighty ugly --- FIXME
+ */
+ if (strcmp(funcname, "count") == 0)
+ basetype = 0;
+ else
+ basetype = exprType(lfirst(fargs));
- /*
- * No exact match yet, so see if there is another entry in the
- * aggregate table which is compatible. - thomas 1998-12-05
- */
- ncandidates = agg_get_candidates(funcname, basetype, &candidates);
- if (ncandidates > 0)
- {
- Oid type;
+ /* try for exact match first... */
+ if (SearchSysCacheTuple(AGGNAME,
+ PointerGetDatum(funcname),
+ ObjectIdGetDatum(basetype),
+ 0, 0))
+ return (Node *) ParseAgg(pstate, funcname, basetype,
+ fargs, agg_star, agg_distinct,
+ precedence);
- type = agg_select_candidate(basetype, candidates);
- if (OidIsValid(type))
- {
- lfirst(fargs) = coerce_type(pstate, lfirst(fargs),
- basetype, type, -1);
- basetype = type;
+ /*
+ * No exact match yet, so see if there is another entry in the
+ * aggregate table which is compatible. - thomas 1998-12-05
+ */
+ ncandidates = agg_get_candidates(funcname, basetype, &candidates);
+ if (ncandidates > 0)
+ {
+ Oid type;
- return (Node *) ParseAgg(pstate, funcname, basetype,
- fargs, precedence);
- }
- else
- {
- elog(ERROR, "Unable to select an aggregate function %s(%s)",
- funcname, typeidTypeName(basetype));
- }
+ 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,
+ precedence);
+ }
+ else
+ {
+ /* Multiple possible matches --- give up */
+ elog(ERROR, "Unable to select an aggregate function %s(%s)",
+ funcname, typeidTypeName(basetype));
}
+ }
+ if (must_be_agg)
+ {
/*
- * See if this is a single argument function with the function
- * name also a type name and the input argument and type name
- * binary compatible... This means that you are trying for a
- * type conversion which does not need to take place, so we'll
- * just pass through the argument itself. (make this clearer
- * with some extra brackets - thomas 1998-12-05)
+ * No matching agg, but we had '*' or DISTINCT, so a plain
+ * function could not have been meant.
*/
- if ((HeapTupleIsValid(tp = SearchSysCacheTuple(TYPENAME,
- PointerGetDatum(funcname),
- 0, 0, 0)))
- && IS_BINARY_COMPATIBLE(typeTypeId(tp), basetype))
- return ((Node *) lfirst(fargs));
+ elog(ERROR, "There is no aggregate function %s(%s)",
+ funcname, typeidTypeName(basetype));
}
}
+ /*
+ * See if this is a single argument function with the function
+ * name also a type name and the input argument and type name
+ * binary compatible... This means that you are trying for a
+ * type conversion which does not need to take place, so we'll
+ * just pass through the argument itself. (make this clearer
+ * with some extra brackets - thomas 1998-12-05)
+ */
+ if (nargs == 1)
+ {
+ Type tp;
+
+ tp = SearchSysCacheTuple(TYPENAME,
+ PointerGetDatum(funcname),
+ 0, 0, 0);
+ if (HeapTupleIsValid(tp) &&
+ IS_BINARY_COMPATIBLE(typeTypeId(tp), exprType(lfirst(fargs))))
+ {
+ /* XXX FIXME: probably need to change expression's marked type? */
+ return (Node *) lfirst(fargs);
+ }
+ }
/*
* If we dropped through to here it's really a function (or a set,
{ /* set functions don't have parameters */
/*
- * any functiona args which are typed "unknown", but aren't
+ * any function args which are typed "unknown", but aren't
* constants, we don't know what to do with, because we can't
* cast them - jolly
*/
if (exprType(pair) == UNKNOWNOID && !IsA(pair, Const))
elog(ERROR, "There is no function '%s'"
" with argument #%d of type UNKNOWN",
- funcname, nargs);
+ funcname, nargs+1);
else
toid = exprType(pair);
}
text *seqname;
int32 aclcheck_result = -1;
- Assert(length(fargs) == ((funcid == F_SETVAL) ? 2 : 1));
+ Assert(nargs == ((funcid == F_SETVAL) ? 2 : 1));
seq = (Const *) lfirst(fargs);
if (!IsA((Node *) seq, Const))
elog(ERROR, "Only constant sequence names are acceptable for function '%s'", funcname);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.49 1999/11/22 17:56:21 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.50 1999/12/10 07:37:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (HeapTupleIsValid(ftup))
{
- FuncCall *func;
- A_Const *cons;
+ A_Const *cons = makeNode(A_Const);
+ FuncCall *func = makeNode(FuncCall);
- func = makeNode(FuncCall);
- func->funcname = funcname;
-
- cons = makeNode(A_Const);
cons->val.type = T_Integer;
cons->val.val.ival = attrtypmod;
+
+ func->funcname = funcname;
func->args = lappend(lcons(expr, NIL), cons);
+ func->agg_star = false;
+ func->agg_distinct = false;
expr = transformExpr(pstate, (Node *) func, EXPR_COLUMN_FIRST);
}
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: parsenodes.h,v 1.89 1999/12/10 03:56:09 momjian Exp $
+ * $Id: parsenodes.h,v 1.90 1999/12/10 07:37:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/****************************************************************************
* Supporting data structures for Parse Trees
+ *
+ * Most of these node types appear in raw parsetrees output by the grammar,
+ * and get transformed to something else by the analyzer. A few of them
+ * are used as-is in transformed querytrees.
****************************************************************************/
/*
} Ident;
/*
- * FuncCall - a function/aggregate invocation
+ * FuncCall - a function or aggregate invocation
+ *
+ * agg_star indicates we saw a 'foo(*)' construct, while agg_distinct
+ * indicates we saw 'foo(DISTINCT ...)'. In either case, the construct
+ * *must* be an aggregate call. Otherwise, it might be either an
+ * aggregate or some other kind of function.
*/
typedef struct FuncCall
{
NodeTag type;
char *funcname; /* name of function */
List *args; /* the arguments (list of exprs) */
+ bool agg_star; /* argument was really '*' */
+ bool agg_distinct; /* arguments were labeled DISTINCT */
} FuncCall;
/*
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: parse_agg.h,v 1.12 1999/07/15 23:04:01 momjian Exp $
+ * $Id: parse_agg.h,v 1.13 1999/12/10 07:37:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern void AddAggToParseState(ParseState *pstate, Aggref *aggref);
extern void parseCheckAggregates(ParseState *pstate, Query *qry);
extern Aggref *ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
- List *target, int precedence);
+ List *args, bool agg_star, bool agg_distinct,
+ int precedence);
extern void agg_error(char *caller, char *aggname, Oid basetypeID);
#endif /* PARSE_AGG_H */
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: parse_func.h,v 1.19 1999/08/21 03:49:17 tgl Exp $
+ * $Id: parse_func.h,v 1.20 1999/12/10 07:37:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern Node *ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr,
int *curr_resno, int precedence);
-extern Node *ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
- int *curr_resno, int precedence);
+extern Node *ParseFuncOrColumn(ParseState *pstate,
+ char *funcname, List *fargs,
+ bool agg_star, bool agg_distinct,
+ int *curr_resno, int precedence);
extern List *setup_base_tlist(Oid typeid);