aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/gram.y
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/gram.y')
-rw-r--r--src/backend/parser/gram.y2113
1 files changed, 2113 insertions, 0 deletions
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);
+}
+