aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/analyze.c
diff options
context:
space:
mode:
authorMarc G. Fournier <scrappy@hub.org>1996-07-09 06:22:35 +0000
committerMarc G. Fournier <scrappy@hub.org>1996-07-09 06:22:35 +0000
commitd31084e9d1118b25fd16580d9d8c2924b5740dff (patch)
tree3179e66307d54df9c7b966543550e601eb55e668 /src/backend/parser/analyze.c
downloadpostgresql-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.c2467
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;
+}
+
+