aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/Makefile.inc46
-rw-r--r--src/backend/parser/analyze.c2467
-rw-r--r--src/backend/parser/catalog_utils.c1470
-rw-r--r--src/backend/parser/catalog_utils.h64
-rw-r--r--src/backend/parser/dbcommands.c259
-rw-r--r--src/backend/parser/dbcommands.h28
-rw-r--r--src/backend/parser/gram.y2113
-rw-r--r--src/backend/parser/keywords.c179
-rw-r--r--src/backend/parser/keywords.h25
-rw-r--r--src/backend/parser/parse_query.c653
-rw-r--r--src/backend/parser/parse_query.h72
-rw-r--r--src/backend/parser/parse_state.h27
-rw-r--r--src/backend/parser/parser.c449
-rw-r--r--src/backend/parser/parsetree.h80
-rw-r--r--src/backend/parser/scan.l255
-rw-r--r--src/backend/parser/scansup.c148
-rw-r--r--src/backend/parser/scansup.h17
17 files changed, 8352 insertions, 0 deletions
diff --git a/src/backend/parser/Makefile.inc b/src/backend/parser/Makefile.inc
new file mode 100644
index 00000000000..38607f6fb01
--- /dev/null
+++ b/src/backend/parser/Makefile.inc
@@ -0,0 +1,46 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+# Makefile for the parser module
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+# $Header: /cvsroot/pgsql/src/backend/parser/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:39 scrappy Exp $
+#
+#-------------------------------------------------------------------------
+
+VPATH:= $(VPATH):$(CURDIR)/parser
+
+#PARSEYACCS= gram.c parse.h
+PARSEYACCS= gram.c
+
+$(PARSEYACCS): gram.y
+ cd $(objdir); \
+ $(YACC) $(YFLAGS) $<; \
+ mv y.tab.c gram.c; \
+ mv y.tab.h parse.h
+
+$(objdir)/gram.o: gram.c
+ $(cc_inobjdir)
+
+
+scan.c: scan.l
+ cd $(objdir); $(LEX) $<; mv lex.yy.c scan.c
+
+$(objdir)/scan.o: scan.c
+ $(cc_inobjdir)
+
+
+SRCS_PARSER+= analyze.c catalog_utils.c dbcommands.c gram.c \
+ keywords.c parser.c parse_query.c scan.c scansup.c
+
+CLEANFILES+= scan.c ${PARSEYACCS}
+
+POSTGRES_DEPEND+= scan.c $(PARSEYACCS)
+
+HEADERS+= catalog_utils.h io.h parse_query.h parsetree.h \
+ dbcommands.h keywords.h
+
+
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;
+}
+
+
diff --git a/src/backend/parser/catalog_utils.c b/src/backend/parser/catalog_utils.c
new file mode 100644
index 00000000000..a4fc775c452
--- /dev/null
+++ b/src/backend/parser/catalog_utils.c
@@ -0,0 +1,1470 @@
+/*-------------------------------------------------------------------------
+ *
+ * catalog_utils.c--
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/parser/Attic/catalog_utils.c,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "lib/dllist.h"
+#include "utils/datum.h"
+
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "fmgr.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+#include "utils/syscache.h"
+#include "catalog/catname.h"
+
+#include "catalog_utils.h"
+#include "catalog/pg_inherits.h"
+#include "catalog/pg_type.h"
+#include "catalog/indexing.h"
+#include "catalog/catname.h"
+
+#include "access/skey.h"
+#include "access/relscan.h"
+#include "access/tupdesc.h"
+#include "access/htup.h"
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/itup.h"
+#include "access/tupmacs.h"
+
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "utils/lsyscache.h"
+#include "storage/lmgr.h"
+
+struct {
+ char *field;
+ int code;
+} special_attr[] = {
+ { "ctid", SelfItemPointerAttributeNumber },
+ { "oid", ObjectIdAttributeNumber },
+ { "xmin", MinTransactionIdAttributeNumber },
+ { "cmin", MinCommandIdAttributeNumber },
+ { "xmax", MaxTransactionIdAttributeNumber },
+ { "cmax", MaxCommandIdAttributeNumber },
+ { "chain", ChainItemPointerAttributeNumber },
+ { "anchor", AnchorItemPointerAttributeNumber },
+ { "tmin", MinAbsoluteTimeAttributeNumber },
+ { "tmax", MaxAbsoluteTimeAttributeNumber },
+ { "vtype", VersionTypeAttributeNumber }
+};
+
+#define SPECIALS (sizeof(special_attr)/sizeof(*special_attr))
+
+static char *attnum_type[SPECIALS] = {
+ "tid",
+ "oid",
+ "xid",
+ "cid",
+ "xid",
+ "cid",
+ "tid",
+ "tid",
+ "abstime",
+ "abstime",
+ "char"
+ };
+
+#define MAXFARGS 8 /* max # args to a c or postquel function */
+
+static Oid **argtype_inherit();
+static Oid **genxprod();
+
+static int findsupers(Oid relid, Oid **supervec);
+/*
+ * This structure is used to explore the inheritance hierarchy above
+ * nodes in the type tree in order to disambiguate among polymorphic
+ * functions.
+ */
+
+typedef struct _InhPaths {
+ int nsupers; /* number of superclasses */
+ Oid self; /* this class */
+ Oid *supervec; /* vector of superclasses */
+} InhPaths;
+
+/*
+ * This structure holds a list of possible functions or operators that
+ * agree with the known name and argument types of the function/operator.
+ */
+typedef struct _CandidateList {
+ Oid *args;
+ struct _CandidateList *next;
+} *CandidateList;
+
+/* check to see if a type id is valid,
+ * returns true if it is. By using this call before calling
+ * get_id_type or get_id_typname, more meaningful error messages
+ * can be produced because the caller typically has more context of
+ * what's going on - jolly
+ */
+bool
+check_typeid(long id)
+{
+ return (SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(id),
+ 0,0,0) != NULL);
+}
+
+
+/* return a Type structure, given an typid */
+Type
+get_id_type(long id)
+{
+ HeapTuple tup;
+
+ if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id),
+ 0,0,0))) {
+ elog ( WARN, "type id lookup of %d failed", id);
+ return(NULL);
+ }
+ return((Type) tup);
+}
+
+/* return a type name, given a typeid */
+char*
+get_id_typname(long id)
+{
+ HeapTuple tup;
+ TypeTupleForm typetuple;
+
+ if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id),
+ 0,0,0))) {
+ elog ( WARN, "type id lookup of %d failed", id);
+ return(NULL);
+ }
+ typetuple = (TypeTupleForm)GETSTRUCT(tup);
+ return (typetuple->typname).data;
+}
+
+/* return a Type structure, given type name */
+Type
+type(char *s)
+{
+ HeapTuple tup;
+
+ if (s == NULL) {
+ elog ( WARN , "type(): Null type" );
+ }
+
+ if (!(tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(s), 0,0,0))) {
+ elog (WARN , "type name lookup of %s failed", s);
+ }
+ return((Type) tup);
+}
+
+/* given attribute id, return type of that attribute */
+/* XXX Special case for pseudo-attributes is a hack */
+Oid
+att_typeid(Relation rd, int attid)
+{
+
+ if (attid < 0) {
+ return(typeid(type(attnum_type[-attid-1])));
+ }
+ /* -1 because varattno (where attid comes from) returns one
+ more than index */
+ return(rd->rd_att->attrs[attid-1]->atttypid);
+}
+
+
+int
+att_attnelems(Relation rd, int attid)
+{
+ return(rd->rd_att->attrs[attid-1]->attnelems);
+}
+
+/* given type, return the type OID */
+Oid
+typeid(Type tp)
+{
+ if (tp == NULL) {
+ elog ( WARN , "typeid() called with NULL type struct");
+ }
+ return(tp->t_oid);
+}
+
+/* given type (as type struct), return the length of type */
+int16
+tlen(Type t)
+{
+ TypeTupleForm typ;
+
+ typ = (TypeTupleForm)GETSTRUCT(t);
+ return(typ->typlen);
+}
+
+/* given type (as type struct), return the value of its 'byval' attribute.*/
+bool
+tbyval(Type t)
+{
+ TypeTupleForm typ;
+
+ typ = (TypeTupleForm)GETSTRUCT(t);
+ return(typ->typbyval);
+}
+
+/* given type (as type struct), return the name of type */
+char*
+tname(Type t)
+{
+ TypeTupleForm typ;
+
+ typ = (TypeTupleForm)GETSTRUCT(t);
+ return (typ->typname).data;
+}
+
+/* given type (as type struct), return wether type is passed by value */
+int
+tbyvalue(Type t)
+{
+ TypeTupleForm typ;
+
+ typ = (TypeTupleForm) GETSTRUCT(t);
+ return(typ->typbyval);
+}
+
+/* given a type, return its typetype ('c' for 'c'atalog types) */
+char
+typetypetype(Type t)
+{
+ TypeTupleForm typ;
+
+ typ = (TypeTupleForm) GETSTRUCT(t);
+ return(typ->typtype);
+}
+
+/* given operator, return the operator OID */
+Oid
+oprid(Operator op)
+{
+ return(op->t_oid);
+}
+
+/*
+ * given opname, leftTypeId and rightTypeId,
+ * find all possible (arg1, arg2) pairs for which an operator named
+ * opname exists, such that leftTypeId can be coerced to arg1 and
+ * rightTypeId can be coerced to arg2
+ */
+static int
+binary_oper_get_candidates(char *opname,
+ int leftTypeId,
+ int rightTypeId,
+ CandidateList *candidates)
+{
+ CandidateList current_candidate;
+ Relation pg_operator_desc;
+ HeapScanDesc pg_operator_scan;
+ HeapTuple tup;
+ OperatorTupleForm oper;
+ Buffer buffer;
+ int nkeys;
+ int ncandidates = 0;
+ ScanKeyData opKey[3];
+
+ *candidates = NULL;
+
+ ScanKeyEntryInitialize(&opKey[0], 0,
+ Anum_pg_operator_oprname,
+ NameEqualRegProcedure,
+ NameGetDatum(opname));
+
+ ScanKeyEntryInitialize(&opKey[1], 0,
+ Anum_pg_operator_oprkind,
+ CharacterEqualRegProcedure,
+ CharGetDatum('b'));
+
+
+ if (leftTypeId == UNKNOWNOID) {
+ if (rightTypeId == UNKNOWNOID) {
+ nkeys = 2;
+ } else {
+ nkeys = 3;
+
+ ScanKeyEntryInitialize(&opKey[2], 0,
+ Anum_pg_operator_oprright,
+ ObjectIdEqualRegProcedure,
+ ObjectIdGetDatum(rightTypeId));
+ }
+ } else if (rightTypeId == UNKNOWNOID) {
+ nkeys = 3;
+
+ ScanKeyEntryInitialize(&opKey[2], 0,
+ Anum_pg_operator_oprleft,
+ ObjectIdEqualRegProcedure,
+ ObjectIdGetDatum(leftTypeId));
+ } else {
+ /* currently only "unknown" can be coerced */
+ return 0;
+ }
+
+ pg_operator_desc = heap_openr(OperatorRelationName);
+ pg_operator_scan = heap_beginscan(pg_operator_desc,
+ 0,
+ SelfTimeQual,
+ nkeys,
+ opKey);
+
+ do {
+ tup = heap_getnext(pg_operator_scan, 0, &buffer);
+ if (HeapTupleIsValid(tup)) {
+ current_candidate = (CandidateList)palloc(sizeof(struct _CandidateList));
+ current_candidate->args = (Oid *)palloc(2 * sizeof(Oid));
+
+ oper = (OperatorTupleForm)GETSTRUCT(tup);
+ current_candidate->args[0] = oper->oprleft;
+ current_candidate->args[1] = oper->oprright;
+ current_candidate->next = *candidates;
+ *candidates = current_candidate;
+ ncandidates++;
+ ReleaseBuffer(buffer);
+ }
+ } while(HeapTupleIsValid(tup));
+
+ heap_endscan(pg_operator_scan);
+ heap_close(pg_operator_desc);
+
+ return ncandidates;
+}
+
+/*
+ * equivalentOpersAfterPromotion -
+ * checks if a list of candidate operators obtained from
+ * binary_oper_get_candidates() contain equivalent operators. If
+ * this routine is called, we have more than 1 candidate and need to
+ * decided whether to pick one of them. This routine returns true if
+ * the all the candidates operate on the same data types after
+ * promotion (int2, int4, float4 -> float8).
+ */
+static bool
+equivalentOpersAfterPromotion(CandidateList candidates)
+{
+ CandidateList result;
+ CandidateList promotedCandidates = NULL;
+ int leftarg, rightarg;
+
+ for (result = candidates; result != NULL; result = result->next) {
+ CandidateList c;
+ c = (CandidateList)palloc(sizeof(*c));
+ c->args = (Oid *)palloc(2 * sizeof(Oid));
+ switch (result->args[0]) {
+ case FLOAT4OID:
+ case INT4OID:
+ case INT2OID:
+ c->args[0] = FLOAT8OID;
+ break;
+ default:
+ c->args[0] = result->args[0];
+ break;
+ }
+ switch (result->args[1]) {
+ case FLOAT4OID:
+ case INT4OID:
+ case INT2OID:
+ c->args[1] = FLOAT8OID;
+ break;
+ default:
+ c->args[1] = result->args[1];
+ break;
+ }
+ c->next = promotedCandidates;
+ promotedCandidates = c;
+ }
+
+ /* if we get called, we have more than 1 candidates so we can do the
+ following safely */
+ leftarg = promotedCandidates->args[0];
+ rightarg = promotedCandidates->args[1];
+
+ for (result=promotedCandidates->next; result!=NULL; result=result->next) {
+ if (result->args[0]!=leftarg || result->args[1]!=rightarg)
+ /*
+ * this list contains operators that operate on different
+ * data types even after promotion. Hence we can't decide on
+ * which one to pick. The user must do explicit type casting.
+ */
+ return FALSE;
+ }
+
+ /* all the candidates are equivalent in the following sense: they operate
+ on equivalent data types and picking any one of them is as good. */
+ return TRUE;
+}
+
+
+/*
+ * given a choice of argument type pairs for a binary operator,
+ * try to choose a default pair
+ */
+static CandidateList
+binary_oper_select_candidate(int arg1,
+ int arg2,
+ CandidateList candidates)
+{
+ CandidateList result;
+
+ /*
+ * if both are "unknown", there is no way to select a candidate
+ *
+ * current wisdom holds that the default operator should be one
+ * in which both operands have the same type (there will only
+ * be one such operator)
+ *
+ * 7.27.93 - I have decided not to do this; it's too hard to
+ * justify, and it's easy enough to typecast explicitly -avi
+ * [the rest of this routine were commented out since then -ay]
+ */
+
+ if (arg1 == UNKNOWNOID && arg2 == UNKNOWNOID)
+ return (NULL);
+
+ /*
+ * 6/23/95 - I don't complete agree with avi. In particular, casting
+ * floats is a pain for users. Whatever the rationale behind not doing
+ * this is, I need the following special case to work.
+ *
+ * In the WHERE clause of a query, if a float is specified without
+ * quotes, we treat it as float8. I added the float48* operators so
+ * that we can operate on float4 and float8. But now we have more
+ * than one matching operator if the right arg is unknown (eg. float
+ * specified with quotes). This break some stuff in the regression
+ * test where there are floats in quotes not properly casted. Below
+ * is the solution. In addition to requiring the operator operates
+ * on the same type for both operands [as in the code Avi originally
+ * commented out], we also require that the operators be equivalent
+ * in some sense. (see equivalentOpersAfterPromotion for details.)
+ * - ay 6/95
+ */
+ if (!equivalentOpersAfterPromotion(candidates))
+ return NULL;
+
+ /* if we get here, any one will do but we're more picky and require
+ both operands be the same. */
+ for (result = candidates; result != NULL; result = result->next) {
+ if (result->args[0] == result->args[1])
+ return result;
+ }
+
+ return (NULL);
+}
+
+/* Given operator, types of arg1, and arg2, return oper struct */
+/* arg1, arg2 --typeids */
+Operator
+oper(char *op, int arg1, int arg2)
+{
+ HeapTuple tup;
+ CandidateList candidates;
+ int ncandidates;
+
+ if (!(tup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum(op),
+ ObjectIdGetDatum(arg1),
+ ObjectIdGetDatum(arg2),
+ Int8GetDatum('b')))) {
+ ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates);
+ if (ncandidates == 0) {
+ /*
+ * no operators of the desired types found
+ */
+ op_error(op, arg1, arg2);
+ return(NULL);
+ } else if (ncandidates == 1) {
+ /*
+ * exactly one operator of the desired types found
+ */
+ tup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum(op),
+ ObjectIdGetDatum(candidates->args[0]),
+ ObjectIdGetDatum(candidates->args[1]),
+ Int8GetDatum('b'));
+ Assert(HeapTupleIsValid(tup));
+ } else {
+ /*
+ * multiple operators of the desired types found
+ */
+ candidates = binary_oper_select_candidate(arg1, arg2, candidates);
+ if (candidates != NULL) {
+ /* we chose one of them */
+ tup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum(op),
+ ObjectIdGetDatum(candidates->args[0]),
+ ObjectIdGetDatum(candidates->args[1]),
+ Int8GetDatum('b'));
+ Assert(HeapTupleIsValid(tup));
+ } else {
+ Type tp1, tp2;
+
+ /* we chose none of them */
+ tp1 = get_id_type(arg1);
+ tp2 = get_id_type(arg2);
+ elog(NOTICE, "there is more than one operator %s for types", op);
+ elog(NOTICE, "%s and %s. You will have to retype this query",
+ tname(tp1), tname(tp2));
+ elog(WARN, "using an explicit cast");
+
+ return(NULL);
+ }
+ }
+ }
+ return((Operator) tup);
+}
+
+/*
+ * given opname and typeId, find all possible types for which
+ * a right/left unary operator named opname exists,
+ * such that typeId can be coerced to it
+ */
+static int
+unary_oper_get_candidates(char *op,
+ int typeId,
+ CandidateList *candidates,
+ char rightleft)
+{
+ CandidateList current_candidate;
+ Relation pg_operator_desc;
+ HeapScanDesc pg_operator_scan;
+ HeapTuple tup;
+ OperatorTupleForm oper;
+ Buffer buffer;
+ int ncandidates = 0;
+
+ static ScanKeyData opKey[2] = {
+ { 0, Anum_pg_operator_oprname, NameEqualRegProcedure },
+ { 0, Anum_pg_operator_oprkind, CharacterEqualRegProcedure } };
+
+ *candidates = NULL;
+
+ fmgr_info(NameEqualRegProcedure, (func_ptr *) &opKey[0].sk_func,
+ &opKey[0].sk_nargs);
+ opKey[0].sk_argument = NameGetDatum(op);
+ fmgr_info(CharacterEqualRegProcedure, (func_ptr *) &opKey[1].sk_func,
+ &opKey[1].sk_nargs);
+ opKey[1].sk_argument = CharGetDatum(rightleft);
+
+ /* currently, only "unknown" can be coerced */
+ if (typeId != UNKNOWNOID) {
+ return 0;
+ }
+
+ pg_operator_desc = heap_openr(OperatorRelationName);
+ pg_operator_scan = heap_beginscan(pg_operator_desc,
+ 0,
+ SelfTimeQual,
+ 2,
+ opKey);
+
+ do {
+ tup = heap_getnext(pg_operator_scan, 0, &buffer);
+ if (HeapTupleIsValid(tup)) {
+ current_candidate = (CandidateList)palloc(sizeof(struct _CandidateList));
+ current_candidate->args = (Oid *)palloc(sizeof(Oid));
+
+ oper = (OperatorTupleForm)GETSTRUCT(tup);
+ if (rightleft == 'r')
+ current_candidate->args[0] = oper->oprleft;
+ else
+ current_candidate->args[0] = oper->oprright;
+ current_candidate->next = *candidates;
+ *candidates = current_candidate;
+ ncandidates++;
+ ReleaseBuffer(buffer);
+ }
+ } while(HeapTupleIsValid(tup));
+
+ heap_endscan(pg_operator_scan);
+ heap_close(pg_operator_desc);
+
+ return ncandidates;
+}
+
+/* Given unary right-side operator (operator on right), return oper struct */
+/* arg-- type id */
+Operator
+right_oper(char *op, int arg)
+{
+ HeapTuple tup;
+ CandidateList candidates;
+ int ncandidates;
+
+ /*
+ if (!OpCache) {
+ init_op_cache();
+ }
+ */
+ if (!(tup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum(op),
+ ObjectIdGetDatum(arg),
+ ObjectIdGetDatum(InvalidOid),
+ Int8GetDatum('r')))) {
+ ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'r');
+ if (ncandidates == 0) {
+ elog ( WARN ,
+ "Can't find right op: %s for type %d", op, arg );
+ return(NULL);
+ }
+ else if (ncandidates == 1) {
+ tup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum(op),
+ ObjectIdGetDatum(candidates->args[0]),
+ ObjectIdGetDatum(InvalidOid),
+ Int8GetDatum('r'));
+ Assert(HeapTupleIsValid(tup));
+ }
+ else {
+ elog(NOTICE, "there is more than one right operator %s", op);
+ elog(NOTICE, "you will have to retype this query");
+ elog(WARN, "using an explicit cast");
+ return(NULL);
+ }
+ }
+ return((Operator) tup);
+}
+
+/* Given unary left-side operator (operator on left), return oper struct */
+/* arg--type id */
+Operator
+left_oper(char *op, int arg)
+{
+ HeapTuple tup;
+ CandidateList candidates;
+ int ncandidates;
+
+ /*
+ if (!OpCache) {
+ init_op_cache();
+ }
+ */
+ if (!(tup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum(op),
+ ObjectIdGetDatum(InvalidOid),
+ ObjectIdGetDatum(arg),
+ Int8GetDatum('l')))) {
+ ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'l');
+ if (ncandidates == 0) {
+ elog ( WARN ,
+ "Can't find left op: %s for type %d", op, arg );
+ return(NULL);
+ }
+ else if (ncandidates == 1) {
+ tup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum(op),
+ ObjectIdGetDatum(InvalidOid),
+ ObjectIdGetDatum(candidates->args[0]),
+ Int8GetDatum('l'));
+ Assert(HeapTupleIsValid(tup));
+ }
+ else {
+ elog(NOTICE, "there is more than one left operator %s", op);
+ elog(NOTICE, "you will have to retype this query");
+ elog(WARN, "using an explicit cast");
+ return(NULL);
+ }
+ }
+ return((Operator) tup);
+}
+
+/* given range variable, return id of variable */
+
+int
+varattno(Relation rd, char *a)
+{
+ int i;
+
+ for (i = 0; i < rd->rd_rel->relnatts; i++) {
+ if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a)) {
+ return(i+1);
+ }
+ }
+ for (i = 0; i < SPECIALS; i++) {
+ if (!strcmp(special_attr[i].field, a)) {
+ return(special_attr[i].code);
+ }
+ }
+
+ elog(WARN,"Relation %s does not have attribute %s\n",
+ RelationGetRelationName(rd), a );
+ return(-1);
+}
+
+/* Given range variable, return whether attribute of this name
+ * is a set.
+ * NOTE the ASSUMPTION here that no system attributes are, or ever
+ * will be, sets.
+ */
+bool
+varisset(Relation rd, char *name)
+{
+ int i;
+
+ /* First check if this is a system attribute */
+ for (i = 0; i < SPECIALS; i++) {
+ if (! strcmp(special_attr[i].field, name)) {
+ return(false); /* no sys attr is a set */
+ }
+ }
+ return (get_attisset(rd->rd_id, name));
+}
+
+/* given range variable, return id of variable */
+int
+nf_varattno(Relation rd, char *a)
+{
+ int i;
+
+ for (i = 0; i < rd->rd_rel->relnatts; i++) {
+ if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a)) {
+ return(i+1);
+ }
+ }
+ for (i = 0; i < SPECIALS; i++) {
+ if (!strcmp(special_attr[i].field, a)) {
+ return(special_attr[i].code);
+ }
+ }
+ return InvalidAttrNumber;
+}
+
+/*-------------
+ * given an attribute number and a relation, return its relation name
+ */
+char*
+getAttrName(Relation rd, int attrno)
+{
+ char *name;
+ int i;
+
+ if (attrno<0) {
+ for (i = 0; i < SPECIALS; i++) {
+ if (special_attr[i].code == attrno) {
+ name = special_attr[i].field;
+ return(name);
+ }
+ }
+ elog(WARN, "Illegal attr no %d for relation %s\n",
+ attrno, RelationGetRelationName(rd));
+ } else if (attrno >=1 && attrno<= RelationGetNumberOfAttributes(rd)) {
+ name = (rd->rd_att->attrs[attrno-1]->attname).data;
+ return(name);
+ } else {
+ elog(WARN, "Illegal attr no %d for relation %s\n",
+ attrno, RelationGetRelationName(rd));
+ }
+
+ /*
+ * Shouldn't get here, but we want lint to be happy...
+ */
+
+ return(NULL);
+}
+
+/* Given a typename and value, returns the ascii form of the value */
+
+char *
+outstr(char *typename, /* Name of type of value */
+ char *value) /* Could be of any type */
+{
+ TypeTupleForm tp;
+ Oid op;
+
+ tp = (TypeTupleForm ) GETSTRUCT(type(typename));
+ op = tp->typoutput;
+ return((char *) fmgr(op, value));
+}
+
+/* Given a Type and a string, return the internal form of that string */
+char *
+instr2(Type tp, char *string, int typlen)
+{
+ return(instr1((TypeTupleForm ) GETSTRUCT(tp), string, typlen));
+}
+
+/* Given a type structure and a string, returns the internal form of
+ that string */
+char *
+instr1(TypeTupleForm tp, char *string, int typlen)
+{
+ Oid op;
+ Oid typelem;
+
+ op = tp->typinput;
+ typelem = tp->typelem; /* XXX - used for array_in */
+ /* typlen is for bpcharin() and varcharin() */
+ return((char *) fmgr(op, string, typelem, typlen));
+}
+
+/* Given the attribute type of an array return the arrtribute type of
+ an element of the array */
+
+Oid
+GetArrayElementType(Oid typearray)
+{
+ HeapTuple type_tuple;
+ TypeTupleForm type_struct_array;
+
+ type_tuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(typearray),
+ 0,0,0);
+
+ if (!HeapTupleIsValid(type_tuple))
+ elog(WARN, "GetArrayElementType: Cache lookup failed for type %d\n",
+ typearray);
+
+ /* get the array type struct from the type tuple */
+ type_struct_array = (TypeTupleForm) GETSTRUCT(type_tuple);
+
+ if (type_struct_array->typelem == InvalidOid) {
+ elog(WARN, "GetArrayElementType: type %s is not an array",
+ (Name)&(type_struct_array->typname.data[0]));
+ }
+
+ return(type_struct_array->typelem);
+}
+
+Oid
+funcid_get_rettype(Oid funcid)
+{
+ HeapTuple func_tuple = NULL;
+ Oid funcrettype = (Oid)0;
+
+ func_tuple = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid),
+ 0,0,0);
+
+ if ( !HeapTupleIsValid ( func_tuple ))
+ elog (WARN, "function %d does not exist", funcid);
+
+ funcrettype = (Oid)
+ ((Form_pg_proc)GETSTRUCT(func_tuple))->prorettype ;
+
+ return (funcrettype);
+}
+
+/*
+ * get a list of all argument type vectors for which a function named
+ * funcname taking nargs arguments exists
+ */
+static CandidateList
+func_get_candidates(char *funcname, int nargs)
+{
+ Relation heapRelation;
+ Relation idesc;
+ ScanKeyData skey;
+ HeapTuple tuple;
+ IndexScanDesc sd;
+ RetrieveIndexResult indexRes;
+ Buffer buffer;
+ Form_pg_proc pgProcP;
+ bool bufferUsed = FALSE;
+ CandidateList candidates = NULL;
+ CandidateList current_candidate;
+ int i;
+
+ heapRelation = heap_openr(ProcedureRelationName);
+ ScanKeyEntryInitialize(&skey,
+ (bits16)0x0,
+ (AttrNumber)1,
+ (RegProcedure)NameEqualRegProcedure,
+ (Datum)funcname);
+
+ idesc = index_openr(ProcedureNameIndex);
+
+ sd = index_beginscan(idesc, false, 1, &skey);
+
+ do {
+ tuple = (HeapTuple)NULL;
+ if (bufferUsed) {
+ ReleaseBuffer(buffer);
+ bufferUsed = FALSE;
+ }
+
+ indexRes = index_getnext(sd, ForwardScanDirection);
+ if (indexRes) {
+ ItemPointer iptr;
+
+ iptr = &indexRes->heap_iptr;
+ tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
+ pfree(indexRes);
+ if (HeapTupleIsValid(tuple)) {
+ pgProcP = (Form_pg_proc)GETSTRUCT(tuple);
+ bufferUsed = TRUE;
+ if (pgProcP->pronargs == nargs) {
+ current_candidate = (CandidateList)
+ palloc(sizeof(struct _CandidateList));
+ current_candidate->args = (Oid *)
+ palloc(8 * sizeof(Oid));
+ memset(current_candidate->args, 0, 8 * sizeof(Oid));
+ for (i=0; i<nargs; i++) {
+ current_candidate->args[i] =
+ pgProcP->proargtypes[i];
+ }
+
+ current_candidate->next = candidates;
+ candidates = current_candidate;
+ }
+ }
+ }
+ } while (indexRes);
+
+ index_endscan(sd);
+ index_close(idesc);
+ heap_close(heapRelation);
+
+ return candidates;
+}
+
+/*
+ * can input_typeids be coerced to func_typeids?
+ */
+static bool
+can_coerce(int nargs, Oid *input_typeids, Oid *func_typeids)
+{
+ int i;
+ Type tp;
+
+ /*
+ * right now, we only coerce "unknown", and we cannot coerce it to a
+ * relation type
+ */
+ for (i=0; i<nargs; i++) {
+ if (input_typeids[i] != func_typeids[i]) {
+ if (input_typeids[i] != UNKNOWNOID || func_typeids[i] == 0)
+ return false;
+
+ tp = get_id_type(input_typeids[i]);
+ if (typetypetype(tp) == 'c' )
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * given a list of possible typeid arrays to a function and an array of
+ * input typeids, produce a shortlist of those function typeid arrays
+ * that match the input typeids (either exactly or by coercion), and
+ * return the number of such arrays
+ */
+static int
+match_argtypes(int nargs,
+ Oid *input_typeids,
+ CandidateList function_typeids,
+ CandidateList *candidates) /* return value */
+{
+ CandidateList current_candidate;
+ CandidateList matching_candidate;
+ Oid *current_typeids;
+ int ncandidates = 0;
+
+ *candidates = NULL;
+
+ for (current_candidate = function_typeids;
+ current_candidate != NULL;
+ current_candidate = current_candidate->next) {
+ current_typeids = current_candidate->args;
+ if (can_coerce(nargs, input_typeids, current_typeids)) {
+ matching_candidate = (CandidateList)
+ palloc(sizeof(struct _CandidateList));
+ matching_candidate->args = current_typeids;
+ matching_candidate->next = *candidates;
+ *candidates = matching_candidate;
+ ncandidates++;
+ }
+ }
+
+ return ncandidates;
+}
+
+/*
+ * given the input argtype array and more than one candidate
+ * for the function argtype array, attempt to resolve the conflict.
+ * returns the selected argtype array if the conflict can be resolved,
+ * otherwise returns NULL
+ */
+static Oid *
+func_select_candidate(int nargs,
+ Oid *input_typeids,
+ CandidateList candidates)
+{
+ /* XXX no conflict resolution implemeneted yet */
+ return (NULL);
+}
+
+bool
+func_get_detail(char *funcname,
+ int nargs,
+ Oid *oid_array,
+ Oid *funcid, /* return value */
+ Oid *rettype, /* return value */
+ bool *retset, /* return value */
+ Oid **true_typeids) /* return value */
+{
+ Oid **input_typeid_vector;
+ Oid *current_input_typeids;
+ CandidateList function_typeids;
+ CandidateList current_function_typeids;
+ HeapTuple ftup;
+ Form_pg_proc pform;
+
+ /*
+ * attempt to find named function in the system catalogs
+ * with arguments exactly as specified - so that the normal
+ * case is just as quick as before
+ */
+ ftup = SearchSysCacheTuple(PRONAME,
+ PointerGetDatum(funcname),
+ Int32GetDatum(nargs),
+ PointerGetDatum(oid_array),
+ 0);
+ *true_typeids = oid_array;
+
+ /*
+ * If an exact match isn't found :
+ * 1) get a vector of all possible input arg type arrays constructed
+ * from the superclasses of the original input arg types
+ * 2) get a list of all possible argument type arrays to the
+ * function with given name and number of arguments
+ * 3) for each input arg type array from vector #1 :
+ * a) find how many of the function arg type arrays from list #2
+ * it can be coerced to
+ * b) - if the answer is one, we have our function
+ * - if the answer is more than one, attempt to resolve the
+ * conflict
+ * - if the answer is zero, try the next array from vector #1
+ */
+ if (!HeapTupleIsValid(ftup)) {
+ function_typeids = func_get_candidates(funcname, nargs);
+
+ if (function_typeids != NULL) {
+ int ncandidates = 0;
+
+ input_typeid_vector = argtype_inherit(nargs, oid_array);
+ current_input_typeids = oid_array;
+
+ do {
+ ncandidates = match_argtypes(nargs, current_input_typeids,
+ function_typeids,
+ &current_function_typeids);
+ if (ncandidates == 1) {
+ *true_typeids = current_function_typeids->args;
+ ftup = SearchSysCacheTuple(PRONAME,
+ PointerGetDatum(funcname),
+ Int32GetDatum(nargs),
+ PointerGetDatum(*true_typeids),
+ 0);
+ Assert(HeapTupleIsValid(ftup));
+ }
+ else if (ncandidates > 1) {
+ *true_typeids =
+ func_select_candidate(nargs,
+ current_input_typeids,
+ current_function_typeids);
+ if (*true_typeids == NULL) {
+ elog(NOTICE, "there is more than one function named \"%s\"",
+ funcname);
+ elog(NOTICE, "that satisfies the given argument types. you will have to");
+ elog(NOTICE, "retype your query using explicit typecasts.");
+ func_error("func_get_detail", funcname, nargs, (int*)oid_array);
+ }
+ else {
+ ftup = SearchSysCacheTuple(PRONAME,
+ PointerGetDatum(funcname),
+ Int32GetDatum(nargs),
+ PointerGetDatum(*true_typeids),
+ 0);
+ Assert(HeapTupleIsValid(ftup));
+ }
+ }
+ current_input_typeids = *input_typeid_vector++;
+ }
+ while (current_input_typeids !=
+ InvalidOid && ncandidates == 0);
+ }
+ }
+
+ if (!HeapTupleIsValid(ftup)) {
+ Type tp;
+
+ if (nargs == 1) {
+ tp = get_id_type(oid_array[0]);
+ if (typetypetype(tp) == 'c')
+ elog(WARN, "no such attribute or function \"%s\"",
+ funcname);
+ }
+ func_error("func_get_detail", funcname, nargs, (int*)oid_array);
+ } else {
+ pform = (Form_pg_proc) GETSTRUCT(ftup);
+ *funcid = ftup->t_oid;
+ *rettype = (Oid) pform->prorettype;
+ *retset = (Oid) pform->proretset;
+
+ return (true);
+ }
+/* shouldn't reach here */
+ return (false);
+
+}
+
+/*
+ * argtype_inherit() -- Construct an argtype vector reflecting the
+ * inheritance properties of the supplied argv.
+ *
+ * This function is used to disambiguate among functions with the
+ * same name but different signatures. It takes an array of eight
+ * type ids. For each type id in the array that's a complex type
+ * (a class), it walks up the inheritance tree, finding all
+ * superclasses of that type. A vector of new Oid type arrays
+ * is returned to the caller, reflecting the structure of the
+ * inheritance tree above the supplied arguments.
+ *
+ * The order of this vector is as follows: all superclasses of the
+ * rightmost complex class are explored first. The exploration
+ * continues from right to left. This policy means that we favor
+ * keeping the leftmost argument type as low in the inheritance tree
+ * as possible. This is intentional; it is exactly what we need to
+ * do for method dispatch. The last type array we return is all
+ * zeroes. This will match any functions for which return types are
+ * not defined. There are lots of these (mostly builtins) in the
+ * catalogs.
+ */
+static Oid **
+argtype_inherit(int nargs, Oid *oid_array)
+{
+ Oid relid;
+ int i;
+ InhPaths arginh[MAXFARGS];
+
+ for (i = 0; i < MAXFARGS; i++) {
+ if (i < nargs) {
+ arginh[i].self = oid_array[i];
+ if ((relid = typeid_get_relid(oid_array[i])) != InvalidOid) {
+ arginh[i].nsupers = findsupers(relid, &(arginh[i].supervec));
+ } else {
+ arginh[i].nsupers = 0;
+ arginh[i].supervec = (Oid *) NULL;
+ }
+ } else {
+ arginh[i].self = InvalidOid;
+ arginh[i].nsupers = 0;
+ arginh[i].supervec = (Oid *) NULL;
+ }
+ }
+
+ /* return an ordered cross-product of the classes involved */
+ return (genxprod(arginh, nargs));
+}
+
+typedef struct _SuperQE {
+ Oid sqe_relid;
+} SuperQE;
+
+static int
+findsupers(Oid relid, Oid **supervec)
+{
+ Oid *relidvec;
+ Relation inhrel;
+ HeapScanDesc inhscan;
+ ScanKeyData skey;
+ HeapTuple inhtup;
+ TupleDesc inhtupdesc;
+ int nvisited;
+ SuperQE *qentry, *vnode;
+ Dllist *visited, *queue;
+ Dlelem *qe, *elt;
+
+ Relation rd;
+ Buffer buf;
+ Datum d;
+ bool newrelid;
+ char isNull;
+
+ nvisited = 0;
+ queue = DLNewList();
+ visited = DLNewList();
+
+
+ inhrel = heap_openr(InheritsRelationName);
+ RelationSetLockForRead(inhrel);
+ inhtupdesc = RelationGetTupleDescriptor(inhrel);
+
+ /*
+ * Use queue to do a breadth-first traversal of the inheritance
+ * graph from the relid supplied up to the root.
+ */
+ do {
+ ScanKeyEntryInitialize(&skey, 0x0, Anum_pg_inherits_inhrel,
+ ObjectIdEqualRegProcedure,
+ ObjectIdGetDatum(relid));
+
+ inhscan = heap_beginscan(inhrel, 0, NowTimeQual, 1, &skey);
+
+ while (HeapTupleIsValid(inhtup = heap_getnext(inhscan, 0, &buf))) {
+ qentry = (SuperQE *) palloc(sizeof(SuperQE));
+
+ d = (Datum) fastgetattr(inhtup, Anum_pg_inherits_inhparent,
+ inhtupdesc, &isNull);
+ qentry->sqe_relid = DatumGetObjectId(d);
+
+ /* put this one on the queue */
+ DLAddTail(queue, DLNewElem(qentry));
+
+ ReleaseBuffer(buf);
+ }
+
+ heap_endscan(inhscan);
+
+ /* pull next unvisited relid off the queue */
+ do {
+ qe = DLRemHead(queue);
+ qentry = qe ? (SuperQE*)DLE_VAL(qe) : NULL;
+
+ if (qentry == (SuperQE *) NULL)
+ break;
+
+ relid = qentry->sqe_relid;
+ newrelid = true;
+
+ for (elt = DLGetHead(visited); elt; elt = DLGetSucc(elt)) {
+ vnode = (SuperQE*)DLE_VAL(elt);
+ if (vnode && (qentry->sqe_relid == vnode->sqe_relid)) {
+ newrelid = false;
+ break;
+ }
+ }
+ } while (!newrelid);
+
+ if (qentry != (SuperQE *) NULL) {
+
+ /* save the type id, rather than the relation id */
+ if ((rd = heap_open(qentry->sqe_relid)) == (Relation) NULL)
+ elog(WARN, "relid %d does not exist", qentry->sqe_relid);
+ qentry->sqe_relid = typeid(type(RelationGetRelationName(rd)->data));
+ heap_close(rd);
+
+ DLAddTail(visited, qe);
+
+ nvisited++;
+ }
+ } while (qentry != (SuperQE *) NULL);
+
+ RelationUnsetLockForRead(inhrel);
+ heap_close(inhrel);
+
+ if (nvisited > 0) {
+ relidvec = (Oid *) palloc(nvisited * sizeof(Oid));
+ *supervec = relidvec;
+
+ for (elt = DLGetHead(visited); elt; elt = DLGetSucc(elt)) {
+ vnode = (SuperQE*)DLE_VAL(elt);
+ *relidvec++ = vnode->sqe_relid;
+ }
+
+ } else {
+ *supervec = (Oid *) NULL;
+ }
+
+ return (nvisited);
+}
+
+static Oid **
+genxprod(InhPaths *arginh, int nargs)
+{
+ int nanswers;
+ Oid **result, **iter;
+ Oid *oneres;
+ int i, j;
+ int cur[MAXFARGS];
+
+ nanswers = 1;
+ for (i = 0; i < nargs; i++) {
+ nanswers *= (arginh[i].nsupers + 2);
+ cur[i] = 0;
+ }
+
+ iter = result = (Oid **) palloc(sizeof(Oid *) * nanswers);
+
+ /* compute the cross product from right to left */
+ for (;;) {
+ oneres = (Oid *) palloc(MAXFARGS * sizeof(Oid));
+ memset(oneres, 0, MAXFARGS * sizeof(Oid));
+
+ for (i = nargs - 1; i >= 0 && cur[i] > arginh[i].nsupers; i--)
+ continue;
+
+ /* if we're done, terminate with NULL pointer */
+ if (i < 0) {
+ *iter = NULL;
+ return (result);
+ }
+
+ /* no, increment this column and zero the ones after it */
+ cur[i] = cur[i] + 1;
+ for (j = nargs - 1; j > i; j--)
+ cur[j] = 0;
+
+ for (i = 0; i < nargs; i++) {
+ if (cur[i] == 0)
+ oneres[i] = arginh[i].self;
+ else if (cur[i] > arginh[i].nsupers)
+ oneres[i] = 0; /* wild card */
+ else
+ oneres[i] = arginh[i].supervec[cur[i] - 1];
+ }
+
+ *iter++ = oneres;
+ }
+}
+
+/* Given a type id, returns the in-conversion function of the type */
+Oid
+typeid_get_retinfunc(int type_id)
+{
+ HeapTuple typeTuple;
+ TypeTupleForm type;
+ Oid infunc;
+ typeTuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(type_id),
+ 0,0,0);
+ if ( !HeapTupleIsValid ( typeTuple ))
+ elog(WARN,
+ "typeid_get_retinfunc: Invalid type - oid = %d",
+ type_id);
+
+ type = (TypeTupleForm) GETSTRUCT(typeTuple);
+ infunc = type->typinput;
+ return(infunc);
+}
+
+Oid
+typeid_get_relid(int type_id)
+{
+ HeapTuple typeTuple;
+ TypeTupleForm type;
+ Oid infunc;
+ typeTuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(type_id),
+ 0,0,0);
+ if ( !HeapTupleIsValid ( typeTuple ))
+ elog(WARN, "typeid_get_relid: Invalid type - oid = %d ", type_id);
+
+ type = (TypeTupleForm) GETSTRUCT(typeTuple);
+ infunc = type->typrelid;
+ return(infunc);
+}
+
+Oid get_typrelid(Type typ)
+{
+ TypeTupleForm typtup;
+
+ typtup = (TypeTupleForm) GETSTRUCT(typ);
+
+ return (typtup->typrelid);
+}
+
+Oid
+get_typelem(Oid type_id)
+{
+ HeapTuple typeTuple;
+ TypeTupleForm type;
+
+ if (!(typeTuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(type_id),
+ 0,0,0))) {
+ elog (WARN , "type id lookup of %d failed", type_id);
+ }
+ type = (TypeTupleForm) GETSTRUCT(typeTuple);
+
+ return (type->typelem);
+}
+
+char
+FindDelimiter(char *typename)
+{
+ char delim;
+ HeapTuple typeTuple;
+ TypeTupleForm type;
+
+
+ if (!(typeTuple = SearchSysCacheTuple(TYPNAME,
+ PointerGetDatum(typename),
+ 0,0,0))) {
+ elog (WARN , "type name lookup of %s failed", typename);
+ }
+ type = (TypeTupleForm) GETSTRUCT(typeTuple);
+
+ delim = type->typdelim;
+ return (delim);
+}
+
+/*
+ * Give a somewhat useful error message when the operator for two types
+ * is not found.
+ */
+void
+op_error(char *op, int arg1, int arg2)
+{
+ Type tp1, tp2;
+
+ if (check_typeid(arg1)) {
+ tp1 = get_id_type(arg1);
+ } else {
+ elog(WARN, "left hand side of operator %s has an unknown type, probably a bad attribute name", op);
+ }
+
+ if (check_typeid(arg2)) {
+ tp2 = get_id_type(arg2);
+ } else {
+ elog(WARN, "right hand side of operator %s has an unknown type, probably a bad attribute name", op);
+ }
+
+ elog(NOTICE, "there is no operator %s for types %s and %s",
+ op, tname(tp1),tname(tp2));
+ elog(NOTICE, "You will either have to retype this query using an");
+ elog(NOTICE, "explicit cast, or you will have to define the operator");
+ elog(WARN, "%s for %s and %s using DEFINE OPERATOR",
+ op, tname(tp1),tname(tp2));
+}
+
+/*
+ * Error message when function lookup fails that gives details of the
+ * argument types
+ */
+void
+func_error(char *caller, char *funcname, int nargs, int *argtypes)
+{
+ Type get_id_type();
+ char p[(NAMEDATALEN+2)*MAXFMGRARGS], *ptr;
+ int i;
+
+ ptr = p;
+ *ptr = '\0';
+ for (i=0; i<nargs; i++) {
+ if (i) {
+ *ptr++ = ',';
+ *ptr++ = ' ';
+ }
+ if (argtypes[i] != 0) {
+ (void) strcpy(ptr, tname(get_id_type(argtypes[i])));
+ *(ptr + NAMEDATALEN) = '\0';
+ } else
+ strcpy(ptr, "opaque");
+ ptr += strlen(ptr);
+ }
+
+ elog(WARN, "%s: function %s(%s) does not exist", caller, funcname, p);
+}
+
diff --git a/src/backend/parser/catalog_utils.h b/src/backend/parser/catalog_utils.h
new file mode 100644
index 00000000000..55bb6fa52d0
--- /dev/null
+++ b/src/backend/parser/catalog_utils.h
@@ -0,0 +1,64 @@
+/*-------------------------------------------------------------------------
+ *
+ * catalog_utils.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: catalog_utils.h,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CATALOG_UTILS_H
+#define CATALOG_UTILS_H
+
+
+#include "postgres.h"
+
+#include "access/htup.h"
+#include "utils/rel.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "utils/syscache.h"
+
+typedef HeapTuple Type;
+typedef HeapTuple Operator;
+
+extern Oid typeid_get_relid();
+
+extern bool check_typeid(long id);
+extern Type get_id_type(long id);
+extern char *get_id_typname(long id);
+extern Type type(char *);
+extern Oid att_typeid(Relation rd, int attid);
+extern int att_attnelems(Relation rd, int attid);
+extern Oid typeid(Type tp);
+extern int16 tlen(Type t);
+extern bool tbyval(Type t);
+extern char *tname(Type t);
+extern int tbyvalue(Type t);
+extern Oid oprid(Operator op);
+extern Operator oper(char *op, int arg1, int arg2);
+extern Operator right_oper(char *op, int arg);
+extern Operator left_oper(char *op, int arg);
+extern int varattno(Relation rd, char *a);
+extern bool varisset(Relation rd, char *name);
+extern int nf_varattno(Relation rd, char *a);
+extern char *getAttrName(Relation rd, int attrno);
+extern char *outstr(char *typename, char *value);
+extern char *instr2(Type tp, char *string, int typlen);
+extern char *instr1(TypeTupleForm tp, char *string, int typlen);
+extern Oid GetArrayElementType(Oid typearray);
+extern Oid funcid_get_rettype(Oid funcid);
+extern bool func_get_detail(char *funcname, int nargs, Oid *oid_array,
+ Oid *funcid, Oid *rettype, bool *retset, Oid **true_typeids);
+extern Oid typeid_get_retinfunc(int type_id);
+extern Oid typeid_get_relid(int type_id);
+extern Oid get_typrelid(Type typ);
+extern Oid get_typelem(Oid type_id);
+extern char FindDelimiter(char *typename);
+extern void op_error(char *op, int arg1, int arg2);
+extern void func_error(char *caller, char *funcname, int nargs, int *argtypes);
+
+#endif /* CATALOG_UTILS_H */
diff --git a/src/backend/parser/dbcommands.c b/src/backend/parser/dbcommands.c
new file mode 100644
index 00000000000..07543fb8dcc
--- /dev/null
+++ b/src/backend/parser/dbcommands.c
@@ -0,0 +1,259 @@
+/*-------------------------------------------------------------------------
+ *
+ * dbcommands.c--
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/parser/Attic/dbcommands.c,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <signal.h>
+
+#include "postgres.h"
+#include "miscadmin.h" /* for DataDir */
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/relscan.h"
+#include "utils/rel.h"
+#include "utils/elog.h"
+#include "catalog/catname.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_user.h"
+#include "catalog/pg_database.h"
+#include "utils/syscache.h"
+#include "parser/dbcommands.h"
+#include "tcop/tcopprot.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+
+
+/* non-export function prototypes */
+static void check_permissions(char *command, char *dbname,
+ Oid *dbIdP, Oid *userIdP);
+static HeapTuple get_pg_dbtup(char *command, char *dbname, Relation dbrel);
+
+void
+createdb(char *dbname)
+{
+ Oid db_id, user_id;
+ char buf[512];
+
+ /*
+ * If this call returns, the database does not exist and we're allowed
+ * to create databases.
+ */
+ check_permissions("createdb", dbname, &db_id, &user_id);
+
+ /* close virtual file descriptors so we can do system() calls */
+ closeAllVfds();
+
+ sprintf(buf, "mkdir %s%cbase%c%s", DataDir, SEP_CHAR, SEP_CHAR, dbname);
+ system(buf);
+ sprintf(buf, "%s %s%cbase%ctemplate1%c* %s%cbase%c%s",
+ COPY_CMD, DataDir, SEP_CHAR, SEP_CHAR, SEP_CHAR, DataDir,
+ SEP_CHAR, SEP_CHAR, dbname);
+ system(buf);
+
+/* sprintf(buf, "insert into pg_database (datname, datdba, datpath) \
+ values (\'%s\'::char16, \'%d\'::oid, \'%s\'::text);",
+ dbname, user_id, dbname);
+*/
+ sprintf(buf, "insert into pg_database (datname, datdba, datpath) \
+ values (\'%s\', \'%d\', \'%s\');",
+ dbname, user_id, dbname);
+
+ pg_eval(buf, (char **) NULL, (Oid *) NULL, 0);
+}
+
+void
+destroydb(char *dbname)
+{
+ Oid user_id, db_id;
+ char buf[512];
+
+ /*
+ * If this call returns, the database exists and we're allowed to
+ * remove it.
+ */
+ check_permissions("destroydb", dbname, &db_id, &user_id);
+
+ if (!OidIsValid(db_id)) {
+ elog(FATAL, "impossible: pg_database instance with invalid OID.");
+ }
+
+ /* stop the vacuum daemon */
+ stop_vacuum(dbname);
+
+ /* remove the pg_database tuple FIRST,
+ this may fail due to permissions problems*/
+ sprintf(buf, "delete from pg_database where pg_database.oid = \'%d\'::oid",
+ db_id);
+ pg_eval(buf, (char **) NULL, (Oid *) NULL, 0);
+
+ /* remove the data directory. If the DELETE above failed, this will
+ not be reached */
+ sprintf(buf, "rm -r %s/base/%s", DataDir, dbname);
+ system(buf);
+
+ /* drop pages for this database that are in the shared buffer cache */
+ DropBuffers(db_id);
+}
+
+static HeapTuple
+get_pg_dbtup(char *command, char *dbname, Relation dbrel)
+{
+ HeapTuple dbtup;
+ HeapTuple tup;
+ Buffer buf;
+ HeapScanDesc scan;
+ ScanKeyData scanKey;
+
+ ScanKeyEntryInitialize(&scanKey, 0, Anum_pg_database_datname,
+ NameEqualRegProcedure, NameGetDatum(dbname));
+
+ scan = heap_beginscan(dbrel, 0, NowTimeQual, 1, &scanKey);
+ if (!HeapScanIsValid(scan))
+ elog(WARN, "%s: cannot begin scan of pg_database.", command);
+
+ /*
+ * since we want to return the tuple out of this proc, and we're
+ * going to close the relation, copy the tuple and return the copy.
+ */
+ tup = heap_getnext(scan, 0, &buf);
+
+ if (HeapTupleIsValid(tup)) {
+ dbtup = heap_copytuple(tup);
+ ReleaseBuffer(buf);
+ } else
+ dbtup = tup;
+
+ heap_endscan(scan);
+ return (dbtup);
+}
+
+/*
+ * check_permissions() -- verify that the user is permitted to do this.
+ *
+ * If the user is not allowed to carry out this operation, this routine
+ * elog(WARN, ...)s, which will abort the xact. As a side effect, the
+ * user's pg_user tuple OID is returned in userIdP and the target database's
+ * OID is returned in dbIdP.
+ */
+
+static void
+check_permissions(char *command,
+ char *dbname,
+ Oid *dbIdP,
+ Oid *userIdP)
+{
+ Relation dbrel;
+ HeapTuple dbtup, utup;
+ Oid dbowner;
+ char use_createdb;
+ bool dbfound;
+ bool use_super;
+ char *userName;
+
+ userName = GetPgUserName();
+ utup = SearchSysCacheTuple(USENAME, PointerGetDatum(userName),
+ 0,0,0);
+ *userIdP = ((Form_pg_user)GETSTRUCT(utup))->usesysid;
+ use_super = ((Form_pg_user)GETSTRUCT(utup))->usesuper;
+ use_createdb = ((Form_pg_user)GETSTRUCT(utup))->usecreatedb;
+
+ /* Check to make sure user has permission to use createdb */
+ if (!use_createdb) {
+ elog(WARN, "user \"%-.*s\" is not allowed to create/destroy databases",
+ NAMEDATALEN, userName);
+ }
+
+ /* Make sure we are not mucking with the template database */
+ if (!strcmp(dbname, "template1")) {
+ elog(WARN, "%s cannot be executed on the template database.", command);
+ }
+
+ /* Check to make sure database is not the currently open database */
+ if (!strcmp(dbname, GetDatabaseName())) {
+ elog(WARN, "%s cannot be executed on an open database", command);
+ }
+
+ /* Check to make sure database is owned by this user */
+
+ /*
+ * need the reldesc to get the database owner out of dbtup
+ * and to set a write lock on it.
+ */
+ dbrel = heap_openr(DatabaseRelationName);
+
+ if (!RelationIsValid(dbrel))
+ elog(FATAL, "%s: cannot open relation \"%-.*s\"",
+ command, DatabaseRelationName);
+
+ /*
+ * Acquire a write lock on pg_database from the beginning to avoid
+ * upgrading a read lock to a write lock. Upgrading causes long delays
+ * when multiple 'createdb's or 'destroydb's are run simult. -mer 7/3/91
+ */
+ RelationSetLockForWrite(dbrel);
+ dbtup = get_pg_dbtup(command, dbname, dbrel);
+ dbfound = HeapTupleIsValid(dbtup);
+
+ if (dbfound) {
+ dbowner = (Oid) heap_getattr(dbtup, InvalidBuffer,
+ Anum_pg_database_datdba,
+ RelationGetTupleDescriptor(dbrel),
+ (char *) NULL);
+ *dbIdP = dbtup->t_oid;
+ } else {
+ *dbIdP = InvalidOid;
+ }
+
+ heap_close(dbrel);
+
+ /*
+ * Now be sure that the user is allowed to do this.
+ */
+
+ if (dbfound && !strcmp(command, "createdb")) {
+
+ elog(WARN, "createdb: database %s already exists.", dbname);
+
+ } else if (!dbfound && !strcmp(command, "destroydb")) {
+
+ elog(WARN, "destroydb: database %s does not exist.", dbname);
+
+ } else if (dbfound && !strcmp(command, "destroydb")
+ && dbowner != *userIdP && use_super == false) {
+
+ elog(WARN, "%s: database %s is not owned by you.", command, dbname);
+
+ }
+}
+
+/*
+ * stop_vacuum() -- stop the vacuum daemon on the database, if one is
+ * running.
+ */
+void
+stop_vacuum(char *dbname)
+{
+ char filename[256];
+ FILE *fp;
+ int pid;
+
+ sprintf(filename, "%s%cbase%c%s%c%s.vacuum", DataDir, SEP_CHAR, SEP_CHAR,
+ dbname, SEP_CHAR, dbname);
+ if ((fp = fopen(filename, "r")) != (FILE *) NULL) {
+ fscanf(fp, "%d", &pid);
+ fclose(fp);
+ if (kill(pid, SIGKILLDAEMON1) < 0) {
+ elog(WARN, "can't kill vacuum daemon (pid %d) on %s",
+ pid, dbname);
+ }
+ }
+}
diff --git a/src/backend/parser/dbcommands.h b/src/backend/parser/dbcommands.h
new file mode 100644
index 00000000000..a2811493c75
--- /dev/null
+++ b/src/backend/parser/dbcommands.h
@@ -0,0 +1,28 @@
+/*-------------------------------------------------------------------------
+ *
+ * dbcommands.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: dbcommands.h,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef DBCOMMANDS_H
+#define DBCOMMANDS_H
+
+/*
+ * Originally from tmp/daemon.h. The functions declared in daemon.h does not
+ * exist; hence removed. -- AY 7/29/94
+ */
+#define SIGKILLDAEMON1 SIGINT
+#define SIGKILLDAEMON2 SIGTERM
+
+extern void createdb(char *dbname);
+extern void destroydb(char *dbname);
+void stop_vacuum(char *dbname);
+
+#endif /* DBCOMMANDS_H */
+
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
new file mode 100644
index 00000000000..19529d1fa28
--- /dev/null
+++ b/src/backend/parser/gram.y
@@ -0,0 +1,2113 @@
+%{ /* -*-text-*- */
+
+#define YYDEBUG 1
+/*-------------------------------------------------------------------------
+ *
+ * gram.y--
+ * POSTGRES SQL YACC rules/actions
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $
+ *
+ * HISTORY
+ * AUTHOR DATE MAJOR EVENT
+ * Andrew Yu Sept, 1994 POSTQUEL to SQL conversion
+ * Andrew Yu Oct, 1994 lispy code conversion
+ *
+ * NOTES
+ * CAPITALS are used to represent terminal symbols.
+ * non-capitals are used to represent non-terminals.
+ *
+ * if you use list, make sure the datum is a node so that the printing
+ * routines work
+ *
+ * WARNING
+ * sometimes we assign constants to makeStrings. Make sure we don't free
+ * those.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include <ctype.h>
+#include "postgres.h"
+#include "nodes/parsenodes.h"
+#include "parser/catalog_utils.h"
+#include "parser/parse_query.h"
+#include "utils/acl.h"
+#include "catalog/catname.h"
+#include "utils/elog.h"
+#include "access/xact.h"
+
+static char saved_relname[BUFSIZ]; /* need this for complex attributes */
+static bool QueryIsRule = FALSE;
+
+extern List *parsetree;
+
+/*
+ * If you need access to certain yacc-generated variables and find that
+ * they're static by default, uncomment the next line. (this is not a
+ * problem, yet.)
+ */
+/*#define __YYSCLASS*/
+
+extern void yyerror(char message[]);
+
+static char *xlateSqlType(char *);
+static Node *makeA_Expr(int op, char *opname, Node *lexpr, Node *rexpr);
+
+/* old versions of flex define this as a macro */
+#if defined(yywrap)
+#undef yywrap
+#endif /* yywrap */
+%}
+
+
+%union {
+ double dval;
+ int ival;
+ char chr;
+ char *str;
+ bool boolean;
+ List *list;
+ Node *node;
+ Value *value;
+
+ Attr *attr;
+
+ ColumnDef *coldef;
+ TypeName *typnam;
+ DefElem *defelt;
+ ParamString *param;
+ SortBy *sortby;
+ IndexElem *ielem;
+ RangeVar *range;
+ RelExpr *relexp;
+ TimeRange *trange;
+ A_Indices *aind;
+ ResTarget *target;
+ ParamNo *paramno;
+
+ VersionStmt *vstmt;
+ DefineStmt *dstmt;
+ PurgeStmt *pstmt;
+ RuleStmt *rstmt;
+ AppendStmt *astmt;
+}
+
+%type <node> query, stmt, AddAttrStmt, ClosePortalStmt,
+ CopyStmt, CreateStmt, DefineStmt, DestroyStmt,
+ ExtendStmt, FetchStmt, GrantStmt,
+ IndexStmt, MoveStmt, ListenStmt, OptimizableStmt,
+ ProcedureStmt, PurgeStmt,
+ RecipeStmt, RemoveOperStmt, RemoveFuncStmt, RemoveStmt, RenameStmt,
+ RevokeStmt, RuleStmt, TransactionStmt, ViewStmt, LoadStmt,
+ CreatedbStmt, DestroydbStmt, VacuumStmt, RetrieveStmt, CursorStmt,
+ ReplaceStmt, AppendStmt, NotifyStmt, DeleteStmt, ClusterStmt,
+ ExplainStmt
+
+%type <str> relation_name, copy_file_name, copy_delimiter, def_name,
+ database_name, access_method, attr_name, class, index_name,
+ var_name, name, file_name, recipe_name
+
+%type <str> opt_id, opt_portal_name,
+ before_clause, after_clause, all_Op, MathOp, opt_name, opt_unique
+ result, OptUseOp, opt_class, opt_range_start, opt_range_end,
+ SpecialRuleRelation
+
+%type <str> privileges, operation_commalist, grantee
+%type <chr> operation
+
+%type <list> queryblock, relation_name_list, OptTableElementList,
+ tableElementList, OptInherit, definition,
+ opt_with, def_args, def_name_list, func_argtypes, oper_argtypes,
+ OptStmtList, OptStmtBlock, opt_column_list, columnList,
+ exprList, sort_clause, sortby_list, index_params,
+ name_list, from_clause, from_list, opt_array_bounds, nest_array_bounds,
+ expr_list, attrs, res_target_list, res_target_list2, def_list,
+ opt_indirection, group_clause, groupby_list, explain_options
+
+%type <boolean> opt_inh_star, opt_binary, opt_instead
+
+%type <ival> copy_dirn, archive_type, OptArchiveType, OptArchiveLocation,
+ def_type, opt_direction, remove_type, opt_column, event
+
+%type <ival> OptLocation, opt_move_where, fetch_how_many
+
+%type <dstmt> def_rest
+%type <pstmt> purge_quals
+%type <astmt> insert_rest
+
+%type <typnam> Typename, typname
+%type <coldef> columnDef
+%type <defelt> def_elem
+%type <node> def_arg, columnElem, exprElem, where_clause,
+ a_expr, AexprConst, having_clause, groupby
+%type <value> NumConst
+%type <attr> event_object, attr
+%type <sortby> sortby
+%type <ielem> index_elem, func_index
+%type <range> from_val
+%type <relexp> relation_expr
+%type <trange> time_range
+%type <target> res_target_el, res_target_el2
+%type <paramno> ParamNo
+
+%type <ival> Iconst
+%type <str> Sconst
+%type <str> Id, date
+
+
+/*
+ * If you make any token changes, remember to:
+ * - use "yacc -d" and update parse.h
+ * - update the keyword table in parser/keywords.c
+ */
+
+/* Keywords */
+%token ABORT_TRANS, ACL, ADD, AFTER, AGGREGATE, ALL, ALTER, AND, APPEND,
+ ARCHIVE, ARCH_STORE, AS, ASC, BACKWARD, BEFORE, BEGIN_TRANS, BINARY,
+ BY, CAST, CHANGE, CLOSE, CLUSTER, COLUMN, COMMIT, COPY, CREATE, CURRENT,
+ CURSOR, DATABASE, DECLARE, DELETE, DELIMITERS, DESC, DISTINCT, DO,
+ DROP, END_TRANS,
+ EXTEND, FETCH, FOR, FORWARD, FROM, FUNCTION, GRANT, GROUP,
+ HAVING, HEAVY, IN, INDEX, INHERITS, INSERT, INSTEAD, INTO,
+ ISNULL, LANGUAGE, LIGHT, LISTEN, LOAD, MERGE, MOVE, NEW,
+ NONE, NOT, NOTHING, NOTIFY, NOTNULL,
+ ON, OPERATOR, OPTION, OR, ORDER,
+ PNULL, PRIVILEGES, PUBLIC, PURGE, P_TYPE,
+ RENAME, REPLACE, RETRIEVE, RETURNS, REVOKE, ROLLBACK, RULE,
+ SELECT, SET, SETOF, STDIN, STDOUT, STORE,
+ TABLE, TO, TRANSACTION, UPDATE, USING, VACUUM, VALUES
+ VERSION, VIEW, WHERE, WITH, WORK
+%token EXECUTE, RECIPE, EXPLAIN, LIKE
+
+/* Special keywords, not in the query language - see the "lex" file */
+%token <str> IDENT, SCONST, Op
+%token <ival> ICONST, PARAM
+%token <dval> FCONST
+
+/* these are not real. they are here so that they gets generated as #define's*/
+%token OP
+
+/* precedence */
+%left OR
+%left AND
+%right NOT
+%right '='
+%nonassoc LIKE
+%nonassoc Op
+%nonassoc NOTNULL
+%nonassoc ISNULL
+%left '+' '-'
+%left '*' '/'
+%left '|' /* this is the relation union op, not logical or */
+%right ';' ':' /* Unary Operators */
+%nonassoc '<' '>'
+%right UMINUS
+%left '.'
+%left '[' ']'
+%nonassoc TYPECAST
+%nonassoc REDUCE
+%%
+
+queryblock: query queryblock
+ { parsetree = lcons($1, parsetree); }
+ | query
+ { parsetree = lcons($1, NIL); }
+ ;
+
+query: stmt
+ | stmt ';' { $$ = $1; }
+ ;
+
+stmt : AddAttrStmt
+ | ClosePortalStmt
+ | CopyStmt
+ | CreateStmt
+ | ClusterStmt
+ | DefineStmt
+ | DestroyStmt
+ | ExtendStmt
+ | ExplainStmt
+ | FetchStmt
+ | GrantStmt
+ | IndexStmt
+ | MoveStmt
+ | ListenStmt
+ | ProcedureStmt
+ | PurgeStmt
+ | RecipeStmt
+ | RemoveOperStmt
+ | RemoveFuncStmt
+ | RemoveStmt
+ | RenameStmt
+ | RevokeStmt
+ | OptimizableStmt
+ | RuleStmt
+ | TransactionStmt
+ | ViewStmt
+ | LoadStmt
+ | CreatedbStmt
+ | DestroydbStmt
+ | VacuumStmt
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY :
+ * addattr ( attr1 = type1 .. attrn = typen ) to <relname> [*]
+ *
+ *****************************************************************************/
+
+AddAttrStmt: ALTER TABLE relation_name opt_inh_star ADD COLUMN columnDef
+ {
+ AddAttrStmt *n = makeNode(AddAttrStmt);
+ n->relname = $3;
+ n->inh = $4;
+ n->colDef = $7;
+ $$ = (Node *)n;
+ }
+ ;
+
+columnDef: Id Typename
+ {
+ $$ = makeNode(ColumnDef);
+ $$->colname = $1;
+ $$->typename = $2;
+ }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY :
+ * close <optname>
+ *
+ *****************************************************************************/
+
+ClosePortalStmt: CLOSE opt_id
+ {
+ ClosePortalStmt *n = makeNode(ClosePortalStmt);
+ n->portalname = $2;
+ $$ = (Node *)n;
+ }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY :
+ * COPY [BINARY] <relname> FROM/TO
+ * [USING DELIMITERS <delimiter>]
+ *
+ *****************************************************************************/
+
+CopyStmt: COPY opt_binary relation_name copy_dirn copy_file_name copy_delimiter
+ {
+ CopyStmt *n = makeNode(CopyStmt);
+ n->binary = $2;
+ n->relname = $3;
+ n->direction = $4;
+ n->filename = $5;
+ n->delimiter = $6;
+ $$ = (Node *)n;
+ }
+ ;
+
+copy_dirn: TO
+ { $$ = TO; }
+ | FROM
+ { $$ = FROM; }
+ ;
+
+/*
+ * copy_file_name NULL indicates stdio is used. Whether stdin or stdout is
+ * used depends on the direction. (It really doesn't make sense to copy from
+ * stdout. We silently correct the "typo". - AY 9/94
+ */
+copy_file_name: Sconst { $$ = $1; }
+ | STDIN { $$ = NULL; }
+ | STDOUT { $$ = NULL; }
+ ;
+
+opt_binary: BINARY { $$ = TRUE; }
+ | /*EMPTY*/ { $$ = FALSE; }
+ ;
+
+/*
+ * the default copy delimiter is tab but the user can configure it
+ */
+copy_delimiter: USING DELIMITERS Sconst { $$ = $3;}
+ | /* EMPTY */ { $$ = "\t"; }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY :
+ * CREATE relname
+ *
+ *****************************************************************************/
+
+CreateStmt: CREATE TABLE relation_name '(' OptTableElementList ')'
+ OptInherit OptArchiveType OptLocation OptArchiveLocation
+ {
+ CreateStmt *n = makeNode(CreateStmt);
+ n->relname = $3;
+ n->tableElts = $5;
+ n->inhRelnames = $7;
+ n->archiveType = $8;
+ n->location = $9;
+ n->archiveLoc = $10;
+ $$ = (Node *)n;
+ }
+ ;
+
+OptTableElementList: tableElementList { $$ = $1; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
+tableElementList :
+ tableElementList ',' columnDef
+ { $$ = lappend($1, $3); }
+ | columnDef
+ { $$ = lcons($1, NIL); }
+ ;
+
+
+OptArchiveType: ARCHIVE '=' archive_type { $$ = $3; }
+ | /*EMPTY*/ { $$ = ARCH_NONE; }
+ ;
+
+archive_type: HEAVY { $$ = ARCH_HEAVY; }
+ | LIGHT { $$ = ARCH_LIGHT; }
+ | NONE { $$ = ARCH_NONE; }
+ ;
+
+OptLocation: STORE '=' Sconst
+ { $$ = smgrin($3); }
+ | /*EMPTY*/
+ { $$ = -1; }
+ ;
+
+OptArchiveLocation: ARCH_STORE '=' Sconst
+ { $$ = smgrin($3); }
+ | /*EMPTY*/
+ { $$ = -1; }
+ ;
+
+OptInherit: INHERITS '(' relation_name_list ')' { $$ = $3; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY :
+ * define (type,operator,aggregate)
+ *
+ *****************************************************************************/
+
+DefineStmt: CREATE def_type def_rest
+ {
+ $3->defType = $2;
+ $$ = (Node *)$3;
+ }
+ ;
+
+def_rest: def_name definition
+ {
+ $$ = makeNode(DefineStmt);
+ $$->defname = $1;
+ $$->definition = $2;
+ }
+ ;
+
+def_type: OPERATOR { $$ = OPERATOR; }
+ | Type { $$ = P_TYPE; }
+ | AGGREGATE { $$ = AGGREGATE; }
+ ;
+
+def_name: Id | MathOp | Op
+ ;
+
+
+definition: '(' def_list ')' { $$ = $2; }
+ ;
+
+
+def_list: def_elem
+ { $$ = lcons($1, NIL); }
+ | def_list ',' def_elem
+ { $$ = lappend($1, $3); }
+ ;
+
+def_elem: def_name '=' def_arg
+ {
+ $$ = makeNode(DefElem);
+ $$->defname = $1;
+ $$->arg = (Node *)$3;
+ }
+ | def_name
+ {
+ $$ = makeNode(DefElem);
+ $$->defname = $1;
+ $$->arg = (Node *)NULL;
+ }
+ ;
+
+def_arg: Id { $$ = (Node *)makeString($1); }
+ | all_Op { $$ = (Node *)makeString($1); }
+ | NumConst { $$ = (Node *)$1; /* already a Value */ }
+ | Sconst { $$ = (Node *)makeString($1); }
+ | SETOF Id {
+ TypeName *n = makeNode(TypeName);
+ n->name = $2;
+ n->setof = TRUE;
+ n->arrayBounds = NULL;
+ $$ = (Node *)n;
+ }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * destroy <relname1> [, <relname2> .. <relnameN> ]
+ *
+ *****************************************************************************/
+
+DestroyStmt: DROP TABLE relation_name_list
+ {
+ DestroyStmt *n = makeNode(DestroyStmt);
+ n->relNames = $3;
+ $$ = (Node *)n;
+ }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * fetch [forward | backward] [number | all ] [ in <portalname> ]
+ *
+ *****************************************************************************/
+
+FetchStmt: FETCH opt_direction fetch_how_many opt_portal_name
+ {
+ FetchStmt *n = makeNode(FetchStmt);
+ n->direction = $2;
+ n->howMany = $3;
+ n->portalname = $4;
+ $$ = (Node *)n;
+ }
+ ;
+
+opt_direction: FORWARD { $$ = FORWARD; }
+ | BACKWARD { $$ = BACKWARD; }
+ | /*EMPTY*/ { $$ = FORWARD; /* default */ }
+ ;
+
+fetch_how_many: Iconst
+ { $$ = $1;
+ if ($1 <= 0) elog(WARN,"Please specify nonnegative count for fetch"); }
+ | ALL { $$ = 0; /* 0 means fetch all tuples*/}
+ | /*EMPTY*/ { $$ = 0; /*default*/ }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * GRANT [privileges] ON [relation_name_list] TO [GROUP] grantee
+ *
+ *****************************************************************************/
+
+GrantStmt: GRANT privileges ON relation_name_list TO grantee opt_with_grant
+ {
+ $$ = (Node*)makeAclStmt($2,$4,$6,'+');
+ free($2);
+ free($6);
+ }
+ ;
+
+privileges: ALL PRIVILEGES
+ {
+ $$ = aclmakepriv("rwaR",0);
+ }
+ | ALL
+ {
+ $$ = aclmakepriv("rwaR",0);
+ }
+ | operation_commalist {
+ $$ = $1;
+ }
+ ;
+
+operation_commalist: operation {
+ $$ = aclmakepriv("",$1);
+ }
+ | operation_commalist ',' operation
+ {
+ $$ = aclmakepriv($1,$3);
+ free($1);
+ }
+ ;
+
+operation: SELECT {
+ $$ = ACL_MODE_RD_CHR;
+ }
+ | INSERT {
+ $$ = ACL_MODE_AP_CHR;
+ }
+ | UPDATE {
+ $$ = ACL_MODE_WR_CHR;
+ }
+ | DELETE {
+ $$ = ACL_MODE_WR_CHR;
+ }
+ | RULE {
+ $$ = ACL_MODE_RU_CHR;
+ }
+ ;
+
+grantee: PUBLIC {
+ $$ = aclmakeuser("A","");
+ }
+ | GROUP Id {
+ $$ = aclmakeuser("G",$2);
+ }
+ | Id {
+ $$ = aclmakeuser("U",$1);
+ }
+ ;
+
+opt_with_grant : /* empty */
+ | WITH GRANT OPTION
+ {
+ yyerror("WITH GRANT OPTION is not supported. Only relation owners can set privileges");
+ }
+ ;
+/*****************************************************************************
+ *
+ * QUERY:
+ * REVOKE [privileges] ON [relation_name] FROM [user]
+ *
+ *****************************************************************************/
+
+RevokeStmt: REVOKE privileges ON relation_name_list FROM grantee
+ {
+ $$ = (Node*)makeAclStmt($2,$4,$6,'-');
+ free($2);
+ free($6);
+ }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * move [<dirn>] [<whereto>] [<portalname>]
+ *
+ *****************************************************************************/
+
+MoveStmt: MOVE opt_direction opt_move_where opt_portal_name
+ {
+ MoveStmt *n = makeNode(MoveStmt);
+ n->direction = $2;
+ n->to = FALSE;
+ n->where = $3;
+ n->portalname = $4;
+ $$ = (Node *)n;
+ }
+ | MOVE opt_direction TO Iconst opt_portal_name
+ {
+ MoveStmt *n = makeNode(MoveStmt);
+ n->direction = $2;
+ n->to = TRUE;
+ n->where = $4;
+ n->portalname = $5;
+ $$ = (Node *)n;
+ }
+ ;
+
+opt_move_where: Iconst { $$ = $1; }
+ | /*EMPTY*/ { $$ = 1; /* default */ }
+ ;
+
+opt_portal_name: IN name { $$ = $2;}
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * define [archive] index <indexname> on <relname>
+ * using <access> "(" (<col> with <op>)+ ")" [with
+ * <target_list>]
+ *
+ * [where <qual>] is not supported anymore
+ *****************************************************************************/
+
+IndexStmt: CREATE INDEX index_name ON relation_name
+ USING access_method '(' index_params ')'
+ {
+ /* should check that access_method is valid,
+ etc ... but doesn't */
+ IndexStmt *n = makeNode(IndexStmt);
+ n->idxname = $3;
+ n->relname = $5;
+ n->accessMethod = $7;
+ n->indexParams = $9;
+ n->withClause = NIL;
+ n->whereClause = NULL;
+ $$ = (Node *)n;
+ }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * extend index <indexname> [where <qual>]
+ *
+ *****************************************************************************/
+
+ExtendStmt: EXTEND INDEX index_name where_clause
+ {
+ ExtendStmt *n = makeNode(ExtendStmt);
+ n->idxname = $3;
+ n->whereClause = $4;
+ $$ = (Node *)n;
+ }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * execute recipe <recipeName>
+ *
+ *****************************************************************************/
+
+RecipeStmt: EXECUTE RECIPE recipe_name
+ {
+ RecipeStmt *n;
+ if (!IsTransactionBlock())
+ elog(WARN, "EXECUTE RECIPE may only be used in begin/end transaction blocks.");
+
+ n = makeNode(RecipeStmt);
+ n->recipeName = $3;
+ $$ = (Node *)n;
+ }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * define function <fname>
+ * (language = <lang>, returntype = <typename>
+ * [, arch_pct = <percentage | pre-defined>]
+ * [, disk_pct = <percentage | pre-defined>]
+ * [, byte_pct = <percentage | pre-defined>]
+ * [, perbyte_cpu = <int | pre-defined>]
+ * [, percall_cpu = <int | pre-defined>]
+ * [, iscachable])
+ * [arg is (<type-1> { , <type-n>})]
+ * as <filename or code in language as appropriate>
+ *
+ *****************************************************************************/
+
+ProcedureStmt: CREATE FUNCTION def_name def_args
+ RETURNS def_arg opt_with AS Sconst LANGUAGE Sconst
+ {
+ ProcedureStmt *n = makeNode(ProcedureStmt);
+ n->funcname = $3;
+ n->defArgs = $4;
+ n->returnType = (Node *)$6;
+ n->withClause = $7;
+ n->as = $9;
+ n->language = $11;
+ $$ = (Node *)n;
+ };
+
+opt_with: WITH definition { $$ = $2; }
+ | /* EMPTY */ { $$ = NIL; }
+ ;
+
+def_args: '(' def_name_list ')' { $$ = $2; }
+ | '(' ')' { $$ = NIL; }
+ ;
+
+def_name_list: name_list;
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * purge <relname> [before <date>] [after <date>]
+ * or
+ * purge <relname> [after<date>][before <date>]
+ *
+ *****************************************************************************/
+
+PurgeStmt: PURGE relation_name purge_quals
+ {
+ $3->relname = $2;
+ $$ = (Node *)$3;
+ }
+ ;
+
+purge_quals: before_clause
+ {
+ $$ = makeNode(PurgeStmt);
+ $$->beforeDate = $1;
+ $$->afterDate = NULL;
+ }
+ | after_clause
+ {
+ $$ = makeNode(PurgeStmt);
+ $$->beforeDate = NULL;
+ $$->afterDate = $1;
+ }
+ | before_clause after_clause
+ {
+ $$ = makeNode(PurgeStmt);
+ $$->beforeDate = $1;
+ $$->afterDate = $2;
+ }
+ | after_clause before_clause
+ {
+ $$ = makeNode(PurgeStmt);
+ $$->beforeDate = $2;
+ $$->afterDate = $1;
+ }
+ | /*EMPTY*/
+ {
+ $$ = makeNode(PurgeStmt);
+ $$->beforeDate = NULL;
+ $$->afterDate = NULL;
+ }
+ ;
+
+before_clause: BEFORE date { $$ = $2; }
+after_clause: AFTER date { $$ = $2; }
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ *
+ * remove function <funcname>
+ * (REMOVE FUNCTION "funcname" (arg1, arg2, ...))
+ * remove operator <opname>
+ * (REMOVE OPERATOR "opname" (leftoperand_typ rightoperand_typ))
+ * remove type <typename>
+ * (REMOVE TYPE "typename")
+ * remove rule <rulename>
+ * (REMOVE RULE "rulename")
+ *
+ *****************************************************************************/
+
+RemoveStmt: DROP remove_type name
+ {
+ RemoveStmt *n = makeNode(RemoveStmt);
+ n->removeType = $2;
+ n->name = $3;
+ $$ = (Node *)n;
+ }
+ ;
+
+remove_type: AGGREGATE { $$ = AGGREGATE; }
+ | Type { $$ = P_TYPE; }
+ | INDEX { $$ = INDEX; }
+ | RULE { $$ = RULE; }
+ | VIEW { $$ = VIEW; }
+ ;
+
+RemoveFuncStmt: DROP FUNCTION name '(' func_argtypes ')'
+ {
+ RemoveFuncStmt *n = makeNode(RemoveFuncStmt);
+ n->funcname = $3;
+ n->args = $5;
+ $$ = (Node *)n;
+ }
+ ;
+
+func_argtypes: name_list { $$ = $1; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
+RemoveOperStmt: DROP OPERATOR all_Op '(' oper_argtypes ')'
+ {
+ RemoveOperStmt *n = makeNode(RemoveOperStmt);
+ n->opname = $3;
+ n->args = $5;
+ $$ = (Node *)n;
+ }
+ ;
+
+all_Op: Op | MathOp;
+
+MathOp: '+' { $$ = "+"; }
+ | '-' { $$ = "-"; }
+ | '*' { $$ = "*"; }
+ | '/' { $$ = "/"; }
+ | '<' { $$ = "<"; }
+ | '>' { $$ = ">"; }
+ | '=' { $$ = "="; }
+ ;
+
+oper_argtypes: name
+ {
+ elog(WARN, "parser: argument type missing (use NONE for unary operators)");
+ }
+ | name ',' name
+ { $$ = makeList(makeString($1), makeString($3), -1); }
+ | NONE ',' name /* left unary */
+ { $$ = makeList(NULL, makeString($3), -1); }
+ | name ',' NONE /* right unary */
+ { $$ = makeList(makeString($1), NULL, -1); }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * rename <attrname1> in <relname> [*] to <attrname2>
+ * rename <relname1> to <relname2>
+ *
+ *****************************************************************************/
+
+RenameStmt: ALTER TABLE relation_name opt_inh_star
+ RENAME opt_column opt_name TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->relname = $3;
+ n->inh = $4;
+ n->column = $7;
+ n->newname = $9;
+ $$ = (Node *)n;
+ }
+ ;
+
+opt_name: name { $$ = $1; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+opt_column: COLUMN { $$ = COLUMN; }
+ | /*EMPTY*/ { $$ = 0; }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY: Define Rewrite Rule , Define Tuple Rule
+ * Define Rule <old rules >
+ *
+ * only rewrite rule is supported -- ay 9/94
+ *
+ *****************************************************************************/
+
+RuleStmt: CREATE RULE name AS
+ { QueryIsRule=TRUE; }
+ ON event TO event_object where_clause
+ DO opt_instead OptStmtList
+ {
+ RuleStmt *n = makeNode(RuleStmt);
+ n->rulename = $3;
+ n->event = $7;
+ n->object = $9;
+ n->whereClause = $10;
+ n->instead = $12;
+ n->actions = $13;
+ $$ = (Node *)n;
+ }
+ ;
+
+OptStmtList: NOTHING { $$ = NIL; }
+ | OptimizableStmt { $$ = lcons($1, NIL); }
+ | '[' OptStmtBlock ']' { $$ = $2; }
+ ;
+
+OptStmtBlock: OptimizableStmt
+ { $$ = lcons($1, NIL); }
+ | OptimizableStmt ';'
+ { $$ = lcons($1, NIL); }
+ | OptStmtBlock OptimizableStmt
+ { $$ = lappend($1, $2); }
+ ;
+
+event_object: relation_name '.' attr_name
+ {
+ $$ = makeNode(Attr);
+ $$->relname = $1;
+ $$->paramNo = NULL;
+ $$->attrs = lcons(makeString($3), NIL);
+ $$->indirection = NIL;
+ }
+ | relation_name
+ {
+ $$ = makeNode(Attr);
+ $$->relname = $1;
+ $$->paramNo = NULL;
+ $$->attrs = NIL;
+ $$->indirection = NIL;
+ }
+ ;
+
+/* change me to select, update, etc. some day */
+event: SELECT { $$ = CMD_SELECT; }
+ | UPDATE { $$ = CMD_UPDATE; }
+ | DELETE { $$ = CMD_DELETE; }
+ | INSERT { $$ = CMD_INSERT; }
+ ;
+
+opt_instead: INSTEAD { $$ = TRUE; }
+ | /* EMPTY */ { $$ = FALSE; }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * NOTIFY <relation_name> can appear both in rule bodies and
+ * as a query-level command
+ *
+ *****************************************************************************/
+
+NotifyStmt: NOTIFY relation_name
+ {
+ NotifyStmt *n = makeNode(NotifyStmt);
+ n->relname = $2;
+ $$ = (Node *)n;
+ }
+ ;
+
+ListenStmt: LISTEN relation_name
+ {
+ ListenStmt *n = makeNode(ListenStmt);
+ n->relname = $2;
+ $$ = (Node *)n;
+ }
+;
+
+
+/*****************************************************************************
+ *
+ * Transactions:
+ *
+ * abort transaction
+ * (ABORT)
+ * begin transaction
+ * (BEGIN)
+ * end transaction
+ * (END)
+ *
+ *****************************************************************************/
+
+TransactionStmt: ABORT_TRANS TRANSACTION
+ {
+ TransactionStmt *n = makeNode(TransactionStmt);
+ n->command = ABORT_TRANS;
+ $$ = (Node *)n;
+ }
+ | BEGIN_TRANS TRANSACTION
+ {
+ TransactionStmt *n = makeNode(TransactionStmt);
+ n->command = BEGIN_TRANS;
+ $$ = (Node *)n;
+ }
+ | BEGIN_TRANS WORK
+ {
+ TransactionStmt *n = makeNode(TransactionStmt);
+ n->command = BEGIN_TRANS;
+ $$ = (Node *)n;
+ }
+ | COMMIT WORK
+ {
+ TransactionStmt *n = makeNode(TransactionStmt);
+ n->command = END_TRANS;
+ $$ = (Node *)n;
+ }
+ | END_TRANS TRANSACTION
+ {
+ TransactionStmt *n = makeNode(TransactionStmt);
+ n->command = END_TRANS;
+ $$ = (Node *)n;
+ }
+ | ROLLBACK WORK
+ {
+ TransactionStmt *n = makeNode(TransactionStmt);
+ n->command = ABORT_TRANS;
+ $$ = (Node *)n;
+ }
+
+ | ABORT_TRANS
+ {
+ TransactionStmt *n = makeNode(TransactionStmt);
+ n->command = ABORT_TRANS;
+ $$ = (Node *)n;
+ }
+ | BEGIN_TRANS
+ {
+ TransactionStmt *n = makeNode(TransactionStmt);
+ n->command = BEGIN_TRANS;
+ $$ = (Node *)n;
+ }
+ | COMMIT
+ {
+ TransactionStmt *n = makeNode(TransactionStmt);
+ n->command = END_TRANS;
+ $$ = (Node *)n;
+ }
+
+ | END_TRANS
+ {
+ TransactionStmt *n = makeNode(TransactionStmt);
+ n->command = END_TRANS;
+ $$ = (Node *)n;
+ }
+ | ROLLBACK
+ {
+ TransactionStmt *n = makeNode(TransactionStmt);
+ n->command = ABORT_TRANS;
+ $$ = (Node *)n;
+ }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * define view <viewname> '('target-list ')' [where <quals> ]
+ *
+ *****************************************************************************/
+
+ViewStmt: CREATE VIEW name AS RetrieveStmt
+ {
+ ViewStmt *n = makeNode(ViewStmt);
+ n->viewname = $3;
+ n->query = (Query *)$5;
+ $$ = (Node *)n;
+ }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * load "filename"
+ *
+ *****************************************************************************/
+
+LoadStmt: LOAD file_name
+ {
+ LoadStmt *n = makeNode(LoadStmt);
+ n->filename = $2;
+ $$ = (Node *)n;
+ }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * createdb dbname
+ *
+ *****************************************************************************/
+
+CreatedbStmt: CREATE DATABASE database_name
+ {
+ CreatedbStmt *n = makeNode(CreatedbStmt);
+ n->dbname = $3;
+ $$ = (Node *)n;
+ }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * destroydb dbname
+ *
+ *****************************************************************************/
+
+DestroydbStmt: DROP DATABASE database_name
+ {
+ DestroydbStmt *n = makeNode(DestroydbStmt);
+ n->dbname = $3;
+ $$ = (Node *)n;
+ }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * cluster <index_name> on <relation_name>
+ *
+ *****************************************************************************/
+
+ClusterStmt: CLUSTER index_name ON relation_name
+ {
+ ClusterStmt *n = makeNode(ClusterStmt);
+ n->relname = $4;
+ n->indexname = $2;
+ $$ = (Node*)n;
+ }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * vacuum
+ *
+ *****************************************************************************/
+
+VacuumStmt: VACUUM
+ {
+ $$ = (Node *)makeNode(VacuumStmt);
+ }
+ | VACUUM relation_name
+ {
+ VacuumStmt *n = makeNode(VacuumStmt);
+ n->vacrel = $2;
+ $$ = (Node *)n;
+ }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * EXPLAIN query
+ *
+ *****************************************************************************/
+
+ExplainStmt: EXPLAIN explain_options OptimizableStmt
+ {
+ ExplainStmt *n = makeNode(ExplainStmt);
+ n->query = (Query*)$3;
+ n->options = $2;
+ $$ = (Node *)n;
+ }
+ ;
+
+explain_options: WITH name_list
+ { $$ = $2; }
+ | /*EMPTY*/
+ { $$ = NIL; }
+ ;
+
+/*****************************************************************************
+ * *
+ * Optimizable Stmts: *
+ * *
+ * one of the five queries processed by the planner *
+ * *
+ * [ultimately] produces query-trees as specified *
+ * in the query-spec document in ~postgres/ref *
+ * *
+ *****************************************************************************/
+
+OptimizableStmt: RetrieveStmt
+ | CursorStmt
+ | ReplaceStmt
+ | AppendStmt
+ | NotifyStmt
+ | DeleteStmt /* by default all are $$=$1 */
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * INSERT STATEMENTS
+ *
+ *****************************************************************************/
+
+AppendStmt: INSERT INTO relation_name opt_column_list insert_rest
+ {
+ $5->relname = $3;
+ $5->cols = $4;
+ $$ = (Node *)$5;
+ }
+ ;
+
+insert_rest: VALUES '(' exprList ')'
+ {
+ $$ = makeNode(AppendStmt);
+ $$->exprs = $3;
+ $$->fromClause = NIL;
+ $$->whereClause = NULL;
+ }
+ | SELECT exprList from_clause where_clause
+ {
+ $$ = makeNode(AppendStmt);
+ $$->exprs = $2;
+ $$->fromClause = $3;
+ $$->whereClause = $4;
+ }
+ ;
+
+opt_column_list: '(' columnList ')' { $$ = $2; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
+columnList:
+ columnList ',' columnElem
+ { $$ = lappend($1, $3); }
+ | columnElem
+ { $$ = lcons($1, NIL); }
+ ;
+
+columnElem: Id opt_indirection
+ {
+ Ident *id = makeNode(Ident);
+ id->name = $1;
+ id->indirection = $2;
+ $$ = (Node *)id;
+ }
+ ;
+
+exprList: exprList ',' exprElem
+ { $$ = lappend($1, $3); }
+ | exprElem
+ { $$ = lcons($1, NIL); }
+
+ ;
+
+exprElem: a_expr
+ { $$ = (Node *)$1; }
+ | relation_name '.' '*'
+ {
+ Attr *n = makeNode(Attr);
+ n->relname = $1;
+ n->paramNo = NULL;
+ n->attrs = lcons(makeString("*"), NIL);
+ n->indirection = NIL;
+ $$ = (Node *)n;
+ }
+ | '*'
+ {
+ Attr *n = makeNode(Attr);
+ n->relname = "*";
+ n->paramNo = NULL;
+ n->attrs = NIL;
+ n->indirection = NIL;
+ $$ = (Node *)n;
+ }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * DELETE STATEMENTS
+ *
+ *****************************************************************************/
+
+DeleteStmt: DELETE FROM relation_name
+ where_clause
+ {
+ DeleteStmt *n = makeNode(DeleteStmt);
+ n->relname = $3;
+ n->whereClause = $4;
+ $$ = (Node *)n;
+ }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * ReplaceStmt (UPDATE)
+ *
+ *****************************************************************************/
+
+ReplaceStmt: UPDATE relation_name
+ SET res_target_list
+ from_clause
+ where_clause
+ {
+ ReplaceStmt *n = makeNode(ReplaceStmt);
+ n->relname = $2;
+ n->targetList = $4;
+ n->fromClause = $5;
+ n->whereClause = $6;
+ $$ = (Node *)n;
+ }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * CURSOR STATEMENTS
+ *
+ *****************************************************************************/
+
+CursorStmt: DECLARE name opt_binary CURSOR FOR
+ SELECT opt_unique res_target_list2
+ from_clause where_clause sort_clause
+ {
+ CursorStmt *n = makeNode(CursorStmt);
+
+ /* from PORTAL name */
+ /*
+ * 15 august 1991 -- since 3.0 postgres does locking
+ * right, we discovered that portals were violating
+ * locking protocol. portal locks cannot span xacts.
+ * as a short-term fix, we installed the check here.
+ * -- mao
+ */
+ if (!IsTransactionBlock())
+ elog(WARN, "Named portals may only be used in begin/end transaction blocks.");
+
+ n->portalname = $2;
+ n->binary = $3;
+ n->unique = $7;
+ n->targetList = $8;
+ n->fromClause = $9;
+ n->whereClause = $10;
+ n->orderClause = $11;
+ $$ = (Node *)n;
+ }
+ ;
+
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * SELECT STATEMENTS
+ *
+ *****************************************************************************/
+
+RetrieveStmt: SELECT opt_unique res_target_list2
+ result from_clause where_clause
+ group_clause having_clause
+ sort_clause
+ {
+ RetrieveStmt *n = makeNode(RetrieveStmt);
+ n->unique = $2;
+ n->targetList = $3;
+ n->into = $4;
+ n->fromClause = $5;
+ n->whereClause = $6;
+ n->groupClause = $7;
+ n->havingClause = $8;
+ n->orderClause = $9;
+ $$ = (Node *)n;
+ }
+ ;
+
+result: INTO TABLE relation_name
+ { $$= $3; /* should check for archive level */ }
+ | /*EMPTY*/
+ { $$ = NULL; }
+ ;
+
+opt_unique: DISTINCT { $$ = "*"; }
+ | DISTINCT ON Id { $$ = $3; }
+ | /*EMPTY*/ { $$ = NULL;}
+ ;
+
+sort_clause: ORDER BY sortby_list { $$ = $3; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
+sortby_list: sortby
+ { $$ = lcons($1, NIL); }
+ | sortby_list ',' sortby
+ { $$ = lappend($1, $3); }
+ ;
+
+sortby: Id OptUseOp
+ {
+ $$ = makeNode(SortBy);
+ $$->name = $1;
+ $$->useOp = $2;
+ }
+ | attr OptUseOp
+ {
+ yyerror("parse error: use 'sort by attribute_name'");
+ }
+ ;
+
+OptUseOp: USING Op { $$ = $2; }
+ | USING '<' { $$ = "<"; }
+ | USING '>' { $$ = ">"; }
+ | ASC { $$ = "<"; }
+ | DESC { $$ = ">"; }
+ | /*EMPTY*/ { $$ = "<"; /*default*/ }
+ ;
+
+
+index_params: index_elem { $$ = lcons($1,NIL); }
+ | func_index { $$ = lcons($1,NIL); }
+ ;
+
+/*index_list:
+ index_list ',' index_elem
+ { $$ = lappend($1, $3); }
+ | index_elem
+ { $$ = lcons($1, NIL); }
+ ;*/
+
+func_index: name '(' name_list ')' opt_class
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = $1;
+ $$->args = $3;
+ $$->class = $5;
+ }
+ ;
+
+index_elem: attr_name opt_class
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = $1;
+ $$->args = NIL;
+ $$->class = $2;
+ }
+ ;
+
+opt_class: class
+ | WITH class { $$ = $2; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+/*
+ * jimmy bell-style recursive queries aren't supported in the
+ * current system.
+ *
+ * ...however, recursive addattr and rename supported. make special
+ * cases for these.
+ *
+ * XXX i believe '*' should be the default behavior, but...
+ */
+opt_inh_star: '*' { $$ = TRUE; }
+ | /*EMPTY*/ { $$ = FALSE; }
+ ;
+
+relation_name_list: name_list ;
+
+name_list: name
+ { $$=lcons(makeString($1),NIL); }
+ | name_list ',' name
+ { $$=lappend($1,makeString($3)); }
+ ;
+
+group_clause: GROUP BY groupby_list { $$ = $3; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
+groupby_list: groupby { $$ = lcons($1, NIL); }
+ | groupby_list ',' groupby { $$ = lappend($1, $3); }
+ ;
+
+groupby: Id
+ {
+ Ident *n = makeNode(Ident);
+ n->name = $1;
+ n->indirection = NULL;
+ $$ = (Node*)n;
+ }
+ | attr
+ {
+ $$ = (Node*)$1;
+ }
+ ;
+
+having_clause: HAVING a_expr { $$ = $2; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+/*****************************************************************************
+ *
+ * clauses common to all Optimizable Stmts:
+ * from_clause -
+ * where_clause -
+ *
+ *****************************************************************************/
+
+from_clause: FROM from_list { $$ = $2; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
+from_list: from_list ',' from_val
+ { $$ = lappend($1, $3); }
+ | from_val
+ { $$ = lcons($1, NIL); }
+ ;
+
+from_val: relation_expr AS var_name
+ {
+ $$ = makeNode(RangeVar);
+ $$->relExpr = $1;
+ $$->name = $3;
+ }
+ | relation_expr var_name
+ {
+ $$ = makeNode(RangeVar);
+ $$->relExpr = $1;
+ $$->name = $2;
+ }
+ | relation_expr
+ {
+ $$ = makeNode(RangeVar);
+ $$->relExpr = $1;
+ $$->name = NULL;
+ }
+ ;
+
+where_clause: WHERE a_expr { $$ = $2; }
+ | /*EMPTY*/ { $$ = NULL; /* no qualifiers */ }
+ ;
+
+relation_expr: relation_name
+ {
+ /* normal relations */
+ $$ = makeNode(RelExpr);
+ $$->relname = $1;
+ $$->inh = FALSE;
+ $$->timeRange = NULL;
+ }
+ | relation_name '*' %prec '='
+ {
+ /* inheiritance query */
+ $$ = makeNode(RelExpr);
+ $$->relname = $1;
+ $$->inh = TRUE;
+ $$->timeRange = NULL;
+ }
+ | relation_name time_range
+ {
+ /* time-qualified query */
+ $$ = makeNode(RelExpr);
+ $$->relname = $1;
+ $$->inh = FALSE;
+ $$->timeRange = $2;
+ }
+ ;
+
+
+time_range: '[' opt_range_start ',' opt_range_end ']'
+ {
+ $$ = makeNode(TimeRange);
+ $$->startDate = $2;
+ $$->endDate = $4;
+ }
+ | '[' date ']'
+ {
+ $$ = makeNode(TimeRange);
+ $$->startDate = $2;
+ $$->endDate = NULL;
+ }
+ ;
+
+opt_range_start: date
+ | /*EMPTY*/ { $$ = "epoch"; }
+ ;
+
+opt_range_end: date
+ | /*EMPTY*/ { $$ = "now"; }
+ ;
+
+opt_array_bounds: '[' ']' nest_array_bounds
+ { $$ = lcons(makeInteger(-1), $3); }
+ | '[' Iconst ']' nest_array_bounds
+ { $$ = lcons(makeInteger($2), $4); }
+ | /* EMPTY */
+ { $$ = NIL; }
+ ;
+
+nest_array_bounds: '[' ']' nest_array_bounds
+ { $$ = lcons(makeInteger(-1), $3); }
+ | '[' Iconst ']' nest_array_bounds
+ { $$ = lcons(makeInteger($2), $4); }
+ | /*EMPTY*/
+ { $$ = NIL; }
+ ;
+
+typname: name
+ {
+ char *tname = xlateSqlType($1);
+ $$ = makeNode(TypeName);
+ $$->name = tname;
+
+ /* Is this the name of a complex type? If so, implement
+ * it as a set.
+ */
+ if (!strcmp(saved_relname, tname)) {
+ /* This attr is the same type as the relation
+ * being defined. The classic example: create
+ * emp(name=text,mgr=emp)
+ */
+ $$->setof = TRUE;
+ }else if (get_typrelid((Type)type(tname))
+ != InvalidOid) {
+ /* (Eventually add in here that the set can only
+ * contain one element.)
+ */
+ $$->setof = TRUE;
+ } else {
+ $$->setof = FALSE;
+ }
+ }
+ | SETOF name
+ {
+ $$ = makeNode(TypeName);
+ $$->name = $2;
+ $$->setof = TRUE;
+ }
+ ;
+
+Typename: typname opt_array_bounds
+ {
+ $$ = $1;
+ $$->arrayBounds = $2;
+ }
+ | name '(' Iconst ')'
+ {
+ /*
+ * The following implements char() and varchar().
+ * We do it here instead of the 'typname:' production
+ * because we don't want to allow arrays of varchar().
+ * I haven't thought about whether that will work or not.
+ * - ay 6/95
+ */
+ $$ = makeNode(TypeName);
+ if (!strcasecmp($1, "char")) {
+ $$->name = "bpchar"; /* strdup("bpchar"); */
+ } else if (!strcasecmp($1, "varchar")) {
+ $$->name = "varchar"; /* strdup("varchar"); */
+ } else {
+ yyerror("parse error");
+ }
+ if ($3 < 1) {
+ elog(WARN, "length for '%s' type must be at least 1",
+ $1);
+ } else if ($3 > 4096) {
+ /* we can store a char() of length up to the size
+ of a page (8KB) - page headers and friends but
+ just to be safe here... - ay 6/95 */
+ elog(WARN, "length for '%s' type cannot exceed 4096",
+ $1);
+ }
+ /* we actually implement this sort of like a varlen, so
+ the first 4 bytes is the length. (the difference
+ between this and "text" is that we blank-pad and
+ truncate where necessary */
+ $$->typlen = 4 + $3;
+ }
+ ;
+
+
+/*****************************************************************************
+ *
+ * expression grammar, still needs some cleanup
+ *
+ *****************************************************************************/
+
+a_expr: attr opt_indirection
+ {
+ $1->indirection = $2;
+ $$ = (Node *)$1;
+ }
+ | AexprConst
+ { $$ = $1; }
+ | '-' a_expr %prec UMINUS
+ { $$ = makeA_Expr(OP, "-", NULL, $2); }
+ | a_expr '+' a_expr
+ { $$ = makeA_Expr(OP, "+", $1, $3); }
+ | a_expr '-' a_expr
+ { $$ = makeA_Expr(OP, "-", $1, $3); }
+ | a_expr '/' a_expr
+ { $$ = makeA_Expr(OP, "/", $1, $3); }
+ | a_expr '*' a_expr
+ { $$ = makeA_Expr(OP, "*", $1, $3); }
+ | a_expr '<' a_expr
+ { $$ = makeA_Expr(OP, "<", $1, $3); }
+ | a_expr '>' a_expr
+ { $$ = makeA_Expr(OP, ">", $1, $3); }
+ | a_expr '=' a_expr
+ { $$ = makeA_Expr(OP, "=", $1, $3); }
+ | ':' a_expr
+ { $$ = makeA_Expr(OP, ":", NULL, $2); }
+ | ';' a_expr
+ { $$ = makeA_Expr(OP, ";", NULL, $2); }
+ | '|' a_expr
+ { $$ = makeA_Expr(OP, "|", NULL, $2); }
+ | AexprConst TYPECAST Typename
+ {
+ /* AexprConst can be either A_Const or ParamNo */
+ if (nodeTag($1) == T_A_Const) {
+ ((A_Const *)$1)->typename = $3;
+ }else {
+ ((ParamNo *)$1)->typename = $3;
+ }
+ $$ = (Node *)$1;
+ }
+ | CAST AexprConst AS Typename
+ {
+ /* AexprConst can be either A_Const or ParamNo */
+ if (nodeTag($2) == T_A_Const) {
+ ((A_Const *)$2)->typename = $4;
+ }else {
+ ((ParamNo *)$2)->typename = $4;
+ }
+ $$ = (Node *)$2;
+ }
+ | '(' a_expr ')'
+ { $$ = $2; }
+ | a_expr Op a_expr
+ { $$ = makeA_Expr(OP, $2, $1, $3); }
+ | a_expr LIKE a_expr
+ { $$ = makeA_Expr(OP, "~~", $1, $3); }
+ | a_expr NOT LIKE a_expr
+ { $$ = makeA_Expr(OP, "!~~", $1, $4); }
+ | Op a_expr
+ { $$ = makeA_Expr(OP, $1, NULL, $2); }
+ | a_expr Op
+ { $$ = makeA_Expr(OP, $2, $1, NULL); }
+ | Id
+ { /* could be a column name or a relation_name */
+ Ident *n = makeNode(Ident);
+ n->name = $1;
+ n->indirection = NULL;
+ $$ = (Node *)n;
+ }
+ | name '(' '*' ')'
+ {
+ FuncCall *n = makeNode(FuncCall);
+ Ident *star = makeNode(Ident);
+
+ /* cheap hack for aggregate (eg. count) */
+ star->name = "oid";
+ n->funcname = $1;
+ n->args = lcons(star, NIL);
+ $$ = (Node *)n;
+ }
+ | name '(' ')'
+ {
+ FuncCall *n = makeNode(FuncCall);
+ n->funcname = $1;
+ n->args = NIL;
+ $$ = (Node *)n;
+ }
+ | name '(' expr_list ')'
+ {
+ FuncCall *n = makeNode(FuncCall);
+ n->funcname = $1;
+ n->args = $3;
+ $$ = (Node *)n;
+ }
+ | a_expr ISNULL
+ { $$ = makeA_Expr(ISNULL, NULL, $1, NULL); }
+ | a_expr NOTNULL
+ { $$ = makeA_Expr(NOTNULL, NULL, $1, NULL); }
+ | a_expr AND a_expr
+ { $$ = makeA_Expr(AND, NULL, $1, $3); }
+ | a_expr OR a_expr
+ { $$ = makeA_Expr(OR, NULL, $1, $3); }
+ | NOT a_expr
+ { $$ = makeA_Expr(NOT, NULL, NULL, $2); }
+ ;
+
+opt_indirection: '[' a_expr ']' opt_indirection
+ {
+ A_Indices *ai = makeNode(A_Indices);
+ ai->lidx = NULL;
+ ai->uidx = $2;
+ $$ = lcons(ai, $4);
+ }
+ | '[' a_expr ':' a_expr ']' opt_indirection
+ {
+ A_Indices *ai = makeNode(A_Indices);
+ ai->lidx = $2;
+ ai->uidx = $4;
+ $$ = lcons(ai, $6);
+ }
+ | /* EMPTY */
+ { $$ = NIL; }
+ ;
+
+expr_list: a_expr
+ { $$ = lcons($1, NIL); }
+ | expr_list ',' a_expr
+ { $$ = lappend($1, $3); }
+ ;
+
+attr: relation_name '.' attrs
+ {
+ $$ = makeNode(Attr);
+ $$->relname = $1;
+ $$->paramNo = NULL;
+ $$->attrs = $3;
+ $$->indirection = NULL;
+ }
+ | ParamNo '.' attrs
+ {
+ $$ = makeNode(Attr);
+ $$->relname = NULL;
+ $$->paramNo = $1;
+ $$->attrs = $3;
+ $$->indirection = NULL;
+ }
+ ;
+
+attrs: attr_name
+ { $$ = lcons(makeString($1), NIL); }
+ | attrs '.' attr_name
+ { $$ = lappend($1, makeString($3)); }
+ | attrs '.' '*'
+ { $$ = lappend($1, makeString("*")); }
+ ;
+
+
+/*****************************************************************************
+ *
+ * target lists
+ *
+ *****************************************************************************/
+
+res_target_list: res_target_list ',' res_target_el
+ { $$ = lappend($1,$3); }
+ | res_target_el
+ { $$ = lcons($1, NIL); }
+ | '*'
+ {
+ ResTarget *rt = makeNode(ResTarget);
+ Attr *att = makeNode(Attr);
+ att->relname = "*";
+ att->paramNo = NULL;
+ att->attrs = NULL;
+ att->indirection = NIL;
+ rt->name = NULL;
+ rt->indirection = NULL;
+ rt->val = (Node *)att;
+ $$ = lcons(rt, NIL);
+ }
+ ;
+
+res_target_el: Id opt_indirection '=' a_expr
+ {
+ $$ = makeNode(ResTarget);
+ $$->name = $1;
+ $$->indirection = $2;
+ $$->val = (Node *)$4;
+ }
+ | attr opt_indirection
+ {
+ $$ = makeNode(ResTarget);
+ $$->name = NULL;
+ $$->indirection = $2;
+ $$->val = (Node *)$1;
+ }
+ | relation_name '.' '*'
+ {
+ Attr *att = makeNode(Attr);
+ att->relname = $1;
+ att->paramNo = NULL;
+ att->attrs = lcons(makeString("*"), NIL);
+ att->indirection = NIL;
+ $$ = makeNode(ResTarget);
+ $$->name = NULL;
+ $$->indirection = NULL;
+ $$->val = (Node *)att;
+ }
+ ;
+
+/*
+** target list for select.
+** should get rid of the other but is still needed by the defunct retrieve into
+** and update (uses a subset)
+*/
+res_target_list2:
+ res_target_list2 ',' res_target_el2
+ { $$ = lappend($1, $3); }
+ | res_target_el2
+ { $$ = lcons($1, NIL); }
+ | '*'
+ {
+ ResTarget *rt = makeNode(ResTarget);
+ Attr *att = makeNode(Attr);
+ att->relname = "*";
+ att->paramNo = NULL;
+ att->attrs = NULL;
+ att->indirection = NIL;
+ rt->name = NULL;
+ rt->indirection = NULL;
+ rt->val = (Node *)att;
+ $$ = lcons(rt, NIL);
+ }
+ ;
+
+/* AS is not optional because shift/red conflict with unary ops */
+res_target_el2: a_expr AS Id
+ {
+ $$ = makeNode(ResTarget);
+ $$->name = $3;
+ $$->indirection = NULL;
+ $$->val = (Node *)$1;
+ }
+ | a_expr
+ {
+ $$ = makeNode(ResTarget);
+ $$->name = NULL;
+ $$->indirection = NULL;
+ $$->val = (Node *)$1;
+ }
+ | relation_name '.' '*'
+ {
+ Attr *att = makeNode(Attr);
+ att->relname = $1;
+ att->paramNo = NULL;
+ att->attrs = lcons(makeString("*"), NIL);
+ att->indirection = NIL;
+ $$ = makeNode(ResTarget);
+ $$->name = NULL;
+ $$->indirection = NULL;
+ $$->val = (Node *)att;
+ }
+ ;
+
+opt_id: Id { $$ = $1; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
+relation_name: SpecialRuleRelation
+ {
+ $$ = $1;
+ strcpy(saved_relname, $1);
+ }
+ | Id
+ {
+ /* disallow refs to magic system tables */
+ if (strcmp(LogRelationName, $1) == 0
+ || strcmp(VariableRelationName, $1) == 0
+ || strcmp(TimeRelationName, $1) == 0
+ || strcmp(MagicRelationName, $1) == 0) {
+ elog(WARN, "%s cannot be accessed by users", $1);
+ } else {
+ $$ = $1;
+ }
+ strcpy(saved_relname, $1);
+ }
+ ;
+
+database_name: Id { $$ = $1; };
+access_method: Id { $$ = $1; };
+attr_name: Id { $$ = $1; };
+class: Id { $$ = $1; };
+index_name: Id { $$ = $1; };
+var_name: Id { $$ = $1; };
+name: Id { $$ = $1; };
+
+date: Sconst { $$ = $1; };
+file_name: Sconst { $$ = $1; };
+recipe_name: Id { $$ = $1; };
+
+AexprConst: Iconst
+ {
+ A_Const *n = makeNode(A_Const);
+ n->val.type = T_Integer;
+ n->val.val.ival = $1;
+ $$ = (Node *)n;
+ }
+ | FCONST
+ {
+ A_Const *n = makeNode(A_Const);
+ n->val.type = T_Float;
+ n->val.val.dval = $1;
+ $$ = (Node *)n;
+ }
+ | Sconst
+ {
+ A_Const *n = makeNode(A_Const);
+ n->val.type = T_String;
+ n->val.val.str = $1;
+ $$ = (Node *)n;
+ }
+ | ParamNo
+ { $$ = (Node *)$1; }
+ | Pnull
+ {
+ A_Const *n = makeNode(A_Const);
+ n->val.type = T_Null;
+ $$ = (Node *)n;
+ }
+ ;
+
+ParamNo: PARAM
+ {
+ $$ = makeNode(ParamNo);
+ $$->number = $1;
+ }
+ ;
+
+NumConst: Iconst { $$ = makeInteger($1); }
+ | FCONST { $$ = makeFloat($1); }
+ ;
+
+Iconst: ICONST { $$ = $1; };
+Sconst: SCONST { $$ = $1; };
+
+Id: IDENT { $$ = $1; };
+
+SpecialRuleRelation: CURRENT
+ {
+ if (QueryIsRule)
+ $$ = "*CURRENT*";
+ else
+ elog(WARN,"CURRENT used in non-rule query");
+ }
+ | NEW
+ {
+ if (QueryIsRule)
+ $$ = "*NEW*";
+ else
+ elog(WARN,"NEW used in non-rule query");
+ }
+ ;
+
+Type: P_TYPE;
+Pnull: PNULL;
+
+
+%%
+
+static Node *makeA_Expr(int op, char *opname, Node *lexpr, Node *rexpr)
+{
+ A_Expr *a = makeNode(A_Expr);
+ a->oper = op;
+ a->opname = opname;
+ a->lexpr = lexpr;
+ a->rexpr = rexpr;
+ return (Node *)a;
+}
+
+static char *
+xlateSqlType(char *name)
+{
+ if (!strcasecmp(name,"int") ||
+ !strcasecmp(name,"integer"))
+ return "int4"; /* strdup("int4") -- strdup leaks memory here */
+ else if (!strcasecmp(name, "smallint"))
+ return "int2";
+ else if (!strcasecmp(name, "float") ||
+ !strcasecmp(name, "real"))
+ return "float4";
+ else
+ return name;
+}
+
+void parser_init(Oid *typev, int nargs)
+{
+ QueryIsRule = false;
+ saved_relname[0]= '\0';
+
+ param_type_init(typev, nargs);
+}
+
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
new file mode 100644
index 00000000000..b6cd549bf9b
--- /dev/null
+++ b/src/backend/parser/keywords.c
@@ -0,0 +1,179 @@
+/*-------------------------------------------------------------------------
+ *
+ * keywords.c--
+ * lexical token lookup for reserved words in postgres SQL
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <ctype.h>
+#include <string.h>
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+#include "parse.h"
+#include "utils/elog.h"
+#include "parser/keywords.h"
+#include "parser/dbcommands.h" /* createdb, destroydb stop_vacuum */
+
+
+/*
+ * List of (keyword-name, keyword-token-value) pairs.
+ *
+ * !!WARNING!!: This list must be sorted, because binary
+ * search is used to locate entries.
+ */
+static ScanKeyword ScanKeywords[] = {
+ /* name value */
+ { "abort", ABORT_TRANS },
+ { "acl", ACL },
+ { "add", ADD },
+ { "after", AFTER },
+ { "aggregate", AGGREGATE },
+ { "all", ALL },
+ { "alter", ALTER },
+ { "and", AND },
+ { "append", APPEND },
+ { "archIve", ARCHIVE }, /* XXX crooked: I < _ */
+ { "arch_store", ARCH_STORE },
+ { "archive", ARCHIVE }, /* XXX crooked: i > _ */
+ { "as", AS },
+ { "asc", ASC },
+ { "backward", BACKWARD },
+ { "before", BEFORE },
+ { "begin", BEGIN_TRANS },
+ { "binary", BINARY },
+ { "by", BY },
+ { "cast", CAST },
+ { "change", CHANGE },
+ { "close", CLOSE },
+ { "cluster", CLUSTER },
+ { "column", COLUMN },
+ { "commit", COMMIT },
+ { "copy", COPY },
+ { "create", CREATE },
+ { "current", CURRENT },
+ { "cursor", CURSOR },
+ { "database", DATABASE },
+ { "declare", DECLARE },
+ { "delete", DELETE },
+ { "delimiters", DELIMITERS },
+ { "desc", DESC },
+ { "distinct", DISTINCT },
+ { "do", DO },
+ { "drop", DROP },
+ { "end", END_TRANS },
+ { "execute", EXECUTE },
+ { "explain", EXPLAIN },
+ { "extend", EXTEND },
+ { "fetch", FETCH },
+ { "for", FOR },
+ { "forward", FORWARD },
+ { "from", FROM },
+ { "function", FUNCTION },
+ { "grant", GRANT },
+ { "group", GROUP },
+ { "having", HAVING },
+ { "heavy", HEAVY },
+ { "in", IN },
+ { "index", INDEX },
+ { "inherits", INHERITS },
+ { "insert", INSERT },
+ { "instead", INSTEAD },
+ { "into", INTO },
+ { "isnull", ISNULL },
+ { "language", LANGUAGE },
+ { "light", LIGHT },
+ { "like", LIKE },
+ { "listen", LISTEN },
+ { "load", LOAD },
+ { "merge", MERGE },
+ { "move", MOVE },
+ { "new", NEW },
+ { "none", NONE },
+ { "not", NOT },
+ { "nothing", NOTHING },
+ { "notify", NOTIFY },
+ { "notnull", NOTNULL },
+ { "null", PNULL },
+ { "on", ON },
+ { "operator", OPERATOR },
+ { "option", OPTION },
+ { "or", OR },
+ { "order", ORDER },
+ { "privileges", PRIVILEGES },
+ { "public", PUBLIC },
+ { "purge", PURGE },
+ { "recipe", RECIPE },
+ { "rename", RENAME },
+ { "replace", REPLACE },
+ { "retrieve", RETRIEVE },
+ { "returns", RETURNS },
+ { "revoke", REVOKE },
+ { "rollback", ROLLBACK },
+ { "rule", RULE },
+ { "select", SELECT },
+ { "set", SET },
+ { "setof", SETOF },
+ { "stdin", STDIN },
+ { "stdout", STDOUT },
+ { "store", STORE },
+ { "table", TABLE },
+ { "to", TO },
+ { "transaction", TRANSACTION },
+ { "type", P_TYPE },
+ { "update", UPDATE },
+ { "using", USING },
+ { "vacuum", VACUUM },
+ { "values", VALUES },
+ { "version", VERSION },
+ { "view", VIEW },
+ { "where", WHERE },
+ { "with", WITH },
+ { "work", WORK },
+};
+
+ScanKeyword *
+ScanKeywordLookup(char *text)
+{
+ ScanKeyword *low = &ScanKeywords[0];
+ ScanKeyword *high = endof(ScanKeywords) - 1;
+ ScanKeyword *middle;
+ int difference;
+
+ while (low <= high) {
+ middle = low + (high - low) / 2;
+ /* keywords case-insensitive (for SQL) -- ay 8/94 */
+ difference = strcasecmp(middle->name, text);
+ if (difference == 0)
+ return (middle);
+ else if (difference < 0)
+ low = middle + 1;
+ else
+ high = middle - 1;
+ }
+
+ return (NULL);
+}
+
+char*
+AtomValueGetString(int atomval)
+{
+ ScanKeyword *low = &ScanKeywords[0];
+ ScanKeyword *high = endof(ScanKeywords) - 1;
+ int keyword_list_length = (high-low);
+ int i;
+
+ for (i=0; i < keyword_list_length ; i++ )
+ if (ScanKeywords[i].value == atomval )
+ return(ScanKeywords[i].name);
+
+ elog(WARN,"AtomGetString called with bogus atom # : %d", atomval );
+ return(NULL);
+}
diff --git a/src/backend/parser/keywords.h b/src/backend/parser/keywords.h
new file mode 100644
index 00000000000..d26d76fbaeb
--- /dev/null
+++ b/src/backend/parser/keywords.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * keywords.h--
+ * string,atom lookup thingy, reduces strcmp traffic greatly
+ * in the bowels of the system. Look for actual defs in lib/C/atoms.c
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: keywords.h,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef KEYWORDS_H
+#define KEYWORDS_H
+
+typedef struct ScanKeyword {
+ char *name;
+ int value;
+} ScanKeyword;
+
+extern ScanKeyword *ScanKeywordLookup(char *text);
+extern char* AtomValueGetString(int atomval);
+
+#endif /* KEYWORDS_H */
diff --git a/src/backend/parser/parse_query.c b/src/backend/parser/parse_query.c
new file mode 100644
index 00000000000..37c955017ef
--- /dev/null
+++ b/src/backend/parser/parse_query.c
@@ -0,0 +1,653 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_query.c--
+ * take an "optimizable" stmt and make the query tree that
+ * the planner requires.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/parser/Attic/parse_query.c,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <ctype.h>
+#include <string.h>
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "utils/tqual.h"
+#include "access/tupmacs.h"
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/acl.h" /* for ACL_NO_PRIV_WARNING */
+#include "utils/rel.h" /* Relation stuff */
+
+#include "utils/syscache.h"
+#include "catalog/pg_type.h"
+#include "catalog_utils.h"
+#include "parser/parse_query.h"
+/* #include "parser/io.h" */
+#include "utils/lsyscache.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/makefuncs.h"
+
+Oid *param_type_info;
+int pfunc_num_args;
+
+extern int Quiet;
+
+
+/* given range variable, return id of variable; position starts with 1 */
+int
+RangeTablePosn(List *rtable, char *rangevar)
+{
+ int index;
+ List *temp;
+
+ index = 1;
+/* temp = pstate->p_rtable; */
+ temp = rtable;
+ while (temp != NIL) {
+ RangeTblEntry *rt_entry = lfirst(temp);
+
+ if (!strcmp(rt_entry->refname, rangevar))
+ return index;
+
+ temp = lnext(temp);
+ index++;
+ }
+ return(0);
+}
+
+char*
+VarnoGetRelname(ParseState *pstate, int vnum)
+{
+ int i;
+ List *temp = pstate->p_rtable;
+ for( i = 1; i < vnum ; i++)
+ temp = lnext(temp);
+ return(((RangeTblEntry*)lfirst(temp))->relname);
+}
+
+
+RangeTblEntry *
+makeRangeTableEntry(char *relname,
+ bool inh,
+ TimeRange *timeRange,
+ char *refname)
+{
+ Relation relation;
+ RangeTblEntry *ent = makeNode(RangeTblEntry);
+
+ ent->relname = pstrdup(relname);
+ ent->refname = refname;
+
+ relation = heap_openr(ent->relname);
+ if (relation == NULL) {
+ elog(WARN,"%s: %s",
+ relname, ACL_NO_PRIV_WARNING);
+ }
+
+ /*
+ * Flags - zero or more from archive,inheritance,union,version
+ * or recursive (transitive closure)
+ * [we don't support them all -- ay 9/94 ]
+ */
+ ent->inh = inh;
+
+ ent->timeRange = timeRange;
+
+ /* RelOID */
+ ent->relid = RelationGetRelationId(relation);
+
+ /*
+ * close the relation we're done with it for now.
+ */
+ heap_close(relation);
+ return ent;
+}
+
+/*
+ * expandAll -
+ * makes a list of attributes
+ * assumes reldesc caching works
+ */
+List *
+expandAll(ParseState* pstate, char *relname, int *this_resno)
+{
+ Relation rdesc;
+ List *tall = NIL;
+ Var *varnode;
+ int i, maxattrs, first_resno;
+ int type_id, type_len, vnum;
+ char *physical_relname;
+
+ first_resno = *this_resno;
+
+ /* printf("\nExpanding %.*s.all\n", NAMEDATALEN, relname); */
+ 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);
+ }
+
+ physical_relname = VarnoGetRelname(pstate, vnum);
+
+ rdesc = heap_openr(physical_relname);
+
+ if (rdesc == NULL ) {
+ elog(WARN,"Unable to expand all -- heap_openr failed on %s",
+ physical_relname);
+ return NIL;
+ }
+ maxattrs = RelationGetNumberOfAttributes(rdesc);
+
+ for ( i = maxattrs-1 ; i > -1 ; --i ) {
+ char *attrname;
+ TargetEntry *rte = makeNode(TargetEntry);
+
+ attrname = pstrdup ((rdesc->rd_att->attrs[i]->attname).data);
+ varnode = (Var*)make_var(pstate, relname, attrname, &type_id);
+ type_len = (int)tlen(get_id_type(type_id));
+
+ /* Even if the elements making up a set are complex, the
+ * set itself is not. */
+
+ rte->resdom = makeResdom((AttrNumber) i + first_resno,
+ (Oid)type_id,
+ (Size)type_len,
+ attrname,
+ (Index)0,
+ (Oid)0,
+ 0);
+ rte->expr = (Node *)varnode;
+ tall = lcons(rte, tall);
+ }
+
+ /*
+ * Close the reldesc - we're done with it now
+ */
+ heap_close(rdesc);
+ *this_resno = first_resno + maxattrs;
+ return(tall);
+}
+
+TimeQual
+makeTimeRange(char *datestring1,
+ char *datestring2,
+ int timecode) /* 0 = snapshot , 1 = timerange */
+{
+ TimeQual qual;
+ AbsoluteTime t1,t2;
+
+ switch (timecode) {
+ case 0:
+ if (datestring1 == NULL) {
+ elog(WARN, "MakeTimeRange: bad snapshot arg");
+ }
+ t1 = nabstimein(datestring1);
+ if (!AbsoluteTimeIsValid(t1)) {
+ elog(WARN, "bad snapshot time: \"%s\"",
+ datestring1);
+ }
+ qual = TimeFormSnapshotTimeQual(t1);
+ break;
+ case 1:
+ if (datestring1 == NULL) {
+ t1 = NOSTART_ABSTIME;
+ } else {
+ t1 = nabstimein(datestring1);
+ if (!AbsoluteTimeIsValid(t1)) {
+ elog(WARN,
+ "bad range start time: \"%s\"",
+ datestring1);
+ }
+ }
+ if (datestring2 == NULL) {
+ t2 = NOEND_ABSTIME;
+ } else {
+ t2 = nabstimein(datestring2);
+ if (!AbsoluteTimeIsValid(t2)) {
+ elog(WARN,
+ "bad range end time: \"%s\"",
+ datestring2);
+ }
+ }
+ qual = TimeFormRangedTimeQual(t1,t2);
+ break;
+ default:
+ elog(WARN, "MakeTimeRange: internal parser error");
+ }
+ return qual;
+}
+
+static void
+disallow_setop(char *op, Type optype, Node *operand)
+{
+ if (operand==NULL)
+ return;
+
+ if (nodeTag(operand) == T_Iter) {
+ elog(NOTICE, "An operand to the '%s' operator returns a set of %s,",
+ op, tname(optype));
+ elog(WARN, "but '%s' takes single values, not sets.",
+ op);
+ }
+}
+
+static Node *
+make_operand(char *opname,
+ Node *tree,
+ int orig_typeId,
+ int true_typeId)
+{
+ Node *result;
+ Type true_type;
+ Datum val;
+ Oid infunc;
+
+ if (tree != NULL) {
+ result = tree;
+ true_type = get_id_type(true_typeId);
+ disallow_setop(opname, true_type, result);
+ if (true_typeId != orig_typeId) { /* must coerce */
+ Const *con= (Const *)result;
+
+ Assert(nodeTag(result)==T_Const);
+ val = (Datum)textout((struct varlena *)
+ con->constvalue);
+ infunc = typeid_get_retinfunc(true_typeId);
+ con = makeNode(Const);
+ con->consttype = true_typeId;
+ con->constlen = tlen(true_type);
+ con->constvalue = (Datum)fmgr(infunc,
+ val,
+ get_typelem(true_typeId),
+ -1 /* for varchar() type */);
+ con->constisnull = false;
+ con->constbyval = true;
+ con->constisset = false;
+ result = (Node *)con;
+ }
+ }else {
+ Const *con= makeNode(Const);
+
+ con->consttype = true_typeId;
+ con->constlen = 0;
+ con->constvalue = (Datum)(struct varlena *)NULL;
+ con->constisnull = true;
+ con->constbyval = true;
+ con->constisset = false;
+ result = (Node *)con;
+ }
+
+ return result;
+}
+
+
+Expr *
+make_op(char *opname, Node *ltree, Node *rtree)
+{
+ int ltypeId, rtypeId;
+ Operator temp;
+ OperatorTupleForm opform;
+ Oper *newop;
+ Node *left, *right;
+ Expr *result;
+
+ if (rtree == NULL) {
+
+ /* right operator */
+ ltypeId = (ltree==NULL) ? UNKNOWNOID : exprType(ltree);
+ temp = right_oper(opname, ltypeId);
+ opform = (OperatorTupleForm) GETSTRUCT(temp);
+ left = make_operand(opname, ltree, ltypeId, opform->oprleft);
+ right = NULL;
+
+ }else if (ltree == NULL) {
+
+ /* left operator */
+ rtypeId = (rtree==NULL) ? UNKNOWNOID : exprType(rtree);
+ temp = left_oper(opname, rtypeId);
+ opform = (OperatorTupleForm) GETSTRUCT(temp);
+ right = make_operand(opname, rtree, rtypeId, opform->oprright);
+ left = NULL;
+
+ }else {
+
+ /* binary operator */
+ ltypeId = (ltree==NULL) ? UNKNOWNOID : exprType(ltree);
+ rtypeId = (rtree==NULL) ? UNKNOWNOID : exprType(rtree);
+ temp = oper(opname, ltypeId, rtypeId);
+ opform = (OperatorTupleForm) GETSTRUCT(temp);
+ left = make_operand(opname, ltree, ltypeId, opform->oprleft);
+ right = make_operand(opname, rtree, rtypeId, opform->oprright);
+ }
+
+ newop = makeOper(oprid(temp), /* opno */
+ InvalidOid, /* opid */
+ opform->oprresult, /* operator result type */
+ 0,
+ NULL);
+
+ result = makeNode(Expr);
+ result->typeOid = opform->oprresult;
+ result->opType = OP_EXPR;
+ result->oper = (Node *)newop;
+
+ if (!left) {
+ result->args = lcons(right, NIL);
+ } else if (!right) {
+ result->args = lcons(left, NIL);
+ } else {
+ result->args = lcons(left, lcons(right, NIL));
+ }
+
+ return result;
+}
+
+int
+find_atttype(Oid relid, char *attrname)
+{
+ int attid, vartype;
+ Relation rd;
+
+ rd = heap_open(relid);
+ if (!RelationIsValid(rd)) {
+ rd = heap_openr(tname(get_id_type(relid)));
+ if (!RelationIsValid(rd))
+ elog(WARN, "cannot compute type of att %s for relid %d",
+ attrname, relid);
+ }
+
+ attid = nf_varattno(rd, attrname);
+
+ if (attid == InvalidAttrNumber)
+ elog(WARN, "Invalid attribute %s\n", attrname);
+
+ vartype = att_typeid(rd , attid);
+
+ /*
+ * close relation we're done with it now
+ */
+ heap_close(rd);
+
+ return (vartype);
+}
+
+
+Var *
+make_var(ParseState *pstate, char *relname, char *attrname, int *type_id)
+{
+ Var *varnode;
+ int vnum, attid, vartypeid;
+ Relation rd;
+
+ 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);
+ relname = VarnoGetRelname(pstate, vnum);
+ } else {
+ relname = VarnoGetRelname(pstate, vnum);
+ }
+
+ rd = heap_openr(relname);
+/* relid = RelationGetRelationId(rd); */
+ attid = nf_varattno(rd, (char *) attrname);
+ if (attid == InvalidAttrNumber)
+ elog(WARN, "Invalid attribute %s\n", attrname);
+ vartypeid = att_typeid(rd, attid);
+
+ varnode = makeVar(vnum, attid, vartypeid, vnum, attid);
+
+ /*
+ * close relation we're done with it now
+ */
+ heap_close(rd);
+
+ *type_id = vartypeid;
+ return varnode;
+}
+
+/*
+ * make_array_ref() -- Make an array reference node.
+ *
+ * Array references can hang off of arbitrary nested dot (or
+ * function invocation) expressions. This routine takes a
+ * tree generated by ParseFunc() and an array index and
+ * generates a new array reference tree. We do some simple
+ * typechecking to be sure the dereference is valid in the
+ * type system, but we don't do any bounds checking here.
+ *
+ * indirection is a list of A_Indices
+ */
+ArrayRef *
+make_array_ref(Node *expr,
+ List *indirection)
+{
+ Oid typearray;
+ HeapTuple type_tuple;
+ TypeTupleForm type_struct_array, type_struct_element;
+ ArrayRef *aref;
+ int reftype;
+ List *upperIndexpr=NIL;
+ List *lowerIndexpr=NIL;
+
+ typearray = (Oid) exprType(expr);
+
+ type_tuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(typearray),
+ 0,0,0);
+
+ if (!HeapTupleIsValid(type_tuple))
+ elog(WARN, "make_array_ref: Cache lookup failed for type %d\n",
+ typearray);
+
+ /* get the array type struct from the type tuple */
+ type_struct_array = (TypeTupleForm) GETSTRUCT(type_tuple);
+
+ if (type_struct_array->typelem == InvalidOid) {
+ elog(WARN, "make_array_ref: type %s is not an array",
+ (Name)&(type_struct_array->typname.data[0]));
+ }
+
+ /* get the type tuple for the element type */
+ type_tuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(type_struct_array->typelem),
+ 0,0,0);
+ if (!HeapTupleIsValid(type_tuple))
+ elog(WARN, "make_array_ref: Cache lookup failed for type %d\n",
+ typearray);
+
+ type_struct_element = (TypeTupleForm) GETSTRUCT(type_tuple);
+
+ while(indirection!=NIL) {
+ A_Indices *ind = lfirst(indirection);
+ if (ind->lidx) {
+ /* XXX assumes all lower indices non null in this case
+ */
+ lowerIndexpr = lappend(lowerIndexpr, ind->lidx);
+ }
+ upperIndexpr = lappend(upperIndexpr, ind->uidx);
+ indirection = lnext(indirection);
+ }
+ aref = makeNode(ArrayRef);
+ aref->refattrlength = type_struct_array->typlen;
+ aref->refelemlength = type_struct_element->typlen;
+ aref->refelemtype = type_struct_array->typelem;
+ aref->refelembyval = type_struct_element->typbyval;
+ aref->refupperindexpr = upperIndexpr;
+ aref->reflowerindexpr = lowerIndexpr;
+ aref->refexpr = expr;
+ aref->refassgnexpr = NULL;
+
+ if (lowerIndexpr == NIL) /* accessing a single array element */
+ reftype = aref->refelemtype;
+ else /* request to clip a part of the array, the result is another array */
+ reftype = typearray;
+
+ /* we change it to reflect the true type; since the original refelemtype
+ * doesn't seem to get used anywhere. - ay 10/94
+ */
+ aref->refelemtype = reftype;
+
+ return aref;
+}
+
+ArrayRef *
+make_array_set(Expr *target_expr,
+ List *upperIndexpr,
+ List *lowerIndexpr,
+ Expr *expr)
+{
+ Oid typearray;
+ HeapTuple type_tuple;
+ TypeTupleForm type_struct_array;
+ TypeTupleForm type_struct_element;
+ ArrayRef *aref;
+ int reftype;
+
+ typearray = exprType((Node*)target_expr);
+
+ type_tuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(typearray),
+ 0,0,0);
+
+ if (!HeapTupleIsValid(type_tuple))
+ elog(WARN, "make_array_ref: Cache lookup failed for type %d\n",
+ typearray);
+
+ /* get the array type struct from the type tuple */
+ type_struct_array = (TypeTupleForm) GETSTRUCT(type_tuple);
+
+ if (type_struct_array->typelem == InvalidOid) {
+ elog(WARN, "make_array_ref: type %s is not an array",
+ (Name)&(type_struct_array->typname.data[0]));
+ }
+ /* get the type tuple for the element type */
+ type_tuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(type_struct_array->typelem),
+ 0,0,0);
+
+ if (!HeapTupleIsValid(type_tuple))
+ elog(WARN, "make_array_ref: Cache lookup failed for type %d\n",
+ typearray);
+
+ type_struct_element = (TypeTupleForm) GETSTRUCT(type_tuple);
+
+ aref = makeNode(ArrayRef);
+ aref->refattrlength = type_struct_array->typlen;
+ aref->refelemlength = type_struct_element->typlen;
+ aref->refelemtype = type_struct_array->typelem;
+ aref->refelembyval = type_struct_element->typbyval;
+ aref->refupperindexpr = upperIndexpr;
+ aref->reflowerindexpr = lowerIndexpr;
+ aref->refexpr = (Node*)target_expr;
+ aref->refassgnexpr = (Node*)expr;
+
+ if (lowerIndexpr == NIL) /* accessing a single array element */
+ reftype = aref->refelemtype;
+ else /* request to set a part of the array, by another array */
+ reftype = typearray;
+
+ aref->refelemtype = reftype;
+
+ return aref;
+}
+
+/*
+ *
+ * make_const -
+ *
+ * - takes a lispvalue, (as returned to the yacc routine by the lexer)
+ * extracts the type, and makes the appropriate type constant
+ * by invoking the (c-callable) lisp routine c-make-const
+ * via the lisp_call() mechanism
+ *
+ * eventually, produces a "const" lisp-struct as per nodedefs.cl
+ */
+Const *
+make_const(Value *value)
+{
+ Type tp;
+ Datum val;
+ Const *con;
+
+ switch(nodeTag(value)) {
+ case T_Integer:
+ tp = type("int4");
+ val = Int32GetDatum(intVal(value));
+ break;
+
+ case T_Float:
+ {
+ float32 dummy;
+ tp = type("float4");
+
+ dummy = (float32)palloc(sizeof(float32data));
+ *dummy = floatVal(value);
+
+ val = Float32GetDatum(dummy);
+ }
+ break;
+
+ case T_String:
+ tp = type("unknown"); /* unknown for now, will be type coerced */
+ val = PointerGetDatum(textin(strVal(value)));
+ break;
+
+ case T_Null:
+ default:
+ {
+ if (nodeTag(value)!=T_Null)
+ elog(NOTICE,"unknown type : %d\n", nodeTag(value));
+
+ /* null const */
+ con = makeConst(0, 0, (Datum)NULL, TRUE, 0, FALSE);
+ return NULL /*con*/;
+ }
+ }
+
+ con = makeConst(typeid(tp),
+ tlen(tp),
+ val,
+ FALSE,
+ tbyval(tp),
+ FALSE); /* not a set */
+
+ return (con);
+}
+
+/*
+ * param_type_init()
+ *
+ * keep enough information around fill out the type of param nodes
+ * used in postquel functions
+ */
+void
+param_type_init(Oid* typev, int nargs)
+{
+ pfunc_num_args = nargs;
+ param_type_info = typev;
+}
+
+Oid
+param_type(int t)
+{
+ if ((t >pfunc_num_args) ||(t ==0)) return InvalidOid;
+ return param_type_info[t-1];
+}
+
diff --git a/src/backend/parser/parse_query.h b/src/backend/parser/parse_query.h
new file mode 100644
index 00000000000..d9541c56cd4
--- /dev/null
+++ b/src/backend/parser/parse_query.h
@@ -0,0 +1,72 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_query.h--
+ * prototypes for parse_query.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: parse_query.h,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PARSE_QUERY_H
+#define PARSE_QUERY_H
+
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+#include "parser/catalog_utils.h"
+#include "parser/parse_state.h"
+
+typedef struct QueryTreeList {
+ int len; /* number of queries */
+ Query** qtrees;
+} QueryTreeList;
+
+extern int RangeTablePosn(List *rtable, char *rangevar);
+extern char *VarnoGetRelname(ParseState *pstate, int vnum);
+extern RangeTblEntry *makeRangeTableEntry(char *relname, bool inh,
+ TimeRange *timeRange, char *refname);
+extern List *expandAll(ParseState *pstate, char *relname, int *this_resno);
+extern TimeQual makeTimeRange(char *datestring1, char *datestring2,
+ int timecode);
+extern Expr *make_op(char *opname, Node *ltree, Node *rtree);
+
+extern int find_atttype(Oid relid, char *attrname);
+extern Var *make_var(ParseState *pstate,
+ char *relname, char *attrname, int *type_id);
+extern ArrayRef *make_array_ref(Node *array, List *indirection);
+extern ArrayRef *make_array_set(Expr *target_expr, List *upperIndexpr,
+ List *lowerIndexpr, Expr *expr);
+extern Const *make_const(Value *value);
+
+extern void param_type_init(Oid* typev, int nargs);
+extern Oid param_type(int t);
+
+/* parser.c (was ylib.c) */
+extern QueryTreeList *parser(char *str, Oid *typev, int nargs);
+extern Node *parser_typecast(Value *expr, TypeName *typename, int typlen);
+extern Node *parser_typecast2(Node *expr, int exprType, Type tp, int typlen);
+extern Aggreg *ParseAgg(char *aggname, Oid basetype, Node *target);
+
+/*
+ * analyze.c
+ */
+
+#if 0
+extern List *p_rtable;
+extern int NumLevels;
+#endif
+
+Oid exprType(Node *expr);
+ParseState* makeParseState();
+QueryTreeList *parse_analyze(List *querytree_list);
+
+/* define in parse_query.c, used in gram.y */
+extern Oid *param_type_info;
+extern int pfunc_num_args;
+
+/* useful macros */
+#define ISCOMPLEX(type) (typeid_get_relid((Oid)type) ? true : false)
+
+#endif /* PARSE_QUERY_H */
diff --git a/src/backend/parser/parse_state.h b/src/backend/parser/parse_state.h
new file mode 100644
index 00000000000..6ea7219e6bf
--- /dev/null
+++ b/src/backend/parser/parse_state.h
@@ -0,0 +1,27 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_state.h--
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: parse_state.h,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef PARSE_STATE_H
+#define PARSE_STATE_H
+
+/* state information used during parse analysis */
+typedef struct ParseState {
+ int p_last_resno;
+ List *p_target_resnos;
+ Relation parser_current_rel;
+ List *p_rtable;
+ int p_query_is_rule;
+ int p_numAgg;
+ List *p_aggs;
+} ParseState;
+
+
+#endif /*PARSE_QUERY_H*/
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
new file mode 100644
index 00000000000..e0dae907588
--- /dev/null
+++ b/src/backend/parser/parser.c
@@ -0,0 +1,449 @@
+/*-------------------------------------------------------------------------
+ *
+ * ylib.c--
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include <stdio.h>
+#ifndef WIN32
+#include <pwd.h>
+#endif /*WIN32 */
+#include <sys/param.h> /* for MAXPATHLEN */
+
+#include "utils/elog.h"
+#include "parser/catalog_utils.h"
+#include "nodes/pg_list.h"
+#include "utils/exc.h"
+#include "utils/excid.h"
+#include "utils/palloc.h"
+#include "catalog/pg_aggregate.h"
+#include "catalog/pg_type.h"
+#include "nodes/primnodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
+#include "nodes/relation.h"
+#include "parser/parse_query.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "access/heapam.h"
+#include "nodes/makefuncs.h"
+#include "optimizer/clauses.h"
+
+char *parseString; /* the char* which holds the string to be parsed */
+char *parseCh; /* a pointer used during parsing to walk down ParseString*/
+
+List *parsetree = NIL;
+
+static void fixupsets();
+static void define_sets();
+/*
+ * parser-- returns a list of parse trees
+ *
+ * CALLER is responsible for free'ing the list returned
+ */
+QueryTreeList *
+parser(char *str, Oid *typev, int nargs)
+{
+ QueryTreeList* queryList;
+ int yyresult;
+
+#if defined(FLEX_SCANNER)
+ extern void DeleteBuffer(void);
+#endif /* FLEX_SCANNER */
+
+ init_io();
+
+ /* Set things up to read from the string, if there is one */
+ if (strlen(str) != 0) {
+ parseString = (char *) palloc(strlen(str) + 1);
+ memmove(parseString,str,strlen(str)+1);
+ }
+
+ parser_init(typev, nargs);
+ yyresult = yyparse();
+
+#if defined(FLEX_SCANNER)
+ DeleteBuffer();
+#endif /* FLEX_SCANNER */
+
+ clearerr(stdin);
+
+ if (yyresult) { /* error */
+ return((QueryTreeList*)NULL);
+ }
+
+ queryList = parse_analyze(parsetree);
+
+#ifdef SETS_FIXED
+ /* Fixing up sets calls the parser, so it reassigns the global
+ * variable parsetree. So save the real parsetree.
+ */
+ savetree = parsetree;
+ foreach (parse, savetree) { /* savetree is really a list of parses */
+
+ /* find set definitions embedded in query */
+ fixupsets((Query *)lfirst(parse));
+
+ }
+ return savetree;
+#endif
+
+ return queryList;
+}
+
+static void
+fixupsets(Query *parse)
+{
+ if (parse == NULL)
+ return;
+ if (parse->commandType==CMD_UTILITY) /* utility */
+ return;
+ if (parse->commandType!=CMD_INSERT)
+ return;
+ define_sets(parse);
+}
+
+/* Recursively find all of the Consts in the parsetree. Some of
+ * these may represent a set. The value of the Const will be the
+ * query (a string) which defines the set. Call SetDefine to define
+ * the set, and store the OID of the new set in the Const instead.
+ */
+static void
+define_sets(Node *clause)
+{
+#ifdef SETS_FIXED
+ Oid setoid;
+ Type t = type("oid");
+ Oid typeoid = typeid(t);
+ Size oidsize = tlen(t);
+ bool oidbyval = tbyval(t);
+
+ if (clause==NULL) {
+ return;
+ } else if (IsA(clause,LispList)) {
+ define_sets(lfirst(clause));
+ define_sets(lnext(clause));
+ } else if (IsA(clause,Const)) {
+ if (get_constisnull((Const)clause) ||
+ !get_constisset((Const)clause)) {
+ return;
+ }
+ setoid = SetDefine(((Const*)clause)->constvalue,
+ get_id_typname(((Const*)clause)->consttype));
+ set_constvalue((Const)clause, setoid);
+ set_consttype((Const)clause,typeoid);
+ set_constlen((Const)clause,oidsize);
+ set_constbyval((Const)clause,oidbyval);
+ } else if ( IsA(clause,Iter) ) {
+ define_sets(((Iter*)clause)->iterexpr);
+ } else if (single_node (clause)) {
+ return;
+ } else if (or_clause(clause)) {
+ List *temp;
+ /* mapcan */
+ foreach (temp, ((Expr*)clause)->args) {
+ define_sets(lfirst(temp));
+ }
+ } else if (is_funcclause (clause)) {
+ List *temp;
+ /* mapcan */
+ foreach(temp, ((Expr*)clause)->args) {
+ define_sets(lfirst(temp));
+ }
+ } else if (IsA(clause,ArrayRef)) {
+ define_sets(((ArrayRef*)clause)->refassgnexpr);
+ } else if (not_clause (clause)) {
+ define_sets (get_notclausearg (clause));
+ } else if (is_opclause (clause)) {
+ define_sets(get_leftop (clause));
+ define_sets(get_rightop (clause));
+ }
+#endif
+}
+
+#define PSIZE(PTR) (*((int32 *)(PTR) - 1))
+Node *
+parser_typecast(Value *expr, TypeName *typename, int typlen)
+{
+ /* check for passing non-ints */
+ Const *adt;
+ Datum lcp;
+ Type tp;
+ char type_string[16];
+ int32 len;
+ char *cp = NULL;
+ char *const_string;
+ bool string_palloced = false;
+
+ switch(nodeTag(expr)) {
+ case T_String:
+ const_string = DatumGetPointer(expr->val.str);
+ break;
+ case T_Integer:
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string, "%ld", expr->val.ival);
+ break;
+ default:
+ elog(WARN,
+ "parser_typecast: cannot cast this expression to type \"%s\"",
+ typename->name);
+ }
+
+ if (typename->arrayBounds != NIL) {
+ sprintf(type_string,"_%s", typename->name);
+ tp = (Type) type(type_string);
+ } else {
+ tp = (Type) type(typename->name);
+ }
+
+ len = tlen(tp);
+
+#if 0 /* fix me */
+ switch ( CInteger(lfirst(expr)) ) {
+ case 23: /* int4 */
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string,"%d", ((Const*)lnext(expr))->constvalue);
+ break;
+
+ case 19: /* char16 */
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string,"%s", ((Const*)lnext(expr))->constvalue);
+ break;
+
+ case 18: /* char */
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string,"%c", ((Const)lnext(expr))->constvalue);
+ break;
+
+ case 701:/* float8 */
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string,"%f", ((Const)lnext(expr))->constvalue);
+ break;
+
+ case 25: /* text */
+ const_string = DatumGetPointer(((Const)lnext(expr))->constvalue);
+ const_string = (char *) textout((struct varlena *)const_string);
+ break;
+
+ case 705: /* unknown */
+ const_string = DatumGetPointer(((Const)lnext(expr))->constvalue);
+ const_string = (char *) textout((struct varlena *)const_string);
+ break;
+
+ default:
+ elog(WARN,"unknown type %d", CInteger(lfirst(expr)));
+ }
+#endif
+
+ cp = instr2 (tp, const_string, typlen);
+
+ if (!tbyvalue(tp)) {
+ if (len >= 0 && len != PSIZE(cp)) {
+ char *pp;
+ pp = (char *) palloc(len);
+ memmove(pp, cp, len);
+ cp = pp;
+ }
+ lcp = PointerGetDatum(cp);
+ } else {
+ switch(len) {
+ case 1:
+ lcp = Int8GetDatum(cp);
+ break;
+ case 2:
+ lcp = Int16GetDatum(cp);
+ break;
+ case 4:
+ lcp = Int32GetDatum(cp);
+ break;
+ default:
+ lcp = PointerGetDatum(cp);
+ break;
+ }
+ }
+
+ adt = makeConst(typeid(tp),
+ len,
+ (Datum)lcp ,
+ 0,
+ tbyvalue(tp),
+ 0 /* not a set */);
+
+ if (string_palloced)
+ pfree(const_string);
+
+ return (Node*)adt;
+}
+
+Node *
+parser_typecast2(Node *expr, int exprType, Type tp, int typlen)
+{
+ /* check for passing non-ints */
+ Const *adt;
+ Datum lcp;
+ int32 len = tlen(tp);
+ char *cp = NULL;
+
+ char *const_string;
+ bool string_palloced = false;
+
+ Assert(IsA(expr,Const));
+
+ switch (exprType) {
+ case 23: /* int4 */
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string,"%d",
+ (int) ((Const*)expr)->constvalue);
+ break;
+ case 19: /* char16 */
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string,"%s",
+ (char*) ((Const*)expr)->constvalue);
+ break;
+ case 18: /* char */
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string,"%c",
+ (char) ((Const*)expr)->constvalue);
+ break;
+ case 700: /* float4 */
+ {
+ float32 floatVal =
+ DatumGetFloat32(((Const*)expr)->constvalue);
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string,"%f", *floatVal);
+ break;
+ }
+ case 701:/* float8 */
+ {
+ float64 floatVal =
+ DatumGetFloat64(((Const*)expr)->constvalue);
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string,"%f", *floatVal);
+ break;
+ }
+ case 25: /* text */
+ const_string =
+ DatumGetPointer(((Const*)expr)->constvalue );
+ const_string = (char *) textout((struct varlena *)const_string);
+ break;
+ case 705: /* unknown */
+ const_string =
+ DatumGetPointer(((Const*)expr)->constvalue );
+ const_string = (char *) textout((struct varlena *)const_string);
+ break;
+ default:
+ elog(WARN,"unknown type%d ",exprType);
+ }
+
+ cp = instr2 (tp, const_string, typlen);
+
+
+ if (!tbyvalue(tp)) {
+ if (len >= 0 && len != PSIZE(cp)) {
+ char *pp;
+ pp = (char *) palloc(len);
+ memmove(pp, cp, len);
+ cp = pp;
+ }
+ lcp = PointerGetDatum(cp);
+ } else {
+ switch(len) {
+ case 1:
+ lcp = Int8GetDatum(cp);
+ break;
+ case 2:
+ lcp = Int16GetDatum(cp);
+ break;
+ case 4:
+ lcp = Int32GetDatum(cp);
+ break;
+ default:
+ lcp = PointerGetDatum(cp);
+ break;
+ }
+ }
+
+ adt = makeConst((Oid)typeid(tp),
+ (Size)len,
+ (Datum)lcp,
+ 0,
+ 0 /*was omitted*/,
+ 0 /* not a set */);
+ /*
+ printf("adt %s : %d %d %d\n",CString(expr),typeid(tp) ,
+ len,cp);
+ */
+ if (string_palloced) pfree(const_string);
+
+ return ((Node*) adt);
+}
+
+Aggreg *
+ParseAgg(char *aggname, Oid basetype, Node *target)
+{
+ Oid fintype;
+ Oid vartype;
+ Oid xfn1;
+ Form_pg_aggregate aggform;
+ Aggreg *aggreg;
+ HeapTuple theAggTuple;
+
+ theAggTuple = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggname),
+ ObjectIdGetDatum(basetype),
+ 0, 0);
+ if (!HeapTupleIsValid(theAggTuple)) {
+ elog(WARN, "aggregate %s does not exist", aggname);
+ }
+
+ aggform = (Form_pg_aggregate) GETSTRUCT(theAggTuple);
+ fintype = aggform->aggfinaltype;
+ xfn1 = aggform->aggtransfn1;
+
+ if (nodeTag(target) != T_Var)
+ elog(WARN, "parser: aggregate can only be applied on an attribute");
+
+ /* only aggregates with transfn1 need a base type */
+ if (OidIsValid(xfn1)) {
+ basetype = aggform->aggbasetype;
+ vartype = ((Var*)target)->vartype;
+
+ if (basetype != vartype) {
+ Type tp1, tp2, get_id_type();
+
+ tp1 = get_id_type(basetype);
+ tp2 = get_id_type(vartype);
+ elog(NOTICE, "Aggregate type mismatch:");
+ elog(WARN, "%s works on %s, not %s", aggname,
+ tname(tp1), tname(tp2));
+ }
+ }
+
+ aggreg = makeNode(Aggreg);
+ aggreg->aggname = pstrdup(aggname);
+ aggreg->basetype = aggform->aggbasetype;
+ aggreg->aggtype = fintype;
+
+ aggreg->target = target;
+
+ return aggreg;
+}
+
+
+
diff --git a/src/backend/parser/parsetree.h b/src/backend/parser/parsetree.h
new file mode 100644
index 00000000000..37a9f4a1765
--- /dev/null
+++ b/src/backend/parser/parsetree.h
@@ -0,0 +1,80 @@
+/*-------------------------------------------------------------------------
+ *
+ * parsetree.h--
+ * Routines to access various components and subcomponents of
+ * parse trees.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: parsetree.h,v 1.1.1.1 1996/07/09 06:21:41 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PARSETREE_H
+#define PARSETREE_H /* include once only */
+
+/* ----------------
+ * need pg_list.h for definitions of CAR(), etc. macros
+ * ----------------
+ */
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+
+/* ----------------
+ * range table macros
+ *
+ * parse tree:
+ * (root targetlist qual)
+ * ^^^^
+ * parse root:
+ * (numlevels cmdtype resrel rangetable priority ruleinfo nestdotinfo)
+ * ^^^^^^^^^^
+ * range table:
+ * (rtentry ...)
+ *
+ * rtentry:
+ * note: this might be wrong, I don't understand how
+ * rt_time / rt_archive_time work together. anyways it
+ * looks something like:
+ *
+ * (relname ? relid timestuff flags rulelocks)
+ * or (new/cur relname relid timestuff flags rulelocks)
+ *
+ * someone who knows more should correct this -cim 6/9/91
+ * ----------------
+ */
+
+#define rt_relname(rt_entry) \
+ ((!strcmp(((rt_entry)->refname),"*CURRENT*") ||\
+ !strcmp(((rt_entry)->refname),"*NEW*")) ? ((rt_entry)->refname) : \
+ ((char *)(rt_entry)->relname))
+
+/*
+ * rt_fetch
+ * rt_store
+ *
+ * Access and (destructively) replace rangetable entries.
+ *
+ */
+#define rt_fetch(rangetable_index, rangetable) \
+ ((RangeTblEntry*)nth((rangetable_index)-1, rangetable))
+
+#define rt_store(rangetable_index, rangetable, rt) \
+ set_nth(rangetable, (rangetable_index)-1, rt)
+
+/*
+ * getrelid
+ * getrelname
+ *
+ * Given the range index of a relation, return the corresponding
+ * relation id or relation name.
+ */
+#define getrelid(rangeindex,rangetable) \
+ ((RangeTblEntry*)nth((rangeindex)-1, rangetable))->relid
+
+#define getrelname(rangeindex, rangetable) \
+ rt_relname((RangeTblEntry*)nth((rangeindex)-1, rangetable))
+
+#endif /* PARSETREE_H */
+
diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l
new file mode 100644
index 00000000000..d3b3b9a3f26
--- /dev/null
+++ b/src/backend/parser/scan.l
@@ -0,0 +1,255 @@
+%{
+/*-------------------------------------------------------------------------
+ *
+ * scan.l--
+ * lexical scanner for POSTGRES
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/parser/scan.l,v 1.1.1.1 1996/07/09 06:21:41 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <ctype.h>
+#ifndef WIN32
+#include <unistd.h>
+#endif /* WIN32 */
+#ifndef __linux__
+#include <math.h>
+#else
+#include <stdlib.h>
+#endif /* __linux__ */
+#include <string.h>
+
+#include "postgres.h"
+#include "miscadmin.h"
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+#include "parser/keywords.h"
+#include "parser/scansup.h"
+#include "parse.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+extern char *parseString;
+extern char *parseCh;
+
+/* some versions of lex define this as a macro */
+#if defined(yywrap)
+#undef yywrap
+#endif /* yywrap */
+
+#if defined(FLEX_SCANNER)
+/* MAX_PARSE_BUFFER is defined in miscadmin.h */
+#define YYLMAX MAX_PARSE_BUFFER
+extern int myinput(char* buf, int max);
+#undef YY_INPUT
+#define YY_INPUT(buf,result,max) {result = myinput(buf,max);}
+#else
+#undef input
+int input();
+#undef unput
+void unput(char);
+#endif /* FLEX_SCANNER */
+
+extern YYSTYPE yylval;
+%}
+
+digit [0-9]
+letter [_A-Za-z]
+letter_or_digit [_A-Za-z0-9]
+
+identifier {letter}{letter_or_digit}*
+
+self [,()\[\].;$\:\+\-\*\/\<\>\=\|]
+op_and_self [\~\!\@\#\%\^\&\|\`\?\$\:\+\-\*\/\<\>\=]
+op_only [\~\!\@\#\%\^\&\`\?]
+
+operator ({op_and_self}{op_and_self}+)|{op_only}+
+ /* we used to allow double-quoted strings, but SQL doesn't */
+ /* so we won't either*/
+quote '
+
+integer -?{digit}+
+real -?{digit}+\.{digit}+([Ee][-+]?{digit}+)?
+
+param \${integer}
+
+comment "--".*\n
+
+space [ \t\n\f]
+other .
+
+%%
+{comment} { /* ignore */ }
+
+"::" { return TYPECAST; }
+
+{self} { return (yytext[0]); }
+
+{operator} {
+ yylval.str = pstrdup((char*)yytext);
+ return (Op);
+ }
+{param} { yylval.ival = atoi((char*)&yytext[1]);
+ return (PARAM);
+ }
+{integer} {
+ yylval.ival = atoi((char*)yytext);
+ return (ICONST);
+ }
+{real} {
+ yylval.dval = atof((char*)yytext);
+ return (FCONST);
+ }
+{quote} {
+ char literal[MAX_PARSE_BUFFER];
+ int i = 0;
+ int c = 0;
+ /* quote_seen can be either \ or ' because
+ we handle both cases of \' and '' for
+ quoting quotes*/
+ int quote_seen = 0;
+
+ while (i < MAX_PARSE_BUFFER - 1) {
+ c = input();
+ if (quote_seen != 0) {
+ if (quote_seen == '\'' &&
+ c != '\'') {
+ /* a non-quote follows a single quote */
+ /* so we've hit the end of the literal */
+ if (c != '\0' && c != EOF)
+ unput(c); /* put back the extra char we read*/
+ i = i - 1;
+ break; /* break out of the while loop */
+ }
+ /* if we reach here, we're still in */
+ /* the string literal */
+ literal[i++] = c;
+ quote_seen = 0;
+ continue;
+ }
+ if (c == '\0' || c == EOF) {
+ elog(WARN,"unterminated quoted string literal");
+ /* not reached */
+ }
+ literal[i++] = c;
+ if (c == '\'' || c == '\\')
+ quote_seen = c;
+ }
+ if ( i == MAX_PARSE_BUFFER - 1) {
+ elog (WARN, "unterminated quote string. parse buffer of %d chars exceeded", MAX_PARSE_BUFFER);
+ /* not reached */
+ }
+ literal[i] = '\0';
+ yylval.str = pstrdup(scanstr(literal));
+ return (SCONST);
+ }
+{identifier} {
+ ScanKeyword *keyword;
+
+ keyword = ScanKeywordLookup((char*)yytext);
+ if (keyword != NULL) {
+ return (keyword->value);
+ } else {
+ yylval.str = pstrdup((char*)yytext);
+ return (IDENT);
+ }
+ }
+{space} { /* ignore */ }
+
+{other} { return (yytext[0]); }
+
+%%
+
+void yyerror(char message[])
+{
+ elog(WARN, "parser: %s at or near \"%s\"\n", message, yytext);
+}
+
+int yywrap()
+{
+ return(1);
+}
+
+/*
+ init_io:
+ called by postgres before any actual parsing is done
+*/
+void
+init_io()
+{
+ /* it's important to set this to NULL
+ because input()/myinput() checks the non-nullness of parseCh
+ to know when to pass the string to lex/flex */
+ parseCh = NULL;
+#if defined(FLEX_SCANNER)
+ if (YY_CURRENT_BUFFER)
+ yy_flush_buffer(YY_CURRENT_BUFFER);
+#endif /* FLEX_SCANNER */
+ BEGIN INITIAL;
+}
+
+
+#if !defined(FLEX_SCANNER)
+/* get lex input from a string instead of from stdin */
+int
+input()
+{
+ if (parseCh == NULL) {
+ parseCh = parseString;
+ return(*parseCh++);
+ } else if (*parseCh == '\0') {
+ return(0);
+ } else {
+ return(*parseCh++);
+ }
+}
+
+/* undo lex input from a string instead of from stdin */
+void
+unput(char c)
+{
+ if (parseCh == NULL) {
+ elog(FATAL, "Unput() failed.\n");
+ } else if (c != 0) {
+ *--parseCh = c;
+ }
+}
+#endif /* !defined(FLEX_SCANNER) */
+
+#ifdef FLEX_SCANNER
+/* input routine for flex to read input from a string instead of a file */
+int
+myinput(char* buf, int max)
+{
+ int len, copylen;
+
+ if (parseCh == NULL) {
+ len = strlen(parseString);
+ if (len >= max)
+ copylen = max - 1;
+ else
+ copylen = len;
+ if (copylen > 0)
+ memcpy(buf, parseString, copylen);
+ buf[copylen] = '\0';
+ parseCh = parseString;
+ return copylen;
+ } else {
+ return 0; /* end of string */
+ }
+}
+
+char*
+CurScan(void)
+{
+/*
+ return (InputFrag ? InputFrag : parseCh) +
+ (yy_c_buf_p - &yy_current_buffer->yy_ch_buf[yy_n_chars]);
+*/
+}
+#endif /* FLEX_SCANNER */
+
diff --git a/src/backend/parser/scansup.c b/src/backend/parser/scansup.c
new file mode 100644
index 00000000000..bd7ef26004e
--- /dev/null
+++ b/src/backend/parser/scansup.c
@@ -0,0 +1,148 @@
+/*-------------------------------------------------------------------------
+ *
+ * scansup.c--
+ * support routines for the lex/flex scanner, used by both the normal
+ * backend as well as the bootstrap backend
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/parser/scansup.c,v 1.1.1.1 1996/07/09 06:21:41 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <ctype.h>
+#include <string.h>
+#include "c.h"
+#include "postgres.h"
+#include "miscadmin.h"
+#include "utils/elog.h"
+#include "parser/scansup.h"
+
+/*
+ * Scanner error handler.
+ */
+static void
+serror(char *str)
+{
+ elog(WARN, "*** scanner error: %s\n", str);
+}
+
+/* ----------------
+ * scanstr
+ *
+ * if the string passed in has escaped codes, map the escape codes to actual
+ * chars
+ *
+ * also, remove leading and ending quotes '"' if any
+ *
+ * the string passed in must be non-null
+ *
+ * the string returned is a pointer to static storage and should NOT
+ * be freed by the CALLER.
+ * ----------------
+ */
+
+char*
+scanstr(char *s)
+{
+ static char newStr[MAX_PARSE_BUFFER];
+ int len, i, start, j;
+ char delimiter;
+
+ if (s == NULL || s[0] == '\0')
+ return s;
+
+ len = strlen(s);
+ start = 0;
+
+ /* remove leading and trailing quotes, if any */
+ /* the normal backend lexer only accepts single quotes, but the
+ bootstrap lexer accepts double quotes */
+ delimiter = 0;
+ if (s[0] == '"' || s[0] == '\''){
+ delimiter = s[0];
+ start = 1;
+ }
+ if (delimiter != 0) {
+ if (s[len-1] == delimiter)
+ len = len - 1;
+ else
+ serror("mismatched quote delimiters");
+ }
+
+ for (i = start, j = 0; i < len ; i++) {
+ if (s[i] == '\'') {
+ i = i + 1;
+ if (s[i] == '\'')
+ newStr[j] = '\'';
+ }
+ else {
+ if (s[i] == '\\') {
+ i = i + 1;
+ switch (s[i]) {
+ case '\\':
+ newStr[j] = '\\';
+ break;
+ case 'b':
+ newStr[j] = '\b';
+ break;
+ case 'f':
+ newStr[j] = '\f';
+ break;
+ case 'n':
+ newStr[j] = '\n';
+ break;
+ case 'r':
+ newStr[j] = '\r';
+ break;
+ case 't':
+ newStr[j] = '\t';
+ break;
+ case '"':
+ newStr[j] = '"';
+ break;
+ case '\'':
+ newStr[j] = '\'';
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ char octal[4];
+ int k;
+ long octVal;
+
+ for (k=0;
+ s[i+k] >= '0' && s[i+k] <= '7' && k < 3;
+ k++)
+ octal[k] = s[i+k];
+ i += k-1;
+ octal[3] = '\0';
+
+ octVal = strtol(octal,0,8);
+/* elog (NOTICE, "octal = %s octVal = %d, %od", octal, octVal, octVal);*/
+ if (octVal <= 0377) {
+ newStr[j] = ((char)octVal);
+ break;
+ }
+ }
+ default:
+ elog (WARN, "Bad escape sequence, s[i] = %d", s[i]);
+ } /* switch */
+ } /* s[i] == '\\' */
+ else
+ newStr[j] = s[i];
+ }
+ j++;
+ }
+ newStr[j] = '\0';
+ return newStr;
+}
+
diff --git a/src/backend/parser/scansup.h b/src/backend/parser/scansup.h
new file mode 100644
index 00000000000..95e625aabcf
--- /dev/null
+++ b/src/backend/parser/scansup.h
@@ -0,0 +1,17 @@
+/*-------------------------------------------------------------------------
+ *
+ * scansup.h--
+ * scanner support routines. used by both the bootstrap lexer
+ * as well as the normal lexer
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: scansup.h,v 1.1.1.1 1996/07/09 06:21:41 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+extern char* scanstr(char *s);
+
+
+