diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2002-03-21 16:02:16 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2002-03-21 16:02:16 +0000 |
commit | 95ef6a344821655ce4d0a74999ac49dd6af6d342 (patch) | |
tree | df484a4c9dde9827894ab707917c001a1f376749 /src/backend/parser/parse_func.c | |
parent | 8c9c8ca2b57e4edef218245ccdc9eef7c06425d8 (diff) | |
download | postgresql-95ef6a344821655ce4d0a74999ac49dd6af6d342.tar.gz postgresql-95ef6a344821655ce4d0a74999ac49dd6af6d342.zip |
First phase of SCHEMA changes, concentrating on fixing the grammar and
the parsetree representation. As yet we don't *do* anything with schema
names, just drop 'em on the floor; but you can enter schema-compatible
command syntax, and there's even a primitive CREATE SCHEMA command.
No doc updates yet, except to note that you can now extract a field
from a function-returning-row's result with (foo(...)).fieldname.
Diffstat (limited to 'src/backend/parser/parse_func.c')
-rw-r--r-- | src/backend/parser/parse_func.c | 384 |
1 files changed, 115 insertions, 269 deletions
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 6dc7b440fa8..ee1bf6c0578 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,11 +8,10 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.118 2002/03/20 19:44:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.119 2002/03/21 16:01:06 tgl Exp $ * *------------------------------------------------------------------------- */ - #include "postgres.h" #include "access/genam.h" @@ -36,8 +35,7 @@ static Node *ParseComplexProjection(ParseState *pstate, char *funcname, - Node *first_arg, - bool *attisset); + Node *first_arg); static Oid **argtype_inherit(int nargs, Oid *argtypes); static int find_inheritors(Oid relid, Oid **supervec); @@ -60,75 +58,31 @@ static Oid agg_select_candidate(Oid typeid, CandidateList candidates); /* - ** ParseNestedFuncOrColumn - ** Given a nested dot expression (i.e. (relation func ... attr), build up - ** a tree with of Iter and Func nodes. - */ -Node * -ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int precedence) -{ - List *mutator_iter; - Node *retval = NULL; - - if (attr->paramNo != NULL) - { - Param *param = (Param *) transformExpr(pstate, - (Node *) attr->paramNo, - EXPR_RELATION_FIRST); - - retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)), - makeList1(param), - false, false, - precedence); - } - else - { - Ident *ident = makeNode(Ident); - - ident->name = attr->relname; - ident->isRel = TRUE; - retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)), - makeList1(ident), - false, false, - precedence); - } - - /* Do more attributes follow this one? */ - foreach(mutator_iter, lnext(attr->attrs)) - { - retval = ParseFuncOrColumn(pstate, strVal(lfirst(mutator_iter)), - makeList1(retval), - false, false, - precedence); - } - - return retval; -} - -/* - * parse function - * - * This code is confusing because the database can accept - * relation.column, column.function, or relation.column.function. - * In these cases, funcname is the last parameter, and fargs are - * the rest. + * Parse a function call * - * It can also be called as func(col) or func(col,col). - * In this case, Funcname is the part before parens, and fargs - * are the part in parens. + * For historical reasons, Postgres tries to treat the notations tab.col + * and col(tab) as equivalent: if a single-argument function call has an + * argument of complex type and the function name matches any attribute + * of the type, we take it as a column projection. * - * FYI, projection is choosing column from a table. + * Hence, both cases come through here. The is_column parameter tells us + * which syntactic construct is actually being dealt with, but this is + * intended to be used only to deliver an appropriate error message, + * not to affect the semantics. When is_column is true, we should have + * a single argument (the putative table), function name equal to the + * column name, and no aggregate decoration. * + * In the function-call case, the argument expressions have been transformed + * already. In the column case, we may get either a transformed expression + * or a RangeVar node as argument. */ Node * ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, - bool agg_star, bool agg_distinct, - int precedence) + bool agg_star, bool agg_distinct, bool is_column) { - Oid rettype = InvalidOid; - Oid argrelid = InvalidOid; - Oid funcid = InvalidOid; - List *i = NIL; + Oid rettype; + Oid funcid; + List *i; Node *first_arg = NULL; char *refname; int nargs = length(fargs); @@ -140,9 +94,8 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, bool retset; bool must_be_agg = agg_star || agg_distinct; bool could_be_agg; - bool attisset = false; - Oid toid = InvalidOid; Expr *expr; + FuncDetailCode fdresult; /* * Most of the rest of the parser just assumes that functions do not @@ -157,33 +110,26 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, if (fargs) { first_arg = lfirst(fargs); - if (first_arg == NULL) + if (first_arg == NULL) /* should not happen */ elog(ERROR, "Function '%s' does not allow NULL input", funcname); } /* - * test for relation.column - * - * check for projection methods: if function takes one argument, and that - * argument is a relation, param, or PQ function returning a complex * - * type, then the function could be a projection. + * check for column projection: if function has one argument, and that + * argument is of complex type, then the function could be a projection. */ /* We only have one parameter, and it's not got aggregate decoration */ if (nargs == 1 && !must_be_agg) { - /* Is it a plain Relation name from the parser? */ - if (IsA(first_arg, Ident) &&((Ident *) first_arg)->isRel) + /* Is it a not-yet-transformed RangeVar node? */ + if (IsA(first_arg, RangeVar)) { - Ident *ident = (Ident *) first_arg; - /* First arg is a relation. This could be a projection. */ - refname = ident->name; + refname = ((RangeVar *) first_arg)->relname; retval = qualifiedNameToVar(pstate, refname, funcname, true); if (retval) return retval; - - /* else drop through - attr is a set or function */ } else if (ISCOMPLEX(exprType(first_arg))) { @@ -194,24 +140,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, */ retval = ParseComplexProjection(pstate, funcname, - first_arg, - &attisset); - if (attisset) - { - toid = exprType(first_arg); - argrelid = typeidTypeRelid(toid); - if (argrelid == InvalidOid) - elog(ERROR, "Type '%s' is not a relation type", - typeidTypeName(toid)); - - /* - * A projection must match an attribute name of the rel. - */ - if (get_attnum(argrelid, funcname) == InvalidAttrNumber) - elog(ERROR, "No such attribute or function '%s'", - funcname); - } - + first_arg); if (retval) return retval; } @@ -226,15 +155,14 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, 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, Ident) &&((Ident *) first_arg)->isRel) + 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, Ident) &&((Ident *) first_arg)->isRel); + could_be_agg = (nargs == 1) && !(IsA(first_arg, RangeVar)); } if (could_be_agg) @@ -249,8 +177,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, ObjectIdGetDatum(basetype), 0, 0)) return (Node *) ParseAgg(pstate, funcname, basetype, - fargs, agg_star, agg_distinct, - precedence); + fargs, agg_star, agg_distinct); /* check for aggregate-that-accepts-any-type (eg, COUNT) */ if (SearchSysCacheExists(AGGNAME, @@ -258,8 +185,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, ObjectIdGetDatum(0), 0, 0)) return (Node *) ParseAgg(pstate, funcname, 0, - fargs, agg_star, agg_distinct, - precedence); + fargs, agg_star, agg_distinct); /* * No exact match yet, so see if there is another entry in the @@ -277,8 +203,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, basetype, type, -1); basetype = type; return (Node *) ParseAgg(pstate, funcname, basetype, - fargs, agg_star, agg_distinct, - precedence); + fargs, agg_star, agg_distinct); } else { @@ -300,10 +225,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, } /* - * If we dropped through to here it's really a function (or a set, - * which is implemented as a function). Extract arg type info and - * transform relation name arguments into varnodes of the appropriate - * form. + * Okay, it's not a column projection, so it must really be a function. + * Extract arg type info and transform RangeVar arguments into varnodes + * of the appropriate form. */ MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid)); @@ -311,8 +235,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, foreach(i, fargs) { Node *arg = lfirst(i); + Oid toid; - if (IsA(arg, Ident) &&((Ident *) arg)->isRel) + if (IsA(arg, RangeVar)) { RangeTblEntry *rte; int vnum; @@ -321,7 +246,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, /* * a relation */ - refname = ((Ident *) arg)->name; + refname = ((RangeVar *) arg)->relname; rte = refnameRangeTblEntry(pstate, refname, &sublevels_up); @@ -346,16 +271,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, * RTE is a join or subselect; must fail for lack of a * named tuple type */ - if (nargs == 1) - { - /* - * Here, we probably have an unrecognized attribute of - * a sub-select; again can't tell if it was x.f or - * f(x) - */ - elog(ERROR, "No such attribute or function %s.%s", + if (is_column) + elog(ERROR, "No such attribute %s.%s", refname, funcname); - } else { elog(ERROR, "Cannot pass result of sub-select or join %s to a function", @@ -365,93 +283,53 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, toid = typenameTypeId(rte->relname); - /* replace it in the arg list */ + /* replace RangeVar in the arg list */ lfirst(i) = makeVar(vnum, InvalidAttrNumber, toid, sizeof(Pointer), sublevels_up); } - else if (!attisset) - toid = exprType(arg); else - { - /* if attisset is true, we already set toid for the single arg */ - } + toid = exprType(arg); oid_array[argn++] = toid; } /* - * Is it a set, or a function? + * 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. */ - if (attisset) - { /* we know all of these fields already */ - + fdresult = func_get_detail(funcname, fargs, nargs, oid_array, + &funcid, &rettype, &retset, + &true_oid_array); + if (fdresult == FUNCDETAIL_COERCION) + { /* - * We create a funcnode with a placeholder function seteval(). At - * runtime, seteval() will execute the function identified by the - * funcid it receives as parameter. - * - * Example: retrieve (emp.mgr.name). The plan for this will scan the - * emp relation, projecting out the mgr attribute, which is a - * funcid. This function is then called (via seteval()) and "name" - * is projected from its result. + * We can do it as a trivial coercion. coerce_type can handle + * these cases, so why duplicate code... */ - funcid = F_SETEVAL; - rettype = toid; - retset = true; - true_oid_array = oid_array; + return coerce_type(pstate, lfirst(fargs), + oid_array[0], rettype, -1); } - else + if (fdresult != FUNCDETAIL_NORMAL) { - 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. + * Oops. Time to die. + * + * If we are dealing with the attribute notation rel.function, + * give an error message that is appropriate for that case. */ - fdresult = func_get_detail(funcname, fargs, nargs, oid_array, - &funcid, &rettype, &retset, - &true_oid_array); - if (fdresult == FUNCDETAIL_COERCION) - { - /* - * We can do it as a trivial coercion. coerce_type can handle - * these cases, so why duplicate code... - */ - return coerce_type(pstate, lfirst(fargs), - oid_array[0], rettype, -1); - } - if (fdresult != FUNCDETAIL_NORMAL) - { - /* - * Oops. Time to die. - * - * If there is a single argument of complex type, we might be - * dealing with the PostQuel notation rel.function instead of - * the more usual function(rel). Give a nonspecific error - * message that will cover both cases. - */ - if (nargs == 1) - { - Type tp = typeidType(oid_array[0]); - - if (typeTypeFlag(tp) == 'c') - elog(ERROR, "No such attribute or function '%s'", - funcname); - ReleaseSysCache(tp); - } - - /* Else generate a detailed complaint */ - func_error(NULL, funcname, nargs, oid_array, - "Unable to identify a function that satisfies the " - "given argument types" - "\n\tYou may need to add explicit typecasts"); - } + if (is_column) + elog(ERROR, "Attribute \"%s\" not found", funcname); + /* Else generate a detailed complaint */ + func_error(NULL, funcname, nargs, oid_array, + "Unable to identify a function that satisfies the " + "given argument types" + "\n\tYou may need to add explicit typecasts"); } /* got it */ @@ -471,25 +349,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, retval = (Node *) expr; /* - * For sets, we want to project out the desired attribute of the - * tuples. - */ - if (attisset) - { - FieldSelect *fselect; - - fselect = setup_field_select(retval, funcname, argrelid); - rettype = fselect->resulttype; - retval = (Node *) fselect; - } - - /* * 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); @@ -1497,10 +1361,10 @@ make_arguments(ParseState *pstate, } /* - ** setup_field_select - ** Build a FieldSelect node that says which attribute to project to. - ** This routine is called by ParseFuncOrColumn() when we have found - ** a projection on a function result or parameter. + * setup_field_select + * Build a FieldSelect node that says which attribute to project to. + * This routine is called by ParseFuncOrColumn() when we have found + * a projection on a function result or parameter. */ static FieldSelect * setup_field_select(Node *input, char *attname, Oid relid) @@ -1521,18 +1385,31 @@ setup_field_select(Node *input, char *attname, Oid relid) /* * ParseComplexProjection - * handles function calls with a single argument that is of complex type. - * This routine returns NULL if it can't handle the projection (eg. sets). + * If the function call is actually a column projection, return a suitably + * transformed expression tree. If not, return NULL. + * + * NB: argument is expected to be transformed already, ie, not a RangeVar. */ static Node * ParseComplexProjection(ParseState *pstate, char *funcname, - Node *first_arg, - bool *attisset) + Node *first_arg) { - Oid argtype; + Oid argtype = exprType(first_arg); Oid argrelid; + AttrNumber attnum; FieldSelect *fselect; + argrelid = typeidTypeRelid(argtype); + if (!argrelid) + return NULL; /* probably should not happen */ + attnum = get_attnum(argrelid, funcname); + if (attnum == InvalidAttrNumber) + return NULL; /* funcname does not match any column */ + + /* + * Check for special cases where we don't want to return a FieldSelect. + */ switch (nodeTag(first_arg)) { case T_Iter: @@ -1540,75 +1417,42 @@ ParseComplexProjection(ParseState *pstate, Iter *iter = (Iter *) first_arg; /* - * If the argument of the Iter returns a tuple, funcname - * may be a projection. If so, we stick the FieldSelect + * If it's an Iter, we stick the FieldSelect * *inside* the Iter --- this is klugy, but necessary * because ExecTargetList() currently does the right thing * only when the Iter node is at the top level of a * targetlist item. + * + * XXX Iter should go away altogether... */ - argtype = iter->itertype; - argrelid = typeidTypeRelid(argtype); - if (argrelid && - get_attnum(argrelid, funcname) != InvalidAttrNumber) - { - fselect = setup_field_select(iter->iterexpr, - funcname, argrelid); - iter->iterexpr = (Node *) fselect; - iter->itertype = fselect->resulttype; - return (Node *) iter; - } + fselect = setup_field_select(iter->iterexpr, + funcname, argrelid); + iter->iterexpr = (Node *) fselect; + iter->itertype = fselect->resulttype; + return (Node *) iter; break; } case T_Var: { - /* - * The argument is a set, so this is either a projection - * or a function call on this set. - */ - *attisset = true; - break; - } - case T_Expr: - { - Expr *expr = (Expr *) first_arg; - Func *funcnode; - - if (expr->opType != FUNC_EXPR) - break; + Var *var = (Var *) first_arg; /* - * If the argument is a function returning a tuple, - * funcname may be a projection + * If the Var is a whole-row tuple, we can just replace it + * with a simple Var reference. */ - funcnode = (Func *) expr->oper; - argtype = funcnode->functype; - argrelid = typeidTypeRelid(argtype); - if (argrelid && - get_attnum(argrelid, funcname) != InvalidAttrNumber) + if (var->varattno == InvalidAttrNumber) { - fselect = setup_field_select((Node *) expr, - funcname, argrelid); - return (Node *) fselect; - } - break; - } - case T_Param: - { - Param *param = (Param *) first_arg; + Oid vartype; + int32 vartypmod; - /* - * If the Param is a complex type, this could be a - * projection - */ - argtype = param->paramtype; - argrelid = typeidTypeRelid(argtype); - if (argrelid && - get_attnum(argrelid, funcname) != InvalidAttrNumber) - { - fselect = setup_field_select((Node *) param, - funcname, argrelid); - return (Node *) fselect; + get_atttypetypmod(argrelid, attnum, + &vartype, &vartypmod); + + return (Node *) makeVar(var->varno, + attnum, + vartype, + vartypmod, + var->varlevelsup); } break; } @@ -1616,7 +1460,9 @@ ParseComplexProjection(ParseState *pstate, break; } - return NULL; + /* Else generate a FieldSelect expression */ + fselect = setup_field_select(first_arg, funcname, argrelid); + return (Node *) fselect; } /* |