diff options
author | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
---|---|---|
committer | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
commit | d31084e9d1118b25fd16580d9d8c2924b5740dff (patch) | |
tree | 3179e66307d54df9c7b966543550e601eb55e668 /src/backend/parser/analyze.c | |
download | postgresql-PG95-1_01.tar.gz postgresql-PG95-1_01.zip |
Postgres95 1.01 Distribution - Virgin SourcesPG95-1_01
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r-- | src/backend/parser/analyze.c | 2467 |
1 files changed, 2467 insertions, 0 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c new file mode 100644 index 00000000000..504e557abee --- /dev/null +++ b/src/backend/parser/analyze.c @@ -0,0 +1,2467 @@ +/*------------------------------------------------------------------------- + * + * 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.1.1.1 1996/07/09 06:21:39 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "postgres.h" +#include "nodes/nodes.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 "utils/elog.h" +#include "utils/builtins.h" /* namecmp(), textout() */ +#include "utils/lsyscache.h" +#include "utils/palloc.h" +#include "utils/mcxt.h" +#include "parser/parse_query.h" +#include "parser/parse_state.h" +#include "nodes/makefuncs.h" /* for makeResdom(), etc. */ +#include "nodes/nodeFuncs.h" + +#include "optimizer/clauses.h" +#include "access/heapam.h" + +/* 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); + +static Node *transformExpr(ParseState *pstate, Node *expr); + +static void makeRangeTable(ParseState *pstate, char *relname, List *frmList); +static List *expandAllTables(ParseState *pstate); +static char *figureColname(Node *expr, Node *resval); +static List *makeTargetList(ParseState *pstate, List *cols, List *exprs); +static List *transformTargetList(ParseState *pstate, + List *targetlist, bool isInsert, + bool isUpdate); +static TargetEntry *make_targetlist_expr(ParseState *pstate, + char *name, Node *expr, + List *arrayRef, + bool ResdomNoIsAttrNo); +static Node *transformWhereClause(ParseState *pstate, Node *a_expr); +static List *transformGroupClause(ParseState *pstate, List *grouplist); +static List *transformSortClause(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 char *ParseColumnName(ParseState *pstate, char *name, bool *isRelName); +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); + +/***************************************************************************** + * + *****************************************************************************/ + +/* + * makeParseState() -- + * allocate and initialize a new ParseState. + * the CALLERS is responsible for freeing the ParseState* returned + * + */ + +ParseState* +makeParseState() { + ParseState *pstate; + + pstate = malloc(sizeof(ParseState)); + pstate->p_last_resno = 1; + pstate->p_target_resnos = NIL; + pstate->p_rtable = NIL; + pstate->p_query_is_rule = 0; + pstate->p_numAgg = 0; + pstate->p_aggs = 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*)); + + while(pl!=NIL) { + pstate = makeParseState(); + result->qtrees[i++] = transformStmt(pstate, lfirst(pl)); + pl = lnext(pl); + 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 = FALSE; */ + qry->uniqueFlag = NULL; + + /* fix where clause */ + qry->qual = transformWhereClause(pstate, stmt->whereClause); + + qry->rtable = pstate->p_rtable; + qry->resultRelation = RangeTablePosn(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 */ + List *targetlist; + + qry->commandType = CMD_INSERT; + + /* set up a range table */ + makeRangeTable(pstate, stmt->relname, stmt->fromClause); + +/* qry->uniqueFlag = FALSE; */ + qry->uniqueFlag = NULL; + + /* fix the target list */ + targetlist = makeTargetList(pstate, stmt->cols, stmt->exprs); + qry->targetList = transformTargetList(pstate, + targetlist, + TRUE /* is insert */, + FALSE /*not update*/); + + /* fix where clause */ + qry->qual = transformWhereClause(pstate, stmt->whereClause); + + /* now the range table will not change */ + qry->rtable = pstate->p_rtable; + qry->resultRelation = RangeTablePosn(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) { + RangeTblEntry *curEnt, *newEnt; + + /* + * NOTE: 'CURRENT' must always have a varno equal to 1 and 'NEW' + * equal to 2. + */ + curEnt = makeRangeTableEntry(stmt->object->relname, FALSE, + NULL, "*CURRENT*"); + newEnt = makeRangeTableEntry(stmt->object->relname, FALSE, + NULL, "*NEW*"); + pstate->p_rtable = makeList(curEnt, newEnt, -1); + + pstate->p_last_resno = 1; + pstate->p_target_resnos = NIL; + pstate->p_query_is_rule = 1; /* 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, + FALSE, /*is insert */ + FALSE /*not update*/); + + /* fix where clause */ + qry->qual = transformWhereClause(pstate,stmt->whereClause); + + /* fix order clause */ + qry->sortClause = transformSortClause(stmt->orderClause, + qry->targetList, + qry->uniqueFlag); + + /* fix group by clause */ + qry->groupClause = transformGroupClause(pstate, + stmt->groupClause); + 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; + + /* + * 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, + FALSE, /* not insert */ + TRUE /* is update */); + + /* fix where clause */ + qry->qual = transformWhereClause(pstate,stmt->whereClause); + + qry->rtable = pstate->p_rtable; + qry->resultRelation = RangeTablePosn(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, + FALSE, /*is insert */ + FALSE /*not update*/); + + /* fix where clause */ + qry->qual = transformWhereClause(pstate,stmt->whereClause); + + /* fix order clause */ + qry->sortClause = transformSortClause(stmt->orderClause, + qry->targetList, + qry->uniqueFlag); + 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) +{ + Node *result; + + 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); /* must exists */ + if (exprType(uexpr) != INT4OID) + elog(WARN, "array index expressions must be int4's"); + if (ai->lidx != NULL) { + lexpr = transformExpr(pstate, ai->lidx); + 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 = "<unnamed>"; + 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); + Node *rexpr = transformExpr(pstate, a->rexpr); + result = (Node *)make_op(a->opname, lexpr, rexpr); + } + break; + case ISNULL: + { + Node *lexpr = transformExpr(pstate, a->lexpr); + result = ParseFunc(pstate, + "NullValue", lcons(lexpr, NIL), + &pstate->p_last_resno); + } + break; + case NOTNULL: + { + Node *lexpr = transformExpr(pstate, a->lexpr); + result = ParseFunc(pstate, + "NonNullValue", lcons(lexpr, NIL), + &pstate->p_last_resno); + } + break; + case AND: + { + Expr *expr = makeNode(Expr); + Node *lexpr = transformExpr(pstate, a->lexpr); + Node *rexpr = transformExpr(pstate, a->rexpr); + 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); + Node *rexpr = transformExpr(pstate, a->rexpr); + 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); + 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: { + Ident *ident = (Ident*)expr; + bool isrel; + char *reln= ParseColumnName(pstate,ident->name, &isrel); + + /* could be a column name or a relation_name */ + if (reln==NULL) { + /* + * may be a relation_name + * + * ??? in fact, every ident left after transfromExpr() is called + * will be assumed to be a relation. + */ + if (isrel) { + ident->isRel = TRUE; + result = (Node*)ident; + } else { + elog(WARN, "attribute \"%s\" not found", ident->name); + } + }else { + Attr *att = makeNode(Attr); + att->relname = reln; + att->attrs = lcons(makeString(ident->name), NIL); + /* + * a column name + */ + result = + (Node*)handleNestedDots(pstate, att, &pstate->p_last_resno); + } + 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)); + } + 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; +} + +/***************************************************************************** + * + * 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= frmList; + + while(fl!=NIL) { + RangeVar *r = lfirst(fl); + RelExpr *baserel = r->relExpr; + RangeTblEntry *ent; + char *relname = baserel->relname; + char *refname = r->name; + + if (refname==NULL) { + refname = relname; + } else { + /* + * check whether refname exists already + */ + if (RangeTablePosn(pstate->p_rtable, refname) != 0) + elog(WARN, "parser: range variable \"%s\" duplicated", + refname); + } + + ent = makeRangeTableEntry(relname, baserel->inh, + baserel->timeRange, refname); + /* + * 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. + */ + ent->inFromCl = true; + + pstate->p_rtable = lappend(pstate->p_rtable, ent); + fl= lnext(fl); + } +} + +/* + * makeRangeTable - + * make a range table with the specified relation (optional) and the + * from-clause. + */ +static void +makeRangeTable(ParseState *pstate, char *relname, List *frmList) +{ + int x; + + parseFromClause(pstate, frmList); + + if (relname == NULL) + return; + + if (RangeTablePosn(pstate->p_rtable, relname) < 1) { + RangeTblEntry *ent; + + ent = makeRangeTableEntry(relname, FALSE, NULL, relname); + pstate->p_rtable = lappend(pstate->p_rtable, ent); + } + x = RangeTablePosn(pstate->p_rtable, relname); + pstate->parser_current_rel = heap_openr(VarnoGetRelname(pstate,x)); + if (pstate->parser_current_rel == NULL) + elog(WARN,"invalid relation name"); +} + +/* + * exprType - + * returns the Oid of the type of the expression. (Used for typechecking.) + */ +Oid +exprType(Node *expr) +{ + Oid type; + + 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_query_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); + char *rt_name= rte->refname; /* use refname here so that we + refer to the right entry */ + List *temp = target; + + if(temp == NIL ) + target = expandAll(pstate, rt_name, &pstate->p_last_resno); + else { + while (temp != NIL && lnext(temp) != NIL) + temp = lnext(temp); + lnext(temp) = expandAll(pstate, rt_name, &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 + * + *****************************************************************************/ + +/* + * makeTargetList - + * turn a list of column names and expressions (in the same order) into + * a target list (used exclusively for inserts) + */ +static List * +makeTargetList(ParseState *pstate, List *cols, List *exprs) +{ + List *tlist, *tl=NULL; + if (cols != NIL) { + /* has to transform colElem too (opt_indirection can be exprs) */ + while(cols!=NIL) { + ResTarget *res = makeNode(ResTarget); + Ident *id = lfirst(cols); + /* Id opt_indirection */ + res->name = id->name; + res->indirection = id->indirection; + if (exprs == NIL) { + elog(WARN, "insert: number of expressions less than columns"); + }else { + res->val = (Node *)lfirst(exprs); + } + if (tl==NIL) { + tlist = tl = lcons(res, NIL); + }else { + lnext(tl) = lcons(res,NIL); + tl = lnext(tl); + } + cols = lnext(cols); + exprs = lnext(exprs); + } + if (cols != NIL) { + elog(WARN, "insert: number of columns more than expressions"); + } + }else { + bool has_star = false; + + if (exprs==NIL) + return NIL; + if (IsA(lfirst(exprs),Attr)) { + Attr *att = lfirst(exprs); + + if ((att->relname!=NULL && !strcmp(att->relname,"*")) || + (att->attrs!=NIL && !strcmp(strVal(lfirst(att->attrs)),"*"))) + has_star = true; + } + if (has_star) { + /* + * right now, these better be 'relname.*' or '*' (this can happen + * in eg. insert into tenk2 values (tenk1.*); or + * insert into tenk2 select * from tenk1; + */ + while(exprs!=NIL) { + ResTarget *res = makeNode(ResTarget); + res->name = NULL; + res->indirection = NULL; + res->val = (Node *)lfirst(exprs); + if (tl==NIL) { + tlist = tl = lcons(res, NIL); + }else { + lnext(tl) = lcons(res,NIL); + tl = lnext(tl); + } + exprs = lnext(exprs); + } + } else { + Relation insertRel = pstate->parser_current_rel; + int numcol; + int i; + AttributeTupleForm *attr = insertRel->rd_att->attrs; + + numcol = Min(length(exprs), insertRel->rd_rel->relnatts); + for(i=0; i < numcol; i++) { + ResTarget *res = makeNode(ResTarget); + + res->name = palloc(NAMEDATALEN+1); + strncpy(res->name, attr[i]->attname.data, NAMEDATALEN); + res->name[NAMEDATALEN]='\0'; + res->indirection = NULL; + res->val = (Node *)lfirst(exprs); + if (tl==NIL) { + tlist = tl = lcons(res, NIL); + }else { + lnext(tl) = lcons(res,NIL); + tl = lnext(tl); + } + exprs = lnext(exprs); + } + } + } + return tlist; +} + +/* + * transformTargetList - + * turns a list of ResTarget's into a list of TargetEntry's + */ +static List * +transformTargetList(ParseState *pstate, + List *targetlist, + bool isInsert, + bool isUpdate) +{ + List *p_target= NIL; + List *temp = 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; + expr = transformExpr(pstate, (Node*)res->val); + 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); + + if (isInsert && res->name==NULL) + elog(WARN, "Sorry, have to specify the column list"); + + /* note indirection has not been transformed */ + if (isInsert && 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); + 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); + 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->parser_current_rel; + 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, + (isInsert||isUpdate)); + 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); + ind->uidx = transformExpr(pstate, ind->uidx); + ilist = lnext(ilist); + } + } + tent = make_targetlist_expr(pstate, colname, expr, + res->indirection, + (isInsert||isUpdate)); + } + 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(lnext(targetlist)!=NULL) + elog(WARN, "cannot expand target list *, ..."); + p_target = expandAllTables(pstate); + + /* + * 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,"*")) { + /* temp is the target list we're building in the while + * loop. Make sure we fix it after appending more nodes. + */ + if (temp == NIL) { + p_target = temp = + expandAll(pstate, att->relname, &pstate->p_last_resno); + } else { + lnext(temp) = + expandAll(pstate, att->relname, &pstate->p_last_resno); + } + while(lnext(temp)!=NIL) + temp = lnext(temp); /* make sure we point to the last + target entry */ + /* + * 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); + if (att->indirection != NIL) { + List *ilist = att->indirection; + while (ilist!=NIL) { + A_Indices *ind = lfirst(ilist); + ind->lidx = transformExpr(pstate, ind->lidx); + ind->uidx = transformExpr(pstate, ind->uidx); + ilist = lnext(ilist); + } + result = (Node*)make_array_ref(result, att->indirection); + } + type_id = exprType(result); + type_len = tlen(get_id_type(type_id)); + 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 = temp = lcons(tent, NIL); + }else { + lnext(temp) = lcons(tent, NIL); + temp = lnext(temp); + } + targetlist = lnext(targetlist); + } + return p_target; +} + + +/* + * make_targetlist_expr - + * make a TargetEntry + * + * arrayRef is a list of transformed A_Indices + */ +static TargetEntry * +make_targetlist_expr(ParseState *pstate, + char *name, + Node *expr, + List *arrayRef, + bool ResdomNoIsAttrNo) +{ + int type_id, type_len, attrtype, 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); + type_len = tlen(get_id_type(type_id)); + + /* I have no idea what the following does! */ + if (ResdomNoIsAttrNo) { + /* + * append or replace query -- + * append, replace work only on one relation, + * so multiple occurence of same resdomno is bogus + */ + rd = pstate->parser_current_rel; + Assert(rd != NULL); + resdomno = varattno(rd,name); + attrisset = varisset(rd,name); + 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 */); + } 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 */); + } + } else if((Typecast_ok) && (attrtype != type_id)){ + lnext(expr) = + parser_typecast2(expr, get_id_type((long)attrtype)); + } else + if (attrtype != type_id) { + if ((attrtype == INT2OID) && (type_id == INT4OID)) + lfirst(expr) = lispInteger (INT2OID); + else if ((attrtype == FLOAT4OID) && (type_id == FLOAT8OID)) + lfirst(expr) = lispInteger (FLOAT4OID); + else + elog(WARN, "unequal type in tlist : %s \n", + name)); + } + + 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 */ + expr = (Node*)parser_typecast2(expr, + type_id, + get_id_type((long)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'", + name, + NAMEDATALEN, get_id_typname(attrtype), + NAMEDATALEN, get_id_typname(type_id)); + } + } + + if (intMember(resdomno, pstate->p_target_resnos)) { + elog(WARN,"two or more occurrences of same attr"); + } else { + pstate->p_target_resnos = lconsi(resdomno, + pstate->p_target_resnos); + } + 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(name), NIL); + target_expr = (Expr*)handleNestedDots(pstate, att, + &pstate->p_last_resno); + while(ar!=NIL) { + A_Indices *ind = lfirst(ar); + if (lowerIndexpr) { + /* 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, + name, + (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 */ + + qual = transformExpr(pstate, a_expr); + 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_tl_elt - + * returns the Resdom in the target list matching the specified varname + * + */ +static Resdom * +find_tl_elt(char *varname, List *tlist) +{ + List *i; + + foreach(i, tlist) { + TargetEntry *target = (TargetEntry *)lfirst(i); + Resdom *resnode = target->resdom; + char *resname = resnode->resname; + + if (!strcmp(resname, varname)) + return (resnode); + } + return ((Resdom *)NULL); +} + +static Oid +any_ordering_op(int restype) +{ + Operator order_op; + Oid order_opid; + + order_op = oper("<",restype,restype); + order_opid = (Oid)oprid(order_op); + + return order_opid; +} + +/* + * transformGroupClause - + * transform an Group By clause + * + */ +static List * +transformGroupClause(ParseState *pstate, List *grouplist) +{ + List *glist = NIL, *gl; + + while (grouplist != NIL) { + GroupClause *grpcl = makeNode(GroupClause); + Var *groupAttr = (Var*)transformExpr(pstate, (Node*)lfirst(grouplist)); + + if (nodeTag(groupAttr) != T_Var) { + elog(WARN, "parser: can only specify attribute in group by"); + } + grpcl->grpAttr = groupAttr; + grpcl->grpOpoid = any_ordering_op(groupAttr->vartype); + 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(List *orderlist, List *targetlist, + char* uniqueFlag) +{ + List *sortlist = NIL; + List *s, *i; + + while(orderlist != NIL) { + SortBy *sortby = lfirst(orderlist); + SortClause *sortcl = makeNode(SortClause); + Resdom *resdom; + + resdom = find_tl_elt(sortby->name, targetlist); + if (resdom == NULL) + elog(WARN,"The field being sorted by must appear in the target list"); + + sortcl->resdom = resdom; + sortcl->opoid = oprid(oper(sortby->useOp, + resdom->restype, + resdom->restype)); + 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; + 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); + + 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] != 0) { + 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)), + "<noname>", + 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 funcid = (Oid)0; + List *i = NIL; + Node *first_arg= NULL; + char *relname, *oldname; + 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; + 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) { + Ident *ident = (Ident*)first_arg; + + /* + * first arg is a relation. This could be a projection. + */ + relname = ident->name; + if (RangeTablePosn(pstate->p_rtable, relname)== 0) { + RangeTblEntry *ent; + + ent = + makeRangeTableEntry(relname, + FALSE, NULL, relname); + pstate->p_rtable = lappend(pstate->p_rtable, ent); + } + oldname = relname; + relname = VarnoGetRelname(pstate, + RangeTablePosn(pstate->p_rtable, + oldname)); + rd = heap_openr(relname); + relid = RelationGetRelationId(rd); + heap_close(rd); + /* 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) { + int dummyTypeId; + + return + ((Node*)make_var(pstate, + oldname, + 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 + * word "all". + */ + if ((get_attnum(argrelid, funcname) == InvalidAttrNumber) + && strcmp(funcname, "all")) { + 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; + Node *pair = lfirst(i); + + if (nodeTag(pair)==T_Ident && ((Ident*)pair)->isRel) { + /* + * a relation + */ + relname = ((Ident*)pair)->name; + + /* get the range table entry for the var node */ + vnum = RangeTablePosn(pstate->p_rtable, relname); + if (vnum == 0) { + pstate->p_rtable = + lappend(pstate->p_rtable , + makeRangeTableEntry(relname, FALSE, + NULL, relname)); + vnum = RangeTablePosn (pstate->p_rtable, relname); + } + + /* + * We have to do this because the relname in the pair + * may have been a range table variable name, rather + * than a real relation name. + */ + relname = VarnoGetRelname(pstate, vnum); + + rd = heap_openr(relname); + relid = RelationGetRelationId(rd); + heap_close(rd); + + /* + * 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, "all")) { + funcnode->func_tlist = + expandAll(pstate, (char*)relname, curr_resno); + } else { + funcnode->func_tlist = setup_tlist(funcname,argrelid); + rettype = find_atttype(argrelid, funcname); + } + } + + 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); +} + +/* + * returns (relname) if found, NIL if not a column + */ +static char* +ParseColumnName(ParseState *pstate, char *name, bool *isRelName) +{ + List *et; + Relation rd; + List *rtable; + + /* + * see if it is a relation name. If so, leave it as it is + */ + if (RangeTablePosn(pstate->p_rtable, name)!=0) { + *isRelName = TRUE; + return NULL; + } + + if (pstate->p_query_is_rule) { + rtable = lnext(lnext(pstate->p_rtable)); + } else { + rtable = pstate->p_rtable; + } + /* + * search each relation in the FROM list and see if we have a match + */ + foreach(et, rtable) { + RangeTblEntry *rte = lfirst(et); + char *relname= rte->relname; + char *refname= rte->refname; + Oid relid; + + rd= heap_openr(relname); + relid = RelationGetRelationId(rd); + heap_close(rd); + if (get_attnum(relid, name) != InvalidAttrNumber) { + /* found */ + *isRelName = FALSE; + return refname; + } + + } + + /* attribute not found */ + *isRelName = FALSE; + return NULL; +} + + +/***************************************************************************** + * + *****************************************************************************/ + +/* + * 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) +{ + if (expr==NULL) + return TRUE; + else if (IsA(expr,Const)) + return TRUE; + else if (IsA(expr,Var)) { + List *gl; + Var *var = (Var*)expr; + /* + * only group columns are legal + */ + foreach (gl, groupClause) { + GroupClause *grpcl = lfirst(gl); + if ((grpcl->grpAttr->varno == var->varno) && + (grpcl->grpAttr->varattno == var->varattno)) + return TRUE; + } + return FALSE; + } else if (IsA(expr,Aggreg)) + /* aggregates can take group column or non-group column as argument, + no further check necessary. */ + return TRUE; + else 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 (!exprIsAggOrGroupCol(tle->expr, 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. + */ + if (!exprIsAggOrGroupCol(qry->havingQual, qry->groupClause)) + elog(WARN, + "parser: illegal use of aggregates or non-group column in HAVING clause"); + + return; +} + + |