📄 parse_func.c
字号:
/*------------------------------------------------------------------------- * * parse_func.c * handle function calls in parser * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /usr/local/cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.47 1999/06/17 22:21:40 tgl Exp $ * *------------------------------------------------------------------------- */#include <string.h>#include "postgres.h"#include "access/genam.h"#include "access/heapam.h"#include "access/itup.h"#include "access/relscan.h"#include "access/sdir.h"#include "catalog/catname.h"#include "catalog/heap.h"#include "catalog/indexing.h"#include "catalog/pg_inherits.h"#include "catalog/pg_proc.h"#include "catalog/pg_type.h"#include "catalog/pg_aggregate.h"#include "fmgr.h"#include "lib/dllist.h"#include "miscadmin.h"#include "nodes/makefuncs.h"#include "nodes/relation.h"#include "parser/parse_agg.h"#include "parser/parse_expr.h"#include "parser/parse_func.h"#include "parser/parse_node.h"#include "parser/parse_relation.h"#include "parser/parse_target.h"#include "parser/parse_type.h"#include "parser/parse_coerce.h"#include "storage/bufmgr.h"#include "storage/lmgr.h"#include "utils/acl.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/syscache.h"static Node *ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg, bool *attisset);static Oid **argtype_inherit(int nargs, Oid *oid_array);static int find_inheritors(Oid relid, Oid **supervec);static CandidateList func_get_candidates(char *funcname, int nargs);static boolfunc_get_detail(char *funcname, int nargs, Oid *oid_array, Oid *funcid, /* return value */ Oid *rettype, /* return value */ bool *retset, /* return value */ Oid **true_typeids);static Oid funcid_get_rettype(Oid funcid);static Oid **gen_cross_product(InhPaths *arginh, int nargs);static void make_arguments(ParseState *pstate, int nargs, List *fargs, Oid *input_typeids, Oid *function_typeids);static int match_argtypes(int nargs, Oid *input_typeids, CandidateList function_typeids, CandidateList *candidates);static List *setup_tlist(char *attname, Oid relid);static List *setup_base_tlist(Oid typeid);static Oid *func_select_candidate(int nargs, Oid *input_typeids, CandidateList candidates);static int agg_get_candidates(char *aggname, Oid typeId, CandidateList *candidates);static Oid agg_select_candidate(Oid typeid, CandidateList candidates);#define ISCOMPLEX(type) (typeidTypeRelid(type) ? true : false)typedef struct _SuperQE{ Oid sqe_relid;} SuperQE;/* ** 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 *curr_resno, 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)), lcons(param, NIL), curr_resno, precedence); } else { Ident *ident = makeNode(Ident); ident->name = attr->relname; ident->isRel = TRUE; retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)), lcons(ident, NIL), curr_resno, precedence); } /* Do more attributes follow this one? */ foreach(mutator_iter, lnext(attr->attrs)) { retval = ParseFuncOrColumn(pstate, strVal(lfirst(mutator_iter)), lcons(retval, NIL), curr_resno, precedence); } return retval;}static intagg_get_candidates(char *aggname, Oid typeId, CandidateList *candidates){ CandidateList current_candidate; Relation pg_aggregate_desc; HeapScanDesc pg_aggregate_scan; HeapTuple tup; Form_pg_aggregate agg; int ncandidates = 0; static ScanKeyData aggKey[1] = { {0, Anum_pg_aggregate_aggname, F_NAMEEQ}}; *candidates = NULL; fmgr_info(F_NAMEEQ, (FmgrInfo *) &aggKey[0].sk_func); aggKey[0].sk_argument = NameGetDatum(aggname); pg_aggregate_desc = heap_openr(AggregateRelationName); pg_aggregate_scan = heap_beginscan(pg_aggregate_desc, 0, SnapshotSelf, /* ??? */ 1, aggKey); while (HeapTupleIsValid(tup = heap_getnext(pg_aggregate_scan, 0))) { current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList)); current_candidate->args = (Oid *) palloc(sizeof(Oid)); agg = (Form_pg_aggregate) GETSTRUCT(tup); current_candidate->args[0] = agg->aggbasetype; current_candidate->next = *candidates; *candidates = current_candidate; ncandidates++; } heap_endscan(pg_aggregate_scan); heap_close(pg_aggregate_desc); return ncandidates;} /* agg_get_candidates() *//* agg_select_candidate() * Try to choose only one candidate aggregate function from a list of possibles. */static Oidagg_select_candidate(Oid typeid, CandidateList candidates){ CandidateList current_candidate; CandidateList last_candidate; Oid current_typeid; int ncandidates; CATEGORY category, current_category;/* * Look for candidates which allow coersion 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... */ else if (last_candidate != NULL) last_candidate->next = NULL; } return ((ncandidates == 1) ? candidates->args[0] : 0);} /* agg_select_candidate() *//* * parse function */Node *ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, int *curr_resno, int precedence){ Oid rettype = InvalidOid; Oid argrelid = InvalidOid; Oid funcid = InvalidOid; List *i = NIL; Node *first_arg = NULL; char *relname = NULL; char *refname = NULL; Relation rd; Oid relid; int nargs; Func *funcnode; Oid oid_array[MAXFARGS]; Oid *true_oid_array; Node *retval; bool retset; bool attisset = false; Oid toid = InvalidOid; Expr *expr; if (fargs) { first_arg = lfirst(fargs); if (first_arg == NULL) elog(ERROR, "Function '%s' does not allow NULL input", funcname); } /* * 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. */ /* We only have one parameter */ if (length(fargs) == 1) { /* Is is a plain Relation name from the parser? */ if (nodeTag(first_arg) == T_Ident && ((Ident *) first_arg)->isRel) { RangeTblEntry *rte; Ident *ident = (Ident *) first_arg; /* * first arg is a relation. This could be a projection. */ refname = ident->name; rte = refnameRangeTableEntry(pstate, refname); if (rte == NULL) rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE); relname = rte->relname; relid = rte->relid; /* * If the attr isn't a set, just make a var for it. If it is * a set, treat it like a function and drop through. */ if (get_attnum(relid, funcname) != InvalidAttrNumber) { return (Node *) make_var(pstate, relid, refname, funcname); } 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 * to keep going. */ retval = ParseComplexProjection(pstate, funcname, first_arg, &attisset); if (attisset) { toid = exprType(first_arg); rd = heap_openr(typeidTypeName(toid)); if (RelationIsValid(rd)) { relname = RelationGetRelationName(rd)->data; heap_close(rd); } else elog(ERROR, "Type '%s' is not a relation type", typeidTypeName(toid)); argrelid = typeidTypeRelid(toid); /* * A projection contains either an attribute name or the * "*". */ if ((get_attnum(argrelid, funcname) == InvalidAttrNumber) && strcmp(funcname, "*")) elog(ERROR, "Functions on sets are not yet supported"); } if (retval) return retval; } else { /* * Parsing aggregates. */ Type tp; 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)); /* try for exact match first... */ if (SearchSysCacheTuple(AGGNAME, PointerGetDatum(funcname), ObjectIdGetDatum(basetype), 0, 0)) return (Node *) ParseAgg(pstate, funcname, basetype, fargs, precedence); /* * 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; 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, precedence); } else { elog(ERROR, "Unable to select an 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 ((HeapTupleIsValid(tp = SearchSysCacheTuple(TYPNAME, PointerGetDatum(funcname), 0, 0, 0))) && IS_BINARY_COMPATIBLE(typeTypeId(tp), basetype)) return ((Node *) lfirst(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. */ MemSet(oid_array, 0, MAXFARGS * sizeof(Oid)); nargs = 0; foreach(i, fargs) { int vnum; RangeTblEntry *rte; Node *pair = lfirst(i); if (nodeTag(pair) == T_Ident && ((Ident *) pair)->isRel) { /* * a relation */ refname = ((Ident *) pair)->name; rte = refnameRangeTableEntry(pstate, refname); if (rte == NULL) rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE); relname = rte->relname; vnum = refnameRangeTablePosn(pstate, rte->refname, NULL); /* * for func(relname), the param to the function is the tuple * under consideration. we build a special VarNode to reflect * this -- it has varno set to the correct range table entry, * but has varattno == 0 to signal that the whole tuple is the * argument. */ toid = typeTypeId(typenameType(relname)); /* replace it in the arg list */ lfirst(fargs) = makeVar(vnum, 0, toid, -1, 0, vnum, 0); } else if (!attisset) { /* set functions don't have parameters */ /* * any functiona 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); else toid = exprType(pair); } /* Most of the rest of the parser just assumes that functions do not * have more than MAXFARGS parameters. We have to test here to protect * against array overruns, etc. */ if (nargs >= MAXFARGS) elog(ERROR, "Cannot pass more than %d arguments to a function", MAXFARGS); oid_array[nargs++] = toid; } /* * 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 func_get_detail returns true, the function * exists. otherwise, there was an error. */ if (attisset) { /* we know all of these fields already */ /* * We create a funcnode with a placeholder function SetEval. * SetEval() never actually gets executed. When the function * evaluation routines see it, they use the funcid projected out * from the relation as the actual function to call. 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 (instead of SetEval) and "name" is * projected from its result. */ funcid = F_SETEVAL; rettype = toid; retset = true; true_oid_array = oid_array; } else { bool exists; exists = func_get_detail(funcname, nargs, oid_array, &funcid, &rettype, &retset, &true_oid_array); if (!exists) elog(ERROR, "No such function '%s' with the specified attributes", funcname);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -