diff options
author | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
---|---|---|
committer | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
commit | d31084e9d1118b25fd16580d9d8c2924b5740dff (patch) | |
tree | 3179e66307d54df9c7b966543550e601eb55e668 /src/backend/parser | |
download | postgresql-PG95-1_01.tar.gz postgresql-PG95-1_01.zip |
Postgres95 1.01 Distribution - Virgin SourcesPG95-1_01
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/Makefile.inc | 46 | ||||
-rw-r--r-- | src/backend/parser/analyze.c | 2467 | ||||
-rw-r--r-- | src/backend/parser/catalog_utils.c | 1470 | ||||
-rw-r--r-- | src/backend/parser/catalog_utils.h | 64 | ||||
-rw-r--r-- | src/backend/parser/dbcommands.c | 259 | ||||
-rw-r--r-- | src/backend/parser/dbcommands.h | 28 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 2113 | ||||
-rw-r--r-- | src/backend/parser/keywords.c | 179 | ||||
-rw-r--r-- | src/backend/parser/keywords.h | 25 | ||||
-rw-r--r-- | src/backend/parser/parse_query.c | 653 | ||||
-rw-r--r-- | src/backend/parser/parse_query.h | 72 | ||||
-rw-r--r-- | src/backend/parser/parse_state.h | 27 | ||||
-rw-r--r-- | src/backend/parser/parser.c | 449 | ||||
-rw-r--r-- | src/backend/parser/parsetree.h | 80 | ||||
-rw-r--r-- | src/backend/parser/scan.l | 255 | ||||
-rw-r--r-- | src/backend/parser/scansup.c | 148 | ||||
-rw-r--r-- | src/backend/parser/scansup.h | 17 |
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, + ¤t_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); + + + |