/*------------------------------------------------------------------------- * * analyze.c-- * transform the parse tree into a query tree * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.44 1997/09/18 20:20:58 momjian Exp $ * *------------------------------------------------------------------------- */ #include #include #include #include "postgres.h" #include "nodes/nodes.h" #include "nodes/params.h" #include "nodes/primnodes.h" #include "nodes/parsenodes.h" #include "nodes/relation.h" #include "parse.h" /* for AND, OR, etc. */ #include "catalog/pg_type.h" /* for INT4OID, etc. */ #include "catalog/pg_proc.h" #include "utils/elog.h" #include "utils/builtins.h" /* namecmp(), textout() */ #include "utils/lsyscache.h" #include "utils/palloc.h" #include "utils/mcxt.h" #include "utils/syscache.h" #include "utils/acl.h" #include "parser/parse_query.h" #include "parser/parse_state.h" #include "nodes/makefuncs.h" /* for makeResdom(), etc. */ #include "nodes/nodeFuncs.h" #include "commands/sequence.h" #include "optimizer/clauses.h" #include "access/heapam.h" #include "miscadmin.h" #include "port-protos.h" /* strdup() */ /* convert the parse tree into a query tree */ static Query *transformStmt(ParseState *pstate, Node *stmt); static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt); static Query *transformInsertStmt(ParseState *pstate, AppendStmt *stmt); static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt); static Query *transformExtendStmt(ParseState *pstate, ExtendStmt *stmt); static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt); static Query *transformSelectStmt(ParseState *pstate, RetrieveStmt *stmt); static Query *transformUpdateStmt(ParseState *pstate, ReplaceStmt *stmt); static Query *transformCursorStmt(ParseState *pstate, CursorStmt *stmt); static Node *handleNestedDots(ParseState *pstate, Attr *attr, int *curr_resno); #define EXPR_COLUMN_FIRST 1 #define EXPR_RELATION_FIRST 2 static Node *transformExpr(ParseState *pstate, Node *expr, int precedence); static Node *transformIdent(ParseState *pstate, Node *expr, int precedence); static void makeRangeTable(ParseState *pstate, char *relname, List *frmList); static List *expandAllTables(ParseState *pstate); static char *figureColname(Node *expr, Node *resval); static List *makeTargetNames(ParseState *pstate, List *cols); static List *transformTargetList(ParseState *pstate, List *targetlist); static TargetEntry * make_targetlist_expr(ParseState *pstate, char *colname, Node *expr, List *arrayRef); static bool inWhereClause = false; static Node *transformWhereClause(ParseState *pstate, Node *a_expr); static List * transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist); static List * transformSortClause(ParseState *pstate, List *orderlist, List *targetlist, char *uniqueFlag); static void parseFromClause(ParseState *pstate, List *frmList); static Node * ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno); static List *setup_tlist(char *attname, Oid relid); static List *setup_base_tlist(Oid typeid); static void make_arguments(int nargs, List *fargs, Oid *input_typeids, Oid *function_typeids); static void AddAggToParseState(ParseState *pstate, Aggreg *aggreg); static void finalizeAggregates(ParseState *pstate, Query *qry); static void parseCheckAggregates(ParseState *pstate, Query *qry); static ParseState *makeParseState(void); /***************************************************************************** * *****************************************************************************/ /* * makeParseState() -- * allocate and initialize a new ParseState. * the CALLERS is responsible for freeing the ParseState* returned * */ static ParseState * makeParseState(void) { ParseState *pstate; pstate = malloc(sizeof(ParseState)); pstate->p_last_resno = 1; pstate->p_rtable = NIL; pstate->p_numAgg = 0; pstate->p_aggs = NIL; pstate->p_is_insert = false; pstate->p_insert_columns = NIL; pstate->p_is_update = false; pstate->p_is_rule = false; pstate->p_target_relation = NULL; pstate->p_target_rangetblentry = NULL; return (pstate); } /* * parse_analyze - * analyze a list of parse trees and transform them if necessary. * * Returns a list of transformed parse trees. Optimizable statements are * all transformed to Query while the rest stays the same. * * CALLER is responsible for freeing the QueryTreeList* returned */ QueryTreeList * parse_analyze(List *pl) { QueryTreeList *result; ParseState *pstate; int i = 0; result = malloc(sizeof(QueryTreeList)); result->len = length(pl); result->qtrees = (Query **) malloc(result->len * sizeof(Query *)); inWhereClause = false; /* to avoid nextval(sequence) in WHERE */ while (pl != NIL) { pstate = makeParseState(); result->qtrees[i++] = transformStmt(pstate, lfirst(pl)); pl = lnext(pl); if (pstate->p_target_relation != NULL) heap_close(pstate->p_target_relation); free(pstate); } return result; } /* * transformStmt - * transform a Parse tree. If it is an optimizable statement, turn it * into a Query tree. */ static Query * transformStmt(ParseState *pstate, Node *parseTree) { Query *result = NULL; switch (nodeTag(parseTree)) { /*------------------------ * Non-optimizable statements *------------------------ */ case T_IndexStmt: result = transformIndexStmt(pstate, (IndexStmt *) parseTree); break; case T_ExtendStmt: result = transformExtendStmt(pstate, (ExtendStmt *) parseTree); break; case T_RuleStmt: result = transformRuleStmt(pstate, (RuleStmt *) parseTree); break; case T_ViewStmt: { ViewStmt *n = (ViewStmt *) parseTree; n->query = (Query *) transformStmt(pstate, (Node *) n->query); result = makeNode(Query); result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) n; } break; case T_VacuumStmt: { MemoryContext oldcontext; /* * make sure that this Query is allocated in TopMemory * context because vacuum spans transactions and we don't * want to lose the vacuum Query due to end-of-transaction * free'ing */ oldcontext = MemoryContextSwitchTo(TopMemoryContext); result = makeNode(Query); result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) parseTree; MemoryContextSwitchTo(oldcontext); break; } case T_ExplainStmt: { ExplainStmt *n = (ExplainStmt *) parseTree; result = makeNode(Query); result->commandType = CMD_UTILITY; n->query = transformStmt(pstate, (Node *) n->query); result->utilityStmt = (Node *) parseTree; } break; /*------------------------ * Optimizable statements *------------------------ */ case T_AppendStmt: result = transformInsertStmt(pstate, (AppendStmt *) parseTree); break; case T_DeleteStmt: result = transformDeleteStmt(pstate, (DeleteStmt *) parseTree); break; case T_ReplaceStmt: result = transformUpdateStmt(pstate, (ReplaceStmt *) parseTree); break; case T_CursorStmt: result = transformCursorStmt(pstate, (CursorStmt *) parseTree); break; case T_RetrieveStmt: result = transformSelectStmt(pstate, (RetrieveStmt *) parseTree); break; default: /* * other statments don't require any transformation-- just * return the original parsetree */ result = makeNode(Query); result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) parseTree; break; } return result; } /* * transformDeleteStmt - * transforms a Delete Statement */ static Query * transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) { Query *qry = makeNode(Query); qry->commandType = CMD_DELETE; /* set up a range table */ makeRangeTable(pstate, stmt->relname, NULL); qry->uniqueFlag = NULL; /* fix where clause */ qry->qual = transformWhereClause(pstate, stmt->whereClause); qry->rtable = pstate->p_rtable; qry->resultRelation = refnameRangeTablePosn(pstate->p_rtable, stmt->relname); /* make sure we don't have aggregates in the where clause */ if (pstate->p_numAgg > 0) parseCheckAggregates(pstate, qry); return (Query *) qry; } /* * transformInsertStmt - * transform an Insert Statement */ static Query * transformInsertStmt(ParseState *pstate, AppendStmt *stmt) { Query *qry = makeNode(Query); /* make a new query tree */ qry->commandType = CMD_INSERT; pstate->p_is_insert = true; /* set up a range table */ makeRangeTable(pstate, stmt->relname, stmt->fromClause); qry->uniqueFlag = NULL; /* fix the target list */ pstate->p_insert_columns = makeTargetNames(pstate, stmt->cols); qry->targetList = transformTargetList(pstate, stmt->targetList); /* fix where clause */ qry->qual = transformWhereClause(pstate, stmt->whereClause); /* now the range table will not change */ qry->rtable = pstate->p_rtable; qry->resultRelation = refnameRangeTablePosn(pstate->p_rtable, stmt->relname); if (pstate->p_numAgg > 0) finalizeAggregates(pstate, qry); return (Query *) qry; } /* * transformIndexStmt - * transforms the qualification of the index statement */ static Query * transformIndexStmt(ParseState *pstate, IndexStmt *stmt) { Query *q; q = makeNode(Query); q->commandType = CMD_UTILITY; /* take care of the where clause */ stmt->whereClause = transformWhereClause(pstate, stmt->whereClause); stmt->rangetable = pstate->p_rtable; q->utilityStmt = (Node *) stmt; return q; } /* * transformExtendStmt - * transform the qualifications of the Extend Index Statement * */ static Query * transformExtendStmt(ParseState *pstate, ExtendStmt *stmt) { Query *q; q = makeNode(Query); q->commandType = CMD_UTILITY; /* take care of the where clause */ stmt->whereClause = transformWhereClause(pstate, stmt->whereClause); stmt->rangetable = pstate->p_rtable; q->utilityStmt = (Node *) stmt; return q; } /* * transformRuleStmt - * transform a Create Rule Statement. The actions is a list of parse * trees which is transformed into a list of query trees. */ static Query * transformRuleStmt(ParseState *pstate, RuleStmt *stmt) { Query *q; List *actions; q = makeNode(Query); q->commandType = CMD_UTILITY; actions = stmt->actions; /* * transform each statment, like parse_analyze() */ while (actions != NIL) { /* * NOTE: 'CURRENT' must always have a varno equal to 1 and 'NEW' * equal to 2. */ addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*", FALSE, FALSE, NULL); addRangeTableEntry(pstate, stmt->object->relname, "*NEW*", FALSE, FALSE, NULL); pstate->p_last_resno = 1; pstate->p_is_rule = true; /* for expand all */ pstate->p_numAgg = 0; pstate->p_aggs = NULL; lfirst(actions) = transformStmt(pstate, lfirst(actions)); actions = lnext(actions); } /* take care of the where clause */ stmt->whereClause = transformWhereClause(pstate, stmt->whereClause); q->utilityStmt = (Node *) stmt; return q; } /* * transformSelectStmt - * transforms a Select Statement * */ static Query * transformSelectStmt(ParseState *pstate, RetrieveStmt *stmt) { Query *qry = makeNode(Query); qry->commandType = CMD_SELECT; /* set up a range table */ makeRangeTable(pstate, NULL, stmt->fromClause); qry->uniqueFlag = stmt->unique; qry->into = stmt->into; qry->isPortal = FALSE; /* fix the target list */ qry->targetList = transformTargetList(pstate, stmt->targetList); /* fix where clause */ qry->qual = transformWhereClause(pstate, stmt->whereClause); /* check subselect clause */ if (stmt->selectClause) elog(NOTICE, "UNION not yet supported; using first SELECT only", NULL); /* check subselect clause */ if (stmt->havingClause) elog(NOTICE, "HAVING not yet supported; ignore clause", NULL); /* fix order clause */ qry->sortClause = transformSortClause(pstate, stmt->sortClause, qry->targetList, qry->uniqueFlag); /* fix group by clause */ qry->groupClause = transformGroupClause(pstate, stmt->groupClause, qry->targetList); qry->rtable = pstate->p_rtable; if (pstate->p_numAgg > 0) finalizeAggregates(pstate, qry); return (Query *) qry; } /* * transformUpdateStmt - * transforms an update statement * */ static Query * transformUpdateStmt(ParseState *pstate, ReplaceStmt *stmt) { Query *qry = makeNode(Query); qry->commandType = CMD_UPDATE; pstate->p_is_update = true; /* * the FROM clause is non-standard SQL syntax. We used to be able to * do this with REPLACE in POSTQUEL so we keep the feature. */ makeRangeTable(pstate, stmt->relname, stmt->fromClause); /* fix the target list */ qry->targetList = transformTargetList(pstate, stmt->targetList); /* fix where clause */ qry->qual = transformWhereClause(pstate, stmt->whereClause); qry->rtable = pstate->p_rtable; qry->resultRelation = refnameRangeTablePosn(pstate->p_rtable, stmt->relname); /* make sure we don't have aggregates in the where clause */ if (pstate->p_numAgg > 0) parseCheckAggregates(pstate, qry); return (Query *) qry; } /* * transformCursorStmt - * transform a Create Cursor Statement * */ static Query * transformCursorStmt(ParseState *pstate, CursorStmt *stmt) { Query *qry = makeNode(Query); /* * in the old days, a cursor statement is a 'retrieve into portal'; If * you change the following, make sure you also go through the code in * various places that tests the kind of operation. */ qry->commandType = CMD_SELECT; /* set up a range table */ makeRangeTable(pstate, NULL, stmt->fromClause); qry->uniqueFlag = stmt->unique; qry->into = stmt->portalname; qry->isPortal = TRUE; qry->isBinary = stmt->binary; /* internal portal */ /* fix the target list */ qry->targetList = transformTargetList(pstate, stmt->targetList); /* fix where clause */ qry->qual = transformWhereClause(pstate, stmt->whereClause); /* fix order clause */ qry->sortClause = transformSortClause(pstate, stmt->sortClause, qry->targetList, qry->uniqueFlag); /* fix group by clause */ qry->groupClause = transformGroupClause(pstate, stmt->groupClause, qry->targetList); qry->rtable = pstate->p_rtable; if (pstate->p_numAgg > 0) finalizeAggregates(pstate, qry); return (Query *) qry; } /***************************************************************************** * * Transform Exprs, Aggs, etc. * *****************************************************************************/ /* * transformExpr - * analyze and transform expressions. Type checking and type casting is * done here. The optimizer and the executor cannot handle the original * (raw) expressions collected by the parse tree. Hence the transformation * here. */ static Node * transformExpr(ParseState *pstate, Node *expr, int precedence) { Node *result = NULL; if (expr == NULL) return NULL; switch (nodeTag(expr)) { case T_Attr: { Attr *att = (Attr *) expr; Node *temp; /* what if att.attrs == "*"?? */ temp = handleNestedDots(pstate, att, &pstate->p_last_resno); if (att->indirection != NIL) { List *idx = att->indirection; while (idx != NIL) { A_Indices *ai = (A_Indices *) lfirst(idx); Node *lexpr = NULL, *uexpr; uexpr = transformExpr(pstate, ai->uidx, precedence); /* must exists */ if (exprType(uexpr) != INT4OID) elog(WARN, "array index expressions must be int4's"); if (ai->lidx != NULL) { lexpr = transformExpr(pstate, ai->lidx, precedence); if (exprType(lexpr) != INT4OID) elog(WARN, "array index expressions must be int4's"); } #if 0 pfree(ai->uidx); if (ai->lidx != NULL) pfree(ai->lidx); #endif ai->lidx = lexpr; ai->uidx = uexpr; /* * note we reuse the list of indices, make sure we * don't free them! Otherwise, make a new list * here */ idx = lnext(idx); } result = (Node *) make_array_ref(temp, att->indirection); } else { result = temp; } break; } case T_A_Const: { A_Const *con = (A_Const *) expr; Value *val = &con->val; if (con->typename != NULL) { result = parser_typecast(val, con->typename, -1); } else { result = (Node *) make_const(val); } break; } case T_ParamNo: { ParamNo *pno = (ParamNo *) expr; Oid toid; int paramno; Param *param; paramno = pno->number; toid = param_type(paramno); if (!OidIsValid(toid)) { elog(WARN, "Parameter '$%d' is out of range", paramno); } param = makeNode(Param); param->paramkind = PARAM_NUM; param->paramid = (AttrNumber) paramno; param->paramname = ""; param->paramtype = (Oid) toid; param->param_tlist = (List *) NULL; result = (Node *) param; break; } case T_A_Expr: { A_Expr *a = (A_Expr *) expr; switch (a->oper) { case OP: { Node *lexpr = transformExpr(pstate, a->lexpr, precedence); Node *rexpr = transformExpr(pstate, a->rexpr, precedence); result = (Node *) make_op(a->opname, lexpr, rexpr); } break; case ISNULL: { Node *lexpr = transformExpr(pstate, a->lexpr, precedence); result = ParseFunc(pstate, "nullvalue", lcons(lexpr, NIL), &pstate->p_last_resno); } break; case NOTNULL: { Node *lexpr = transformExpr(pstate, a->lexpr, precedence); result = ParseFunc(pstate, "nonnullvalue", lcons(lexpr, NIL), &pstate->p_last_resno); } break; case AND: { Expr *expr = makeNode(Expr); Node *lexpr = transformExpr(pstate, a->lexpr, precedence); Node *rexpr = transformExpr(pstate, a->rexpr, precedence); if (exprType(lexpr) != BOOLOID) elog(WARN, "left-hand side of AND is type '%s', not bool", tname(get_id_type(exprType(lexpr)))); if (exprType(rexpr) != BOOLOID) elog(WARN, "right-hand side of AND is type '%s', not bool", tname(get_id_type(exprType(rexpr)))); expr->typeOid = BOOLOID; expr->opType = AND_EXPR; expr->args = makeList(lexpr, rexpr, -1); result = (Node *) expr; } break; case OR: { Expr *expr = makeNode(Expr); Node *lexpr = transformExpr(pstate, a->lexpr, precedence); Node *rexpr = transformExpr(pstate, a->rexpr, precedence); if (exprType(lexpr) != BOOLOID) elog(WARN, "left-hand side of OR is type '%s', not bool", tname(get_id_type(exprType(lexpr)))); if (exprType(rexpr) != BOOLOID) elog(WARN, "right-hand side of OR is type '%s', not bool", tname(get_id_type(exprType(rexpr)))); expr->typeOid = BOOLOID; expr->opType = OR_EXPR; expr->args = makeList(lexpr, rexpr, -1); result = (Node *) expr; } break; case NOT: { Expr *expr = makeNode(Expr); Node *rexpr = transformExpr(pstate, a->rexpr, precedence); if (exprType(rexpr) != BOOLOID) elog(WARN, "argument to NOT is type '%s', not bool", tname(get_id_type(exprType(rexpr)))); expr->typeOid = BOOLOID; expr->opType = NOT_EXPR; expr->args = makeList(rexpr, -1); result = (Node *) expr; } break; } break; } case T_Ident: { /* * look for a column name or a relation name (the default * behavior) */ result = transformIdent(pstate, expr, precedence); break; } case T_FuncCall: { FuncCall *fn = (FuncCall *) expr; List *args; /* transform the list of arguments */ foreach(args, fn->args) lfirst(args) = transformExpr(pstate, (Node *) lfirst(args), precedence); result = ParseFunc(pstate, fn->funcname, fn->args, &pstate->p_last_resno); break; } default: /* should not reach here */ elog(WARN, "transformExpr: does not know how to transform %d\n", nodeTag(expr)); break; } return result; } static Node * transformIdent(ParseState *pstate, Node *expr, int precedence) { Ident *ident = (Ident *) expr; RangeTblEntry *rte; Node *column_result, *relation_result, *result; column_result = relation_result = result = 0; /* try to find the ident as a column */ if ((rte = colnameRangeTableEntry(pstate, ident->name)) != NULL) { Attr *att = makeNode(Attr); att->relname = rte->refname; att->attrs = lcons(makeString(ident->name), NIL); column_result = (Node *) handleNestedDots(pstate, att, &pstate->p_last_resno); } /* try to find the ident as a relation */ if (refnameRangeTableEntry(pstate->p_rtable, ident->name) != NULL) { ident->isRel = TRUE; relation_result = (Node *) ident; } /* choose the right result based on the precedence */ if (precedence == EXPR_COLUMN_FIRST) { if (column_result) result = column_result; else result = relation_result; } else { if (relation_result) result = relation_result; else result = column_result; } if (result == NULL) elog(WARN, "attribute \"%s\" not found", ident->name); return result; } /***************************************************************************** * * From Clause * *****************************************************************************/ /* * parseFromClause - * turns the table references specified in the from-clause into a * range table. The range table may grow as we transform the expressions * in the target list. (Note that this happens because in POSTQUEL, we * allow references to relations not specified in the from-clause. We * also allow that in our POST-SQL) * */ static void parseFromClause(ParseState *pstate, List *frmList) { List *fl; foreach(fl, frmList) { RangeVar *r = lfirst(fl); RelExpr *baserel = r->relExpr; char *relname = baserel->relname; char *refname = r->name; RangeTblEntry *rte; if (refname == NULL) refname = relname; /* * marks this entry to indicate it comes from the FROM clause. In * SQL, the target list can only refer to range variables * specified in the from clause but we follow the more powerful * POSTQUEL semantics and automatically generate the range * variable if not specified. However there are times we need to * know whether the entries are legitimate. * * eg. select * from foo f where f.x = 1; will generate wrong answer * if we expand * to foo.x. */ rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE, baserel->timeRange); } } /* * makeRangeTable - * make a range table with the specified relation (optional) and the * from-clause. */ static void makeRangeTable(ParseState *pstate, char *relname, List *frmList) { RangeTblEntry *rte; parseFromClause(pstate, frmList); if (relname == NULL) return; if (refnameRangeTablePosn(pstate->p_rtable, relname) < 1) rte = addRangeTableEntry(pstate, relname, relname, FALSE, FALSE, NULL); else rte = refnameRangeTableEntry(pstate->p_rtable, relname); pstate->p_target_rangetblentry = rte; Assert(pstate->p_target_relation == NULL); pstate->p_target_relation = heap_open(rte->relid); Assert(pstate->p_target_relation != NULL); /* will close relation later */ } /* * exprType - * returns the Oid of the type of the expression. (Used for typechecking.) */ Oid exprType(Node *expr) { Oid type = (Oid) 0; switch (nodeTag(expr)) { case T_Func: type = ((Func *) expr)->functype; break; case T_Iter: type = ((Iter *) expr)->itertype; break; case T_Var: type = ((Var *) expr)->vartype; break; case T_Expr: type = ((Expr *) expr)->typeOid; break; case T_Const: type = ((Const *) expr)->consttype; break; case T_ArrayRef: type = ((ArrayRef *) expr)->refelemtype; break; case T_Aggreg: type = ((Aggreg *) expr)->aggtype; break; case T_Param: type = ((Param *) expr)->paramtype; break; case T_Ident: /* is this right? */ type = UNKNOWNOID; break; default: elog(WARN, "exprType: don't know how to get type for %d node", nodeTag(expr)); break; } return type; } /* * expandAllTables - * turns '*' (in the target list) into a list of attributes (of all * relations in the range table) */ static List * expandAllTables(ParseState *pstate) { List *target = NIL; List *legit_rtable = NIL; List *rt, *rtable; rtable = pstate->p_rtable; if (pstate->p_is_rule) { /* * skip first two entries, "*new*" and "*current*" */ rtable = lnext(lnext(pstate->p_rtable)); } /* this should not happen */ if (rtable == NULL) elog(WARN, "cannot expand: null p_rtable"); /* * go through the range table and make a list of range table entries * which we will expand. */ foreach(rt, rtable) { RangeTblEntry *rte = lfirst(rt); /* * we only expand those specify in the from clause. (This will * also prevent us from using the wrong table in inserts: eg. * tenk2 in "insert into tenk2 select * from tenk1;") */ if (!rte->inFromCl) continue; legit_rtable = lappend(legit_rtable, rte); } foreach(rt, legit_rtable) { RangeTblEntry *rte = lfirst(rt); List *temp = target; if (temp == NIL) target = expandAll(pstate, rte->relname, rte->refname, &pstate->p_last_resno); else { while (temp != NIL && lnext(temp) != NIL) temp = lnext(temp); lnext(temp) = expandAll(pstate, rte->relname, rte->refname, &pstate->p_last_resno); } } return target; } /* * figureColname - * if the name of the resulting column is not specified in the target * list, we have to guess. * */ static char * figureColname(Node *expr, Node *resval) { switch (nodeTag(expr)) { case T_Aggreg: return (char *) /* XXX */ ((Aggreg *) expr)->aggname; case T_Expr: if (((Expr *) expr)->opType == FUNC_EXPR) { if (nodeTag(resval) == T_FuncCall) return ((FuncCall *) resval)->funcname; } break; default: break; } return "?column?"; } /***************************************************************************** * * Target list * *****************************************************************************/ /* * makeTargetNames - * generate a list of column names if not supplied or * test supplied column names to make sure they are in target table * (used exclusively for inserts) */ static List * makeTargetNames(ParseState *pstate, List *cols) { List *tl = NULL; /* Generate ResTarget if not supplied */ if (cols == NIL) { int numcol; int i; AttributeTupleForm *attr = pstate->p_target_relation->rd_att->attrs; numcol = pstate->p_target_relation->rd_rel->relnatts; for (i = 0; i < numcol; i++) { Ident *id = makeNode(Ident); id->name = palloc(NAMEDATALEN); strNcpy(id->name, attr[i]->attname.data, NAMEDATALEN - 1); id->indirection = NIL; id->isRel = false; if (tl == NIL) cols = tl = lcons(id, NIL); else { lnext(tl) = lcons(id, NIL); tl = lnext(tl); } } } else foreach(tl, cols) /* elog on failure */ varattno(pstate->p_target_relation, ((Ident *) lfirst(tl))->name); return cols; } /* * transformTargetList - * turns a list of ResTarget's into a list of TargetEntry's */ static List * transformTargetList(ParseState *pstate, List *targetlist) { List *p_target = NIL; List *tail_p_target = NIL; while (targetlist != NIL) { ResTarget *res = (ResTarget *) lfirst(targetlist); TargetEntry *tent = makeNode(TargetEntry); switch (nodeTag(res->val)) { case T_Ident: { Node *expr; Oid type_id; int type_len; char *identname; char *resname; identname = ((Ident *) res->val)->name; handleTargetColname(pstate, &res->name, NULL, identname); /* * here we want to look for column names only, not * relation */ /* * names (even though they can be stored in Ident * nodes, */ /* too) */ expr = transformIdent(pstate, (Node *) res->val, EXPR_COLUMN_FIRST); type_id = exprType(expr); type_len = tlen(get_id_type(type_id)); resname = (res->name) ? res->name : identname; tent->resdom = makeResdom((AttrNumber) pstate->p_last_resno++, (Oid) type_id, (Size) type_len, resname, (Index) 0, (Oid) 0, 0); tent->expr = expr; break; } case T_ParamNo: case T_FuncCall: case T_A_Const: case T_A_Expr: { Node *expr = transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST); handleTargetColname(pstate, &res->name, NULL, NULL); /* note indirection has not been transformed */ if (pstate->p_is_insert && res->indirection != NIL) { /* this is an array assignment */ char *val; char *str, *save_str; List *elt; int i = 0, ndims; int lindx[MAXDIM], uindx[MAXDIM]; int resdomno; Relation rd; Value *constval; if (exprType(expr) != UNKNOWNOID || !IsA(expr, Const)) elog(WARN, "yyparse: string constant expected"); val = (char *) textout((struct varlena *) ((Const *) expr)->constvalue); str = save_str = (char *) palloc(strlen(val) + MAXDIM * 25 + 2); foreach(elt, res->indirection) { A_Indices *aind = (A_Indices *) lfirst(elt); aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST); if (!IsA(aind->uidx, Const)) elog(WARN, "Array Index for Append should be a constant"); uindx[i] = ((Const *) aind->uidx)->constvalue; if (aind->lidx != NULL) { aind->lidx = transformExpr(pstate, aind->lidx, EXPR_COLUMN_FIRST); if (!IsA(aind->lidx, Const)) elog(WARN, "Array Index for Append should be a constant"); lindx[i] = ((Const *) aind->lidx)->constvalue; } else { lindx[i] = 1; } if (lindx[i] > uindx[i]) elog(WARN, "yyparse: lower index cannot be greater than upper index"); sprintf(str, "[%d:%d]", lindx[i], uindx[i]); str += strlen(str); i++; } sprintf(str, "=%s", val); rd = pstate->p_target_relation; Assert(rd != NULL); resdomno = varattno(rd, res->name); ndims = att_attnelems(rd, resdomno); if (i != ndims) elog(WARN, "yyparse: array dimensions do not match"); constval = makeNode(Value); constval->type = T_String; constval->val.str = save_str; tent = make_targetlist_expr(pstate, res->name, (Node *) make_const(constval), NULL); pfree(save_str); } else { char *colname = res->name; /* this is not an array assignment */ if (colname == NULL) { /* * if you're wondering why this is here, look * at the yacc grammar for why a name can be * missing. -ay */ colname = figureColname(expr, res->val); } if (res->indirection) { List *ilist = res->indirection; while (ilist != NIL) { A_Indices *ind = lfirst(ilist); ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST); ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST); ilist = lnext(ilist); } } res->name = colname; tent = make_targetlist_expr(pstate, res->name, expr, res->indirection); } break; } case T_Attr: { Oid type_id; int type_len; Attr *att = (Attr *) res->val; Node *result; char *attrname; char *resname; Resdom *resnode; List *attrs = att->attrs; /* * Target item is a single '*', expand all tables (eg. * SELECT * FROM emp) */ if (att->relname != NULL && !strcmp(att->relname, "*")) { if (tail_p_target == NIL) p_target = tail_p_target = expandAllTables(pstate); else lnext(tail_p_target) = expandAllTables(pstate); while (lnext(tail_p_target) != NIL) /* make sure we point to the last target entry */ tail_p_target = lnext(tail_p_target); /* * skip rest of while loop */ targetlist = lnext(targetlist); continue; } /* * Target item is relation.*, expand the table (eg. * SELECT emp.*, dname FROM emp, dept) */ attrname = strVal(lfirst(att->attrs)); if (att->attrs != NIL && !strcmp(attrname, "*")) { /* * tail_p_target is the target list we're building * in the while loop. Make sure we fix it after * appending more nodes. */ if (tail_p_target == NIL) p_target = tail_p_target = expandAll(pstate, att->relname, att->relname, &pstate->p_last_resno); else lnext(tail_p_target) = expandAll(pstate, att->relname, att->relname, &pstate->p_last_resno); while (lnext(tail_p_target) != NIL) /* make sure we point to the last target entry */ tail_p_target = lnext(tail_p_target); /* * skip the rest of the while loop */ targetlist = lnext(targetlist); continue; } /* * Target item is fully specified: ie. * relation.attribute */ result = handleNestedDots(pstate, att, &pstate->p_last_resno); handleTargetColname(pstate, &res->name, att->relname, attrname); if (att->indirection != NIL) { List *ilist = att->indirection; while (ilist != NIL) { A_Indices *ind = lfirst(ilist); ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST); ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST); ilist = lnext(ilist); } result = (Node *) make_array_ref(result, att->indirection); } type_id = exprType(result); type_len = tlen(get_id_type(type_id)); /* move to last entry */ while (lnext(attrs) != NIL) attrs = lnext(attrs); resname = (res->name) ? res->name : strVal(lfirst(attrs)); resnode = makeResdom((AttrNumber) pstate->p_last_resno++, (Oid) type_id, (Size) type_len, resname, (Index) 0, (Oid) 0, 0); tent->resdom = resnode; tent->expr = result; break; } default: /* internal error */ elog(WARN, "internal error: do not know how to transform targetlist"); break; } if (p_target == NIL) { p_target = tail_p_target = lcons(tent, NIL); } else { lnext(tail_p_target) = lcons(tent, NIL); tail_p_target = lnext(tail_p_target); } targetlist = lnext(targetlist); } return p_target; } /* * make_targetlist_expr - * make a TargetEntry from an expression * * arrayRef is a list of transformed A_Indices */ static TargetEntry * make_targetlist_expr(ParseState *pstate, char *colname, Node *expr, List *arrayRef) { Oid type_id, attrtype; int type_len, attrlen; int resdomno; Relation rd; bool attrisset; TargetEntry *tent; Resdom *resnode; if (expr == NULL) elog(WARN, "make_targetlist_expr: invalid use of NULL expression"); type_id = exprType(expr); if (type_id == InvalidOid) { type_len = 0; } else type_len = tlen(get_id_type(type_id)); /* I have no idea what the following does! */ /* It appears to process target columns that will be receiving results */ if (pstate->p_is_insert || pstate->p_is_update) { /* * append or replace query -- append, replace work only on one * relation, so multiple occurence of same resdomno is bogus */ rd = pstate->p_target_relation; Assert(rd != NULL); resdomno = varattno(rd, colname); attrisset = varisset(rd, colname); attrtype = att_typeid(rd, resdomno); if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL)) attrtype = GetArrayElementType(attrtype); if (attrtype == BPCHAROID || attrtype == VARCHAROID) { attrlen = rd->rd_att->attrs[resdomno - 1]->attlen; } else { attrlen = tlen(get_id_type(attrtype)); } #if 0 if (Input_is_string && Typecast_ok) { Datum val; if (type_id == typeid(type("unknown"))) { val = (Datum) textout((struct varlena *) ((Const) lnext(expr))->constvalue); } else { val = ((Const) lnext(expr))->constvalue; } if (attrisset) { lnext(expr) = makeConst(attrtype, attrlen, val, false, true, true, /* is set */ false); } else { lnext(expr) = makeConst(attrtype, attrlen, (Datum) fmgr(typeid_get_retinfunc(attrtype), val, get_typelem(attrtype), -1), false, true /* Maybe correct-- 80% chance */ , false, /* is not a set */ false); } } else if ((Typecast_ok) && (attrtype != type_id)) { lnext(expr) = parser_typecast2(expr, get_id_type(attrtype)); } else if (attrtype != type_id) { if ((attrtype == INT2OID) && (type_id == INT4OID)) lfirst(expr) = lispInteger(INT2OID); /* handle CASHOID too */ else if ((attrtype == FLOAT4OID) && (type_id == FLOAT8OID)) lfirst(expr) = lispInteger(FLOAT4OID); else elog(WARN, "unequal type in tlist : %s \n", colname); } Input_is_string = false; Input_is_integer = false; Typecast_ok = true; #endif if (attrtype != type_id) { if (IsA(expr, Const)) { /* try to cast the constant */ if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx)) { /* updating a single item */ Oid typelem = get_typelem(attrtype); expr = (Node *) parser_typecast2(expr, type_id, get_id_type(typelem), attrlen); } else expr = (Node *) parser_typecast2(expr, type_id, get_id_type(attrtype), attrlen); } else { /* currently, we can't handle casting of expressions */ elog(WARN, "parser: attribute '%s' is of type '%s' but expression is of type '%s'", colname, get_id_typname(attrtype), get_id_typname(type_id)); } } if (arrayRef != NIL) { Expr *target_expr; Attr *att = makeNode(Attr); List *ar = arrayRef; List *upperIndexpr = NIL; List *lowerIndexpr = NIL; att->relname = pstrdup(RelationGetRelationName(rd)->data); att->attrs = lcons(makeString(colname), NIL); target_expr = (Expr *) handleNestedDots(pstate, att, &pstate->p_last_resno); while (ar != NIL) { A_Indices *ind = lfirst(ar); if (lowerIndexpr || (!upperIndexpr && ind->lidx)) { /* * XXX assume all lowerIndexpr is non-null in this * case */ lowerIndexpr = lappend(lowerIndexpr, ind->lidx); } upperIndexpr = lappend(upperIndexpr, ind->uidx); ar = lnext(ar); } expr = (Node *) make_array_set(target_expr, upperIndexpr, lowerIndexpr, (Expr *) expr); attrtype = att_typeid(rd, resdomno); attrlen = tlen(get_id_type(attrtype)); } } else { resdomno = pstate->p_last_resno++; attrtype = type_id; attrlen = type_len; } tent = makeNode(TargetEntry); resnode = makeResdom((AttrNumber) resdomno, (Oid) attrtype, (Size) attrlen, colname, (Index) 0, (Oid) 0, 0); tent->resdom = resnode; tent->expr = expr; return tent; } /***************************************************************************** * * Where Clause * *****************************************************************************/ /* * transformWhereClause - * transforms the qualification and make sure it is of type Boolean * */ static Node * transformWhereClause(ParseState *pstate, Node *a_expr) { Node *qual; if (a_expr == NULL) return (Node *) NULL; /* no qualifiers */ inWhereClause = true; qual = transformExpr(pstate, a_expr, EXPR_COLUMN_FIRST); inWhereClause = false; if (exprType(qual) != BOOLOID) { elog(WARN, "where clause must return type bool, not %s", tname(get_id_type(exprType(qual)))); } return qual; } /***************************************************************************** * * Sort Clause * *****************************************************************************/ /* * find_targetlist_entry - * returns the Resdom in the target list matching the specified varname * and range * */ static TargetEntry * find_targetlist_entry(ParseState *pstate, SortGroupBy *sortgroupby, List *tlist) { List *i; int real_rtable_pos = 0, target_pos = 0; TargetEntry *target_result = NULL; if (sortgroupby->range) real_rtable_pos = refnameRangeTablePosn(pstate->p_rtable, sortgroupby->range); foreach(i, tlist) { TargetEntry *target = (TargetEntry *) lfirst(i); Resdom *resnode = target->resdom; Var *var = (Var *) target->expr; char *resname = resnode->resname; int test_rtable_pos = var->varno; #ifdef PARSEDEBUG printf("find_targetlist_entry- target name is %s, position %d, resno %d\n", (sortgroupby->name ? sortgroupby->name : "(null)"), target_pos + 1, sortgroupby->resno); #endif if (!sortgroupby->name) { if (sortgroupby->resno == ++target_pos) { target_result = target; break; } } else { if (!strcmp(resname, sortgroupby->name)) { if (sortgroupby->range) { if (real_rtable_pos == test_rtable_pos) { if (target_result != NULL) elog(WARN, "Order/Group By %s is ambiguous", sortgroupby->name); else target_result = target; } } else { if (target_result != NULL) elog(WARN, "Order/Group By %s is ambiguous", sortgroupby->name); else target_result = target; } } } } return target_result; } static Oid any_ordering_op(int restype) { Operator order_op; Oid order_opid; order_op = oper("<", restype, restype, false); order_opid = oprid(order_op); return order_opid; } /* * transformGroupClause - * transform a Group By clause * */ static List * transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist) { List *glist = NIL, *gl = NIL; while (grouplist != NIL) { GroupClause *grpcl = makeNode(GroupClause); TargetEntry *restarget; Resdom *resdom; restarget = find_targetlist_entry(pstate, lfirst(grouplist), targetlist); if (restarget == NULL) elog(WARN, "The field being grouped by must appear in the target list"); grpcl->entry = restarget; resdom = restarget->resdom; grpcl->grpOpoid = oprid(oper("<", resdom->restype, resdom->restype, false)); if (glist == NIL) gl = glist = lcons(grpcl, NIL); else { lnext(gl) = lcons(grpcl, NIL); gl = lnext(gl); } grouplist = lnext(grouplist); } return glist; } /* * transformSortClause - * transform an Order By clause * */ static List * transformSortClause(ParseState *pstate, List *orderlist, List *targetlist, char *uniqueFlag) { List *sortlist = NIL; List *s = NIL, *i; while (orderlist != NIL) { SortGroupBy *sortby = lfirst(orderlist); SortClause *sortcl = makeNode(SortClause); TargetEntry *restarget; Resdom *resdom; restarget = find_targetlist_entry(pstate, sortby, targetlist); if (restarget == NULL) elog(WARN, "The field being ordered by must appear in the target list"); sortcl->resdom = resdom = restarget->resdom; sortcl->opoid = oprid(oper(sortby->useOp, resdom->restype, resdom->restype, false)); if (sortlist == NIL) { s = sortlist = lcons(sortcl, NIL); } else { lnext(s) = lcons(sortcl, NIL); s = lnext(s); } orderlist = lnext(orderlist); } if (uniqueFlag) { if (uniqueFlag[0] == '*') { /* * concatenate all elements from target list that are not * already in the sortby list */ foreach(i, targetlist) { TargetEntry *tlelt = (TargetEntry *) lfirst(i); s = sortlist; while (s != NIL) { SortClause *sortcl = lfirst(s); if (sortcl->resdom == tlelt->resdom) break; s = lnext(s); } if (s == NIL) { /* not a member of the sortclauses yet */ SortClause *sortcl = makeNode(SortClause); sortcl->resdom = tlelt->resdom; sortcl->opoid = any_ordering_op(tlelt->resdom->restype); sortlist = lappend(sortlist, sortcl); } } } else { TargetEntry *tlelt = NULL; char *uniqueAttrName = uniqueFlag; /* only create sort clause with the specified unique attribute */ foreach(i, targetlist) { tlelt = (TargetEntry *) lfirst(i); if (strcmp(tlelt->resdom->resname, uniqueAttrName) == 0) break; } if (i == NIL) { elog(WARN, "The field specified in the UNIQUE ON clause is not in the targetlist"); } s = sortlist; foreach(s, sortlist) { SortClause *sortcl = lfirst(s); if (sortcl->resdom == tlelt->resdom) break; } if (s == NIL) { /* not a member of the sortclauses yet */ SortClause *sortcl = makeNode(SortClause); sortcl->resdom = tlelt->resdom; sortcl->opoid = any_ordering_op(tlelt->resdom->restype); sortlist = lappend(sortlist, sortcl); } } } return sortlist; } /* ** HandleNestedDots -- ** Given a nested dot expression (i.e. (relation func ... attr), build up ** a tree with of Iter and Func nodes. */ static Node * handleNestedDots(ParseState *pstate, Attr *attr, int *curr_resno) { List *mutator_iter; Node *retval = NULL; if (attr->paramNo != NULL) { Param *param = (Param *) transformExpr(pstate, (Node *) attr->paramNo, EXPR_RELATION_FIRST); retval = ParseFunc(pstate, strVal(lfirst(attr->attrs)), lcons(param, NIL), curr_resno); } else { Ident *ident = makeNode(Ident); ident->name = attr->relname; ident->isRel = TRUE; retval = ParseFunc(pstate, strVal(lfirst(attr->attrs)), lcons(ident, NIL), curr_resno); } foreach(mutator_iter, lnext(attr->attrs)) { retval = ParseFunc(pstate, strVal(lfirst(mutator_iter)), lcons(retval, NIL), curr_resno); } return (retval); } /* ** make_arguments -- ** Given the number and types of arguments to a function, and the ** actual arguments and argument types, do the necessary typecasting. */ static void make_arguments(int nargs, List *fargs, Oid *input_typeids, Oid *function_typeids) { /* * there are two ways an input typeid can differ from a function * typeid : either the input type inherits the function type, so no * typecasting is necessary, or the input type can be typecast into * the function type. right now, we only typecast unknowns, and that * is all we check for. */ List *current_fargs; int i; for (i = 0, current_fargs = fargs; i < nargs; i++, current_fargs = lnext(current_fargs)) { if (input_typeids[i] == UNKNOWNOID && function_typeids[i] != InvalidOid) { lfirst(current_fargs) = parser_typecast2(lfirst(current_fargs), input_typeids[i], get_id_type(function_typeids[i]), -1); } } } /* ** setup_tlist -- ** Build a tlist that says which attribute to project to. ** This routine is called by ParseFunc() to set up a target list ** on a tuple parameter or return value. Due to a bug in 4.0, ** it's not possible to refer to system attributes in this case. */ static List * setup_tlist(char *attname, Oid relid) { TargetEntry *tle; Resdom *resnode; Var *varnode; Oid typeid; int attno; attno = get_attnum(relid, attname); if (attno < 0) elog(WARN, "cannot reference attribute %s of tuple params/return values for functions", attname); typeid = find_atttype(relid, attname); resnode = makeResdom(1, typeid, tlen(get_id_type(typeid)), get_attname(relid, attno), 0, (Oid) 0, 0); varnode = makeVar(-1, attno, typeid, -1, attno); tle = makeNode(TargetEntry); tle->resdom = resnode; tle->expr = (Node *) varnode; return (lcons(tle, NIL)); } /* ** setup_base_tlist -- ** Build a tlist that extracts a base type from the tuple ** returned by the executor. */ static List * setup_base_tlist(Oid typeid) { TargetEntry *tle; Resdom *resnode; Var *varnode; resnode = makeResdom(1, typeid, tlen(get_id_type(typeid)), "", 0, (Oid) 0, 0); varnode = makeVar(-1, 1, typeid, -1, 1); tle = makeNode(TargetEntry); tle->resdom = resnode; tle->expr = (Node *) varnode; return (lcons(tle, NIL)); } /* * 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). */ static Node * ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg, bool *attisset) { Oid argtype; Oid argrelid; Name relname; Relation rd; Oid relid; int attnum; switch (nodeTag(first_arg)) { case T_Iter: { Func *func; Iter *iter; iter = (Iter *) first_arg; func = (Func *) ((Expr *) iter->iterexpr)->oper; argtype = funcid_get_rettype(func->funcid); argrelid = typeid_get_relid(argtype); if (argrelid && ((attnum = get_attnum(argrelid, funcname)) != InvalidAttrNumber)) { /* * the argument is a function returning a tuple, so * funcname may be a projection */ /* add a tlist to the func node and return the Iter */ rd = heap_openr(tname(get_id_type(argtype))); if (RelationIsValid(rd)) { relid = RelationGetRelationId(rd); relname = RelationGetRelationName(rd); heap_close(rd); } if (RelationIsValid(rd)) { func->func_tlist = setup_tlist(funcname, argrelid); iter->itertype = att_typeid(rd, attnum); return ((Node *) iter); } else { elog(WARN, "Function %s has bad returntype %d", funcname, argtype); } } else { /* drop through */ ; } 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; funcnode = (Func *) expr->oper; argtype = funcid_get_rettype(funcnode->funcid); argrelid = typeid_get_relid(argtype); /* * the argument is a function returning a tuple, so * funcname may be a projection */ if (argrelid && (attnum = get_attnum(argrelid, funcname)) != InvalidAttrNumber) { /* add a tlist to the func node */ rd = heap_openr(tname(get_id_type(argtype))); if (RelationIsValid(rd)) { relid = RelationGetRelationId(rd); relname = RelationGetRelationName(rd); heap_close(rd); } if (RelationIsValid(rd)) { Expr *newexpr; funcnode->func_tlist = setup_tlist(funcname, argrelid); funcnode->functype = att_typeid(rd, attnum); newexpr = makeNode(Expr); newexpr->typeOid = funcnode->functype; newexpr->opType = FUNC_EXPR; newexpr->oper = (Node *) funcnode; newexpr->args = lcons(first_arg, NIL); return ((Node *) newexpr); } } elog(WARN, "Function %s has bad returntype %d", funcname, argtype); break; } case T_Param: { Param *param = (Param *) first_arg; /* * If the Param is a complex type, this could be a * projection */ rd = heap_openr(tname(get_id_type(param->paramtype))); if (RelationIsValid(rd)) { relid = RelationGetRelationId(rd); relname = RelationGetRelationName(rd); heap_close(rd); } if (RelationIsValid(rd) && (attnum = get_attnum(relid, funcname)) != InvalidAttrNumber) { param->paramtype = att_typeid(rd, attnum); param->param_tlist = setup_tlist(funcname, relid); return ((Node *) param); } break; } default: break; } return NULL; } static Node * ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno) { Oid rettype = (Oid) 0; Oid argrelid = (Oid) 0; Oid funcid = (Oid) 0; List *i = NIL; Node *first_arg = NULL; char *relname = NULL; char *refname = NULL; Relation rd; Oid relid; int nargs; Func *funcnode; Oid oid_array[8]; Oid *true_oid_array; Node *retval; bool retset; bool exists; bool attisset = false; Oid toid = (Oid) 0; Expr *expr; if (fargs) { first_arg = lfirst(fargs); if (first_arg == NULL) elog(WARN, "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. */ if (length(fargs) == 1) { 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->p_rtable, refname); if (rte == NULL) rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE, NULL); 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) { Oid dummyTypeId; return ((Node *) make_var(pstate, refname, funcname, &dummyTypeId)); } 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(tname(get_id_type(toid))); if (RelationIsValid(rd)) { relname = RelationGetRelationName(rd)->data; heap_close(rd); } else elog(WARN, "Type %s is not a relation type", tname(get_id_type(toid))); argrelid = typeid_get_relid(toid); /* * A projection contains either an attribute name or the * "*". */ if ((get_attnum(argrelid, funcname) == InvalidAttrNumber) && strcmp(funcname, "*")) { elog(WARN, "Functions on sets are not yet supported"); } } if (retval) return retval; } else { /* * Parsing aggregates. */ Oid basetype; /* * 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)); if (SearchSysCacheTuple(AGGNAME, PointerGetDatum(funcname), ObjectIdGetDatum(basetype), 0, 0)) { Aggreg *aggreg = ParseAgg(funcname, basetype, lfirst(fargs)); AddAggToParseState(pstate, aggreg); return (Node *) aggreg; } } } /* * * 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], 0, 8 * 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->p_rtable, refname); if (rte == NULL) rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE, NULL); relname = rte->relname; vnum = refnameRangeTablePosn(pstate->p_rtable, rte->refname); /* * 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 = typeid(type(relname)); /* replace it in the arg list */ lfirst(fargs) = makeVar(vnum, 0, toid, 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(WARN, "ParseFunc: no function named %s that takes in an unknown type as argument #%d", funcname, nargs); } else toid = exprType(pair); } 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 = SetEvalRegProcedure; rettype = toid; retset = true; true_oid_array = oid_array; exists = true; } else { exists = func_get_detail(funcname, nargs, oid_array, &funcid, &rettype, &retset, &true_oid_array); } if (!exists) elog(WARN, "no such attribute or function %s", funcname); /* got it */ funcnode = makeNode(Func); funcnode->funcid = funcid; funcnode->functype = rettype; funcnode->funcisindex = false; funcnode->funcsize = 0; funcnode->func_fcache = NULL; funcnode->func_tlist = NIL; funcnode->func_planlist = NIL; /* perform the necessary typecasting */ make_arguments(nargs, fargs, oid_array, true_oid_array); /* * for functions returning base types, we want to project out the * return value. set up a target list to do that. the executor will * ignore these for c functions, and do the right thing for postquel * functions. */ if (typeid_get_relid(rettype) == InvalidOid) funcnode->func_tlist = setup_base_tlist(rettype); /* * For sets, we want to make a targetlist to project out this * attribute of the set tuples. */ if (attisset) { if (!strcmp(funcname, "*")) { funcnode->func_tlist = expandAll(pstate, relname, refname, curr_resno); } else { funcnode->func_tlist = setup_tlist(funcname, argrelid); rettype = find_atttype(argrelid, funcname); } } /* * Sequence handling. */ if (funcid == SeqNextValueRegProcedure || funcid == SeqCurrValueRegProcedure) { Const *seq; char *seqrel; text *seqname; int32 aclcheck_result = -1; extern text *lower (text *string); Assert(length(fargs) == 1); seq = (Const *) lfirst(fargs); if (!IsA((Node *) seq, Const)) elog(WARN, "%s: only constant sequence names are acceptable", funcname); seqname = lower ((text*)DatumGetPointer(seq->constvalue)); pfree (DatumGetPointer(seq->constvalue)); seq->constvalue = PointerGetDatum (seqname); seqrel = textout(seqname); if ((aclcheck_result = pg_aclcheck(seqrel, GetPgUserName(), ((funcid == SeqNextValueRegProcedure) ? ACL_WR : ACL_RD))) != ACLCHECK_OK) elog(WARN, "%s.%s: %s", seqrel, funcname, aclcheck_error_strings[aclcheck_result]); pfree(seqrel); if (funcid == SeqNextValueRegProcedure && inWhereClause) elog(WARN, "nextval of a sequence in WHERE disallowed"); } 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) { Iter *iter = makeNode(Iter); iter->itertype = rettype; iter->iterexpr = retval; retval = (Node *) iter; } return (retval); } /***************************************************************************** * *****************************************************************************/ /* * AddAggToParseState - * add the aggregate to the list of unique aggregates in pstate. * * SIDE EFFECT: aggno in target list entry will be modified */ static void AddAggToParseState(ParseState *pstate, Aggreg *aggreg) { List *ag; int i; /* * see if we have the aggregate already (we only need to record the * aggregate once) */ i = 0; foreach(ag, pstate->p_aggs) { Aggreg *a = lfirst(ag); if (!strcmp(a->aggname, aggreg->aggname) && equal(a->target, aggreg->target)) { /* fill in the aggno and we're done */ aggreg->aggno = i; return; } i++; } /* not found, new aggregate */ aggreg->aggno = i; pstate->p_numAgg++; pstate->p_aggs = lappend(pstate->p_aggs, aggreg); return; } /* * finalizeAggregates - * fill in qry_aggs from pstate. Also checks to make sure that aggregates * are used in the proper place. */ static void finalizeAggregates(ParseState *pstate, Query *qry) { List *l; int i; parseCheckAggregates(pstate, qry); qry->qry_numAgg = pstate->p_numAgg; qry->qry_aggs = (Aggreg **) palloc(sizeof(Aggreg *) * qry->qry_numAgg); i = 0; foreach(l, pstate->p_aggs) qry->qry_aggs[i++] = (Aggreg *) lfirst(l); } /* * contain_agg_clause-- * Recursively find aggreg nodes from a clause. * * Returns true if any aggregate found. */ static bool contain_agg_clause(Node *clause) { if (clause == NULL) return FALSE; else if (IsA(clause, Aggreg)) return TRUE; else if (IsA(clause, Iter)) return contain_agg_clause(((Iter *) clause)->iterexpr); else if (single_node(clause)) return FALSE; else if (or_clause(clause)) { List *temp; foreach(temp, ((Expr *) clause)->args) if (contain_agg_clause(lfirst(temp))) return TRUE; return FALSE; } else if (is_funcclause(clause)) { List *temp; foreach(temp, ((Expr *) clause)->args) if (contain_agg_clause(lfirst(temp))) return TRUE; return FALSE; } else if (IsA(clause, ArrayRef)) { List *temp; foreach(temp, ((ArrayRef *) clause)->refupperindexpr) if (contain_agg_clause(lfirst(temp))) return TRUE; foreach(temp, ((ArrayRef *) clause)->reflowerindexpr) if (contain_agg_clause(lfirst(temp))) return TRUE; if (contain_agg_clause(((ArrayRef *) clause)->refexpr)) return TRUE; if (contain_agg_clause(((ArrayRef *) clause)->refassgnexpr)) return TRUE; return FALSE; } else if (not_clause(clause)) return contain_agg_clause((Node *) get_notclausearg((Expr *) clause)); else if (is_opclause(clause)) return (contain_agg_clause((Node *) get_leftop((Expr *) clause)) || contain_agg_clause((Node *) get_rightop((Expr *) clause))); return FALSE; } /* * exprIsAggOrGroupCol - * returns true if the expression does not contain non-group columns. */ static bool exprIsAggOrGroupCol(Node *expr, List *groupClause) { List *gl; if (expr == NULL || IsA(expr, Const) || IsA(expr, Param) ||IsA(expr, Aggreg)) return TRUE; foreach(gl, groupClause) { GroupClause *grpcl = lfirst(gl); if (equal(expr, grpcl->entry->expr)) return TRUE; } if (IsA(expr, Expr)) { List *temp; foreach(temp, ((Expr *) expr)->args) if (!exprIsAggOrGroupCol(lfirst(temp), groupClause)) return FALSE; return TRUE; } return FALSE; } /* * tleIsAggOrGroupCol - * returns true if the TargetEntry is Agg or GroupCol. */ static bool tleIsAggOrGroupCol(TargetEntry *tle, List *groupClause) { Node *expr = tle->expr; List *gl; if (expr == NULL || IsA(expr, Const) ||IsA(expr, Param)) return TRUE; foreach(gl, groupClause) { GroupClause *grpcl = lfirst(gl); if (tle->resdom->resno == grpcl->entry->resdom->resno) { if (contain_agg_clause((Node *) expr)) elog(WARN, "parser: aggregates not allowed in GROUP BY clause"); return TRUE; } } if (IsA(expr, Aggreg)) return TRUE; if (IsA(expr, Expr)) { List *temp; foreach(temp, ((Expr *) expr)->args) if (!exprIsAggOrGroupCol(lfirst(temp), groupClause)) return FALSE; return TRUE; } return FALSE; } /* * parseCheckAggregates - * this should really be done earlier but the current grammar * cannot differentiate functions from aggregates. So we have do check * here when the target list and the qualifications are finalized. */ static void parseCheckAggregates(ParseState *pstate, Query *qry) { List *tl; Assert(pstate->p_numAgg > 0); /* * aggregates never appear in WHERE clauses. (we have to check where * clause first because if there is an aggregate, the check for * non-group column in target list may fail.) */ if (contain_agg_clause(qry->qual)) elog(WARN, "parser: aggregates not allowed in WHERE clause"); /* * the target list can only contain aggregates, group columns and * functions thereof. */ foreach(tl, qry->targetList) { TargetEntry *tle = lfirst(tl); if (!tleIsAggOrGroupCol(tle, qry->groupClause)) elog(WARN, "parser: illegal use of aggregates or non-group column in target list"); } /* * the expression specified in the HAVING clause has the same * restriction as those in the target list. */ /* * Need to change here when we get HAVING works. Currently * qry->havingQual is NULL. - vadim 04/05/97 if (!exprIsAggOrGroupCol(qry->havingQual, qry->groupClause)) elog(WARN, "parser: illegal use of aggregates or non-group column in HAVING clause"); */ return; }