%{ /*#define YYDEBUG 1*/ /*------------------------------------------------------------------------- * * gram.y * POSTGRES SQL YACC rules/actions * * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.367 2002/09/18 21:35:21 tgl 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. * SQL92-specific syntax is separated from plain SQL/Postgres syntax * to help isolate the non-extensible portions of the parser. * * In general, nothing in this file should initiate database accesses * nor depend on changeable state (such as SET variables). If you do * database accesses, your code will fail when we have aborted the * current transaction and are just parsing commands to find the next * ROLLBACK or COMMIT. If you make use of SET variables, then you * will do the wrong thing in multi-query strings like this: * SET SQL_inheritance TO off; SELECT * FROM foo; * because the entire string is parsed by gram.y before the SET gets * executed. Anything that depends on the database or changeable state * should be handled inside parse_analyze() so that it happens at the * right time not the wrong time. The handling of SQL_inheritance is * a good example. * * WARNINGS * If you use a list, make sure the datum is a node so that the printing * routines work. * * Sometimes we assign constants to makeStrings. Make sure we don't free * those. * *------------------------------------------------------------------------- */ #include "postgres.h" #include #include "access/htup.h" #include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_conversion.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "nodes/params.h" #include "nodes/parsenodes.h" #include "parser/gramparse.h" #include "storage/lmgr.h" #include "utils/numeric.h" #include "utils/datetime.h" #include "utils/date.h" #include "mb/pg_wchar.h" extern List *parsetree; /* final parse result is delivered here */ static bool QueryIsRule = FALSE; /* * 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*/ static Node *makeTypeCast(Node *arg, TypeName *typename); static Node *makeStringConst(char *str, TypeName *typename); static Node *makeIntConst(int val); static Node *makeFloatConst(char *str); static Node *makeAConst(Value *v); static Node *makeRowExpr(List *opr, List *largs, List *rargs); static Node *makeDistinctExpr(List *largs, List *rargs); static Node *makeRowNullTest(NullTestType test, List *args); static DefElem *makeDefElem(char *name, Node *arg); static A_Const *makeBoolConst(bool state); static FuncCall *makeOverlaps(List *largs, List *rargs); static SelectStmt *findLeftmostSelect(SelectStmt *node); static void insertSelectOptions(SelectStmt *stmt, List *sortClause, List *forUpdate, Node *limitOffset, Node *limitCount); static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg); static Node *doNegate(Node *n); static void doNegateFloat(Value *v); %} %union { int ival; char chr; char *str; const char *keyword; bool boolean; JoinType jtype; DropBehavior dbehavior; List *list; Node *node; Value *value; ColumnRef *columnref; TypeName *typnam; DefElem *defelt; SortGroupBy *sortgroupby; JoinExpr *jexpr; IndexElem *ielem; Alias *alias; RangeVar *range; A_Indices *aind; ResTarget *target; PrivTarget *privtarget; InsertStmt *istmt; VariableSetStmt *vsetstmt; } %type stmt, schema_stmt, AlterDatabaseSetStmt, AlterGroupStmt, AlterTableStmt, AlterUserStmt, AlterUserSetStmt, AnalyzeStmt, ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt, CopyStmt, CreateAsStmt, CreateCastStmt, CreateDomainStmt, CreateGroupStmt, CreateOpClassStmt, CreatePLangStmt, CreateSchemaStmt, CreateSeqStmt, CreateStmt, CreateAssertStmt, CreateTrigStmt, CreateUserStmt, CreatedbStmt, CursorStmt, DefineStmt, DeleteStmt, DropGroupStmt, DropOpClassStmt, DropPLangStmt, DropStmt, DropAssertStmt, DropTrigStmt, DropRuleStmt, DropCastStmt, DropUserStmt, DropdbStmt, ExplainStmt, FetchStmt, GrantStmt, IndexStmt, InsertStmt, ListenStmt, LoadStmt, LockStmt, NotifyStmt, OptimizableStmt, CreateFunctionStmt, ReindexStmt, RemoveAggrStmt, RemoveFuncStmt, RemoveOperStmt, RenameStmt, RevokeStmt, RuleActionStmt, RuleActionStmtOrEmpty, RuleStmt, SelectStmt, TransactionStmt, TruncateStmt, UnlistenStmt, UpdateStmt, VacuumStmt, VariableResetStmt, VariableSetStmt, VariableShowStmt, ViewStmt, CheckPointStmt, CreateConversionStmt, DeallocateStmt, PrepareStmt, ExecuteStmt %type select_no_parens, select_with_parens, select_clause, simple_select %type alter_column_default, opclass_item %type add_drop %type opt_drop_behavior %type createdb_opt_list, copy_opt_list %type createdb_opt_item, copy_opt_item %type opt_lock, lock_type, cast_context %type opt_force, opt_or_replace %type user_list %type OptGroupList %type OptGroupElem %type OptUserList %type OptUserElem %type OptSchemaName %type OptSchemaEltList %type TriggerActionTime, TriggerForSpec, opt_trusted %type opt_lancompiler %type TriggerEvents %type TriggerFuncArg %type relation_name, copy_file_name, database_name, access_method_clause, access_method, attr_name, index_name, name, function_name, file_name %type func_name, handler_name, qual_Op, qual_all_Op, OptUseOp, opt_class, opt_validator %type qualified_name, OptConstrFromTable %type opt_id, all_Op, MathOp, opt_name, SpecialRuleRelation %type iso_level, opt_encoding %type grantee %type grantee_list %type privilege %type privileges, privilege_list %type privilege_target %type function_with_argtypes %type function_with_argtypes_list %type TriggerOneEvent %type stmtblock, stmtmulti, OptTableElementList, TableElementList, OptInherit, definition, opt_distinct, opt_definition, func_args, func_args_list, func_as, createfunc_opt_list, oper_argtypes, RuleActionList, RuleActionMulti, opt_column_list, columnList, opt_name_list, sort_clause, opt_sort_clause, sortby_list, index_params, index_list,name_list, from_clause, from_list, opt_array_bounds, qualified_name_list, any_name, any_name_list, any_operator, expr_list, dotted_name, attrs, target_list, update_target_list, insert_column_list, insert_target_list, def_list, opt_indirection, group_clause, TriggerFuncArgs, select_limit, opt_select_limit, opclass_item_list, trans_options, TableFuncElementList, convert_args, prep_type_clause, prep_type_list, execute_param_clause, execute_param_list %type into_clause, OptTempTableName %type createfunc_opt_item %type func_arg, func_return, func_type, aggr_argtype %type opt_arg, TriggerForType, OptTemp, OptWithOids %type for_update_clause, opt_for_update_clause, update_list %type opt_all %type join_outer, join_qual %type join_type %type extract_list, overlay_list, position_list %type substr_list, trim_list, convert_list %type opt_interval %type overlay_placing, substr_from, substr_for %type opt_instead, opt_cursor %type index_opt_unique, opt_verbose, opt_full %type opt_freeze, opt_default, opt_recheck %type opt_binary, opt_oids, copy_delimiter %type copy_from %type direction, reindex_type, drop_type, opt_column, event, comment_type %type fetch_how_many %type select_limit_value, select_offset_value %type OptSeqList %type OptSeqElem %type insert_rest %type set_rest %type TableElement, ConstraintElem, TableFuncElement %type columnDef %type def_elem %type def_arg, columnElem, where_clause, insert_column_item, a_expr, b_expr, c_expr, r_expr, AexprConst, in_expr, having_clause, func_table %type row, row_descriptor, row_list, in_expr_nodes, type_list %type case_expr, case_arg, when_clause, case_default %type when_clause_list %type sub_type %type OptCreateAs, CreateAsList %type CreateAsElement %type NumericOnly, FloatOnly, IntegerOnly %type columnref %type alias_clause %type sortby %type index_elem, func_index %type table_ref %type joined_table %type relation_expr %type target_el, insert_target_el, update_target_el %type Typename, SimpleTypename, ConstTypename, GenericType, Numeric, opt_float, Character, ConstCharacter, CharacterWithLength, CharacterWithoutLength, ConstDatetime, ConstInterval, Bit, ConstBit, BitWithLength, BitWithoutLength %type character %type extract_arg %type opt_charset, opt_collate %type opt_numeric, opt_decimal %type opt_varying, opt_timezone %type Iconst %type Sconst, comment_text %type UserId, opt_boolean, ColId_or_Sconst %type var_list, var_list_or_default %type ColId, ColLabel, type_name %type var_value, zone_value %type unreserved_keyword, func_name_keyword %type col_name_keyword, reserved_keyword %type TableConstraint, TableLikeClause %type ColQualList %type ColConstraint, ColConstraintElem, ConstraintAttr %type key_actions, key_delete, key_match, key_update, key_action %type ConstraintAttributeSpec, ConstraintDeferrabilitySpec, ConstraintTimeSpec %type constraints_set_list %type constraints_set_mode /* * If you make any token changes, update the keyword table in * parser/keywords.c and add new keywords to the appropriate one of * the reserved-or-not-so-reserved keyword lists, below. */ /* ordinary key words in alphabetical order */ %token ABORT_TRANS, ABSOLUTE, ACCESS, ACTION, ADD, AFTER, AGGREGATE, ALL, ALTER, ANALYSE, ANALYZE, AND, ANY, AS, ASC, ASSERTION, ASSIGNMENT, AT, AUTHORIZATION, BACKWARD, BEFORE, BEGIN_TRANS, BETWEEN, BIGINT, BINARY, BIT, BOTH, BOOLEAN, BY, CACHE, CALLED, CASCADE, CASE, CAST, CHAIN, CHAR_P, CHARACTER, CHARACTERISTICS, CHECK, CHECKPOINT, CLASS, CLOSE, CLUSTER, COALESCE, COLLATE, COLUMN, COMMENT, COMMIT, COMMITTED, CONSTRAINT, CONSTRAINTS, CONVERSION_P, CONVERT, COPY, CREATE, CREATEDB, CREATEUSER, CROSS, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER, CURSOR, CYCLE, DATABASE, DAY_P, DEALLOCATE, DEC, DECIMAL, DECLARE, DEFAULT, DEFERRABLE, DEFERRED, DEFINER, DELETE_P, DELIMITER, DELIMITERS, DESC, DISTINCT, DO, DOMAIN_P, DOUBLE, DROP, EACH, ELSE, ENCODING, ENCRYPTED, END_TRANS, ESCAPE, EXCEPT, EXCLUSIVE, EXECUTE, EXISTS, EXPLAIN, EXTERNAL, EXTRACT, FALSE_P, FETCH, FLOAT_P, FOR, FORCE, FOREIGN, FORWARD, FREEZE, FROM, FULL, FUNCTION, GET, GLOBAL, GRANT, GROUP_P, HANDLER, HAVING, HOUR_P, ILIKE, IMMEDIATE, IMMUTABLE, IMPLICIT_P, IN_P, INCREMENT, INDEX, INHERITS, INITIALLY, INNER_P, INOUT, INPUT, INSENSITIVE, INSERT, INSTEAD, INT, INTEGER, INTERSECT, INTERVAL, INTO, INVOKER, IS, ISNULL, ISOLATION, JOIN, KEY, LANCOMPILER, LANGUAGE, LEADING, LEFT, LEVEL, LIKE, LIMIT, LISTEN, LOAD, LOCAL, LOCALTIME, LOCALTIMESTAMP, LOCATION, LOCK_P, MATCH, MAXVALUE, MINUTE_P, MINVALUE, MODE, MONTH_P, MOVE, NAMES, NATIONAL, NATURAL, NCHAR, NEW, NEXT, NO, NOCREATEDB, NOCREATEUSER, NONE, NOT, NOTHING, NOTIFY, NOTNULL, NULL_P, NULLIF, NUMERIC, OF, OFF, OFFSET, OIDS, OLD, ON, ONLY, OPERATOR, OPTION, OR, ORDER, OUT_P, OUTER_P, OVERLAPS, OVERLAY, OWNER, PARTIAL, PASSWORD, PATH_P, PENDANT, PLACING, POSITION, PRECISION, PREPARE, PRIMARY, PRIOR, PRIVILEGES, PROCEDURE, PROCEDURAL, READ, REAL, RECHECK, REFERENCES, REINDEX, RELATIVE, RENAME, REPLACE, RESET, RESTRICT, RETURNS, REVOKE, RIGHT, ROLLBACK, ROW, RULE, SCHEMA, SCROLL, SECOND_P, SECURITY, SELECT, SEQUENCE, SERIALIZABLE, SESSION, SESSION_USER, SET, SETOF, SHARE, SHOW, SIMILAR, SIMPLE, SMALLINT, SOME, STABLE, START, STATEMENT, STATISTICS, STDIN, STDOUT, STORAGE, STRICT, SUBSTRING, SYSID, TABLE, TEMP, TEMPLATE, TEMPORARY, THEN, TIME, TIMESTAMP, TO, TOAST, TRAILING, TRANSACTION, TREAT, TRIGGER, TRIM, TRUE_P, TRUNCATE, TRUSTED, TYPE_P, UNENCRYPTED, UNION, UNIQUE, UNKNOWN, UNLISTEN, UNTIL, UPDATE, USAGE, USER, USING, VACUUM, VALID, VALIDATOR, VALUES, VARCHAR, VARYING, VERBOSE, VERSION, VIEW, VOLATILE, WHEN, WHERE, WITH, WITHOUT, WORK, WRITE, YEAR_P, ZONE /* The grammar thinks these are keywords, but they are not in the keywords.c * list and so can never be entered directly. The filter in parser.c * creates these tokens when required. */ %token UNIONJOIN /* Special keywords, not in the query language - see the "lex" file */ %token IDENT, FCONST, SCONST, NCONST, BCONST, XCONST, Op %token ICONST, PARAM /* these are not real. they are here so that they get generated as #define's*/ %token OP /* precedence: lowest to highest */ %left UNION EXCEPT %left INTERSECT %left OR %left AND %right NOT %right '=' %nonassoc '<' '>' %nonassoc LIKE ILIKE SIMILAR %nonassoc ESCAPE %nonassoc OVERLAPS %nonassoc BETWEEN %nonassoc IN_P %left POSTFIXOP /* dummy for postfix Op rules */ %left Op OPERATOR /* multi-character ops and user-defined operators */ %nonassoc NOTNULL %nonassoc ISNULL %nonassoc IS NULL_P TRUE_P FALSE_P UNKNOWN /* sets precedence for IS NULL, etc */ %left '+' '-' %left '*' '/' '%' %left '^' /* Unary Operators */ %left AT ZONE /* sets precedence for AT TIME ZONE */ %right UMINUS %left '[' ']' %left '(' ')' %left COLLATE %left TYPECAST %left '.' /* * These might seem to be low-precedence, but actually they are not part * of the arithmetic hierarchy at all in their use as JOIN operators. * We make them high-precedence to support their use as function names. * They wouldn't be given a precedence at all, were it not that we need * left-associativity among the JOIN rules themselves. */ %left JOIN UNIONJOIN CROSS LEFT FULL RIGHT INNER_P NATURAL %% /* * Handle comment-only lines, and ;; SELECT * FROM pg_class ;;; * psql already handles such cases, but other interfaces don't. * bjm 1999/10/05 */ stmtblock: stmtmulti { parsetree = $1; } ; /* the thrashing around here is to discard "empty" statements... */ stmtmulti: stmtmulti ';' stmt { if ($3 != (Node *)NULL) $$ = lappend($1, $3); else $$ = $1; } | stmt { if ($1 != (Node *)NULL) $$ = makeList1($1); else $$ = NIL; } ; stmt : AlterDatabaseSetStmt | AlterGroupStmt | AlterTableStmt | AlterUserStmt | AlterUserSetStmt | ClosePortalStmt | CopyStmt | CreateStmt | CreateAsStmt | CreateCastStmt | CreateDomainStmt | CreateFunctionStmt | CreateSchemaStmt | CreateGroupStmt | CreateSeqStmt | CreateOpClassStmt | CreatePLangStmt | CreateAssertStmt | CreateTrigStmt | CreateUserStmt | ClusterStmt | DeallocateStmt | DefineStmt | DropStmt | TruncateStmt | CommentStmt | DropCastStmt | DropGroupStmt | DropOpClassStmt | DropPLangStmt | DropAssertStmt | DropTrigStmt | DropRuleStmt | DropUserStmt | ExecuteStmt | ExplainStmt | FetchStmt | GrantStmt | IndexStmt | ListenStmt | UnlistenStmt | LockStmt | NotifyStmt | PrepareStmt | ReindexStmt | RemoveAggrStmt | RemoveOperStmt | RemoveFuncStmt | RenameStmt | RevokeStmt | OptimizableStmt | RuleStmt | TransactionStmt | ViewStmt | LoadStmt | CreatedbStmt | DropdbStmt | VacuumStmt | AnalyzeStmt | VariableSetStmt | VariableShowStmt | VariableResetStmt | ConstraintsSetStmt | CheckPointStmt | CreateConversionStmt | /*EMPTY*/ { $$ = (Node *)NULL; } ; /***************************************************************************** * * Create a new Postgres DBMS user * * *****************************************************************************/ CreateUserStmt: CREATE USER UserId opt_with OptUserList { CreateUserStmt *n = makeNode(CreateUserStmt); n->user = $3; n->options = $5; $$ = (Node *)n; } ; opt_with: WITH {} | /*EMPTY*/ {} ; /***************************************************************************** * * Alter a postgresql DBMS user * * *****************************************************************************/ AlterUserStmt: ALTER USER UserId opt_with OptUserList { AlterUserStmt *n = makeNode(AlterUserStmt); n->user = $3; n->options = $5; $$ = (Node *)n; } ; AlterUserSetStmt: ALTER USER UserId SET set_rest { AlterUserSetStmt *n = makeNode(AlterUserSetStmt); n->user = $3; n->variable = $5->name; n->value = $5->args; $$ = (Node *)n; } | ALTER USER UserId VariableResetStmt { AlterUserSetStmt *n = makeNode(AlterUserSetStmt); n->user = $3; n->variable = ((VariableResetStmt *)$4)->name; n->value = NIL; $$ = (Node *)n; } ; /***************************************************************************** * * Drop a postgresql DBMS user * * XXX Ideally this would have CASCADE/RESTRICT options, but since a user * might own objects in multiple databases, there is presently no way to * implement either cascading or restricting. Caveat DBA. *****************************************************************************/ DropUserStmt: DROP USER user_list { DropUserStmt *n = makeNode(DropUserStmt); n->users = $3; $$ = (Node *)n; } ; /* * Options for CREATE USER and ALTER USER */ OptUserList: OptUserList OptUserElem { $$ = lappend($1, $2); } | /* EMPTY */ { $$ = NIL; } ; OptUserElem: PASSWORD Sconst { $$ = makeDefElem("password", (Node *)makeString($2)); } | ENCRYPTED PASSWORD Sconst { $$ = makeDefElem("encryptedPassword", (Node *)makeString($3)); } | UNENCRYPTED PASSWORD Sconst { $$ = makeDefElem("unencryptedPassword", (Node *)makeString($3)); } | SYSID Iconst { $$ = makeDefElem("sysid", (Node *)makeInteger($2)); } | CREATEDB { $$ = makeDefElem("createdb", (Node *)makeInteger(TRUE)); } | NOCREATEDB { $$ = makeDefElem("createdb", (Node *)makeInteger(FALSE)); } | CREATEUSER { $$ = makeDefElem("createuser", (Node *)makeInteger(TRUE)); } | NOCREATEUSER { $$ = makeDefElem("createuser", (Node *)makeInteger(FALSE)); } | IN_P GROUP_P user_list { $$ = makeDefElem("groupElts", (Node *)$3); } | VALID UNTIL Sconst { $$ = makeDefElem("validUntil", (Node *)makeString($3)); } ; user_list: user_list ',' UserId { $$ = lappend($1, makeString($3)); } | UserId { $$ = makeList1(makeString($1)); } ; /***************************************************************************** * * Create a postgresql group * * *****************************************************************************/ CreateGroupStmt: CREATE GROUP_P UserId opt_with OptGroupList { CreateGroupStmt *n = makeNode(CreateGroupStmt); n->name = $3; n->options = $5; $$ = (Node *)n; } ; /* * Options for CREATE GROUP */ OptGroupList: OptGroupList OptGroupElem { $$ = lappend($1, $2); } | /* EMPTY */ { $$ = NIL; } ; OptGroupElem: USER user_list { $$ = makeDefElem("userElts", (Node *)$2); } | SYSID Iconst { $$ = makeDefElem("sysid", (Node *)makeInteger($2)); } ; /***************************************************************************** * * Alter a postgresql group * * *****************************************************************************/ AlterGroupStmt: ALTER GROUP_P UserId add_drop USER user_list { AlterGroupStmt *n = makeNode(AlterGroupStmt); n->name = $3; n->action = $4; n->listUsers = $6; $$ = (Node *)n; } ; add_drop: ADD { $$ = +1; } | DROP { $$ = -1; } ; /***************************************************************************** * * Drop a postgresql group * * XXX see above notes about cascading DROP USER; groups have same problem. *****************************************************************************/ DropGroupStmt: DROP GROUP_P UserId { DropGroupStmt *n = makeNode(DropGroupStmt); n->name = $3; $$ = (Node *)n; } ; /***************************************************************************** * * Manipulate a schema * *****************************************************************************/ CreateSchemaStmt: CREATE SCHEMA OptSchemaName AUTHORIZATION UserId OptSchemaEltList { CreateSchemaStmt *n = makeNode(CreateSchemaStmt); /* One can omit the schema name or the authorization id. */ if ($3 != NULL) n->schemaname = $3; else n->schemaname = $5; n->authid = $5; n->schemaElts = $6; $$ = (Node *)n; } | CREATE SCHEMA ColId OptSchemaEltList { CreateSchemaStmt *n = makeNode(CreateSchemaStmt); /* ...but not both */ n->schemaname = $3; n->authid = NULL; n->schemaElts = $4; $$ = (Node *)n; } ; OptSchemaName: ColId { $$ = $1; } | /* EMPTY */ { $$ = NULL; } ; OptSchemaEltList: OptSchemaEltList schema_stmt { $$ = lappend($1, $2); } | /* EMPTY */ { $$ = NIL; } ; /* * schema_stmt are the ones that can show up inside a CREATE SCHEMA * statement (in addition to by themselves). */ schema_stmt: CreateStmt | GrantStmt | ViewStmt ; /***************************************************************************** * * Set PG internal variable * SET name TO 'var_value' * Include SQL92 syntax (thomas 1997-10-22): * SET TIME ZONE 'var_value' * *****************************************************************************/ VariableSetStmt: SET set_rest { VariableSetStmt *n = $2; n->is_local = false; $$ = (Node *) n; } | SET LOCAL set_rest { VariableSetStmt *n = $3; n->is_local = true; $$ = (Node *) n; } | SET SESSION set_rest { VariableSetStmt *n = $3; n->is_local = false; $$ = (Node *) n; } ; set_rest: ColId TO var_list_or_default { VariableSetStmt *n = makeNode(VariableSetStmt); n->name = $1; n->args = $3; $$ = n; } | ColId '=' var_list_or_default { VariableSetStmt *n = makeNode(VariableSetStmt); n->name = $1; n->args = $3; $$ = n; } | TIME ZONE zone_value { VariableSetStmt *n = makeNode(VariableSetStmt); n->name = "timezone"; if ($3 != NULL) n->args = makeList1($3); $$ = n; } | TRANSACTION ISOLATION LEVEL iso_level opt_mode { VariableSetStmt *n = makeNode(VariableSetStmt); n->name = "TRANSACTION ISOLATION LEVEL"; n->args = makeList1(makeStringConst($4, NULL)); $$ = n; } | SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL iso_level { VariableSetStmt *n = makeNode(VariableSetStmt); n->name = "default_transaction_isolation"; n->args = makeList1(makeStringConst($7, NULL)); $$ = n; } | NAMES opt_encoding { VariableSetStmt *n = makeNode(VariableSetStmt); n->name = "client_encoding"; if ($2 != NULL) n->args = makeList1(makeStringConst($2, NULL)); $$ = n; } | SESSION AUTHORIZATION ColId_or_Sconst { VariableSetStmt *n = makeNode(VariableSetStmt); n->name = "session_authorization"; n->args = makeList1(makeStringConst($3, NULL)); $$ = n; } | SESSION AUTHORIZATION DEFAULT { VariableSetStmt *n = makeNode(VariableSetStmt); n->name = "session_authorization"; n->args = NIL; $$ = n; } ; var_list_or_default: var_list { $$ = $1; } | DEFAULT { $$ = NIL; } ; var_list: var_value { $$ = makeList1($1); } | var_list ',' var_value { $$ = lappend($1, $3); } ; var_value: opt_boolean { $$ = makeStringConst($1, NULL); } | ColId_or_Sconst { $$ = makeStringConst($1, NULL); } | NumericOnly { $$ = makeAConst($1); } ; iso_level: READ COMMITTED { $$ = "read committed"; } | SERIALIZABLE { $$ = "serializable"; } ; opt_mode: READ WRITE {} | READ ONLY { elog(ERROR, "SET TRANSACTION/READ ONLY not yet supported"); } | /*EMPTY*/ {} ; opt_boolean: TRUE_P { $$ = "true"; } | FALSE_P { $$ = "false"; } | ON { $$ = "on"; } | OFF { $$ = "off"; } ; /* Timezone values can be: * - a string such as 'pst8pdt' * - an identifier such as "pst8pdt" * - an integer or floating point number * - a time interval per SQL99 * ColId gives reduce/reduce errors against ConstInterval and LOCAL, * so use IDENT and reject anything which is a reserved word. */ zone_value: Sconst { $$ = makeStringConst($1, NULL); } | IDENT { $$ = makeStringConst($1, NULL); } | ConstInterval Sconst opt_interval { A_Const *n = (A_Const *) makeStringConst($2, $1); if ($3 != INTERVAL_FULL_RANGE) { if (($3 & ~(INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE))) != 0) elog(ERROR, "Time zone interval" " must be HOUR or HOUR TO MINUTE"); n->typename->typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, $3); } $$ = (Node *)n; } | ConstInterval '(' Iconst ')' Sconst opt_interval { A_Const *n = (A_Const *) makeStringConst($5, $1); if (($3 < 0) || ($3 > MAX_INTERVAL_PRECISION)) elog(ERROR, "INTERVAL(%d) precision must be between %d and %d", $3, 0, MAX_INTERVAL_PRECISION); if (($6 != INTERVAL_FULL_RANGE) && (($6 & ~(INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE))) != 0)) elog(ERROR, "Time zone interval" " must be HOUR or HOUR TO MINUTE"); n->typename->typmod = INTERVAL_TYPMOD($3, $6); $$ = (Node *)n; } | NumericOnly { $$ = makeAConst($1); } | DEFAULT { $$ = NULL; } | LOCAL { $$ = NULL; } ; opt_encoding: Sconst { $$ = $1; } | DEFAULT { $$ = NULL; } | /*EMPTY*/ { $$ = NULL; } ; ColId_or_Sconst: ColId { $$ = $1; } | SCONST { $$ = $1; } ; VariableShowStmt: SHOW ColId { VariableShowStmt *n = makeNode(VariableShowStmt); n->name = $2; $$ = (Node *) n; } | SHOW TIME ZONE { VariableShowStmt *n = makeNode(VariableShowStmt); n->name = "timezone"; $$ = (Node *) n; } | SHOW TRANSACTION ISOLATION LEVEL { VariableShowStmt *n = makeNode(VariableShowStmt); n->name = "TRANSACTION ISOLATION LEVEL"; $$ = (Node *) n; } | SHOW SESSION AUTHORIZATION { VariableShowStmt *n = makeNode(VariableShowStmt); n->name = "session_authorization"; $$ = (Node *) n; } | SHOW ALL { VariableShowStmt *n = makeNode(VariableShowStmt); n->name = "all"; $$ = (Node *) n; } ; VariableResetStmt: RESET ColId { VariableResetStmt *n = makeNode(VariableResetStmt); n->name = $2; $$ = (Node *) n; } | RESET TIME ZONE { VariableResetStmt *n = makeNode(VariableResetStmt); n->name = "timezone"; $$ = (Node *) n; } | RESET TRANSACTION ISOLATION LEVEL { VariableResetStmt *n = makeNode(VariableResetStmt); n->name = "TRANSACTION ISOLATION LEVEL"; $$ = (Node *) n; } | RESET SESSION AUTHORIZATION { VariableResetStmt *n = makeNode(VariableResetStmt); n->name = "session_authorization"; $$ = (Node *) n; } | RESET ALL { VariableResetStmt *n = makeNode(VariableResetStmt); n->name = "all"; $$ = (Node *) n; } ; ConstraintsSetStmt: SET CONSTRAINTS constraints_set_list constraints_set_mode { ConstraintsSetStmt *n = makeNode(ConstraintsSetStmt); n->constraints = $3; n->deferred = $4; $$ = (Node *) n; } ; constraints_set_list: ALL { $$ = NIL; } | name_list { $$ = $1; } ; constraints_set_mode: DEFERRED { $$ = TRUE; } | IMMEDIATE { $$ = FALSE; } ; /* * Checkpoint statement */ CheckPointStmt: CHECKPOINT { CheckPointStmt *n = makeNode(CheckPointStmt); $$ = (Node *)n; } ; /***************************************************************************** * * ALTER TABLE variations * *****************************************************************************/ AlterTableStmt: /* ALTER TABLE ADD [COLUMN] */ ALTER TABLE relation_expr ADD opt_column columnDef { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'A'; n->relation = $3; n->def = $6; $$ = (Node *)n; } /* ALTER TABLE ALTER [COLUMN] {SET DEFAULT |DROP DEFAULT} */ | ALTER TABLE relation_expr ALTER opt_column ColId alter_column_default { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'T'; n->relation = $3; n->name = $6; n->def = $7; $$ = (Node *)n; } /* ALTER TABLE ALTER [COLUMN] DROP NOT NULL */ | ALTER TABLE relation_expr ALTER opt_column ColId DROP NOT NULL_P { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'N'; n->relation = $3; n->name = $6; $$ = (Node *)n; } /* ALTER TABLE ALTER [COLUMN] SET NOT NULL */ | ALTER TABLE relation_expr ALTER opt_column ColId SET NOT NULL_P { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'O'; n->relation = $3; n->name = $6; $$ = (Node *)n; } /* ALTER TABLE ALTER [COLUMN] SET STATISTICS */ | ALTER TABLE relation_expr ALTER opt_column ColId SET STATISTICS IntegerOnly { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'S'; n->relation = $3; n->name = $6; n->def = (Node *) $9; $$ = (Node *)n; } /* ALTER TABLE ALTER [COLUMN] SET STORAGE */ | ALTER TABLE relation_expr ALTER opt_column ColId SET STORAGE ColId { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'M'; n->relation = $3; n->name = $6; n->def = (Node *) makeString($9); $$ = (Node *)n; } /* ALTER TABLE DROP [COLUMN] [RESTRICT|CASCADE] */ | ALTER TABLE relation_expr DROP opt_column ColId opt_drop_behavior { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'D'; n->relation = $3; n->name = $6; n->behavior = $7; $$ = (Node *)n; } /* ALTER TABLE ADD CONSTRAINT ... */ | ALTER TABLE relation_expr ADD TableConstraint { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'C'; n->relation = $3; n->def = $5; $$ = (Node *)n; } /* ALTER TABLE DROP CONSTRAINT [RESTRICT|CASCADE] */ | ALTER TABLE relation_expr DROP CONSTRAINT name opt_drop_behavior { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'X'; n->relation = $3; n->name = $6; n->behavior = $7; $$ = (Node *)n; } /* ALTER TABLE CREATE TOAST TABLE */ | ALTER TABLE qualified_name CREATE TOAST TABLE { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'E'; $3->inhOpt = INH_NO; n->relation = $3; $$ = (Node *)n; } /* ALTER TABLE OWNER TO UserId */ | ALTER TABLE qualified_name OWNER TO UserId { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'U'; $3->inhOpt = INH_NO; n->relation = $3; n->name = $6; $$ = (Node *)n; } ; alter_column_default: SET DEFAULT a_expr { /* Treat SET DEFAULT NULL the same as DROP DEFAULT */ if (exprIsNullConstant($3)) $$ = NULL; else $$ = $3; } | DROP DEFAULT { $$ = NULL; } ; opt_drop_behavior: CASCADE { $$ = DROP_CASCADE; } | RESTRICT { $$ = DROP_RESTRICT; } | /* EMPTY */ { $$ = DROP_RESTRICT; /* default */ } ; /***************************************************************************** * * QUERY : * close * *****************************************************************************/ ClosePortalStmt: CLOSE opt_id { ClosePortalStmt *n = makeNode(ClosePortalStmt); n->portalname = $2; $$ = (Node *)n; } ; opt_id: ColId { $$ = $1; } | /*EMPTY*/ { $$ = NULL; } ; /***************************************************************************** * * QUERY : * COPY ['(' columnList ')'] FROM/TO [WITH options] * * BINARY, OIDS, and DELIMITERS kept in old locations * for backward compatibility. 2002-06-18 * *****************************************************************************/ CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids copy_from copy_file_name copy_delimiter opt_with copy_opt_list { CopyStmt *n = makeNode(CopyStmt); n->relation = $3; n->attlist = $4; n->is_from = $6; n->filename = $7; n->options = NIL; /* Concatenate user-supplied flags */ if ($2) n->options = lappend(n->options, $2); if ($5) n->options = lappend(n->options, $5); if ($8) n->options = lappend(n->options, $8); if ($10) n->options = nconc(n->options, $10); $$ = (Node *)n; } ; copy_from: FROM { $$ = TRUE; } | TO { $$ = FALSE; } ; /* * 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; } ; copy_opt_list: copy_opt_list copy_opt_item { $$ = lappend($1, $2); } | /* EMPTY */ { $$ = NIL; } ; copy_opt_item: BINARY { $$ = makeDefElem("binary", (Node *)makeInteger(TRUE)); } | OIDS { $$ = makeDefElem("oids", (Node *)makeInteger(TRUE)); } | DELIMITER opt_as Sconst { $$ = makeDefElem("delimiter", (Node *)makeString($3)); } | NULL_P opt_as Sconst { $$ = makeDefElem("null", (Node *)makeString($3)); } ; /* The following exist for backward compatibility */ opt_binary: BINARY { $$ = makeDefElem("binary", (Node *)makeInteger(TRUE)); } | /*EMPTY*/ { $$ = NULL; } ; opt_oids: WITH OIDS { $$ = makeDefElem("oids", (Node *)makeInteger(TRUE)); } | /*EMPTY*/ { $$ = NULL; } ; copy_delimiter: /* USING DELIMITERS kept for backward compatibility. 2002-06-15 */ opt_using DELIMITERS Sconst { $$ = makeDefElem("delimiter", (Node *)makeString($3)); } | /*EMPTY*/ { $$ = NULL; } ; opt_using: USING {} | /*EMPTY*/ {} ; /***************************************************************************** * * QUERY : * CREATE relname * *****************************************************************************/ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' OptInherit OptWithOids { CreateStmt *n = makeNode(CreateStmt); $4->istemp = $2; n->relation = $4; n->tableElts = $6; n->inhRelations = $8; n->constraints = NIL; n->hasoids = $9; $$ = (Node *)n; } | CREATE OptTemp TABLE qualified_name OF qualified_name '(' OptTableElementList ')' OptWithOids { /* SQL99 CREATE TABLE OF (cols) seems to be satisfied * by our inheritance capabilities. Let's try it... */ CreateStmt *n = makeNode(CreateStmt); $4->istemp = $2; n->relation = $4; n->tableElts = $8; n->inhRelations = makeList1($6); n->constraints = NIL; n->hasoids = $10; $$ = (Node *)n; } ; /* * Redundancy here is needed to avoid shift/reduce conflicts, * since TEMP is not a reserved word. See also OptTempTableName. */ OptTemp: TEMPORARY { $$ = TRUE; } | TEMP { $$ = TRUE; } | LOCAL TEMPORARY { $$ = TRUE; } | LOCAL TEMP { $$ = TRUE; } | GLOBAL TEMPORARY { elog(ERROR, "GLOBAL TEMPORARY TABLE is not currently supported"); $$ = TRUE; } | GLOBAL TEMP { elog(ERROR, "GLOBAL TEMPORARY TABLE is not currently supported"); $$ = TRUE; } | /*EMPTY*/ { $$ = FALSE; } ; OptTableElementList: TableElementList { $$ = $1; } | /*EMPTY*/ { $$ = NIL; } ; TableElementList: TableElement { $$ = makeList1($1); } | TableElementList ',' TableElement { $$ = lappend($1, $3); } ; TableElement: columnDef { $$ = $1; } | TableLikeClause { $$ = $1; } | TableConstraint { $$ = $1; } ; columnDef: ColId Typename ColQualList opt_collate { ColumnDef *n = makeNode(ColumnDef); n->colname = $1; n->typename = $2; n->constraints = $3; if ($4 != NULL) elog(NOTICE, "CREATE TABLE / COLLATE %s not yet implemented; " "clause ignored", $4); $$ = (Node *)n; } ; ColQualList: ColQualList ColConstraint { $$ = lappend($1, $2); } | /*EMPTY*/ { $$ = NIL; } ; ColConstraint: CONSTRAINT name ColConstraintElem { switch (nodeTag($3)) { case T_Constraint: { Constraint *n = (Constraint *)$3; n->name = $2; } break; case T_FkConstraint: { FkConstraint *n = (FkConstraint *)$3; n->constr_name = $2; } break; default: break; } $$ = $3; } | ColConstraintElem { $$ = $1; } | ConstraintAttr { $$ = $1; } ; /* DEFAULT NULL is already the default for Postgres. * But define it here and carry it forward into the system * to make it explicit. * - thomas 1998-09-13 * * WITH NULL and NULL are not SQL92-standard syntax elements, * so leave them out. Use DEFAULT NULL to explicitly indicate * that a column may have that value. WITH NULL leads to * shift/reduce conflicts with WITH TIME ZONE anyway. * - thomas 1999-01-08 * * DEFAULT expression must be b_expr not a_expr to prevent shift/reduce * conflict on NOT (since NOT might start a subsequent NOT NULL constraint, * or be part of a_expr NOT LIKE or similar constructs). */ ColConstraintElem: NOT NULL_P { Constraint *n = makeNode(Constraint); n->contype = CONSTR_NOTNULL; n->name = NULL; n->raw_expr = NULL; n->cooked_expr = NULL; n->keys = NULL; $$ = (Node *)n; } | NULL_P { Constraint *n = makeNode(Constraint); n->contype = CONSTR_NULL; n->name = NULL; n->raw_expr = NULL; n->cooked_expr = NULL; n->keys = NULL; $$ = (Node *)n; } | UNIQUE { Constraint *n = makeNode(Constraint); n->contype = CONSTR_UNIQUE; n->name = NULL; n->raw_expr = NULL; n->cooked_expr = NULL; n->keys = NULL; $$ = (Node *)n; } | PRIMARY KEY { Constraint *n = makeNode(Constraint); n->contype = CONSTR_PRIMARY; n->name = NULL; n->raw_expr = NULL; n->cooked_expr = NULL; n->keys = NULL; $$ = (Node *)n; } | CHECK '(' a_expr ')' { Constraint *n = makeNode(Constraint); n->contype = CONSTR_CHECK; n->name = NULL; n->raw_expr = $3; n->cooked_expr = NULL; n->keys = NULL; $$ = (Node *)n; } | DEFAULT b_expr { Constraint *n = makeNode(Constraint); n->contype = CONSTR_DEFAULT; n->name = NULL; if (exprIsNullConstant($2)) { /* DEFAULT NULL should be reported as empty expr */ n->raw_expr = NULL; } else { n->raw_expr = $2; } n->cooked_expr = NULL; n->keys = NULL; $$ = (Node *)n; } | REFERENCES qualified_name opt_column_list key_match key_actions { FkConstraint *n = makeNode(FkConstraint); n->constr_name = NULL; n->pktable = $2; n->fk_attrs = NIL; n->pk_attrs = $3; n->fk_matchtype = $4; n->fk_upd_action = (char) ($5 >> 8); n->fk_del_action = (char) ($5 & 0xFF); n->deferrable = FALSE; n->initdeferred = FALSE; $$ = (Node *)n; } ; /* * ConstraintAttr represents constraint attributes, which we parse as if * they were independent constraint clauses, in order to avoid shift/reduce * conflicts (since NOT might start either an independent NOT NULL clause * or an attribute). analyze.c is responsible for attaching the attribute * information to the preceding "real" constraint node, and for complaining * if attribute clauses appear in the wrong place or wrong combinations. * * See also ConstraintAttributeSpec, which can be used in places where * there is no parsing conflict. */ ConstraintAttr: DEFERRABLE { Constraint *n = makeNode(Constraint); n->contype = CONSTR_ATTR_DEFERRABLE; $$ = (Node *)n; } | NOT DEFERRABLE { Constraint *n = makeNode(Constraint); n->contype = CONSTR_ATTR_NOT_DEFERRABLE; $$ = (Node *)n; } | INITIALLY DEFERRED { Constraint *n = makeNode(Constraint); n->contype = CONSTR_ATTR_DEFERRED; $$ = (Node *)n; } | INITIALLY IMMEDIATE { Constraint *n = makeNode(Constraint); n->contype = CONSTR_ATTR_IMMEDIATE; $$ = (Node *)n; } ; /* SQL99 supports wholesale borrowing of a table definition via the LIKE clause. * This seems to be a poor man's inheritance capability, with the resulting * tables completely decoupled except for the original commonality in definitions. * Seems to have much in common with CREATE TABLE AS. - thomas 2002-06-19 */ TableLikeClause: LIKE any_name { elog(ERROR, "LIKE in table definitions not yet supported"); $$ = NULL; } ; /* ConstraintElem specifies constraint syntax which is not embedded into * a column definition. ColConstraintElem specifies the embedded form. * - thomas 1997-12-03 */ TableConstraint: CONSTRAINT name ConstraintElem { switch (nodeTag($3)) { case T_Constraint: { Constraint *n = (Constraint *)$3; n->name = $2; } break; case T_FkConstraint: { FkConstraint *n = (FkConstraint *)$3; n->constr_name = $2; } break; default: break; } $$ = $3; } | ConstraintElem { $$ = $1; } ; ConstraintElem: CHECK '(' a_expr ')' { Constraint *n = makeNode(Constraint); n->contype = CONSTR_CHECK; n->name = NULL; n->raw_expr = $3; n->cooked_expr = NULL; $$ = (Node *)n; } | UNIQUE '(' columnList ')' { Constraint *n = makeNode(Constraint); n->contype = CONSTR_UNIQUE; n->name = NULL; n->raw_expr = NULL; n->cooked_expr = NULL; n->keys = $3; $$ = (Node *)n; } | PRIMARY KEY '(' columnList ')' { Constraint *n = makeNode(Constraint); n->contype = CONSTR_PRIMARY; n->name = NULL; n->raw_expr = NULL; n->cooked_expr = NULL; n->keys = $4; $$ = (Node *)n; } | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name opt_column_list key_match key_actions ConstraintAttributeSpec { FkConstraint *n = makeNode(FkConstraint); n->constr_name = NULL; n->pktable = $7; n->fk_attrs = $4; n->pk_attrs = $8; n->fk_matchtype = $9; n->fk_upd_action = (char) ($10 >> 8); n->fk_del_action = (char) ($10 & 0xFF); n->deferrable = ($11 & 1) != 0; n->initdeferred = ($11 & 2) != 0; $$ = (Node *)n; } ; opt_column_list: '(' columnList ')' { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } ; columnList: columnElem { $$ = makeList1($1); } | columnList ',' columnElem { $$ = lappend($1, $3); } ; columnElem: ColId { $$ = (Node *) makeString($1); } ; key_match: MATCH FULL { $$ = FKCONSTR_MATCH_FULL; } | MATCH PARTIAL { elog(ERROR, "FOREIGN KEY/MATCH PARTIAL not yet implemented"); $$ = FKCONSTR_MATCH_PARTIAL; } | MATCH SIMPLE { $$ = FKCONSTR_MATCH_UNSPECIFIED; } | /*EMPTY*/ { $$ = FKCONSTR_MATCH_UNSPECIFIED; } ; /* * We combine the update and delete actions into one value temporarily * for simplicity of parsing, and then break them down again in the * calling production. update is in the left 8 bits, delete in the right. * Note that NOACTION is the default. */ key_actions: key_update { $$ = ($1 << 8) | (FKCONSTR_ACTION_NOACTION & 0xFF); } | key_delete { $$ = (FKCONSTR_ACTION_NOACTION << 8) | ($1 & 0xFF); } | key_update key_delete { $$ = ($1 << 8) | ($2 & 0xFF); } | key_delete key_update { $$ = ($2 << 8) | ($1 & 0xFF); } | /*EMPTY*/ { $$ = (FKCONSTR_ACTION_NOACTION << 8) | (FKCONSTR_ACTION_NOACTION & 0xFF); } ; key_update: ON UPDATE key_action { $$ = $3; } ; key_delete: ON DELETE_P key_action { $$ = $3; } ; key_action: NO ACTION { $$ = FKCONSTR_ACTION_NOACTION; } | RESTRICT { $$ = FKCONSTR_ACTION_RESTRICT; } | CASCADE { $$ = FKCONSTR_ACTION_CASCADE; } | SET NULL_P { $$ = FKCONSTR_ACTION_SETNULL; } | SET DEFAULT { $$ = FKCONSTR_ACTION_SETDEFAULT; } ; OptInherit: INHERITS '(' qualified_name_list ')' { $$ = $3; } | /*EMPTY*/ { $$ = NIL; } ; OptWithOids: WITH OIDS { $$ = TRUE; } | WITHOUT OIDS { $$ = FALSE; } | /*EMPTY*/ { $$ = TRUE; } ; /* * Note: CREATE TABLE ... AS SELECT ... is just another spelling for * SELECT ... INTO. */ CreateAsStmt: CREATE OptTemp TABLE qualified_name OptCreateAs AS SelectStmt { /* * When the SelectStmt is a set-operation tree, we must * stuff the INTO information into the leftmost component * Select, because that's where analyze.c will expect * to find it. Similarly, the output column names must * be attached to that Select's target list. */ SelectStmt *n = findLeftmostSelect((SelectStmt *) $7); if (n->into != NULL) elog(ERROR, "CREATE TABLE AS may not specify INTO"); $4->istemp = $2; n->into = $4; n->intoColNames = $5; $$ = $7; } ; OptCreateAs: '(' CreateAsList ')' { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } ; CreateAsList: CreateAsElement { $$ = makeList1($1); } | CreateAsList ',' CreateAsElement { $$ = lappend($1, $3); } ; CreateAsElement: ColId { ColumnDef *n = makeNode(ColumnDef); n->colname = $1; n->typename = NULL; n->is_not_null = false; n->raw_default = NULL; n->cooked_default = NULL; n->constraints = NIL; n->support = NULL; $$ = (Node *)n; } ; /***************************************************************************** * * QUERY : * CREATE SEQUENCE seqname * *****************************************************************************/ CreateSeqStmt: CREATE OptTemp SEQUENCE qualified_name OptSeqList { CreateSeqStmt *n = makeNode(CreateSeqStmt); $4->istemp = $2; n->sequence = $4; n->options = $5; $$ = (Node *)n; } ; OptSeqList: OptSeqList OptSeqElem { $$ = lappend($1, $2); } | /*EMPTY*/ { $$ = NIL; } ; OptSeqElem: CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2); } | CYCLE { $$ = makeDefElem("cycle", (Node *)NULL); } | INCREMENT NumericOnly { $$ = makeDefElem("increment", (Node *)$2); } | MAXVALUE NumericOnly { $$ = makeDefElem("maxvalue", (Node *)$2); } | MINVALUE NumericOnly { $$ = makeDefElem("minvalue", (Node *)$2); } | START NumericOnly { $$ = makeDefElem("start", (Node *)$2); } ; NumericOnly: FloatOnly { $$ = $1; } | IntegerOnly { $$ = $1; } ; FloatOnly: FCONST { $$ = makeFloat($1); } | '-' FCONST { $$ = makeFloat($2); doNegateFloat($$); } ; IntegerOnly: Iconst { $$ = makeInteger($1); } | '-' Iconst { $$ = makeInteger($2); $$->val.ival = - $$->val.ival; } ; /***************************************************************************** * * QUERIES : * CREATE PROCEDURAL LANGUAGE ... * DROP PROCEDURAL LANGUAGE ... * *****************************************************************************/ CreatePLangStmt: CREATE opt_trusted opt_procedural LANGUAGE ColId_or_Sconst HANDLER handler_name opt_validator opt_lancompiler { CreatePLangStmt *n = makeNode(CreatePLangStmt); n->plname = $5; n->plhandler = $7; n->plvalidator = $8; n->pltrusted = $2; $$ = (Node *)n; } ; opt_trusted: TRUSTED { $$ = TRUE; } | /*EMPTY*/ { $$ = FALSE; } ; /* This ought to be just func_name, but that causes reduce/reduce conflicts * (CREATE LANGUAGE is the only place where func_name isn't followed by '('). * Work around by using name and dotted_name separately. */ handler_name: name { $$ = makeList1(makeString($1)); } | dotted_name { $$ = $1; } ; opt_lancompiler: LANCOMPILER Sconst { $$ = $2; } | /*EMPTY*/ { $$ = ""; } ; opt_validator: VALIDATOR handler_name { $$ = $2; } | /*EMPTY*/ { $$ = NULL; } ; DropPLangStmt: DROP opt_procedural LANGUAGE ColId_or_Sconst opt_drop_behavior { DropPLangStmt *n = makeNode(DropPLangStmt); n->plname = $4; n->behavior = $5; $$ = (Node *)n; } ; opt_procedural: PROCEDURAL {} | /*EMPTY*/ {} ; /***************************************************************************** * * QUERIES : * CREATE TRIGGER ... * DROP TRIGGER ... * *****************************************************************************/ CreateTrigStmt: CREATE TRIGGER name TriggerActionTime TriggerEvents ON qualified_name TriggerForSpec EXECUTE PROCEDURE func_name '(' TriggerFuncArgs ')' { CreateTrigStmt *n = makeNode(CreateTrigStmt); n->trigname = $3; n->relation = $7; n->funcname = $11; n->args = $13; n->before = $4; n->row = $8; memcpy (n->actions, $5, 4); n->lang = NULL; /* unused */ n->text = NULL; /* unused */ n->attr = NULL; /* unused */ n->when = NULL; /* unused */ n->isconstraint = FALSE; n->deferrable = FALSE; n->initdeferred = FALSE; n->constrrel = NULL; $$ = (Node *)n; } | CREATE CONSTRAINT TRIGGER name AFTER TriggerEvents ON qualified_name OptConstrFromTable ConstraintAttributeSpec FOR EACH ROW EXECUTE PROCEDURE func_name '(' TriggerFuncArgs ')' { CreateTrigStmt *n = makeNode(CreateTrigStmt); n->trigname = $4; n->relation = $8; n->funcname = $16; n->args = $18; n->before = FALSE; n->row = TRUE; memcpy (n->actions, $6, 4); n->lang = NULL; /* unused */ n->text = NULL; /* unused */ n->attr = NULL; /* unused */ n->when = NULL; /* unused */ n->isconstraint = TRUE; n->deferrable = ($10 & 1) != 0; n->initdeferred = ($10 & 2) != 0; n->constrrel = $9; $$ = (Node *)n; } ; TriggerActionTime: BEFORE { $$ = TRUE; } | AFTER { $$ = FALSE; } ; TriggerEvents: TriggerOneEvent { char *e = palloc (4); e[0] = $1; e[1] = 0; $$ = e; } | TriggerOneEvent OR TriggerOneEvent { char *e = palloc (4); e[0] = $1; e[1] = $3; e[2] = 0; $$ = e; } | TriggerOneEvent OR TriggerOneEvent OR TriggerOneEvent { char *e = palloc (4); e[0] = $1; e[1] = $3; e[2] = $5; e[3] = 0; $$ = e; } ; TriggerOneEvent: INSERT { $$ = 'i'; } | DELETE_P { $$ = 'd'; } | UPDATE { $$ = 'u'; } ; TriggerForSpec: FOR TriggerForOpt TriggerForType { $$ = $3; } ; TriggerForOpt: EACH {} | /*EMPTY*/ {} ; TriggerForType: ROW { $$ = TRUE; } | STATEMENT { $$ = FALSE; } ; TriggerFuncArgs: TriggerFuncArg { $$ = makeList1($1); } | TriggerFuncArgs ',' TriggerFuncArg { $$ = lappend($1, $3); } | /*EMPTY*/ { $$ = NIL; } ; TriggerFuncArg: ICONST { char buf[64]; snprintf (buf, sizeof(buf), "%d", $1); $$ = makeString(pstrdup(buf)); } | FCONST { $$ = makeString($1); } | Sconst { $$ = makeString($1); } | BCONST { $$ = makeString($1); } | XCONST { $$ = makeString($1); } | ColId { $$ = makeString($1); } ; OptConstrFromTable: FROM qualified_name { $$ = $2; } | /*EMPTY*/ { $$ = NULL; } ; ConstraintAttributeSpec: ConstraintDeferrabilitySpec { $$ = $1; } | ConstraintDeferrabilitySpec ConstraintTimeSpec { if ($1 == 0 && $2 != 0) elog(ERROR, "INITIALLY DEFERRED constraint must be DEFERRABLE"); $$ = $1 | $2; } | ConstraintTimeSpec { if ($1 != 0) $$ = 3; else $$ = 0; } | ConstraintTimeSpec ConstraintDeferrabilitySpec { if ($2 == 0 && $1 != 0) elog(ERROR, "INITIALLY DEFERRED constraint must be DEFERRABLE"); $$ = $1 | $2; } | /*EMPTY*/ { $$ = 0; } ; ConstraintDeferrabilitySpec: NOT DEFERRABLE { $$ = 0; } | DEFERRABLE { $$ = 1; } ; ConstraintTimeSpec: INITIALLY IMMEDIATE { $$ = 0; } | INITIALLY DEFERRED { $$ = 2; } ; DropTrigStmt: DROP TRIGGER name ON qualified_name opt_drop_behavior { DropPropertyStmt *n = makeNode(DropPropertyStmt); n->relation = $5; n->property = $3; n->behavior = $6; n->removeType = DROP_TRIGGER; $$ = (Node *) n; } ; /***************************************************************************** * * QUERIES : * CREATE ASSERTION ... * DROP ASSERTION ... * *****************************************************************************/ CreateAssertStmt: CREATE ASSERTION name CHECK '(' a_expr ')' ConstraintAttributeSpec { CreateTrigStmt *n = makeNode(CreateTrigStmt); n->trigname = $3; n->args = makeList1($6); n->isconstraint = TRUE; n->deferrable = ($8 & 1) != 0; n->initdeferred = ($8 & 2) != 0; elog(ERROR, "CREATE ASSERTION is not yet supported"); $$ = (Node *)n; } ; DropAssertStmt: DROP ASSERTION name opt_drop_behavior { DropPropertyStmt *n = makeNode(DropPropertyStmt); n->relation = NULL; n->property = $3; n->behavior = $4; n->removeType = DROP_TRIGGER; /* XXX */ elog(ERROR, "DROP ASSERTION is not yet supported"); $$ = (Node *) n; } ; /***************************************************************************** * * QUERY : * define (aggregate,operator,type) * *****************************************************************************/ DefineStmt: CREATE AGGREGATE func_name definition { DefineStmt *n = makeNode(DefineStmt); n->defType = AGGREGATE; n->defnames = $3; n->definition = $4; $$ = (Node *)n; } | CREATE OPERATOR any_operator definition { DefineStmt *n = makeNode(DefineStmt); n->defType = OPERATOR; n->defnames = $3; n->definition = $4; $$ = (Node *)n; } | CREATE TYPE_P any_name definition { DefineStmt *n = makeNode(DefineStmt); n->defType = TYPE_P; n->defnames = $3; n->definition = $4; $$ = (Node *)n; } | CREATE TYPE_P any_name AS '(' TableFuncElementList ')' { CompositeTypeStmt *n = makeNode(CompositeTypeStmt); RangeVar *r = makeNode(RangeVar); /* can't use qualified_name, sigh */ switch (length($3)) { case 1: r->catalogname = NULL; r->schemaname = NULL; r->relname = strVal(lfirst($3)); break; case 2: r->catalogname = NULL; r->schemaname = strVal(lfirst($3)); r->relname = strVal(lsecond($3)); break; case 3: r->catalogname = strVal(lfirst($3)); r->schemaname = strVal(lsecond($3)); r->relname = strVal(lfirst(lnext(lnext($3)))); break; default: elog(ERROR, "Improper qualified name (too many dotted names): %s", NameListToString($3)); break; } n->typevar = r; n->coldeflist = $6; $$ = (Node *)n; } | CREATE CHARACTER SET opt_as any_name GET definition opt_collate { DefineStmt *n = makeNode(DefineStmt); n->defType = CHARACTER; n->defnames = $5; n->definition = $7; $$ = (Node *)n; } ; definition: '(' def_list ')' { $$ = $2; } ; def_list: def_elem { $$ = makeList1($1); } | def_list ',' def_elem { $$ = lappend($1, $3); } ; def_elem: ColLabel '=' def_arg { $$ = makeDefElem($1, (Node *)$3); } | ColLabel { $$ = makeDefElem($1, (Node *)NULL); } ; /* Note: any simple identifier will be returned as a type name! */ def_arg: func_return { $$ = (Node *)$1; } | qual_all_Op { $$ = (Node *)$1; } | NumericOnly { $$ = (Node *)$1; } | Sconst { $$ = (Node *)makeString($1); } ; /***************************************************************************** * * QUERIES : * CREATE OPERATOR CLASS ... * DROP OPERATOR CLASS ... * *****************************************************************************/ CreateOpClassStmt: CREATE OPERATOR CLASS any_name opt_default FOR TYPE_P Typename USING access_method AS opclass_item_list { CreateOpClassStmt *n = makeNode(CreateOpClassStmt); n->opclassname = $4; n->isDefault = $5; n->datatype = $8; n->amname = $10; n->items = $12; $$ = (Node *) n; } ; opclass_item_list: opclass_item { $$ = makeList1($1); } | opclass_item_list ',' opclass_item { $$ = lappend($1, $3); } ; opclass_item: OPERATOR Iconst any_operator opt_recheck { CreateOpClassItem *n = makeNode(CreateOpClassItem); n->itemtype = OPCLASS_ITEM_OPERATOR; n->name = $3; n->args = NIL; n->number = $2; n->recheck = $4; $$ = (Node *) n; } | OPERATOR Iconst any_operator '(' oper_argtypes ')' opt_recheck { CreateOpClassItem *n = makeNode(CreateOpClassItem); n->itemtype = OPCLASS_ITEM_OPERATOR; n->name = $3; n->args = $5; n->number = $2; n->recheck = $7; $$ = (Node *) n; } | FUNCTION Iconst func_name func_args { CreateOpClassItem *n = makeNode(CreateOpClassItem); n->itemtype = OPCLASS_ITEM_FUNCTION; n->name = $3; n->args = $4; n->number = $2; $$ = (Node *) n; } | STORAGE Typename { CreateOpClassItem *n = makeNode(CreateOpClassItem); n->itemtype = OPCLASS_ITEM_STORAGETYPE; n->storedtype = $2; $$ = (Node *) n; } ; opt_default: DEFAULT { $$ = TRUE; } | /*EMPTY*/ { $$ = FALSE; } ; opt_recheck: RECHECK { $$ = TRUE; } | /*EMPTY*/ { $$ = FALSE; } ; DropOpClassStmt: DROP OPERATOR CLASS any_name USING access_method opt_drop_behavior { RemoveOpClassStmt *n = makeNode(RemoveOpClassStmt); n->opclassname = $4; n->amname = $6; n->behavior = $7; $$ = (Node *) n; } ; /***************************************************************************** * * QUERY: * * DROP itemtype itemname [, itemname ...] [ RESTRICT | CASCADE ] * *****************************************************************************/ DropStmt: DROP drop_type any_name_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = $2; n->objects = $3; n->behavior = $4; $$ = (Node *)n; } ; drop_type: TABLE { $$ = DROP_TABLE; } | SEQUENCE { $$ = DROP_SEQUENCE; } | VIEW { $$ = DROP_VIEW; } | INDEX { $$ = DROP_INDEX; } | TYPE_P { $$ = DROP_TYPE; } | DOMAIN_P { $$ = DROP_DOMAIN; } | CONVERSION_P { $$ = DROP_CONVERSION; } | SCHEMA { $$ = DROP_SCHEMA; } ; any_name_list: any_name { $$ = makeList1($1); } | any_name_list ',' any_name { $$ = lappend($1, $3); } ; any_name: ColId { $$ = makeList1(makeString($1)); } | dotted_name { $$ = $1; } ; /***************************************************************************** * * QUERY: * truncate table relname * *****************************************************************************/ TruncateStmt: TRUNCATE opt_table qualified_name { TruncateStmt *n = makeNode(TruncateStmt); n->relation = $3; $$ = (Node *)n; } ; /***************************************************************************** * * The COMMENT ON statement can take different forms based upon the type of * the object associated with the comment. The form of the statement is: * * COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW ] * | AGGREGATE () | FUNCTION * (arg1, arg2, ...) | OPERATOR * (leftoperand_typ rightoperand_typ) | TRIGGER ON * | RULE ON ] IS 'text' * *****************************************************************************/ CommentStmt: COMMENT ON comment_type any_name IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = $3; n->objname = $4; n->objargs = NIL; n->comment = $6; $$ = (Node *) n; } | COMMENT ON AGGREGATE func_name '(' aggr_argtype ')' IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = COMMENT_ON_AGGREGATE; n->objname = $4; n->objargs = makeList1($6); n->comment = $9; $$ = (Node *) n; } | COMMENT ON FUNCTION func_name func_args IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = COMMENT_ON_FUNCTION; n->objname = $4; n->objargs = $5; n->comment = $7; $$ = (Node *) n; } | COMMENT ON OPERATOR any_operator '(' oper_argtypes ')' IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = COMMENT_ON_OPERATOR; n->objname = $4; n->objargs = $6; n->comment = $9; $$ = (Node *) n; } | COMMENT ON CONSTRAINT name ON any_name IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = COMMENT_ON_CONSTRAINT; n->objname = lappend($6, makeString($4)); n->objargs = NIL; n->comment = $8; $$ = (Node *) n; } | COMMENT ON RULE name ON any_name IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = COMMENT_ON_RULE; n->objname = lappend($6, makeString($4)); n->objargs = NIL; n->comment = $8; $$ = (Node *) n; } | COMMENT ON RULE name IS comment_text { /* Obsolete syntax supported for awhile for compatibility */ CommentStmt *n = makeNode(CommentStmt); n->objtype = COMMENT_ON_RULE; n->objname = makeList1(makeString($4)); n->objargs = NIL; n->comment = $6; $$ = (Node *) n; } | COMMENT ON TRIGGER name ON any_name IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = COMMENT_ON_TRIGGER; n->objname = lappend($6, makeString($4)); n->objargs = NIL; n->comment = $8; $$ = (Node *) n; } ; comment_type: COLUMN { $$ = COMMENT_ON_COLUMN; } | DATABASE { $$ = COMMENT_ON_DATABASE; } | SCHEMA { $$ = COMMENT_ON_SCHEMA; } | INDEX { $$ = COMMENT_ON_INDEX; } | SEQUENCE { $$ = COMMENT_ON_SEQUENCE; } | TABLE { $$ = COMMENT_ON_TABLE; } | DOMAIN_P { $$ = COMMENT_ON_TYPE; } | TYPE_P { $$ = COMMENT_ON_TYPE; } | VIEW { $$ = COMMENT_ON_VIEW; } ; comment_text: Sconst { $$ = $1; } | NULL_P { $$ = NULL; } ; /***************************************************************************** * * QUERY: * fetch/move [forward | backward] [ # | all ] [ in ] * fetch [ forward | backward | absolute | relative ] * [ # | all | next | prior ] [ [ in | from ] ] * *****************************************************************************/ FetchStmt: FETCH direction fetch_how_many from_in name { FetchStmt *n = makeNode(FetchStmt); if ($2 == RELATIVE) { if ($3 == 0) elog(ERROR, "FETCH / RELATIVE at current position is not supported"); $2 = FORWARD; } if ($3 < 0) { $3 = -$3; $2 = (($2 == FORWARD)? BACKWARD: FORWARD); } n->direction = $2; n->howMany = $3; n->portalname = $5; n->ismove = FALSE; $$ = (Node *)n; } | FETCH fetch_how_many from_in name { FetchStmt *n = makeNode(FetchStmt); if ($2 < 0) { n->howMany = -$2; n->direction = BACKWARD; } else { n->direction = FORWARD; n->howMany = $2; } n->portalname = $4; n->ismove = FALSE; $$ = (Node *)n; } | FETCH direction from_in name { FetchStmt *n = makeNode(FetchStmt); if ($2 == RELATIVE) { $2 = FORWARD; } n->direction = $2; n->howMany = 1; n->portalname = $4; n->ismove = FALSE; $$ = (Node *)n; } | FETCH from_in name { FetchStmt *n = makeNode(FetchStmt); n->direction = FORWARD; n->howMany = 1; n->portalname = $3; n->ismove = FALSE; $$ = (Node *)n; } | FETCH name { FetchStmt *n = makeNode(FetchStmt); n->direction = FORWARD; n->howMany = 1; n->portalname = $2; n->ismove = FALSE; $$ = (Node *)n; } | MOVE direction fetch_how_many from_in name { FetchStmt *n = makeNode(FetchStmt); if ($3 < 0) { $3 = -$3; $2 = (($2 == FORWARD)? BACKWARD: FORWARD); } n->direction = $2; n->howMany = $3; n->portalname = $5; n->ismove = TRUE; $$ = (Node *)n; } | MOVE fetch_how_many from_in name { FetchStmt *n = makeNode(FetchStmt); if ($2 < 0) { n->howMany = -$2; n->direction = BACKWARD; } else { n->direction = FORWARD; n->howMany = $2; } n->portalname = $4; n->ismove = TRUE; $$ = (Node *)n; } | MOVE direction from_in name { FetchStmt *n = makeNode(FetchStmt); n->direction = $2; n->howMany = 1; n->portalname = $4; n->ismove = TRUE; $$ = (Node *)n; } | MOVE from_in name { FetchStmt *n = makeNode(FetchStmt); n->direction = FORWARD; n->howMany = 1; n->portalname = $3; n->ismove = TRUE; $$ = (Node *)n; } | MOVE name { FetchStmt *n = makeNode(FetchStmt); n->direction = FORWARD; n->howMany = 1; n->portalname = $2; n->ismove = TRUE; $$ = (Node *)n; } ; direction: FORWARD { $$ = FORWARD; } | BACKWARD { $$ = BACKWARD; } | RELATIVE { $$ = RELATIVE; } | ABSOLUTE { elog(NOTICE, "FETCH / ABSOLUTE not supported, using RELATIVE"); $$ = RELATIVE; } ; fetch_how_many: Iconst { $$ = $1; } | '-' Iconst { $$ = - $2; } /* 0 means fetch all tuples*/ | ALL { $$ = 0; } | NEXT { $$ = 1; } | PRIOR { $$ = -1; } ; from_in: IN_P {} | FROM {} ; /***************************************************************************** * * GRANT and REVOKE statements * *****************************************************************************/ GrantStmt: GRANT privileges ON privilege_target TO grantee_list opt_grant_grant_option { GrantStmt *n = makeNode(GrantStmt); n->is_grant = true; n->privileges = $2; n->objtype = ($4)->objtype; n->objects = ($4)->objs; n->grantees = $6; $$ = (Node*)n; } ; RevokeStmt: REVOKE opt_revoke_grant_option privileges ON privilege_target FROM grantee_list { GrantStmt *n = makeNode(GrantStmt); n->is_grant = false; n->privileges = $3; n->objtype = ($5)->objtype; n->objects = ($5)->objs; n->grantees = $7; $$ = (Node *)n; } ; /* either ALL [PRIVILEGES] or a list of individual privileges */ privileges: privilege_list { $$ = $1; } | ALL { $$ = makeListi1(ACL_ALL_RIGHTS); } | ALL PRIVILEGES { $$ = makeListi1(ACL_ALL_RIGHTS); } ; privilege_list: privilege { $$ = makeListi1($1); } | privilege_list ',' privilege { $$ = lappendi($1, $3); } ; /* Not all of these privilege types apply to all objects, but that * gets sorted out later. */ privilege: SELECT { $$ = ACL_SELECT; } | INSERT { $$ = ACL_INSERT; } | UPDATE { $$ = ACL_UPDATE; } | DELETE_P { $$ = ACL_DELETE; } | RULE { $$ = ACL_RULE; } | REFERENCES { $$ = ACL_REFERENCES; } | TRIGGER { $$ = ACL_TRIGGER; } | EXECUTE { $$ = ACL_EXECUTE; } | USAGE { $$ = ACL_USAGE; } | CREATE { $$ = ACL_CREATE; } | TEMPORARY { $$ = ACL_CREATE_TEMP; } | TEMP { $$ = ACL_CREATE_TEMP; } ; /* Don't bother trying to fold the first two rules into one using opt_table. You're going to get conflicts. */ privilege_target: qualified_name_list { PrivTarget *n = makeNode(PrivTarget); n->objtype = ACL_OBJECT_RELATION; n->objs = $1; $$ = n; } | TABLE qualified_name_list { PrivTarget *n = makeNode(PrivTarget); n->objtype = ACL_OBJECT_RELATION; n->objs = $2; $$ = n; } | FUNCTION function_with_argtypes_list { PrivTarget *n = makeNode(PrivTarget); n->objtype = ACL_OBJECT_FUNCTION; n->objs = $2; $$ = n; } | DATABASE name_list { PrivTarget *n = makeNode(PrivTarget); n->objtype = ACL_OBJECT_DATABASE; n->objs = $2; $$ = n; } | LANGUAGE name_list { PrivTarget *n = makeNode(PrivTarget); n->objtype = ACL_OBJECT_LANGUAGE; n->objs = $2; $$ = n; } | SCHEMA name_list { PrivTarget *n = makeNode(PrivTarget); n->objtype = ACL_OBJECT_NAMESPACE; n->objs = $2; $$ = n; } ; grantee_list: grantee { $$ = makeList1($1); } | grantee_list ',' grantee { $$ = lappend($1, $3); } ; grantee: ColId { PrivGrantee *n = makeNode(PrivGrantee); /* This hack lets us avoid reserving PUBLIC as a keyword*/ if (strcmp($1, "public") == 0) n->username = NULL; else n->username = $1; n->groupname = NULL; $$ = (Node *)n; } | GROUP_P ColId { PrivGrantee *n = makeNode(PrivGrantee); /* Treat GROUP PUBLIC as a synonym for PUBLIC */ if (strcmp($2, "public") == 0) n->groupname = NULL; else n->groupname = $2; n->username = NULL; $$ = (Node *)n; } ; opt_grant_grant_option: WITH GRANT OPTION { elog(ERROR, "grant options are not implemented"); } | /*EMPTY*/ ; opt_revoke_grant_option: GRANT OPTION FOR { elog(ERROR, "grant options are not implemented"); } | /*EMPTY*/ ; function_with_argtypes_list: function_with_argtypes { $$ = makeList1($1); } | function_with_argtypes_list ',' function_with_argtypes { $$ = lappend($1, $3); } ; function_with_argtypes: func_name func_args { FuncWithArgs *n = makeNode(FuncWithArgs); n->funcname = $1; n->funcargs = $2; $$ = (Node *)n; } ; /***************************************************************************** * * QUERY: * create index on * [ using ] "(" ( with )+ ")" * [ where ] * *****************************************************************************/ IndexStmt: CREATE index_opt_unique INDEX index_name ON qualified_name access_method_clause '(' index_params ')' where_clause { IndexStmt *n = makeNode(IndexStmt); n->unique = $2; n->idxname = $4; n->relation = $6; n->accessMethod = $7; n->indexParams = $9; n->whereClause = $11; $$ = (Node *)n; } ; index_opt_unique: UNIQUE { $$ = TRUE; } | /*EMPTY*/ { $$ = FALSE; } ; access_method_clause: USING access_method { $$ = $2; } /* If btree changes as our default, update pg_get_indexdef() */ | /*EMPTY*/ { $$ = DEFAULT_INDEX_TYPE; } ; index_params: index_list { $$ = $1; } | func_index { $$ = makeList1($1); } ; index_list: index_elem { $$ = makeList1($1); } | index_list ',' index_elem { $$ = lappend($1, $3); } ; func_index: func_name '(' name_list ')' opt_class { $$ = makeNode(IndexElem); $$->name = NULL; $$->funcname = $1; $$->args = $3; $$->opclass = $5; } ; index_elem: attr_name opt_class { $$ = makeNode(IndexElem); $$->name = $1; $$->funcname = NIL; $$->args = NIL; $$->opclass = $2; } ; opt_class: any_name { /* * Release 7.0 removed network_ops, timespan_ops, and * datetime_ops, so we suppress it from being passed to * the parser so the default *_ops is used. This can be * removed in some later release. bjm 2000/02/07 * * Release 7.1 removes lztext_ops, so suppress that too * for a while. tgl 2000/07/30 * * Release 7.2 renames timestamp_ops to timestamptz_ops, * so suppress that too for awhile. I'm starting to * think we need a better approach. tgl 2000/10/01 */ if (length($1) == 1) { char *claname = strVal(lfirst($1)); if (strcmp(claname, "network_ops") != 0 && strcmp(claname, "timespan_ops") != 0 && strcmp(claname, "datetime_ops") != 0 && strcmp(claname, "lztext_ops") != 0 && strcmp(claname, "timestamp_ops") != 0) $$ = $1; else $$ = NIL; } else $$ = $1; } | USING any_name { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } ; /***************************************************************************** * * QUERY: * create [or replace] function * [( { , })] * returns * as * language [with parameters] * *****************************************************************************/ CreateFunctionStmt: CREATE opt_or_replace FUNCTION func_name func_args RETURNS func_return createfunc_opt_list opt_definition { CreateFunctionStmt *n = makeNode(CreateFunctionStmt); n->replace = $2; n->funcname = $4; n->argTypes = $5; n->returnType = $7; n->options = $8; n->withClause = $9; $$ = (Node *)n; } ; opt_or_replace: OR REPLACE { $$ = TRUE; } | /*EMPTY*/ { $$ = FALSE; } ; func_args: '(' func_args_list ')' { $$ = $2; } | '(' ')' { $$ = NIL; } ; func_args_list: func_arg { $$ = makeList1($1); } | func_args_list ',' func_arg { $$ = lappend($1, $3); } ; func_arg: opt_arg func_type { /* We can catch over-specified arguments here if we want to, * but for now better to silently swallow typmod, etc. * - thomas 2000-03-22 */ $$ = $2; } | func_type { $$ = $1; } ; opt_arg: IN_P { $$ = FALSE; } | OUT_P { elog(ERROR, "CREATE FUNCTION / OUT parameters are not supported"); $$ = TRUE; } | INOUT { elog(ERROR, "CREATE FUNCTION / INOUT parameters are not supported"); $$ = FALSE; } ; func_return: func_type { /* We can catch over-specified arguments here if we want to, * but for now better to silently swallow typmod, etc. * - thomas 2000-03-22 */ $$ = $1; } ; /* * We would like to make the second production here be ColId attrs etc, * but that causes reduce/reduce conflicts. type_name is next best choice. */ func_type: Typename { $$ = $1; } | type_name attrs '%' TYPE_P { $$ = makeNode(TypeName); $$->names = lcons(makeString($1), $2); $$->pct_type = true; $$->typmod = -1; } ; createfunc_opt_list: /* Must be at least one to prevent conflict */ createfunc_opt_item { $$ = makeList1($1); } | createfunc_opt_list createfunc_opt_item { $$ = lappend($1, $2); } ; createfunc_opt_item: AS func_as { $$ = makeDefElem("as", (Node *)$2); } | LANGUAGE ColId_or_Sconst { $$ = makeDefElem("language", (Node *)makeString($2)); } | IMMUTABLE { $$ = makeDefElem("volatility", (Node *)makeString("immutable")); } | STABLE { $$ = makeDefElem("volatility", (Node *)makeString("stable")); } | VOLATILE { $$ = makeDefElem("volatility", (Node *)makeString("volatile")); } | CALLED ON NULL_P INPUT { $$ = makeDefElem("strict", (Node *)makeInteger(FALSE)); } | RETURNS NULL_P ON NULL_P INPUT { $$ = makeDefElem("strict", (Node *)makeInteger(TRUE)); } | STRICT { $$ = makeDefElem("strict", (Node *)makeInteger(TRUE)); } | EXTERNAL SECURITY DEFINER { $$ = makeDefElem("security", (Node *)makeInteger(TRUE)); } | EXTERNAL SECURITY INVOKER { $$ = makeDefElem("security", (Node *)makeInteger(FALSE)); } | SECURITY DEFINER { $$ = makeDefElem("security", (Node *)makeInteger(TRUE)); } | SECURITY INVOKER { $$ = makeDefElem("security", (Node *)makeInteger(FALSE)); } ; func_as: Sconst { $$ = makeList1(makeString($1)); } | Sconst ',' Sconst { $$ = makeList2(makeString($1), makeString($3)); } ; opt_definition: WITH definition { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } ; /***************************************************************************** * * QUERY: * * DROP FUNCTION funcname (arg1, arg2, ...) [ RESTRICT | CASCADE ] * DROP AGGREGATE aggname (aggtype) [ RESTRICT | CASCADE ] * DROP OPERATOR opname (leftoperand_typ, rightoperand_typ) [ RESTRICT | CASCADE ] * *****************************************************************************/ RemoveFuncStmt: DROP FUNCTION func_name func_args opt_drop_behavior { RemoveFuncStmt *n = makeNode(RemoveFuncStmt); n->funcname = $3; n->args = $4; n->behavior = $5; $$ = (Node *)n; } ; RemoveAggrStmt: DROP AGGREGATE func_name '(' aggr_argtype ')' opt_drop_behavior { RemoveAggrStmt *n = makeNode(RemoveAggrStmt); n->aggname = $3; n->aggtype = $5; n->behavior = $7; $$ = (Node *)n; } ; aggr_argtype: Typename { $$ = $1; } | '*' { $$ = NULL; } ; RemoveOperStmt: DROP OPERATOR any_operator '(' oper_argtypes ')' opt_drop_behavior { RemoveOperStmt *n = makeNode(RemoveOperStmt); n->opname = $3; n->args = $5; n->behavior = $7; $$ = (Node *)n; } ; oper_argtypes: Typename { elog(ERROR,"parser: argument type missing (use NONE for unary operators)"); } | Typename ',' Typename { $$ = makeList2($1, $3); } | NONE ',' Typename /* left unary */ { $$ = makeList2(NULL, $3); } | Typename ',' NONE /* right unary */ { $$ = makeList2($1, NULL); } ; any_operator: all_Op { $$ = makeList1(makeString($1)); } | ColId '.' any_operator { $$ = lcons(makeString($1), $3); } ; /***************************************************************************** * * CREATE CAST / DROP CAST * *****************************************************************************/ CreateCastStmt: CREATE CAST '(' ConstTypename AS ConstTypename ')' WITH FUNCTION function_with_argtypes cast_context { CreateCastStmt *n = makeNode(CreateCastStmt); n->sourcetype = $4; n->targettype = $6; n->func = (FuncWithArgs *) $10; n->context = (CoercionContext) $11; $$ = (Node *)n; } | CREATE CAST '(' ConstTypename AS ConstTypename ')' WITHOUT FUNCTION cast_context { CreateCastStmt *n = makeNode(CreateCastStmt); n->sourcetype = $4; n->targettype = $6; n->func = NULL; n->context = (CoercionContext) $10; $$ = (Node *)n; } ; cast_context: AS IMPLICIT_P { $$ = COERCION_IMPLICIT; } | AS ASSIGNMENT { $$ = COERCION_ASSIGNMENT; } | /*EMPTY*/ { $$ = COERCION_EXPLICIT; } ; DropCastStmt: DROP CAST '(' ConstTypename AS ConstTypename ')' opt_drop_behavior { DropCastStmt *n = makeNode(DropCastStmt); n->sourcetype = $4; n->targettype = $6; n->behavior = $8; $$ = (Node *)n; } ; /***************************************************************************** * * QUERY: * * REINDEX type [FORCE] [ALL] * *****************************************************************************/ ReindexStmt: REINDEX reindex_type qualified_name opt_force { ReindexStmt *n = makeNode(ReindexStmt); n->reindexType = $2; n->relation = $3; n->name = NULL; n->force = $4; $$ = (Node *)n; } | REINDEX DATABASE name opt_force { ReindexStmt *n = makeNode(ReindexStmt); n->reindexType = DATABASE; n->name = $3; n->relation = NULL; n->force = $4; $$ = (Node *)n; } ; reindex_type: INDEX { $$ = INDEX; } | TABLE { $$ = TABLE; } ; opt_force: FORCE { $$ = TRUE; } | /* EMPTY */ { $$ = FALSE; } ; /***************************************************************************** * * QUERY: * rename in [*] to * rename to * *****************************************************************************/ RenameStmt: ALTER TABLE relation_expr RENAME opt_column opt_name TO name { RenameStmt *n = makeNode(RenameStmt); n->relation = $3; n->oldname = $6; n->newname = $8; if ($6 == NULL) n->renameType = RENAME_TABLE; else n->renameType = RENAME_COLUMN; $$ = (Node *)n; } | ALTER TRIGGER name ON relation_expr RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->relation = $5; n->oldname = $3; n->newname = $8; n->renameType = RENAME_TRIGGER; $$ = (Node *)n; } ; opt_name: name { $$ = $1; } | /*EMPTY*/ { $$ = NULL; } ; opt_column: COLUMN { $$ = COLUMN; } | /*EMPTY*/ { $$ = 0; } ; /***************************************************************************** * * QUERY: Define Rewrite Rule * *****************************************************************************/ RuleStmt: CREATE opt_or_replace RULE name AS { QueryIsRule=TRUE; } ON event TO qualified_name where_clause DO opt_instead RuleActionList { RuleStmt *n = makeNode(RuleStmt); n->replace = $2; n->relation = $10; n->rulename = $4; n->whereClause = $11; n->event = $8; n->instead = $13; n->actions = $14; $$ = (Node *)n; QueryIsRule=FALSE; } ; RuleActionList: NOTHING { $$ = NIL; } | RuleActionStmt { $$ = makeList1($1); } | '(' RuleActionMulti ')' { $$ = $2; } ; /* the thrashing around here is to discard "empty" statements... */ RuleActionMulti: RuleActionMulti ';' RuleActionStmtOrEmpty { if ($3 != (Node *) NULL) $$ = lappend($1, $3); else $$ = $1; } | RuleActionStmtOrEmpty { if ($1 != (Node *) NULL) $$ = makeList1($1); else $$ = NIL; } ; RuleActionStmt: SelectStmt | InsertStmt | UpdateStmt | DeleteStmt | NotifyStmt ; RuleActionStmtOrEmpty: RuleActionStmt { $$ = $1; } | /*EMPTY*/ { $$ = (Node *)NULL; } ; /* change me to select, update, etc. some day */ event: SELECT { $$ = CMD_SELECT; } | UPDATE { $$ = CMD_UPDATE; } | DELETE_P { $$ = CMD_DELETE; } | INSERT { $$ = CMD_INSERT; } ; opt_instead: INSTEAD { $$ = TRUE; } | /*EMPTY*/ { $$ = FALSE; } ; DropRuleStmt: DROP RULE name ON qualified_name opt_drop_behavior { DropPropertyStmt *n = makeNode(DropPropertyStmt); n->relation = $5; n->property = $3; n->behavior = $6; n->removeType = DROP_RULE; $$ = (Node *) n; } ; /***************************************************************************** * * QUERY: * NOTIFY can appear both in rule bodies and * as a query-level command * *****************************************************************************/ NotifyStmt: NOTIFY qualified_name { NotifyStmt *n = makeNode(NotifyStmt); n->relation = $2; $$ = (Node *)n; } ; ListenStmt: LISTEN qualified_name { ListenStmt *n = makeNode(ListenStmt); n->relation = $2; $$ = (Node *)n; } ; UnlistenStmt: UNLISTEN qualified_name { UnlistenStmt *n = makeNode(UnlistenStmt); n->relation = $2; $$ = (Node *)n; } | UNLISTEN '*' { UnlistenStmt *n = makeNode(UnlistenStmt); n->relation = makeNode(RangeVar); n->relation->relname = "*"; n->relation->schemaname = NULL; $$ = (Node *)n; } ; /***************************************************************************** * * Transactions: * * BEGIN / COMMIT / ROLLBACK * (also older versions END / ABORT) * *****************************************************************************/ TransactionStmt: ABORT_TRANS opt_trans { TransactionStmt *n = makeNode(TransactionStmt); n->command = ROLLBACK; n->options = NIL; $$ = (Node *)n; } | BEGIN_TRANS opt_trans { TransactionStmt *n = makeNode(TransactionStmt); n->command = BEGIN_TRANS; n->options = NIL; $$ = (Node *)n; } | START TRANSACTION trans_options { TransactionStmt *n = makeNode(TransactionStmt); n->command = START; n->options = $3; $$ = (Node *)n; } | COMMIT opt_trans { TransactionStmt *n = makeNode(TransactionStmt); n->command = COMMIT; n->options = NIL; $$ = (Node *)n; } | END_TRANS opt_trans { TransactionStmt *n = makeNode(TransactionStmt); n->command = COMMIT; n->options = NIL; $$ = (Node *)n; } | ROLLBACK opt_trans { TransactionStmt *n = makeNode(TransactionStmt); n->command = ROLLBACK; n->options = NIL; $$ = (Node *)n; } ; trans_options: ISOLATION LEVEL iso_level { $$ = makeList1(makeStringConst($3, NULL)); } | /* EMPTY */ { $$ = NIL; } ; opt_trans: WORK {} | TRANSACTION {} | /*EMPTY*/ {} ; /***************************************************************************** * * QUERY: * create view '('target-list ')' AS * *****************************************************************************/ ViewStmt: CREATE opt_or_replace VIEW qualified_name opt_column_list AS SelectStmt { ViewStmt *n = makeNode(ViewStmt); n->replace = $2; n->view = $4; n->aliases = $5; n->query = (Query *) $7; $$ = (Node *)n; } ; /***************************************************************************** * * QUERY: * load "filename" * *****************************************************************************/ LoadStmt: LOAD file_name { LoadStmt *n = makeNode(LoadStmt); n->filename = $2; $$ = (Node *)n; } ; /***************************************************************************** * * CREATE DATABASE * *****************************************************************************/ CreatedbStmt: CREATE DATABASE database_name opt_with createdb_opt_list { CreatedbStmt *n = makeNode(CreatedbStmt); n->dbname = $3; n->options = $5; $$ = (Node *)n; } ; createdb_opt_list: createdb_opt_list createdb_opt_item { $$ = lappend($1, $2); } | /* EMPTY */ { $$ = NIL; } ; createdb_opt_item: LOCATION opt_equal Sconst { $$ = makeDefElem("location", (Node *)makeString($3)); } | LOCATION opt_equal DEFAULT { $$ = makeDefElem("location", NULL); } | TEMPLATE opt_equal name { $$ = makeDefElem("template", (Node *)makeString($3)); } | TEMPLATE opt_equal DEFAULT { $$ = makeDefElem("template", NULL); } | ENCODING opt_equal Sconst { int encoding; if (pg_valid_server_encoding($3) < 0) elog(ERROR, "%s is not a valid encoding name", $3); encoding = pg_char_to_encoding($3); $$ = makeDefElem("encoding", (Node *)makeInteger(encoding)); } | ENCODING opt_equal Iconst { const char *encoding_name; encoding_name = pg_encoding_to_char($3); if (!strcmp(encoding_name,"") || pg_valid_server_encoding(encoding_name) < 0) elog(ERROR, "%d is not a valid encoding code", $3); $$ = makeDefElem("encoding", (Node *)makeInteger($3)); } | ENCODING opt_equal DEFAULT { $$ = makeDefElem("encoding", (Node *)makeInteger(-1)); } | OWNER opt_equal name { $$ = makeDefElem("owner", (Node *)makeString($3)); } | OWNER opt_equal DEFAULT { $$ = makeDefElem("owner", NULL); } ; /* * Though the equals sign doesn't match other WITH options, pg_dump uses * equals for backward compability, and it doesn't seem worth removing it. * 2002-02-25 */ opt_equal: '=' {} | /*EMPTY*/ {} ; /***************************************************************************** * * ALTER DATABASE * *****************************************************************************/ AlterDatabaseSetStmt: ALTER DATABASE database_name SET set_rest { AlterDatabaseSetStmt *n = makeNode(AlterDatabaseSetStmt); n->dbname = $3; n->variable = $5->name; n->value = $5->args; $$ = (Node *)n; } | ALTER DATABASE database_name VariableResetStmt { AlterDatabaseSetStmt *n = makeNode(AlterDatabaseSetStmt); n->dbname = $3; n->variable = ((VariableResetStmt *)$4)->name; n->value = NIL; $$ = (Node *)n; } ; /***************************************************************************** * * DROP DATABASE * * This is implicitly CASCADE, no need for drop behavior *****************************************************************************/ DropdbStmt: DROP DATABASE database_name { DropdbStmt *n = makeNode(DropdbStmt); n->dbname = $3; $$ = (Node *)n; } ; /***************************************************************************** * * Manipulate a domain * *****************************************************************************/ CreateDomainStmt: CREATE DOMAIN_P any_name opt_as Typename ColQualList opt_collate { CreateDomainStmt *n = makeNode(CreateDomainStmt); n->domainname = $3; n->typename = $5; n->constraints = $6; if ($7 != NULL) elog(NOTICE,"CREATE DOMAIN / COLLATE %s not yet " "implemented; clause ignored", $7); $$ = (Node *)n; } ; opt_as: AS {} | /* EMPTY */ {} ; /***************************************************************************** * * Manipulate a conversion * * CREATE [DEFAULT] CONVERSION * FOR TO FROM * *****************************************************************************/ CreateConversionStmt: CREATE opt_default CONVERSION_P any_name FOR Sconst TO Sconst FROM any_name { CreateConversionStmt *n = makeNode(CreateConversionStmt); n->conversion_name = $4; n->for_encoding_name = $6; n->to_encoding_name = $8; n->func_name = $10; n->def = $2; $$ = (Node *)n; } ; /***************************************************************************** * * QUERY: * cluster on * *****************************************************************************/ ClusterStmt: CLUSTER index_name ON qualified_name { ClusterStmt *n = makeNode(ClusterStmt); n->relation = $4; n->indexname = $2; $$ = (Node*)n; } ; /***************************************************************************** * * QUERY: * vacuum * analyze * *****************************************************************************/ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose { VacuumStmt *n = makeNode(VacuumStmt); n->vacuum = true; n->analyze = false; n->full = $2; n->freeze = $3; n->verbose = $4; n->relation = NULL; n->va_cols = NIL; $$ = (Node *)n; } | VACUUM opt_full opt_freeze opt_verbose qualified_name { VacuumStmt *n = makeNode(VacuumStmt); n->vacuum = true; n->analyze = false; n->full = $2; n->freeze = $3; n->verbose = $4; n->relation = $5; n->va_cols = NIL; $$ = (Node *)n; } | VACUUM opt_full opt_freeze opt_verbose AnalyzeStmt { VacuumStmt *n = (VacuumStmt *) $5; n->vacuum = true; n->full = $2; n->freeze = $3; n->verbose |= $4; $$ = (Node *)n; } ; AnalyzeStmt: analyze_keyword opt_verbose { VacuumStmt *n = makeNode(VacuumStmt); n->vacuum = false; n->analyze = true; n->full = false; n->freeze = false; n->verbose = $2; n->relation = NULL; n->va_cols = NIL; $$ = (Node *)n; } | analyze_keyword opt_verbose qualified_name opt_name_list { VacuumStmt *n = makeNode(VacuumStmt); n->vacuum = false; n->analyze = true; n->full = false; n->freeze = false; n->verbose = $2; n->relation = $3; n->va_cols = $4; $$ = (Node *)n; } ; analyze_keyword: ANALYZE {} | ANALYSE /* British */ {} ; opt_verbose: VERBOSE { $$ = TRUE; } | /*EMPTY*/ { $$ = FALSE; } ; opt_full: FULL { $$ = TRUE; } | /*EMPTY*/ { $$ = FALSE; } ; opt_freeze: FREEZE { $$ = TRUE; } | /*EMPTY*/ { $$ = FALSE; } ; opt_name_list: '(' name_list ')' { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } ; /***************************************************************************** * * QUERY: * EXPLAIN query * EXPLAIN ANALYZE query * *****************************************************************************/ ExplainStmt: EXPLAIN opt_verbose OptimizableStmt { ExplainStmt *n = makeNode(ExplainStmt); n->verbose = $2; n->analyze = FALSE; n->query = (Query*)$3; $$ = (Node *)n; } | EXPLAIN analyze_keyword opt_verbose OptimizableStmt { ExplainStmt *n = makeNode(ExplainStmt); n->verbose = $3; n->analyze = TRUE; n->query = (Query*)$4; $$ = (Node *)n; } ; /***************************************************************************** * * QUERY: * PREPARE [(args, ...)] AS * *****************************************************************************/ PrepareStmt: PREPARE name prep_type_clause AS OptimizableStmt { PrepareStmt *n = makeNode(PrepareStmt); n->name = $2; n->argtypes = $3; n->query = (Query *) $5; $$ = (Node *) n; } ; prep_type_clause: '(' prep_type_list ')' { $$ = $2; } | /* EMPTY */ { $$ = NIL; } ; prep_type_list: Typename { $$ = makeList1($1); } | prep_type_list ',' Typename { $$ = lappend($1, $3); } ; /***************************************************************************** * * QUERY: * EXECUTE [(params, ...)] [INTO ...] * *****************************************************************************/ ExecuteStmt: EXECUTE name execute_param_clause into_clause { ExecuteStmt *n = makeNode(ExecuteStmt); n->name = $2; n->params = $3; n->into = $4; $$ = (Node *) n; } ; execute_param_clause: '(' execute_param_list ')' { $$ = $2; } | /* EMPTY */ { $$ = NIL; } ; execute_param_list: a_expr { $$ = makeList1($1); } | execute_param_list ',' a_expr { $$ = lappend($1, $3); } ; /***************************************************************************** * * QUERY: * DEALLOCATE [PREPARE] * *****************************************************************************/ DeallocateStmt: DEALLOCATE name { DeallocateStmt *n = makeNode(DeallocateStmt); n->name = $2; $$ = (Node *) n; } | DEALLOCATE PREPARE name { DeallocateStmt *n = makeNode(DeallocateStmt); n->name = $3; $$ = (Node *) n; } ; /***************************************************************************** * * * 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: SelectStmt | CursorStmt | UpdateStmt | InsertStmt | DeleteStmt /* by default all are $$=$1 */ ; /***************************************************************************** * * QUERY: * INSERT STATEMENTS * *****************************************************************************/ InsertStmt: INSERT INTO qualified_name insert_rest { $4->relation = $3; $$ = (Node *) $4; } ; insert_rest: VALUES '(' insert_target_list ')' { $$ = makeNode(InsertStmt); $$->cols = NIL; $$->targetList = $3; $$->selectStmt = NULL; } | DEFAULT VALUES { $$ = makeNode(InsertStmt); $$->cols = NIL; $$->targetList = NIL; $$->selectStmt = NULL; } | SelectStmt { $$ = makeNode(InsertStmt); $$->cols = NIL; $$->targetList = NIL; $$->selectStmt = $1; } | '(' insert_column_list ')' VALUES '(' insert_target_list ')' { $$ = makeNode(InsertStmt); $$->cols = $2; $$->targetList = $6; $$->selectStmt = NULL; } | '(' insert_column_list ')' SelectStmt { $$ = makeNode(InsertStmt); $$->cols = $2; $$->targetList = NIL; $$->selectStmt = $4; } ; insert_column_list: insert_column_item { $$ = makeList1($1); } | insert_column_list ',' insert_column_item { $$ = lappend($1, $3); } ; insert_column_item: ColId opt_indirection { ResTarget *n = makeNode(ResTarget); n->name = $1; n->indirection = $2; n->val = NULL; $$ = (Node *)n; } ; /***************************************************************************** * * QUERY: * DELETE STATEMENTS * *****************************************************************************/ DeleteStmt: DELETE_P FROM relation_expr where_clause { DeleteStmt *n = makeNode(DeleteStmt); n->relation = $3; n->whereClause = $4; $$ = (Node *)n; } ; LockStmt: LOCK_P opt_table qualified_name_list opt_lock { LockStmt *n = makeNode(LockStmt); n->relations = $3; n->mode = $4; $$ = (Node *)n; } ; opt_lock: IN_P lock_type MODE { $$ = $2; } | /*EMPTY*/ { $$ = AccessExclusiveLock; } ; lock_type: ACCESS SHARE { $$ = AccessShareLock; } | ROW SHARE { $$ = RowShareLock; } | ROW EXCLUSIVE { $$ = RowExclusiveLock; } | SHARE UPDATE EXCLUSIVE { $$ = ShareUpdateExclusiveLock; } | SHARE { $$ = ShareLock; } | SHARE ROW EXCLUSIVE { $$ = ShareRowExclusiveLock; } | EXCLUSIVE { $$ = ExclusiveLock; } | ACCESS EXCLUSIVE { $$ = AccessExclusiveLock; } ; /***************************************************************************** * * QUERY: * UpdateStmt (UPDATE) * *****************************************************************************/ UpdateStmt: UPDATE relation_expr SET update_target_list from_clause where_clause { UpdateStmt *n = makeNode(UpdateStmt); n->relation = $2; n->targetList = $4; n->fromClause = $5; n->whereClause = $6; $$ = (Node *)n; } ; /***************************************************************************** * * QUERY: * CURSOR STATEMENTS * *****************************************************************************/ CursorStmt: DECLARE name opt_cursor CURSOR FOR SelectStmt { SelectStmt *n = (SelectStmt *)$6; n->portalname = $2; n->binary = $3; $$ = $6; } ; opt_cursor: BINARY { $$ = TRUE; } | INSENSITIVE { $$ = FALSE; } | SCROLL { $$ = FALSE; } | INSENSITIVE SCROLL { $$ = FALSE; } | /*EMPTY*/ { $$ = FALSE; } ; /***************************************************************************** * * QUERY: * SELECT STATEMENTS * *****************************************************************************/ /* A complete SELECT statement looks like this. * * The rule returns either a single SelectStmt node or a tree of them, * representing a set-operation tree. * * There is an ambiguity when a sub-SELECT is within an a_expr and there * are excess parentheses: do the parentheses belong to the sub-SELECT or * to the surrounding a_expr? We don't really care, but yacc wants to know. * To resolve the ambiguity, we are careful to define the grammar so that * the decision is staved off as long as possible: as long as we can keep * absorbing parentheses into the sub-SELECT, we will do so, and only when * it's no longer possible to do that will we decide that parens belong to * the expression. For example, in "SELECT (((SELECT 2)) + 3)" the extra * parentheses are treated as part of the sub-select. The necessity of doing * it that way is shown by "SELECT (((SELECT 2)) UNION SELECT 2)". Had we * parsed "((SELECT 2))" as an a_expr, it'd be too late to go back to the * SELECT viewpoint when we see the UNION. * * This approach is implemented by defining a nonterminal select_with_parens, * which represents a SELECT with at least one outer layer of parentheses, * and being careful to use select_with_parens, never '(' SelectStmt ')', * in the expression grammar. We will then have shift-reduce conflicts * which we can resolve in favor of always treating '(' nonterminal to something else. We use UMINUS * precedence for this, which is a fairly arbitrary choice. * * To be able to define select_with_parens itself without ambiguity, we need * a nonterminal select_no_parens that represents a SELECT structure with no * outermost parentheses. This is a little bit tedious, but it works. * * In non-expression contexts, we use SelectStmt which can represent a SELECT * with or without outer parentheses. */ SelectStmt: select_no_parens %prec UMINUS | select_with_parens %prec UMINUS ; select_with_parens: '(' select_no_parens ')' { $$ = $2; } | '(' select_with_parens ')' { $$ = $2; } ; /* * FOR UPDATE may be before or after LIMIT/OFFSET. * In <=7.2.X, LIMIT/OFFSET had to be after FOR UPDATE * We now support both orderings, but prefer LIMIT/OFFSET before FOR UPDATE * 2002-08-28 bjm */ select_no_parens: simple_select { $$ = $1; } | select_clause sort_clause { insertSelectOptions((SelectStmt *) $1, $2, NIL, NULL, NULL); $$ = $1; } | select_clause opt_sort_clause for_update_clause opt_select_limit { insertSelectOptions((SelectStmt *) $1, $2, $3, nth(0, $4), nth(1, $4)); $$ = $1; } | select_clause opt_sort_clause select_limit opt_for_update_clause { insertSelectOptions((SelectStmt *) $1, $2, $4, nth(0, $3), nth(1, $3)); $$ = $1; } ; select_clause: simple_select { $$ = $1; } | select_with_parens { $$ = $1; } ; /* * This rule parses SELECT statements that can appear within set operations, * including UNION, INTERSECT and EXCEPT. '(' and ')' can be used to specify * the ordering of the set operations. Without '(' and ')' we want the * operations to be ordered per the precedence specs at the head of this file. * * As with select_no_parens, simple_select cannot have outer parentheses, * but can have parenthesized subclauses. * * Note that sort clauses cannot be included at this level --- SQL92 requires * SELECT foo UNION SELECT bar ORDER BY baz * to be parsed as * (SELECT foo UNION SELECT bar) ORDER BY baz * not * SELECT foo UNION (SELECT bar ORDER BY baz) * Likewise FOR UPDATE and LIMIT. Therefore, those clauses are described * as part of the select_no_parens production, not simple_select. * This does not limit functionality, because you can reintroduce sort and * limit clauses inside parentheses. * * NOTE: only the leftmost component SelectStmt should have INTO. * However, this is not checked by the grammar; parse analysis must check it. */ simple_select: SELECT opt_distinct target_list into_clause from_clause where_clause group_clause having_clause { SelectStmt *n = makeNode(SelectStmt); n->distinctClause = $2; n->targetList = $3; n->into = $4; n->intoColNames = NIL; n->fromClause = $5; n->whereClause = $6; n->groupClause = $7; n->havingClause = $8; $$ = (Node *)n; } | select_clause UNION opt_all select_clause { $$ = makeSetOp(SETOP_UNION, $3, $1, $4); } | select_clause INTERSECT opt_all select_clause { $$ = makeSetOp(SETOP_INTERSECT, $3, $1, $4); } | select_clause EXCEPT opt_all select_clause { $$ = makeSetOp(SETOP_EXCEPT, $3, $1, $4); } ; into_clause: INTO OptTempTableName { $$ = $2; } | /*EMPTY*/ { $$ = NULL; } ; /* * Redundancy here is needed to avoid shift/reduce conflicts, * since TEMP is not a reserved word. See also OptTemp. */ OptTempTableName: TEMPORARY opt_table qualified_name { $$ = $3; $$->istemp = true; } | TEMP opt_table qualified_name { $$ = $3; $$->istemp = true; } | LOCAL TEMPORARY opt_table qualified_name { $$ = $4; $$->istemp = true; } | LOCAL TEMP opt_table qualified_name { $$ = $4; $$->istemp = true; } | GLOBAL TEMPORARY opt_table qualified_name { elog(ERROR, "GLOBAL TEMPORARY TABLE is not currently supported"); $$ = $4; $$->istemp = true; } | GLOBAL TEMP opt_table qualified_name { elog(ERROR, "GLOBAL TEMPORARY TABLE is not currently supported"); $$ = $4; $$->istemp = true; } | TABLE qualified_name { $$ = $2; $$->istemp = false; } | qualified_name { $$ = $1; $$->istemp = false; } ; opt_table: TABLE {} | /*EMPTY*/ {} ; opt_all: ALL { $$ = TRUE; } | DISTINCT { $$ = FALSE; } | /*EMPTY*/ { $$ = FALSE; } ; /* We use (NIL) as a placeholder to indicate that all target expressions * should be placed in the DISTINCT list during parsetree analysis. */ opt_distinct: DISTINCT { $$ = makeList1(NIL); } | DISTINCT ON '(' expr_list ')' { $$ = $4; } | ALL { $$ = NIL; } | /*EMPTY*/ { $$ = NIL; } ; opt_sort_clause: sort_clause { $$ = $1;} | /*EMPTY*/ { $$ = NIL; } ; sort_clause: ORDER BY sortby_list { $$ = $3; } ; sortby_list: sortby { $$ = makeList1($1); } | sortby_list ',' sortby { $$ = lappend($1, $3); } ; sortby: a_expr OptUseOp { $$ = makeNode(SortGroupBy); $$->node = $1; $$->useOp = $2; } ; OptUseOp: USING qual_all_Op { $$ = $2; } | ASC { $$ = makeList1(makeString("<")); } | DESC { $$ = makeList1(makeString(">")); } | /*EMPTY*/ { $$ = makeList1(makeString("<")); /*default*/ } ; select_limit: LIMIT select_limit_value OFFSET select_offset_value { $$ = makeList2($4, $2); } | OFFSET select_offset_value LIMIT select_limit_value { $$ = makeList2($2, $4); } | LIMIT select_limit_value { $$ = makeList2(NULL, $2); } | OFFSET select_offset_value { $$ = makeList2($2, NULL); } | LIMIT select_limit_value ',' select_offset_value /* Disabled because it was too confusing, bjm 2002-02-18 */ { elog(ERROR, "LIMIT #,# syntax not supported.\n\tUse separate LIMIT and OFFSET clauses."); } ; opt_select_limit: select_limit { $$ = $1; } | /* EMPTY */ { $$ = makeList2(NULL,NULL); } ; select_limit_value: Iconst { Const *n = makeNode(Const); if ($1 < 0) elog(ERROR, "LIMIT must not be negative"); n->consttype = INT4OID; n->constlen = sizeof(int4); n->constvalue = Int32GetDatum($1); n->constisnull = FALSE; n->constbyval = TRUE; n->constisset = FALSE; n->constiscast = FALSE; $$ = (Node *)n; } | ALL { /* LIMIT ALL is represented as a NULL constant */ Const *n = makeNode(Const); n->consttype = INT4OID; n->constlen = sizeof(int4); n->constvalue = (Datum) 0; n->constisnull = TRUE; n->constbyval = TRUE; n->constisset = FALSE; n->constiscast = FALSE; $$ = (Node *)n; } | PARAM { Param *n = makeNode(Param); n->paramkind = PARAM_NUM; n->paramid = $1; n->paramtype = INT4OID; $$ = (Node *)n; } ; select_offset_value: Iconst { Const *n = makeNode(Const); if ($1 < 0) elog(ERROR, "OFFSET must not be negative"); n->consttype = INT4OID; n->constlen = sizeof(int4); n->constvalue = Int32GetDatum($1); n->constisnull = FALSE; n->constbyval = TRUE; n->constisset = FALSE; n->constiscast = FALSE; $$ = (Node *)n; } | PARAM { Param *n = makeNode(Param); n->paramkind = PARAM_NUM; n->paramid = $1; n->paramtype = INT4OID; $$ = (Node *)n; } ; /* * jimmy bell-style recursive queries aren't supported in the * current system. * * ...however, recursive addattr and rename supported. make special * cases for these. */ group_clause: GROUP_P BY expr_list { $$ = $3; } | /*EMPTY*/ { $$ = NIL; } ; having_clause: HAVING a_expr { $$ = $2; } | /*EMPTY*/ { $$ = NULL; } ; for_update_clause: FOR UPDATE update_list { $$ = $3; } | FOR READ ONLY { $$ = NULL; } ; opt_for_update_clause: for_update_clause { $$ = $1; } | /* EMPTY */ { $$ = NULL; } ; update_list: OF name_list { $$ = $2; } | /* EMPTY */ { $$ = makeList1(NULL); } ; /***************************************************************************** * * clauses common to all Optimizable Stmts: * from_clause - allow list of both JOIN expressions and table names * where_clause - qualifications for joins or restrictions * *****************************************************************************/ from_clause: FROM from_list { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } ; from_list: table_ref { $$ = makeList1($1); } | from_list ',' table_ref { $$ = lappend($1, $3); } ; /* * table_ref is where an alias clause can be attached. Note we cannot make * alias_clause have an empty production because that causes parse conflicts * between table_ref := '(' joined_table ')' alias_clause * and joined_table := '(' joined_table ')'. So, we must have the * redundant-looking productions here instead. */ table_ref: relation_expr { $$ = (Node *) $1; } | relation_expr alias_clause { $1->alias = $2; $$ = (Node *) $1; } | func_table { RangeFunction *n = makeNode(RangeFunction); n->funccallnode = $1; n->coldeflist = NIL; $$ = (Node *) n; } | func_table alias_clause { RangeFunction *n = makeNode(RangeFunction); n->funccallnode = $1; n->alias = $2; n->coldeflist = NIL; $$ = (Node *) n; } | func_table AS '(' TableFuncElementList ')' { RangeFunction *n = makeNode(RangeFunction); n->funccallnode = $1; n->coldeflist = $4; $$ = (Node *) n; } | func_table AS ColId '(' TableFuncElementList ')' { RangeFunction *n = makeNode(RangeFunction); Alias *a = makeNode(Alias); n->funccallnode = $1; a->aliasname = $3; n->alias = a; n->coldeflist = $5; $$ = (Node *) n; } | func_table ColId '(' TableFuncElementList ')' { RangeFunction *n = makeNode(RangeFunction); Alias *a = makeNode(Alias); n->funccallnode = $1; a->aliasname = $2; n->alias = a; n->coldeflist = $4; $$ = (Node *) n; } | select_with_parens { /* * The SQL spec does not permit a subselect * () without an alias clause, * so we don't either. This avoids the problem * of needing to invent a unique refname for it. * That could be surmounted if there's sufficient * popular demand, but for now let's just implement * the spec and see if anyone complains. * However, it does seem like a good idea to emit * an error message that's better than "parse error". */ elog(ERROR, "sub-SELECT in FROM must have an alias" "\n\tFor example, FROM (SELECT ...) [AS] foo"); $$ = NULL; } | select_with_parens alias_clause { RangeSubselect *n = makeNode(RangeSubselect); n->subquery = $1; n->alias = $2; $$ = (Node *) n; } | joined_table { $$ = (Node *) $1; } | '(' joined_table ')' alias_clause { $2->alias = $4; $$ = (Node *) $2; } ; /* * It may seem silly to separate joined_table from table_ref, but there is * method in SQL92's madness: if you don't do it this way you get reduce- * reduce conflicts, because it's not clear to the parser generator whether * to expect alias_clause after ')' or not. For the same reason we must * treat 'JOIN' and 'join_type JOIN' separately, rather than allowing * join_type to expand to empty; if we try it, the parser generator can't * figure out when to reduce an empty join_type right after table_ref. * * Note that a CROSS JOIN is the same as an unqualified * INNER JOIN, and an INNER JOIN/ON has the same shape * but a qualification expression to limit membership. * A NATURAL JOIN implicitly matches column names between * tables and the shape is determined by which columns are * in common. We'll collect columns during the later transformations. */ joined_table: '(' joined_table ')' { $$ = $2; } | table_ref CROSS JOIN table_ref { /* CROSS JOIN is same as unqualified inner join */ JoinExpr *n = makeNode(JoinExpr); n->jointype = JOIN_INNER; n->isNatural = FALSE; n->larg = $1; n->rarg = $4; n->using = NIL; n->quals = NULL; $$ = n; } | table_ref UNIONJOIN table_ref { /* UNION JOIN is made into 1 token to avoid shift/reduce * conflict against regular UNION keyword. */ JoinExpr *n = makeNode(JoinExpr); n->jointype = JOIN_UNION; n->isNatural = FALSE; n->larg = $1; n->rarg = $3; n->using = NIL; n->quals = NULL; $$ = n; } | table_ref join_type JOIN table_ref join_qual { JoinExpr *n = makeNode(JoinExpr); n->jointype = $2; n->isNatural = FALSE; n->larg = $1; n->rarg = $4; if ($5 != NULL && IsA($5, List)) n->using = (List *) $5; /* USING clause */ else n->quals = $5; /* ON clause */ $$ = n; } | table_ref JOIN table_ref join_qual { /* letting join_type reduce to empty doesn't work */ JoinExpr *n = makeNode(JoinExpr); n->jointype = JOIN_INNER; n->isNatural = FALSE; n->larg = $1; n->rarg = $3; if ($4 != NULL && IsA($4, List)) n->using = (List *) $4; /* USING clause */ else n->quals = $4; /* ON clause */ $$ = n; } | table_ref NATURAL join_type JOIN table_ref { JoinExpr *n = makeNode(JoinExpr); n->jointype = $3; n->isNatural = TRUE; n->larg = $1; n->rarg = $5; n->using = NIL; /* figure out which columns later... */ n->quals = NULL; /* fill later */ $$ = n; } | table_ref NATURAL JOIN table_ref { /* letting join_type reduce to empty doesn't work */ JoinExpr *n = makeNode(JoinExpr); n->jointype = JOIN_INNER; n->isNatural = TRUE; n->larg = $1; n->rarg = $4; n->using = NIL; /* figure out which columns later... */ n->quals = NULL; /* fill later */ $$ = n; } ; alias_clause: AS ColId '(' name_list ')' { $$ = makeNode(Alias); $$->aliasname = $2; $$->colnames = $4; } | AS ColId { $$ = makeNode(Alias); $$->aliasname = $2; } | ColId '(' name_list ')' { $$ = makeNode(Alias); $$->aliasname = $1; $$->colnames = $3; } | ColId { $$ = makeNode(Alias); $$->aliasname = $1; } ; join_type: FULL join_outer { $$ = JOIN_FULL; } | LEFT join_outer { $$ = JOIN_LEFT; } | RIGHT join_outer { $$ = JOIN_RIGHT; } | INNER_P { $$ = JOIN_INNER; } ; /* OUTER is just noise... */ join_outer: OUTER_P { $$ = NULL; } | /*EMPTY*/ { $$ = NULL; } ; /* JOIN qualification clauses * Possibilities are: * USING ( column list ) allows only unqualified column names, * which must match between tables. * ON expr allows more general qualifications. * * We return USING as a List node, while an ON-expr will not be a List. */ join_qual: USING '(' name_list ')' { $$ = (Node *) $3; } | ON a_expr { $$ = $2; } ; relation_expr: qualified_name { /* default inheritance */ $$ = $1; $$->inhOpt = INH_DEFAULT; $$->alias = NULL; } | qualified_name '*' { /* inheritance query */ $$ = $1; $$->inhOpt = INH_YES; $$->alias = NULL; } | ONLY qualified_name { /* no inheritance */ $$ = $2; $$->inhOpt = INH_NO; $$->alias = NULL; } | ONLY '(' qualified_name ')' { /* no inheritance, SQL99-style syntax */ $$ = $3; $$->inhOpt = INH_NO; $$->alias = NULL; } ; func_table: func_name '(' ')' { FuncCall *n = makeNode(FuncCall); n->funcname = $1; n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *)n; } | func_name '(' expr_list ')' { FuncCall *n = makeNode(FuncCall); n->funcname = $1; n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *)n; } ; where_clause: WHERE a_expr { $$ = $2; } | /*EMPTY*/ { $$ = NULL; } ; TableFuncElementList: TableFuncElement { $$ = makeList1($1); } | TableFuncElementList ',' TableFuncElement { $$ = lappend($1, $3); } ; TableFuncElement: ColId Typename { ColumnDef *n = makeNode(ColumnDef); n->colname = $1; n->typename = $2; n->constraints = NIL; $$ = (Node *)n; } ; /***************************************************************************** * * Type syntax * SQL92 introduces a large amount of type-specific syntax. * Define individual clauses to handle these cases, and use * the generic case to handle regular type-extensible Postgres syntax. * - thomas 1997-10-10 * *****************************************************************************/ Typename: SimpleTypename opt_array_bounds { $$ = $1; $$->arrayBounds = $2; } | SETOF SimpleTypename { $$ = $2; $$->setof = TRUE; } ; opt_array_bounds: opt_array_bounds '[' ']' { $$ = lappend($1, makeInteger(-1)); } | opt_array_bounds '[' Iconst ']' { $$ = lappend($1, makeInteger($3)); } | /*EMPTY*/ { $$ = NIL; } ; /* * XXX ideally, the production for a qualified typename should be ColId attrs * (there's no obvious reason why the first name should need to be restricted) * and should be an alternative of GenericType (so that it can be used to * specify a type for a literal in AExprConst). However doing either causes * reduce/reduce conflicts that I haven't been able to find a workaround * for. FIXME later. */ SimpleTypename: GenericType { $$ = $1; } | Numeric { $$ = $1; } | Bit { $$ = $1; } | Character { $$ = $1; } | ConstDatetime { $$ = $1; } | ConstInterval opt_interval { $$ = $1; if ($2 != INTERVAL_FULL_RANGE) $$->typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, $2); } | ConstInterval '(' Iconst ')' opt_interval { $$ = $1; if (($3 < 0) || ($3 > MAX_INTERVAL_PRECISION)) elog(ERROR, "INTERVAL(%d) precision must be between %d and %d", $3, 0, MAX_INTERVAL_PRECISION); $$->typmod = INTERVAL_TYPMOD($3, $5); } | type_name attrs { $$ = makeNode(TypeName); $$->names = lcons(makeString($1), $2); $$->typmod = -1; } ; /* We have a separate ConstTypename to allow defaulting fixed-length * types such as CHAR() and BIT() to an unspecified length. * SQL9x requires that these default to a length of one, but this * makes no sense for constructs like CHAR 'hi' and BIT '0101', * where there is an obvious better choice to make. * Note that ConstInterval is not included here since it must * be pushed up higher in the rules to accomodate the postfix * options (e.g. INTERVAL '1' YEAR). */ ConstTypename: GenericType { $$ = $1; } | Numeric { $$ = $1; } | ConstBit { $$ = $1; } | ConstCharacter { $$ = $1; } | ConstDatetime { $$ = $1; } ; GenericType: type_name { $$ = makeTypeName($1); } ; /* SQL92 numeric data types * Check FLOAT() precision limits assuming IEEE floating types. * Provide real DECIMAL() and NUMERIC() implementations now - Jan 1998-12-30 * - thomas 1997-09-18 */ Numeric: INT { $$ = SystemTypeName("int4"); } | INTEGER { $$ = SystemTypeName("int4"); } | SMALLINT { $$ = SystemTypeName("int2"); } | BIGINT { $$ = SystemTypeName("int8"); } | REAL { $$ = SystemTypeName("float4"); } | FLOAT_P opt_float { $$ = $2; } | DOUBLE PRECISION { $$ = SystemTypeName("float8"); } | DECIMAL opt_decimal { $$ = SystemTypeName("numeric"); $$->typmod = $2; } | DEC opt_decimal { $$ = SystemTypeName("numeric"); $$->typmod = $2; } | NUMERIC opt_numeric { $$ = SystemTypeName("numeric"); $$->typmod = $2; } | BOOLEAN { $$ = SystemTypeName("bool"); } ; opt_float: '(' Iconst ')' { if ($2 < 1) elog(ERROR, "precision for FLOAT must be at least 1"); else if ($2 < 7) $$ = SystemTypeName("float4"); else if ($2 < 16) $$ = SystemTypeName("float8"); else elog(ERROR, "precision for FLOAT must be less than 16"); } | /*EMPTY*/ { $$ = SystemTypeName("float8"); } ; opt_numeric: '(' Iconst ',' Iconst ')' { if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION) elog(ERROR, "NUMERIC precision %d must be between 1 and %d", $2, NUMERIC_MAX_PRECISION); if ($4 < 0 || $4 > $2) elog(ERROR, "NUMERIC scale %d must be between 0 and precision %d", $4,$2); $$ = (($2 << 16) | $4) + VARHDRSZ; } | '(' Iconst ')' { if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION) elog(ERROR, "NUMERIC precision %d must be between 1 and %d", $2, NUMERIC_MAX_PRECISION); $$ = ($2 << 16) + VARHDRSZ; } | /*EMPTY*/ { /* Insert "-1" meaning "no limit" */ $$ = -1; } ; opt_decimal: '(' Iconst ',' Iconst ')' { if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION) elog(ERROR, "DECIMAL precision %d must be between 1 and %d", $2, NUMERIC_MAX_PRECISION); if ($4 < 0 || $4 > $2) elog(ERROR, "DECIMAL scale %d must be between 0 and precision %d", $4,$2); $$ = (($2 << 16) | $4) + VARHDRSZ; } | '(' Iconst ')' { if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION) elog(ERROR, "DECIMAL precision %d must be between 1 and %d", $2, NUMERIC_MAX_PRECISION); $$ = ($2 << 16) + VARHDRSZ; } | /*EMPTY*/ { /* Insert "-1" meaning "no limit" */ $$ = -1; } ; /* * SQL92 bit-field data types * The following implements BIT() and BIT VARYING(). */ Bit: BitWithLength { $$ = $1; } | BitWithoutLength { $$ = $1; } ; ConstBit: BitWithLength { $$ = $1; } | BitWithoutLength { $$->typmod = -1; $$ = $1; } ; BitWithLength: BIT opt_varying '(' Iconst ')' { char *typname; typname = $2 ? "varbit" : "bit"; $$ = SystemTypeName(typname); if ($4 < 1) elog(ERROR, "length for type '%s' must be at least 1", typname); else if ($4 > (MaxAttrSize * BITS_PER_BYTE)) elog(ERROR, "length for type '%s' cannot exceed %d", typname, (MaxAttrSize * BITS_PER_BYTE)); $$->typmod = $4; } ; BitWithoutLength: BIT opt_varying { /* bit defaults to bit(1), varbit to no limit */ if ($2) { $$ = SystemTypeName("varbit"); $$->typmod = -1; } else { $$ = SystemTypeName("bit"); $$->typmod = 1; } } ; /* * SQL92 character data types * The following implements CHAR() and VARCHAR(). */ Character: CharacterWithLength { $$ = $1; } | CharacterWithoutLength { $$ = $1; } ; ConstCharacter: CharacterWithLength { $$ = $1; } | CharacterWithoutLength { /* Length was not specified so allow to be unrestricted. * This handles problems with fixed-length (bpchar) strings * which in column definitions must default to a length * of one, but should not be constrained if the length * was not specified. */ $1->typmod = -1; $$ = $1; } ; CharacterWithLength: character '(' Iconst ')' opt_charset { if (($5 != NULL) && (strcmp($5, "sql_text") != 0)) { char *type; type = palloc(strlen($1) + 1 + strlen($5) + 1); strcpy(type, $1); strcat(type, "_"); strcat(type, $5); $1 = type; } $$ = SystemTypeName($1); if ($3 < 1) elog(ERROR, "length for type '%s' must be at least 1", $1); else if ($3 > MaxAttrSize) elog(ERROR, "length for type '%s' cannot exceed %d", $1, MaxAttrSize); /* we actually implement these like a varlen, so * the first 4 bytes is the length. (the difference * between these and "text" is that we blank-pad and * truncate where necessary) */ $$->typmod = VARHDRSZ + $3; } ; CharacterWithoutLength: character opt_charset { if (($2 != NULL) && (strcmp($2, "sql_text") != 0)) { char *type; type = palloc(strlen($1) + 1 + strlen($2) + 1); strcpy(type, $1); strcat(type, "_"); strcat(type, $2); $1 = type; } $$ = SystemTypeName($1); /* char defaults to char(1), varchar to no limit */ if (strcmp($1, "bpchar") == 0) $$->typmod = VARHDRSZ + 1; else $$->typmod = -1; } ; character: CHARACTER opt_varying { $$ = $2 ? "varchar": "bpchar"; } | CHAR_P opt_varying { $$ = $2 ? "varchar": "bpchar"; } | VARCHAR { $$ = "varchar"; } | NATIONAL CHARACTER opt_varying { $$ = $3 ? "varchar": "bpchar"; } | NATIONAL CHAR_P opt_varying { $$ = $3 ? "varchar": "bpchar"; } | NCHAR opt_varying { $$ = $2 ? "varchar": "bpchar"; } ; opt_varying: VARYING { $$ = TRUE; } | /*EMPTY*/ { $$ = FALSE; } ; opt_charset: CHARACTER SET ColId { $$ = $3; } | /*EMPTY*/ { $$ = NULL; } ; opt_collate: COLLATE ColId { $$ = $2; } | /*EMPTY*/ { $$ = NULL; } ; ConstDatetime: TIMESTAMP '(' Iconst ')' opt_timezone { if ($5) $$ = SystemTypeName("timestamptz"); else $$ = SystemTypeName("timestamp"); /* XXX the timezone field seems to be unused * - thomas 2001-09-06 */ $$->timezone = $5; if (($3 < 0) || ($3 > MAX_TIMESTAMP_PRECISION)) elog(ERROR, "TIMESTAMP(%d)%s precision must be between %d and %d", $3, ($5 ? " WITH TIME ZONE": ""), 0, MAX_TIMESTAMP_PRECISION); $$->typmod = $3; } | TIMESTAMP opt_timezone { if ($2) $$ = SystemTypeName("timestamptz"); else $$ = SystemTypeName("timestamp"); /* XXX the timezone field seems to be unused * - thomas 2001-09-06 */ $$->timezone = $2; /* SQL99 specified a default precision of six * for schema definitions. But for timestamp * literals we don't want to throw away precision * so leave this as unspecified for now. * Later, we may want a different production * for schemas. - thomas 2001-12-07 */ $$->typmod = -1; } | TIME '(' Iconst ')' opt_timezone { if ($5) $$ = SystemTypeName("timetz"); else $$ = SystemTypeName("time"); if (($3 < 0) || ($3 > MAX_TIME_PRECISION)) elog(ERROR, "TIME(%d)%s precision must be between %d and %d", $3, ($5 ? " WITH TIME ZONE": ""), 0, MAX_TIME_PRECISION); $$->typmod = $3; } | TIME opt_timezone { if ($2) $$ = SystemTypeName("timetz"); else $$ = SystemTypeName("time"); /* SQL99 specified a default precision of zero. * See comments for timestamp above on why we will * leave this unspecified for now. - thomas 2001-12-07 */ $$->typmod = -1; } ; ConstInterval: INTERVAL { $$ = SystemTypeName("interval"); } ; opt_timezone: WITH TIME ZONE { $$ = TRUE; } | WITHOUT TIME ZONE { $$ = FALSE; } | /*EMPTY*/ { $$ = FALSE; } ; opt_interval: YEAR_P { $$ = INTERVAL_MASK(YEAR); } | MONTH_P { $$ = INTERVAL_MASK(MONTH); } | DAY_P { $$ = INTERVAL_MASK(DAY); } | HOUR_P { $$ = INTERVAL_MASK(HOUR); } | MINUTE_P { $$ = INTERVAL_MASK(MINUTE); } | SECOND_P { $$ = INTERVAL_MASK(SECOND); } | YEAR_P TO MONTH_P { $$ = INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH); } | DAY_P TO HOUR_P { $$ = INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR); } | DAY_P TO MINUTE_P { $$ = INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE); } | DAY_P TO SECOND_P { $$ = INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND); } | HOUR_P TO MINUTE_P { $$ = INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE); } | HOUR_P TO SECOND_P { $$ = INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND); } | MINUTE_P TO SECOND_P { $$ = INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND); } | /*EMPTY*/ { $$ = INTERVAL_FULL_RANGE; } ; /***************************************************************************** * * expression grammar * *****************************************************************************/ /* Expressions using row descriptors * Define row_descriptor to allow yacc to break the reduce/reduce conflict * with singleton expressions. Use SQL99's ROW keyword to allow rows of * one element. */ r_expr: row IN_P select_with_parens { SubLink *n = makeNode(SubLink); n->lefthand = $1; n->oper = (List *) makeSimpleA_Expr(OP, "=", NULL, NULL); n->useor = FALSE; n->subLinkType = ANY_SUBLINK; n->subselect = $3; $$ = (Node *)n; } | row NOT IN_P select_with_parens { SubLink *n = makeNode(SubLink); n->lefthand = $1; n->oper = (List *) makeSimpleA_Expr(OP, "<>", NULL, NULL); n->useor = TRUE; n->subLinkType = ALL_SUBLINK; n->subselect = $4; $$ = (Node *)n; } | row qual_all_Op sub_type select_with_parens %prec Op { SubLink *n = makeNode(SubLink); n->lefthand = $1; n->oper = (List *) makeA_Expr(OP, $2, NULL, NULL); if (strcmp(strVal(llast($2)), "<>") == 0) n->useor = TRUE; else n->useor = FALSE; n->subLinkType = $3; n->subselect = $4; $$ = (Node *)n; } | row qual_all_Op select_with_parens %prec Op { SubLink *n = makeNode(SubLink); n->lefthand = $1; n->oper = (List *) makeA_Expr(OP, $2, NULL, NULL); if (strcmp(strVal(llast($2)), "<>") == 0) n->useor = TRUE; else n->useor = FALSE; n->subLinkType = MULTIEXPR_SUBLINK; n->subselect = $3; $$ = (Node *)n; } | row qual_all_Op row %prec Op { $$ = makeRowExpr($2, $1, $3); } | row IS NULL_P { $$ = makeRowNullTest(IS_NULL, $1); } | row IS NOT NULL_P { $$ = makeRowNullTest(IS_NOT_NULL, $1); } | row OVERLAPS row { $$ = (Node *)makeOverlaps($1, $3); } | row IS DISTINCT FROM row %prec IS { /* IS DISTINCT FROM has the following rules for non-array types: * a) the row lengths must be equal * b) if both rows are zero-length, then they are not distinct * c) if any element is distinct, the rows are distinct * The rules for an element being distinct: * a) if the elements are both NULL, then they are not distinct * b) if the elements compare to be equal, then they are not distinct * c) otherwise, they are distinct */ List *largs = $1; List *rargs = $5; /* lengths don't match? then complain */ if (length(largs) != length(rargs)) { elog(ERROR, "Unequal number of entries in row expression"); } /* both are zero-length rows? then they are not distinct */ else if (length(largs) <= 0) { $$ = (Node *)makeBoolConst(FALSE); } /* otherwise, we need to compare each element */ else { $$ = (Node *)makeDistinctExpr(largs, rargs); } } ; /* Explicit row production. * SQL99 allows an optional ROW keyword, so we can now do single-element productions * without conflicting with the parenthesized a_expr production. */ row: ROW '(' row_descriptor ')' { $$ = $3; } | ROW '(' a_expr ')' { $$ = makeList1($3); } | ROW '(' ')' { $$ = NULL; } | '(' row_descriptor ')' { $$ = $2; } ; row_descriptor: row_list ',' a_expr { $$ = lappend($1, $3); } ; row_list: a_expr { $$ = makeList1($1); } | row_list ',' a_expr { $$ = lappend($1, $3); } ; sub_type: ANY { $$ = ANY_SUBLINK; } | SOME { $$ = ANY_SUBLINK; } | ALL { $$ = ALL_SUBLINK; } ; all_Op: Op { $$ = $1; } | MathOp { $$ = $1; } ; MathOp: '+' { $$ = "+"; } | '-' { $$ = "-"; } | '*' { $$ = "*"; } | '/' { $$ = "/"; } | '%' { $$ = "%"; } | '^' { $$ = "^"; } | '<' { $$ = "<"; } | '>' { $$ = ">"; } | '=' { $$ = "="; } ; qual_Op: Op { $$ = makeList1(makeString($1)); } | OPERATOR '(' any_operator ')' { $$ = $3; } ; qual_all_Op: all_Op { $$ = makeList1(makeString($1)); } | OPERATOR '(' any_operator ')' { $$ = $3; } ; /* * General expressions * This is the heart of the expression syntax. * * We have two expression types: a_expr is the unrestricted kind, and * b_expr is a subset that must be used in some places to avoid shift/reduce * conflicts. For example, we can't do BETWEEN as "BETWEEN a_expr AND a_expr" * because that use of AND conflicts with AND as a boolean operator. So, * b_expr is used in BETWEEN and we remove boolean keywords from b_expr. * * Note that '(' a_expr ')' is a b_expr, so an unrestricted expression can * always be used by surrounding it with parens. * * c_expr is all the productions that are common to a_expr and b_expr; * it's factored out just to eliminate redundant coding. */ a_expr: c_expr { $$ = $1; } | a_expr TYPECAST Typename { $$ = makeTypeCast($1, $3); } | a_expr COLLATE ColId { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName($3); n->args = makeList1($1); n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *) n; } | a_expr AT TIME ZONE c_expr { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("timezone"); n->args = makeList2($5, $1); n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *) n; } /* * These operators must be called out explicitly in order to make use * of yacc/bison's automatic operator-precedence handling. All other * operator names are handled by the generic productions using "Op", * below; and all those operators will have the same precedence. * * If you add more explicitly-known operators, be sure to add them * also to b_expr and to the MathOp list above. */ | '+' a_expr %prec UMINUS { $$ = (Node *) makeSimpleA_Expr(OP, "+", NULL, $2); } | '-' a_expr %prec UMINUS { $$ = doNegate($2); } | '%' a_expr { $$ = (Node *) makeSimpleA_Expr(OP, "%", NULL, $2); } | '^' a_expr { $$ = (Node *) makeSimpleA_Expr(OP, "^", NULL, $2); } | a_expr '%' { $$ = (Node *) makeSimpleA_Expr(OP, "%", $1, NULL); } | a_expr '^' { $$ = (Node *) makeSimpleA_Expr(OP, "^", $1, NULL); } | a_expr '+' a_expr { $$ = (Node *) makeSimpleA_Expr(OP, "+", $1, $3); } | a_expr '-' a_expr { $$ = (Node *) makeSimpleA_Expr(OP, "-", $1, $3); } | a_expr '*' a_expr { $$ = (Node *) makeSimpleA_Expr(OP, "*", $1, $3); } | a_expr '/' a_expr { $$ = (Node *) makeSimpleA_Expr(OP, "/", $1, $3); } | a_expr '%' a_expr { $$ = (Node *) makeSimpleA_Expr(OP, "%", $1, $3); } | a_expr '^' a_expr { $$ = (Node *) makeSimpleA_Expr(OP, "^", $1, $3); } | a_expr '<' a_expr { $$ = (Node *) makeSimpleA_Expr(OP, "<", $1, $3); } | a_expr '>' a_expr { $$ = (Node *) makeSimpleA_Expr(OP, ">", $1, $3); } | a_expr '=' a_expr { $$ = (Node *) makeSimpleA_Expr(OP, "=", $1, $3); } | a_expr qual_Op a_expr %prec Op { $$ = (Node *) makeA_Expr(OP, $2, $1, $3); } | qual_Op a_expr %prec Op { $$ = (Node *) makeA_Expr(OP, $1, NULL, $2); } | a_expr qual_Op %prec POSTFIXOP { $$ = (Node *) makeA_Expr(OP, $2, $1, NULL); } | a_expr AND a_expr { $$ = (Node *) makeA_Expr(AND, NIL, $1, $3); } | a_expr OR a_expr { $$ = (Node *) makeA_Expr(OR, NIL, $1, $3); } | NOT a_expr { $$ = (Node *) makeA_Expr(NOT, NIL, NULL, $2); } | a_expr LIKE a_expr { $$ = (Node *) makeSimpleA_Expr(OP, "~~", $1, $3); } | a_expr LIKE a_expr ESCAPE a_expr { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("like_escape"); n->args = makeList2($3, $5); n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *) makeSimpleA_Expr(OP, "~~", $1, (Node *) n); } | a_expr NOT LIKE a_expr { $$ = (Node *) makeSimpleA_Expr(OP, "!~~", $1, $4); } | a_expr NOT LIKE a_expr ESCAPE a_expr { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("like_escape"); n->args = makeList2($4, $6); n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *) makeSimpleA_Expr(OP, "!~~", $1, (Node *) n); } | a_expr ILIKE a_expr { $$ = (Node *) makeSimpleA_Expr(OP, "~~*", $1, $3); } | a_expr ILIKE a_expr ESCAPE a_expr { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("like_escape"); n->args = makeList2($3, $5); n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *) makeSimpleA_Expr(OP, "~~*", $1, (Node *) n); } | a_expr NOT ILIKE a_expr { $$ = (Node *) makeSimpleA_Expr(OP, "!~~*", $1, $4); } | a_expr NOT ILIKE a_expr ESCAPE a_expr { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("like_escape"); n->args = makeList2($4, $6); n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *) makeSimpleA_Expr(OP, "!~~*", $1, (Node *) n); } | a_expr SIMILAR TO a_expr %prec SIMILAR { $$ = (Node *) makeSimpleA_Expr(OP, "~", $1, $4); } | a_expr SIMILAR TO a_expr ESCAPE a_expr { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("like_escape"); n->args = makeList2($4, $6); n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *) makeSimpleA_Expr(OP, "~", $1, (Node *) n); } | a_expr NOT SIMILAR TO a_expr %prec SIMILAR { $$ = (Node *) makeSimpleA_Expr(OP, "!~", $1, $5); } | a_expr NOT SIMILAR TO a_expr ESCAPE a_expr { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("like_escape"); n->args = makeList2($5, $7); n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *) makeSimpleA_Expr(OP, "!~", $1, (Node *) n); } /* NullTest clause * Define SQL92-style Null test clause. * Allow two forms described in the standard: * a IS NULL * a IS NOT NULL * Allow two SQL extensions * a ISNULL * a NOTNULL */ | a_expr ISNULL { NullTest *n = makeNode(NullTest); n->arg = $1; n->nulltesttype = IS_NULL; $$ = (Node *)n; } | a_expr IS NULL_P { NullTest *n = makeNode(NullTest); n->arg = $1; n->nulltesttype = IS_NULL; $$ = (Node *)n; } | a_expr NOTNULL { NullTest *n = makeNode(NullTest); n->arg = $1; n->nulltesttype = IS_NOT_NULL; $$ = (Node *)n; } | a_expr IS NOT NULL_P { NullTest *n = makeNode(NullTest); n->arg = $1; n->nulltesttype = IS_NOT_NULL; $$ = (Node *)n; } | a_expr IS TRUE_P { BooleanTest *b = makeNode(BooleanTest); b->arg = $1; b->booltesttype = IS_TRUE; $$ = (Node *)b; } | a_expr IS NOT TRUE_P { BooleanTest *b = makeNode(BooleanTest); b->arg = $1; b->booltesttype = IS_NOT_TRUE; $$ = (Node *)b; } | a_expr IS FALSE_P { BooleanTest *b = makeNode(BooleanTest); b->arg = $1; b->booltesttype = IS_FALSE; $$ = (Node *)b; } | a_expr IS NOT FALSE_P { BooleanTest *b = makeNode(BooleanTest); b->arg = $1; b->booltesttype = IS_NOT_FALSE; $$ = (Node *)b; } | a_expr IS UNKNOWN { BooleanTest *b = makeNode(BooleanTest); b->arg = $1; b->booltesttype = IS_UNKNOWN; $$ = (Node *)b; } | a_expr IS NOT UNKNOWN { BooleanTest *b = makeNode(BooleanTest); b->arg = $1; b->booltesttype = IS_NOT_UNKNOWN; $$ = (Node *)b; } | a_expr IS DISTINCT FROM a_expr %prec IS { $$ = (Node *) makeSimpleA_Expr(DISTINCT, "=", $1, $5); } | a_expr IS OF '(' type_list ')' %prec IS { $$ = (Node *) makeSimpleA_Expr(OF, "=", $1, (Node *) $5); } | a_expr IS NOT OF '(' type_list ')' %prec IS { $$ = (Node *) makeSimpleA_Expr(OF, "!=", $1, (Node *) $6); } | a_expr BETWEEN b_expr AND b_expr %prec BETWEEN { $$ = (Node *) makeA_Expr(AND, NIL, (Node *) makeSimpleA_Expr(OP, ">=", $1, $3), (Node *) makeSimpleA_Expr(OP, "<=", $1, $5)); } | a_expr NOT BETWEEN b_expr AND b_expr %prec BETWEEN { $$ = (Node *) makeA_Expr(OR, NIL, (Node *) makeSimpleA_Expr(OP, "<", $1, $4), (Node *) makeSimpleA_Expr(OP, ">", $1, $6)); } | a_expr IN_P in_expr { /* in_expr returns a SubLink or a list of a_exprs */ if (IsA($3, SubLink)) { SubLink *n = (SubLink *)$3; n->lefthand = makeList1($1); n->oper = (List *) makeSimpleA_Expr(OP, "=", NULL, NULL); n->useor = FALSE; n->subLinkType = ANY_SUBLINK; $$ = (Node *)n; } else { Node *n = NULL; List *l; foreach(l, (List *) $3) { Node *cmp; cmp = (Node *) makeSimpleA_Expr(OP, "=", $1, lfirst(l)); if (n == NULL) n = cmp; else n = (Node *) makeA_Expr(OR, NIL, n, cmp); } $$ = n; } } | a_expr NOT IN_P in_expr { /* in_expr returns a SubLink or a list of a_exprs */ if (IsA($4, SubLink)) { SubLink *n = (SubLink *)$4; n->lefthand = makeList1($1); n->oper = (List *) makeSimpleA_Expr(OP, "<>", NULL, NULL); n->useor = FALSE; n->subLinkType = ALL_SUBLINK; $$ = (Node *)n; } else { Node *n = NULL; List *l; foreach(l, (List *) $4) { Node *cmp; cmp = (Node *) makeSimpleA_Expr(OP, "<>", $1, lfirst(l)); if (n == NULL) n = cmp; else n = (Node *) makeA_Expr(AND, NIL, n, cmp); } $$ = n; } } | a_expr qual_all_Op sub_type select_with_parens %prec Op { SubLink *n = makeNode(SubLink); n->lefthand = makeList1($1); n->oper = (List *) makeA_Expr(OP, $2, NULL, NULL); n->useor = FALSE; /* doesn't matter since only one col */ n->subLinkType = $3; n->subselect = $4; $$ = (Node *)n; } | UNIQUE select_with_parens %prec Op { /* Not sure how to get rid of the parentheses * but there are lots of shift/reduce errors without them. * * Should be able to implement this by plopping the entire * select into a node, then transforming the target expressions * from whatever they are into count(*), and testing the * entire result equal to one. * But, will probably implement a separate node in the executor. */ elog(ERROR, "UNIQUE predicate is not yet implemented"); } | r_expr { $$ = $1; } ; /* * Restricted expressions * * b_expr is a subset of the complete expression syntax defined by a_expr. * * Presently, AND, NOT, IS, and IN are the a_expr keywords that would * cause trouble in the places where b_expr is used. For simplicity, we * just eliminate all the boolean-keyword-operator productions from b_expr. */ b_expr: c_expr { $$ = $1; } | b_expr TYPECAST Typename { $$ = makeTypeCast($1, $3); } | '+' b_expr %prec UMINUS { $$ = (Node *) makeSimpleA_Expr(OP, "+", NULL, $2); } | '-' b_expr %prec UMINUS { $$ = doNegate($2); } | '%' b_expr { $$ = (Node *) makeSimpleA_Expr(OP, "%", NULL, $2); } | '^' b_expr { $$ = (Node *) makeSimpleA_Expr(OP, "^", NULL, $2); } | b_expr '%' { $$ = (Node *) makeSimpleA_Expr(OP, "%", $1, NULL); } | b_expr '^' { $$ = (Node *) makeSimpleA_Expr(OP, "^", $1, NULL); } | b_expr '+' b_expr { $$ = (Node *) makeSimpleA_Expr(OP, "+", $1, $3); } | b_expr '-' b_expr { $$ = (Node *) makeSimpleA_Expr(OP, "-", $1, $3); } | b_expr '*' b_expr { $$ = (Node *) makeSimpleA_Expr(OP, "*", $1, $3); } | b_expr '/' b_expr { $$ = (Node *) makeSimpleA_Expr(OP, "/", $1, $3); } | b_expr '%' b_expr { $$ = (Node *) makeSimpleA_Expr(OP, "%", $1, $3); } | b_expr '^' b_expr { $$ = (Node *) makeSimpleA_Expr(OP, "^", $1, $3); } | b_expr '<' b_expr { $$ = (Node *) makeSimpleA_Expr(OP, "<", $1, $3); } | b_expr '>' b_expr { $$ = (Node *) makeSimpleA_Expr(OP, ">", $1, $3); } | b_expr '=' b_expr { $$ = (Node *) makeSimpleA_Expr(OP, "=", $1, $3); } | b_expr qual_Op b_expr %prec Op { $$ = (Node *) makeA_Expr(OP, $2, $1, $3); } | qual_Op b_expr %prec Op { $$ = (Node *) makeA_Expr(OP, $1, NULL, $2); } | b_expr qual_Op %prec POSTFIXOP { $$ = (Node *) makeA_Expr(OP, $2, $1, NULL); } | b_expr IS DISTINCT FROM b_expr %prec IS { $$ = (Node *) makeSimpleA_Expr(DISTINCT, "=", $1, $5); } | b_expr IS OF '(' type_list ')' %prec IS { $$ = (Node *) makeSimpleA_Expr(OF, "=", $1, (Node *) $5); } | b_expr IS NOT OF '(' type_list ')' %prec IS { $$ = (Node *) makeSimpleA_Expr(OF, "!=", $1, (Node *) $6); } ; /* * Productions that can be used in both a_expr and b_expr. * * Note: productions that refer recursively to a_expr or b_expr mostly * cannot appear here. However, it's OK to refer to a_exprs that occur * inside parentheses, such as function arguments; that cannot introduce * ambiguity to the b_expr syntax. */ c_expr: columnref { $$ = (Node *) $1; } | AexprConst { $$ = $1; } | PARAM attrs opt_indirection { /* * PARAM without field names is considered a constant, * but with 'em, it is not. Not very consistent ... */ ParamRef *n = makeNode(ParamRef); n->number = $1; n->fields = $2; n->indirection = $3; $$ = (Node *)n; } | '(' a_expr ')' { $$ = $2; } | '(' a_expr ')' attrs opt_indirection { ExprFieldSelect *n = makeNode(ExprFieldSelect); n->arg = $2; n->fields = $4; n->indirection = $5; $$ = (Node *)n; } | case_expr { $$ = $1; } | func_name '(' ')' { FuncCall *n = makeNode(FuncCall); n->funcname = $1; n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *)n; } | func_name '(' expr_list ')' { FuncCall *n = makeNode(FuncCall); n->funcname = $1; n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *)n; } | func_name '(' ALL expr_list ')' { FuncCall *n = makeNode(FuncCall); n->funcname = $1; n->args = $4; n->agg_star = FALSE; n->agg_distinct = FALSE; /* Ideally we'd mark the FuncCall node to indicate * "must be an aggregate", but there's no provision * for that in FuncCall at the moment. */ $$ = (Node *)n; } | func_name '(' DISTINCT expr_list ')' { FuncCall *n = makeNode(FuncCall); n->funcname = $1; n->args = $4; n->agg_star = FALSE; n->agg_distinct = TRUE; $$ = (Node *)n; } | func_name '(' '*' ')' { /* * For now, we transform AGGREGATE(*) into AGGREGATE(1). * * This does the right thing for COUNT(*) (in fact, * any certainly-non-null expression would do for COUNT), * and there are no other aggregates in SQL92 that accept * '*' as parameter. * * The FuncCall node is also marked agg_star = true, * so that later processing can detect what the argument * really was. */ FuncCall *n = makeNode(FuncCall); A_Const *star = makeNode(A_Const); star->val.type = T_Integer; star->val.val.ival = 1; n->funcname = $1; n->args = makeList1(star); n->agg_star = TRUE; n->agg_distinct = FALSE; $$ = (Node *)n; } | CURRENT_DATE { /* * Translate as "'now'::text::date". * * We cannot use "'now'::date" because coerce_type() will * immediately reduce that to a constant representing * today's date. We need to delay the conversion until * runtime, else the wrong things will happen when * CURRENT_DATE is used in a column default value or rule. * * This could be simplified if we had a way to generate * an expression tree representing runtime application * of type-input conversion functions... */ A_Const *s = makeNode(A_Const); TypeName *d; s->val.type = T_String; s->val.val.str = "now"; s->typename = SystemTypeName("text"); d = SystemTypeName("date"); $$ = (Node *)makeTypeCast((Node *)s, d); } | CURRENT_TIME { /* * Translate as "'now'::text::timetz". * See comments for CURRENT_DATE. */ A_Const *s = makeNode(A_Const); TypeName *d; s->val.type = T_String; s->val.val.str = "now"; s->typename = SystemTypeName("text"); d = SystemTypeName("timetz"); /* SQL99 mandates a default precision of zero for TIME * fields in schemas. However, for CURRENT_TIME * let's preserve the microsecond precision we * might see from the system clock. - thomas 2001-12-07 */ d->typmod = 6; $$ = (Node *)makeTypeCast((Node *)s, d); } | CURRENT_TIME '(' Iconst ')' { /* * Translate as "'now'::text::timetz(n)". * See comments for CURRENT_DATE. */ A_Const *s = makeNode(A_Const); TypeName *d; s->val.type = T_String; s->val.val.str = "now"; s->typename = SystemTypeName("text"); d = SystemTypeName("timetz"); if (($3 < 0) || ($3 > MAX_TIME_PRECISION)) elog(ERROR, "CURRENT_TIME(%d) precision must be between %d and %d", $3, 0, MAX_TIME_PRECISION); d->typmod = $3; $$ = (Node *)makeTypeCast((Node *)s, d); } | CURRENT_TIMESTAMP { /* * Translate as "'now'::text::timestamptz". * See comments for CURRENT_DATE. */ A_Const *s = makeNode(A_Const); TypeName *d; s->val.type = T_String; s->val.val.str = "now"; s->typename = SystemTypeName("text"); d = SystemTypeName("timestamptz"); /* SQL99 mandates a default precision of 6 for timestamp. * Also, that is about as precise as we will get since * we are using a microsecond time interface. * - thomas 2001-12-07 */ d->typmod = 6; $$ = (Node *)makeTypeCast((Node *)s, d); } | CURRENT_TIMESTAMP '(' Iconst ')' { /* * Translate as "'now'::text::timestamptz(n)". * See comments for CURRENT_DATE. */ A_Const *s = makeNode(A_Const); TypeName *d; s->val.type = T_String; s->val.val.str = "now"; s->typename = SystemTypeName("text"); d = SystemTypeName("timestamptz"); if (($3 < 0) || ($3 > MAX_TIMESTAMP_PRECISION)) elog(ERROR, "CURRENT_TIMESTAMP(%d) precision " "must be between %d and %d", $3, 0, MAX_TIMESTAMP_PRECISION); d->typmod = $3; $$ = (Node *)makeTypeCast((Node *)s, d); } | LOCALTIME { /* * Translate as "'now'::text::time". * See comments for CURRENT_DATE. */ A_Const *s = makeNode(A_Const); TypeName *d; s->val.type = T_String; s->val.val.str = "now"; s->typename = SystemTypeName("text"); d = SystemTypeName("time"); /* SQL99 mandates a default precision of zero for TIME * fields in schemas. However, for LOCALTIME * let's preserve the microsecond precision we * might see from the system clock. - thomas 2001-12-07 */ d->typmod = 6; $$ = (Node *)makeTypeCast((Node *)s, d); } | LOCALTIME '(' Iconst ')' { /* * Translate as "'now'::text::time(n)". * See comments for CURRENT_DATE. */ A_Const *s = makeNode(A_Const); TypeName *d; s->val.type = T_String; s->val.val.str = "now"; s->typename = SystemTypeName("text"); d = SystemTypeName("time"); if (($3 < 0) || ($3 > MAX_TIME_PRECISION)) elog(ERROR, "LOCALTIME(%d) precision must be between %d and %d", $3, 0, MAX_TIME_PRECISION); d->typmod = $3; $$ = (Node *)makeTypeCast((Node *)s, d); } | LOCALTIMESTAMP { /* * Translate as "'now'::text::timestamp". * See comments for CURRENT_DATE. */ A_Const *s = makeNode(A_Const); TypeName *d; s->val.type = T_String; s->val.val.str = "now"; s->typename = SystemTypeName("text"); d = SystemTypeName("timestamp"); /* SQL99 mandates a default precision of 6 for timestamp. * Also, that is about as precise as we will get since * we are using a microsecond time interface. * - thomas 2001-12-07 */ d->typmod = 6; $$ = (Node *)makeTypeCast((Node *)s, d); } | LOCALTIMESTAMP '(' Iconst ')' { /* * Translate as "'now'::text::timestamp(n)". * See comments for CURRENT_DATE. */ A_Const *s = makeNode(A_Const); TypeName *d; s->val.type = T_String; s->val.val.str = "now"; s->typename = SystemTypeName("text"); d = SystemTypeName("timestamp"); if (($3 < 0) || ($3 > MAX_TIMESTAMP_PRECISION)) elog(ERROR, "LOCALTIMESTAMP(%d) precision must be " "between %d and %d", $3, 0, MAX_TIMESTAMP_PRECISION); d->typmod = $3; $$ = (Node *)makeTypeCast((Node *)s, d); } | CURRENT_USER { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("current_user"); n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *)n; } | SESSION_USER { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("session_user"); n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *)n; } | USER { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("current_user"); n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *)n; } | CAST '(' a_expr AS Typename ')' { $$ = makeTypeCast($3, $5); } | EXTRACT '(' extract_list ')' { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("date_part"); n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *)n; } | OVERLAY '(' overlay_list ')' { /* overlay(A PLACING B FROM C FOR D) is converted to * substring(A, 1, C-1) || B || substring(A, C+1, C+D) * overlay(A PLACING B FROM C) is converted to * substring(A, 1, C-1) || B || substring(A, C+1, C+char_length(B)) */ FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("overlay"); n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *)n; } | POSITION '(' position_list ')' { /* position(A in B) is converted to position(B, A) */ FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("position"); n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *)n; } | SUBSTRING '(' substr_list ')' { /* substring(A from B for C) is converted to * substring(A, B, C) - thomas 2000-11-28 */ FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("substring"); n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *)n; } | TREAT '(' a_expr AS Typename ')' { /* TREAT(expr AS target) converts expr of a particular type to target, * which is defined to be a subtype of the original expression. * In SQL99, this is intended for use with structured UDTs, * but let's make this a generally useful form allowing stronger * coersions than are handled by implicit casting. */ FuncCall *n = makeNode(FuncCall); /* Convert SystemTypeName() to SystemFuncName() even though * at the moment they result in the same thing. */ n->funcname = SystemFuncName(((Value *)llast($5->names))->val.str); n->args = makeList1($3); $$ = (Node *)n; } | TRIM '(' BOTH trim_list ')' { /* various trim expressions are defined in SQL92 * - thomas 1997-07-19 */ FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("btrim"); n->args = $4; n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *)n; } | TRIM '(' LEADING trim_list ')' { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("ltrim"); n->args = $4; n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *)n; } | TRIM '(' TRAILING trim_list ')' { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("rtrim"); n->args = $4; n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *)n; } | TRIM '(' trim_list ')' { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("btrim"); n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *)n; } | CONVERT '(' convert_list ')' { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("convert"); n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; $$ = (Node *)n; } | select_with_parens %prec UMINUS { SubLink *n = makeNode(SubLink); n->lefthand = NIL; n->oper = NIL; n->useor = FALSE; n->subLinkType = EXPR_SUBLINK; n->subselect = $1; $$ = (Node *)n; } | EXISTS select_with_parens { SubLink *n = makeNode(SubLink); n->lefthand = NIL; n->oper = NIL; n->useor = FALSE; n->subLinkType = EXISTS_SUBLINK; n->subselect = $2; $$ = (Node *)n; } ; /* * Supporting nonterminals for expressions. */ opt_indirection: opt_indirection '[' a_expr ']' { A_Indices *ai = makeNode(A_Indices); ai->lidx = NULL; ai->uidx = $3; $$ = lappend($1, ai); } | opt_indirection '[' a_expr ':' a_expr ']' { A_Indices *ai = makeNode(A_Indices); ai->lidx = $3; ai->uidx = $5; $$ = lappend($1, ai); } | /*EMPTY*/ { $$ = NIL; } ; expr_list: a_expr { $$ = makeList1($1); } | expr_list ',' a_expr { $$ = lappend($1, $3); } | expr_list USING a_expr { $$ = lappend($1, $3); } ; extract_list: extract_arg FROM a_expr { A_Const *n = makeNode(A_Const); n->val.type = T_String; n->val.val.str = $1; $$ = makeList2((Node *) n, $3); } | /*EMPTY*/ { $$ = NIL; } ; type_list: type_list ',' Typename { $$ = lappend($1, $3); } | Typename { $$ = makeList1($1); } ; /* Allow delimited string SCONST in extract_arg as an SQL extension. * - thomas 2001-04-12 */ extract_arg: IDENT { $$ = $1; } | YEAR_P { $$ = "year"; } | MONTH_P { $$ = "month"; } | DAY_P { $$ = "day"; } | HOUR_P { $$ = "hour"; } | MINUTE_P { $$ = "minute"; } | SECOND_P { $$ = "second"; } | SCONST { $$ = $1; } ; /* OVERLAY() arguments * SQL99 defines the OVERLAY() function: * o overlay(text placing text from int for int) * o overlay(text placing text from int) */ overlay_list: a_expr overlay_placing substr_from substr_for { $$ = makeList4($1, $2, $3, $4); } | a_expr overlay_placing substr_from { $$ = makeList3($1, $2, $3); } ; overlay_placing: PLACING a_expr { $$ = $2; } ; /* position_list uses b_expr not a_expr to avoid conflict with general IN */ position_list: b_expr IN_P b_expr { $$ = makeList2($3, $1); } | /*EMPTY*/ { $$ = NIL; } ; /* SUBSTRING() arguments * SQL9x defines a specific syntax for arguments to SUBSTRING(): * o substring(text from int for int) * o substring(text from int) get entire string from starting point "int" * o substring(text from pattern) get entire string matching pattern * o substring(text for int) get first "int" characters of string * We also want to implement generic substring functions which accept * the usual generic list of arguments. So we will accept both styles * here, and convert the SQL9x style to the generic list for further * processing. - thomas 2000-11-28 */ substr_list: a_expr substr_from substr_for { $$ = makeList3($1, $2, $3); } | a_expr substr_for substr_from { $$ = makeList3($1, $3, $2); } | a_expr substr_from { $$ = makeList2($1, $2); } | a_expr substr_for { A_Const *n = makeNode(A_Const); n->val.type = T_Integer; n->val.val.ival = 1; $$ = makeList3($1, (Node *)n, $2); } | expr_list { $$ = $1; } | /*EMPTY*/ { $$ = NIL; } ; substr_from: FROM a_expr { $$ = $2; } ; substr_for: FOR a_expr { $$ = $2; } ; trim_list: a_expr FROM expr_list { $$ = lappend($3, $1); } | FROM expr_list { $$ = $2; } | expr_list { $$ = $1; } ; /* CONVERT() arguments. We accept followings: * SQL99 syntax * o CONVERT(TEXT string USING conversion_name) * * Function calls * o CONVERT(TEXT string, NAME src_encoding_name, NAME dest_encoding_name) * o CONVERT(TEXT string, NAME encoding_name) */ convert_list: a_expr USING any_name { Oid oid = FindConversionByName($3); Const *convoid = makeNode(Const); if (!OidIsValid(oid)) { elog(ERROR, "Conversion \"%s\" does not exist", NameListToString($3)); } convoid->consttype = OIDOID; convoid->constlen = sizeof(Oid); convoid->constvalue = oid; convoid->constisnull = FALSE; convoid->constbyval = TRUE; convoid->constisset = FALSE; convoid->constiscast = FALSE; $$ = makeList2($1, convoid); } | convert_args { $$ = $1; } | /*EMPTY*/ { $$ = NIL; } ; convert_args: a_expr { $$ = makeList1($1); } | convert_args ',' a_expr { $$ = lappend($1, $3); } ; in_expr: select_with_parens { SubLink *n = makeNode(SubLink); n->subselect = $1; $$ = (Node *)n; } | '(' in_expr_nodes ')' { $$ = (Node *)$2; } ; in_expr_nodes: a_expr { $$ = makeList1($1); } | in_expr_nodes ',' a_expr { $$ = lappend($1, $3); } ; /* Case clause * Define SQL92-style case clause. * Allow all four forms described in the standard: * - Full specification * CASE WHEN a = b THEN c ... ELSE d END * - Implicit argument * CASE a WHEN b THEN c ... ELSE d END * - Conditional NULL * NULLIF(x,y) * same as CASE WHEN x = y THEN NULL ELSE x END * - Conditional substitution from list, use first non-null argument * COALESCE(a,b,...) * same as CASE WHEN a IS NOT NULL THEN a WHEN b IS NOT NULL THEN b ... END * - thomas 1998-11-09 */ case_expr: CASE case_arg when_clause_list case_default END_TRANS { CaseExpr *c = makeNode(CaseExpr); c->arg = $2; c->args = $3; c->defresult = $4; $$ = (Node *)c; } | NULLIF '(' a_expr ',' a_expr ')' { CaseExpr *c = makeNode(CaseExpr); CaseWhen *w = makeNode(CaseWhen); w->expr = (Node *) makeSimpleA_Expr(OP, "=", $3, $5); /* w->result is left NULL */ c->args = makeList1(w); c->defresult = $3; $$ = (Node *)c; } | COALESCE '(' expr_list ')' { CaseExpr *c = makeNode(CaseExpr); List *l; foreach (l,$3) { CaseWhen *w = makeNode(CaseWhen); NullTest *n = makeNode(NullTest); n->arg = lfirst(l); n->nulltesttype = IS_NOT_NULL; w->expr = (Node *) n; w->result = lfirst(l); c->args = lappend(c->args, w); } $$ = (Node *)c; } ; when_clause_list: /* There must be at least one */ when_clause { $$ = makeList1($1); } | when_clause_list when_clause { $$ = lappend($1, $2); } ; when_clause: WHEN a_expr THEN a_expr { CaseWhen *w = makeNode(CaseWhen); w->expr = $2; w->result = $4; $$ = (Node *)w; } ; case_default: ELSE a_expr { $$ = $2; } | /*EMPTY*/ { $$ = NULL; } ; case_arg: a_expr { $$ = $1; } | /*EMPTY*/ { $$ = NULL; } ; /* * columnref starts with relation_name not ColId, so that OLD and NEW * references can be accepted. Note that when there are more than two * dotted names, the first name is not actually a relation name... */ columnref: relation_name opt_indirection { $$ = makeNode(ColumnRef); $$->fields = makeList1(makeString($1)); $$->indirection = $2; } | dotted_name opt_indirection { $$ = makeNode(ColumnRef); $$->fields = $1; $$->indirection = $2; } ; dotted_name: relation_name attrs { $$ = lcons(makeString($1), $2); } ; attrs: '.' attr_name { $$ = makeList1(makeString($2)); } | '.' '*' { $$ = makeList1(makeString("*")); } | '.' attr_name attrs { $$ = lcons(makeString($2), $3); } ; /***************************************************************************** * * target lists * *****************************************************************************/ /* Target lists as found in SELECT ... and INSERT VALUES ( ... ) */ target_list: target_el { $$ = makeList1($1); } | target_list ',' target_el { $$ = lappend($1, $3); } ; /* AS is not optional because shift/red conflict with unary ops */ target_el: a_expr AS ColLabel { $$ = makeNode(ResTarget); $$->name = $3; $$->indirection = NIL; $$->val = (Node *)$1; } | a_expr { $$ = makeNode(ResTarget); $$->name = NULL; $$->indirection = NIL; $$->val = (Node *)$1; } | '*' { ColumnRef *n = makeNode(ColumnRef); n->fields = makeList1(makeString("*")); n->indirection = NIL; $$ = makeNode(ResTarget); $$->name = NULL; $$->indirection = NIL; $$->val = (Node *)n; } ; /* Target list as found in UPDATE table SET ... | '(' row_ ')' = '(' row_ ')' { $$ = NULL; } */ update_target_list: update_target_el { $$ = makeList1($1); } | update_target_list ',' update_target_el { $$ = lappend($1,$3); } ; update_target_el: ColId opt_indirection '=' a_expr { $$ = makeNode(ResTarget); $$->name = $1; $$->indirection = $2; $$->val = (Node *)$4; } ; insert_target_list: insert_target_el { $$ = makeList1($1); } | insert_target_list ',' insert_target_el { $$ = lappend($1, $3); } ; insert_target_el: target_el { $$ = $1; } | DEFAULT { InsertDefault *def = makeNode(InsertDefault); $$ = makeNode(ResTarget); $$->name = NULL; $$->indirection = NULL; $$->val = (Node *)def; } ; /***************************************************************************** * * Names and constants * *****************************************************************************/ relation_name: SpecialRuleRelation { $$ = $1; } | ColId { $$ = $1; } ; qualified_name_list: qualified_name { $$ = makeList1($1); } | qualified_name_list ',' qualified_name { $$ = lappend($1, $3); } ; qualified_name: relation_name { $$ = makeNode(RangeVar); $$->catalogname = NULL; $$->schemaname = NULL; $$->relname = $1; } | dotted_name { $$ = makeNode(RangeVar); switch (length($1)) { case 2: $$->catalogname = NULL; $$->schemaname = strVal(lfirst($1)); $$->relname = strVal(lsecond($1)); break; case 3: $$->catalogname = strVal(lfirst($1)); $$->schemaname = strVal(lsecond($1)); $$->relname = strVal(lfirst(lnext(lnext($1)))); break; default: elog(ERROR, "Improper qualified name " "(too many dotted names): %s", NameListToString($1)); break; } } ; name_list: name { $$ = makeList1(makeString($1)); } | name_list ',' name { $$ = lappend($1, makeString($3)); } ; name: ColId { $$ = $1; }; database_name: ColId { $$ = $1; }; access_method: ColId { $$ = $1; }; attr_name: ColId { $$ = $1; }; index_name: ColId { $$ = $1; }; file_name: Sconst { $$ = $1; }; func_name: function_name { $$ = makeList1(makeString($1)); } | dotted_name { $$ = $1; } ; /* * Constants */ 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.str = $1; $$ = (Node *)n; } | Sconst { A_Const *n = makeNode(A_Const); n->val.type = T_String; n->val.val.str = $1; $$ = (Node *)n; } | BCONST { A_Const *n = makeNode(A_Const); n->val.type = T_BitString; n->val.val.str = $1; $$ = (Node *)n; } | XCONST { /* This is a bit constant per SQL99: * Without Feature F511, "BIT data type", * a shall not be a * or a . */ A_Const *n = makeNode(A_Const); n->val.type = T_BitString; n->val.val.str = $1; $$ = (Node *)n; } | ConstTypename Sconst { A_Const *n = makeNode(A_Const); n->typename = $1; n->val.type = T_String; n->val.val.str = $2; $$ = (Node *)n; } | ConstInterval Sconst opt_interval { A_Const *n = makeNode(A_Const); n->typename = $1; n->val.type = T_String; n->val.val.str = $2; /* precision is not specified, but fields may be... */ if ($3 != INTERVAL_FULL_RANGE) n->typename->typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, $3); $$ = (Node *)n; } | ConstInterval '(' Iconst ')' Sconst opt_interval { A_Const *n = makeNode(A_Const); n->typename = $1; n->val.type = T_String; n->val.val.str = $5; /* precision specified, and fields may be... */ if (($3 < 0) || ($3 > MAX_INTERVAL_PRECISION)) elog(ERROR, "INTERVAL(%d) precision must be between %d and %d", $3, 0, MAX_INTERVAL_PRECISION); n->typename->typmod = INTERVAL_TYPMOD($3, $6); $$ = (Node *)n; } | PARAM opt_indirection { ParamRef *n = makeNode(ParamRef); n->number = $1; n->fields = NIL; n->indirection = $2; $$ = (Node *)n; } | TRUE_P { $$ = (Node *)makeBoolConst(TRUE); } | FALSE_P { $$ = (Node *)makeBoolConst(FALSE); } | NULL_P { A_Const *n = makeNode(A_Const); n->val.type = T_Null; $$ = (Node *)n; } ; Iconst: ICONST { $$ = $1; }; Sconst: SCONST { $$ = $1; }; UserId: ColId { $$ = $1; }; /* * Name classification hierarchy. * * IDENT is the lexeme returned by the lexer for identifiers that match * no known keyword. In most cases, we can accept certain keywords as * names, not only IDENTs. We prefer to accept as many such keywords * as possible to minimize the impact of "reserved words" on programmers. * So, we divide names into several possible classes. The classification * is chosen in part to make keywords acceptable as names wherever possible. */ /* Column identifier --- names that can be column, table, etc names. */ ColId: IDENT { $$ = $1; } | unreserved_keyword { $$ = pstrdup($1); } | col_name_keyword { $$ = pstrdup($1); } ; /* Type identifier --- names that can be type names. */ type_name: IDENT { $$ = $1; } | unreserved_keyword { $$ = pstrdup($1); } ; /* Function identifier --- names that can be function names. */ function_name: IDENT { $$ = $1; } | unreserved_keyword { $$ = pstrdup($1); } | func_name_keyword { $$ = pstrdup($1); } ; /* Column label --- allowed labels in "AS" clauses. * This presently includes *all* Postgres keywords. */ ColLabel: IDENT { $$ = $1; } | unreserved_keyword { $$ = pstrdup($1); } | col_name_keyword { $$ = pstrdup($1); } | func_name_keyword { $$ = pstrdup($1); } | reserved_keyword { $$ = pstrdup($1); } ; /* * Keyword classification lists. Generally, every keyword present in * the Postgres grammar should appear in exactly one of these lists. * * Put a new keyword into the first list that it can go into without causing * shift or reduce conflicts. The earlier lists define "less reserved" * categories of keywords. */ /* "Unreserved" keywords --- available for use as any kind of name. */ unreserved_keyword: ABORT_TRANS | ABSOLUTE | ACCESS | ACTION | ADD | AFTER | AGGREGATE | ALTER | ASSERTION | ASSIGNMENT | AT | BACKWARD | BEFORE | BEGIN_TRANS | BY | CACHE | CALLED | CASCADE | CHAIN | CHARACTERISTICS | CHECKPOINT | CLASS | CLOSE | CLUSTER | COMMENT | COMMIT | COMMITTED | CONSTRAINTS | CONVERSION_P | COPY | CREATEDB | CREATEUSER | CURSOR | CYCLE | DATABASE | DAY_P | DEALLOCATE | DECLARE | DEFERRED | DEFINER | DELETE_P | DELIMITER | DELIMITERS | DOMAIN_P | DOUBLE | DROP | EACH | ENCODING | ENCRYPTED | ESCAPE | EXCLUSIVE | EXECUTE | EXPLAIN | EXTERNAL | FETCH | FORCE | FORWARD | FUNCTION | GET | GLOBAL | HANDLER | HOUR_P | IMMEDIATE | IMMUTABLE | IMPLICIT_P | INCREMENT | INDEX | INHERITS | INOUT | INPUT | INSENSITIVE | INSERT | INSTEAD | INVOKER | ISOLATION | KEY | LANGUAGE | LANCOMPILER | LEVEL | LISTEN | LOAD | LOCAL | LOCATION | LOCK_P | MATCH | MAXVALUE | MINUTE_P | MINVALUE | MODE | MONTH_P | MOVE | NAMES | NATIONAL | NEXT | NO | NOCREATEDB | NOCREATEUSER | NOTHING | NOTIFY | OF | OIDS | OPERATOR | OPTION | OUT_P | OWNER | PARTIAL | PASSWORD | PATH_P | PENDANT | PRECISION | PREPARE | PRIOR | PRIVILEGES | PROCEDURAL | PROCEDURE | READ | RECHECK | REINDEX | RELATIVE | RENAME | REPLACE | RESET | RESTRICT | RETURNS | REVOKE | ROLLBACK | RULE | SCHEMA | SCROLL | SECOND_P | SECURITY | SESSION | SEQUENCE | SERIALIZABLE | SET | SHARE | SHOW | SIMPLE | STABLE | START | STATEMENT | STATISTICS | STDIN | STDOUT | STORAGE | STRICT | SYSID | TEMP | TEMPLATE | TEMPORARY | TOAST | TRANSACTION | TRIGGER | TRUNCATE | TRUSTED | TYPE_P | UNENCRYPTED | UNKNOWN | UNLISTEN | UNTIL | UPDATE | USAGE | VACUUM | VALID | VALIDATOR | VALUES | VARYING | VERSION | VIEW | VOLATILE | WITH | WITHOUT | WRITE | WORK | YEAR_P | ZONE ; /* Column identifier --- keywords that can be column, table, etc names. * * Many of these keywords will in fact be recognized as type or function * names too; but they have special productions for the purpose, and so * can't be treated as "generic" type or function names. * * The type names appearing here are not usable as function names * because they can be followed by '(' in typename productions, which * looks too much like a function call for an LR(1) parser. */ col_name_keyword: BIGINT | BIT | BOOLEAN | CHAR_P | CHARACTER | COALESCE | DEC | DECIMAL | EXISTS | EXTRACT | FLOAT_P | INT | INTEGER | INTERVAL | NCHAR | NONE | NULLIF | NUMERIC | OVERLAY | POSITION | REAL | ROW | SETOF | SMALLINT | SUBSTRING | TIME | TIMESTAMP | TREAT | TRIM | VARCHAR ; /* Function identifier --- keywords that can be function names. * * Most of these are keywords that are used as operators in expressions; * in general such keywords can't be column names because they would be * ambiguous with variables, but they are unambiguous as function identifiers. * * Do not include POSITION, SUBSTRING, etc here since they have explicit * productions in a_expr to support the goofy SQL9x argument syntax. * - thomas 2000-11-28 */ func_name_keyword: AUTHORIZATION | BETWEEN | BINARY | CROSS | FREEZE | FULL | ILIKE | IN_P | INNER_P | IS | ISNULL | JOIN | LEFT | LIKE | NATURAL | NOTNULL | OUTER_P | OVERLAPS | RIGHT | SIMILAR | VERBOSE ; /* Reserved keyword --- these keywords are usable only as a ColLabel. * * Keywords appear here if they could not be distinguished from variable, * type, or function names in some contexts. Don't put things here unless * forced to. */ reserved_keyword: ALL | ANALYSE | ANALYZE | AND | ANY | AS | ASC | BOTH | CASE | CAST | CHECK | COLLATE | COLUMN | CONSTRAINT | CREATE | CURRENT_DATE | CURRENT_TIME | CURRENT_TIMESTAMP | CURRENT_USER | DEFAULT | DEFERRABLE | DESC | DISTINCT | DO | ELSE | END_TRANS | EXCEPT | FALSE_P | FOR | FOREIGN | FROM | GRANT | GROUP_P | HAVING | INITIALLY | INTERSECT | INTO | LEADING | LIMIT | LOCALTIME | LOCALTIMESTAMP | NEW | NOT | NULL_P | OFF | OFFSET | OLD | ON | ONLY | OR | ORDER | PLACING | PRIMARY | REFERENCES | SELECT | SESSION_USER | SOME | TABLE | THEN | TO | TRAILING | TRUE_P | UNION | UNIQUE | USER | USING | WHEN | WHERE ; SpecialRuleRelation: OLD { if (QueryIsRule) $$ = "*OLD*"; else elog(ERROR, "OLD used in non-rule query"); } | NEW { if (QueryIsRule) $$ = "*NEW*"; else elog(ERROR, "NEW used in non-rule query"); } ; %% static Node * makeTypeCast(Node *arg, TypeName *typename) { /* * Simply generate a TypeCast node. * * Earlier we would determine whether an A_Const would * be acceptable, however Domains require coerce_type() * to process them -- applying constraints as required. */ TypeCast *n = makeNode(TypeCast); n->arg = arg; n->typename = typename; return (Node *) n; } static Node * makeStringConst(char *str, TypeName *typename) { A_Const *n = makeNode(A_Const); n->val.type = T_String; n->val.val.str = str; n->typename = typename; return (Node *)n; } static Node * makeIntConst(int val) { A_Const *n = makeNode(A_Const); n->val.type = T_Integer; n->val.val.ival = val; n->typename = SystemTypeName("int4"); return (Node *)n; } static Node * makeFloatConst(char *str) { A_Const *n = makeNode(A_Const); n->val.type = T_Float; n->val.val.str = str; n->typename = SystemTypeName("float8"); return (Node *)n; } static Node * makeAConst(Value *v) { Node *n; switch (v->type) { case T_Float: n = makeFloatConst(v->val.str); break; case T_Integer: n = makeIntConst(v->val.ival); break; case T_String: default: n = makeStringConst(v->val.str, NULL); break; } return n; } /* makeDefElem() * Create a DefElem node and set contents. * Could be moved to nodes/makefuncs.c if this is useful elsewhere. */ static DefElem * makeDefElem(char *name, Node *arg) { DefElem *f = makeNode(DefElem); f->defname = name; f->arg = arg; return f; } /* makeBoolConst() * Create an A_Const node and initialize to a boolean constant. */ static A_Const * makeBoolConst(bool state) { A_Const *n = makeNode(A_Const); n->val.type = T_String; n->val.val.str = (state? "t": "f"); n->typename = SystemTypeName("bool"); return n; } /* makeRowExpr() * Generate separate operator nodes for a single row descriptor expression. * Perhaps this should go deeper in the parser someday... * - thomas 1997-12-22 */ static Node * makeRowExpr(List *opr, List *largs, List *rargs) { Node *expr = NULL; Node *larg, *rarg; char *oprname; if (length(largs) != length(rargs)) elog(ERROR, "Unequal number of entries in row expression"); if (lnext(largs) != NIL) expr = makeRowExpr(opr, lnext(largs), lnext(rargs)); larg = lfirst(largs); rarg = lfirst(rargs); oprname = strVal(llast(opr)); if ((strcmp(oprname, "=") == 0) || (strcmp(oprname, "<") == 0) || (strcmp(oprname, "<=") == 0) || (strcmp(oprname, ">") == 0) || (strcmp(oprname, ">=") == 0)) { if (expr == NULL) expr = (Node *) makeA_Expr(OP, opr, larg, rarg); else expr = (Node *) makeA_Expr(AND, NIL, expr, (Node *) makeA_Expr(OP, opr, larg, rarg)); } else if (strcmp(oprname, "<>") == 0) { if (expr == NULL) expr = (Node *) makeA_Expr(OP, opr, larg, rarg); else expr = (Node *) makeA_Expr(OR, NIL, expr, (Node *) makeA_Expr(OP, opr, larg, rarg)); } else { elog(ERROR, "Operator '%s' not implemented for row expressions", oprname); } return expr; } /* makeDistinctExpr() * Generate separate operator nodes for a single row descriptor expression. * Same comments as for makeRowExpr(). */ static Node * makeDistinctExpr(List *largs, List *rargs) { Node *expr = NULL; Node *larg, *rarg; if (length(largs) != length(rargs)) elog(ERROR, "Unequal number of entries in row expression"); if (lnext(largs) != NIL) expr = makeDistinctExpr(lnext(largs), lnext(rargs)); larg = lfirst(largs); rarg = lfirst(rargs); if (expr == NULL) expr = (Node *) makeSimpleA_Expr(DISTINCT, "=", larg, rarg); else expr = (Node *) makeA_Expr(OR, NIL, expr, (Node *) makeSimpleA_Expr(DISTINCT, "=", larg, rarg)); return expr; } /* makeRowNullTest() * Generate separate operator nodes for a single row descriptor test. */ static Node * makeRowNullTest(NullTestType test, List *args) { Node *expr = NULL; Node *arg; NullTest *n; if (lnext(args) != NIL) expr = makeRowNullTest(test, lnext(args)); arg = lfirst(args); n = makeNode(NullTest); n->arg = arg; n->nulltesttype = test; if (expr == NULL) expr = (Node *)n; else if (test == IS_NOT_NULL) expr = (Node *) makeA_Expr(OR, NIL, expr, (Node *)n); else expr = (Node *) makeA_Expr(AND, NIL, expr, (Node *)n); return expr; } /* makeOverlaps() * Create and populate a FuncCall node to support the OVERLAPS operator. */ static FuncCall * makeOverlaps(List *largs, List *rargs) { FuncCall *n = makeNode(FuncCall); n->funcname = SystemFuncName("overlaps"); if (length(largs) == 1) largs = lappend(largs, largs); else if (length(largs) != 2) elog(ERROR, "Wrong number of parameters" " on left side of OVERLAPS expression"); if (length(rargs) == 1) rargs = lappend(rargs, rargs); else if (length(rargs) != 2) elog(ERROR, "Wrong number of parameters" " on right side of OVERLAPS expression"); n->args = nconc(largs, rargs); n->agg_star = FALSE; n->agg_distinct = FALSE; return n; } /* findLeftmostSelect() * Find the leftmost component SelectStmt in a set-operation parsetree. */ static SelectStmt * findLeftmostSelect(SelectStmt *node) { while (node && node->op != SETOP_NONE) node = node->larg; Assert(node && IsA(node, SelectStmt) && node->larg == NULL); return node; } /* insertSelectOptions() * Insert ORDER BY, etc into an already-constructed SelectStmt. * * This routine is just to avoid duplicating code in SelectStmt productions. */ static void insertSelectOptions(SelectStmt *stmt, List *sortClause, List *forUpdate, Node *limitOffset, Node *limitCount) { /* * Tests here are to reject constructs like * (SELECT foo ORDER BY bar) ORDER BY baz */ if (sortClause) { if (stmt->sortClause) elog(ERROR, "Multiple ORDER BY clauses not allowed"); stmt->sortClause = sortClause; } if (forUpdate) { if (stmt->forUpdate) elog(ERROR, "Multiple FOR UPDATE clauses not allowed"); stmt->forUpdate = forUpdate; } if (limitOffset) { if (stmt->limitOffset) elog(ERROR, "Multiple OFFSET clauses not allowed"); stmt->limitOffset = limitOffset; } if (limitCount) { if (stmt->limitCount) elog(ERROR, "Multiple LIMIT clauses not allowed"); stmt->limitCount = limitCount; } } static Node * makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg) { SelectStmt *n = makeNode(SelectStmt); n->op = op; n->all = all; n->larg = (SelectStmt *) larg; n->rarg = (SelectStmt *) rarg; return (Node *) n; } /* SystemFuncName() * Build a properly-qualified reference to a built-in function. */ List * SystemFuncName(char *name) { return makeList2(makeString("pg_catalog"), makeString(name)); } /* SystemTypeName() * Build a properly-qualified reference to a built-in type. * * typmod is defaulted, but may be changed afterwards by caller. */ TypeName * SystemTypeName(char *name) { TypeName *n = makeNode(TypeName); n->names = makeList2(makeString("pg_catalog"), makeString(name)); n->typmod = -1; return n; } /* parser_init() * Initialize to parse one query string */ void parser_init(void) { QueryIsRule = FALSE; } /* exprIsNullConstant() * Test whether an a_expr is a plain NULL constant or not. */ bool exprIsNullConstant(Node *arg) { if (arg && IsA(arg, A_Const)) { A_Const *con = (A_Const *) arg; if (con->val.type == T_Null && con->typename == NULL) return TRUE; } return FALSE; } /* doNegate() * Handle negation of a numeric constant. * * Formerly, we did this here because the optimizer couldn't cope with * indexquals that looked like "var = -4" --- it wants "var = const" * and a unary minus operator applied to a constant didn't qualify. * As of Postgres 7.0, that problem doesn't exist anymore because there * is a constant-subexpression simplifier in the optimizer. However, * there's still a good reason for doing this here, which is that we can * postpone committing to a particular internal representation for simple * negative constants. It's better to leave "-123.456" in string form * until we know what the desired type is. */ static Node * doNegate(Node *n) { if (IsA(n, A_Const)) { A_Const *con = (A_Const *)n; if (con->val.type == T_Integer) { con->val.val.ival = -con->val.val.ival; return n; } if (con->val.type == T_Float) { doNegateFloat(&con->val); return n; } } return (Node *) makeSimpleA_Expr(OP, "-", NULL, n); } static void doNegateFloat(Value *v) { char *oldval = v->val.str; Assert(IsA(v, Float)); if (*oldval == '+') oldval++; if (*oldval == '-') v->val.str = oldval+1; /* just strip the '-' */ else { char *newval = (char *) palloc(strlen(oldval) + 2); *newval = '-'; strcpy(newval+1, oldval); v->val.str = newval; } }