diff options
Diffstat (limited to '')
-rw-r--r-- | src/backend/parser/gram.y | 17264 |
1 files changed, 17264 insertions, 0 deletions
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y new file mode 100644 index 0000000..fe2af56 --- /dev/null +++ b/src/backend/parser/gram.y @@ -0,0 +1,17264 @@ +%{ + +/*#define YYDEBUG 1*/ +/*------------------------------------------------------------------------- + * + * gram.y + * POSTGRESQL BISON rules/actions + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/parser/gram.y + * + * 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. + * + * 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 constraint_exclusion 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 during parse analysis so that it happens at the + * right time not the wrong time. + * + * 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 <ctype.h> +#include <limits.h> + +#include "access/tableam.h" +#include "catalog/index.h" +#include "catalog/namespace.h" +#include "catalog/pg_am.h" +#include "catalog/pg_trigger.h" +#include "commands/defrem.h" +#include "commands/trigger.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "parser/gramparse.h" +#include "parser/parser.h" +#include "storage/lmgr.h" +#include "utils/date.h" +#include "utils/datetime.h" +#include "utils/numeric.h" +#include "utils/xml.h" + + +/* + * Location tracking support --- simpler than bison's default, since we only + * want to track the start position not the end position of each nonterminal. + */ +#define YYLLOC_DEFAULT(Current, Rhs, N) \ + do { \ + if ((N) > 0) \ + (Current) = (Rhs)[1]; \ + else \ + (Current) = (-1); \ + } while (0) + +/* + * The above macro assigns -1 (unknown) as the parse location of any + * nonterminal that was reduced from an empty rule, or whose leftmost + * component was reduced from an empty rule. This is problematic + * for nonterminals defined like + * OptFooList: / * EMPTY * / { ... } | OptFooList Foo { ... } ; + * because we'll set -1 as the location during the first reduction and then + * copy it during each subsequent reduction, leaving us with -1 for the + * location even when the list is not empty. To fix that, do this in the + * action for the nonempty rule(s): + * if (@$ < 0) @$ = @2; + * (Although we have many nonterminals that follow this pattern, we only + * bother with fixing @$ like this when the nonterminal's parse location + * is actually referenced in some rule.) + * + * A cleaner answer would be to make YYLLOC_DEFAULT scan all the Rhs + * locations until it's found one that's not -1. Then we'd get a correct + * location for any nonterminal that isn't entirely empty. But this way + * would add overhead to every rule reduction, and so far there's not been + * a compelling reason to pay that overhead. + */ + +/* + * Bison doesn't allocate anything that needs to live across parser calls, + * so we can easily have it use palloc instead of malloc. This prevents + * memory leaks if we error out during parsing. Note this only works with + * bison >= 2.0. However, in bison 1.875 the default is to use alloca() + * if possible, so there's not really much problem anyhow, at least if + * you're building with gcc. + */ +#define YYMALLOC palloc +#define YYFREE pfree + +/* Private struct for the result of privilege_target production */ +typedef struct PrivTarget +{ + GrantTargetType targtype; + ObjectType objtype; + List *objs; +} PrivTarget; + +/* Private struct for the result of import_qualification production */ +typedef struct ImportQual +{ + ImportForeignSchemaType type; + List *table_names; +} ImportQual; + +/* Private struct for the result of opt_select_limit production */ +typedef struct SelectLimit +{ + Node *limitOffset; + Node *limitCount; + LimitOption limitOption; +} SelectLimit; + +/* Private struct for the result of group_clause production */ +typedef struct GroupClause +{ + bool distinct; + List *list; +} GroupClause; + +/* ConstraintAttributeSpec yields an integer bitmask of these flags: */ +#define CAS_NOT_DEFERRABLE 0x01 +#define CAS_DEFERRABLE 0x02 +#define CAS_INITIALLY_IMMEDIATE 0x04 +#define CAS_INITIALLY_DEFERRED 0x08 +#define CAS_NOT_VALID 0x10 +#define CAS_NO_INHERIT 0x20 + + +#define parser_yyerror(msg) scanner_yyerror(msg, yyscanner) +#define parser_errposition(pos) scanner_errposition(pos, yyscanner) + +static void base_yyerror(YYLTYPE *yylloc, core_yyscan_t yyscanner, + const char *msg); +static RawStmt *makeRawStmt(Node *stmt, int stmt_location); +static void updateRawStmtEnd(RawStmt *rs, int end_location); +static Node *makeColumnRef(char *colname, List *indirection, + int location, core_yyscan_t yyscanner); +static Node *makeTypeCast(Node *arg, TypeName *typename, int location); +static Node *makeStringConst(char *str, int location); +static Node *makeStringConstCast(char *str, int location, TypeName *typename); +static Node *makeIntConst(int val, int location); +static Node *makeFloatConst(char *str, int location); +static Node *makeBitStringConst(char *str, int location); +static Node *makeNullAConst(int location); +static Node *makeAConst(Value *v, int location); +static Node *makeBoolAConst(bool state, int location); +static RoleSpec *makeRoleSpec(RoleSpecType type, int location); +static void check_qualified_name(List *names, core_yyscan_t yyscanner); +static List *check_func_name(List *names, core_yyscan_t yyscanner); +static List *check_indirection(List *indirection, core_yyscan_t yyscanner); +static List *extractArgTypes(List *parameters); +static List *extractAggrArgTypes(List *aggrargs); +static List *makeOrderedSetArgs(List *directargs, List *orderedargs, + core_yyscan_t yyscanner); +static void insertSelectOptions(SelectStmt *stmt, + List *sortClause, List *lockingClause, + SelectLimit *limitClause, + WithClause *withClause, + core_yyscan_t yyscanner); +static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg); +static Node *doNegate(Node *n, int location); +static void doNegateFloat(Value *v); +static Node *makeAndExpr(Node *lexpr, Node *rexpr, int location); +static Node *makeOrExpr(Node *lexpr, Node *rexpr, int location); +static Node *makeNotExpr(Node *expr, int location); +static Node *makeAArrayExpr(List *elements, int location); +static Node *makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod, + int location); +static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, + List *args, int location); +static List *mergeTableFuncParameters(List *func_args, List *columns); +static TypeName *TableFuncTypeName(List *columns); +static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner); +static void SplitColQualList(List *qualList, + List **constraintList, CollateClause **collClause, + core_yyscan_t yyscanner); +static void processCASbits(int cas_bits, int location, const char *constrType, + bool *deferrable, bool *initdeferred, bool *not_valid, + bool *no_inherit, core_yyscan_t yyscanner); +static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); + +%} + +%pure-parser +%expect 0 +%name-prefix="base_yy" +%locations + +%parse-param {core_yyscan_t yyscanner} +%lex-param {core_yyscan_t yyscanner} + +%union +{ + core_YYSTYPE core_yystype; + /* these fields must match core_YYSTYPE: */ + int ival; + char *str; + const char *keyword; + + char chr; + bool boolean; + JoinType jtype; + DropBehavior dbehavior; + OnCommitAction oncommit; + List *list; + Node *node; + Value *value; + ObjectType objtype; + TypeName *typnam; + FunctionParameter *fun_param; + FunctionParameterMode fun_param_mode; + ObjectWithArgs *objwithargs; + DefElem *defelt; + SortBy *sortby; + WindowDef *windef; + JoinExpr *jexpr; + IndexElem *ielem; + StatsElem *selem; + Alias *alias; + RangeVar *range; + IntoClause *into; + WithClause *with; + InferClause *infer; + OnConflictClause *onconflict; + A_Indices *aind; + ResTarget *target; + struct PrivTarget *privtarget; + AccessPriv *accesspriv; + struct ImportQual *importqual; + InsertStmt *istmt; + VariableSetStmt *vsetstmt; + PartitionElem *partelem; + PartitionSpec *partspec; + PartitionBoundSpec *partboundspec; + RoleSpec *rolespec; + struct SelectLimit *selectlimit; + SetQuantifier setquantifier; + struct GroupClause *groupclause; +} + +%type <node> stmt toplevel_stmt schema_stmt routine_body_stmt + AlterEventTrigStmt AlterCollationStmt + AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt + AlterFdwStmt AlterForeignServerStmt AlterGroupStmt + AlterObjectDependsStmt AlterObjectSchemaStmt AlterOwnerStmt + AlterOperatorStmt AlterTypeStmt AlterSeqStmt AlterSystemStmt AlterTableStmt + AlterTblSpcStmt AlterExtensionStmt AlterExtensionContentsStmt + AlterCompositeTypeStmt AlterUserMappingStmt + AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt AlterStatsStmt + AlterDefaultPrivilegesStmt DefACLAction + AnalyzeStmt CallStmt ClosePortalStmt ClusterStmt CommentStmt + ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt + CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt + CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt + CreateSchemaStmt CreateSeqStmt CreateStmt CreateStatsStmt CreateTableSpaceStmt + CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt + CreateAssertionStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt + CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt + CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt + DropOpClassStmt DropOpFamilyStmt DropStmt + DropCastStmt DropRoleStmt + DropdbStmt DropTableSpaceStmt + DropTransformStmt + DropUserMappingStmt ExplainStmt FetchStmt + GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt + ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt + CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt + RemoveFuncStmt RemoveOperStmt RenameStmt ReturnStmt RevokeStmt RevokeRoleStmt + RuleActionStmt RuleActionStmtOrEmpty RuleStmt + SecLabelStmt SelectStmt TransactionStmt TransactionStmtLegacy TruncateStmt + UnlistenStmt UpdateStmt VacuumStmt + VariableResetStmt VariableSetStmt VariableShowStmt + ViewStmt CheckPointStmt CreateConversionStmt + DeallocateStmt PrepareStmt ExecuteStmt + DropOwnedStmt ReassignOwnedStmt + AlterTSConfigurationStmt AlterTSDictionaryStmt + CreateMatViewStmt RefreshMatViewStmt CreateAmStmt + CreatePublicationStmt AlterPublicationStmt + CreateSubscriptionStmt AlterSubscriptionStmt DropSubscriptionStmt + +%type <node> select_no_parens select_with_parens select_clause + simple_select values_clause + PLpgSQL_Expr PLAssignStmt + +%type <node> alter_column_default opclass_item opclass_drop alter_using +%type <ival> add_drop opt_asc_desc opt_nulls_order + +%type <node> alter_table_cmd alter_type_cmd opt_collate_clause + replica_identity partition_cmd index_partition_cmd +%type <list> alter_table_cmds alter_type_cmds +%type <list> alter_identity_column_option_list +%type <defelt> alter_identity_column_option + +%type <dbehavior> opt_drop_behavior + +%type <list> createdb_opt_list createdb_opt_items copy_opt_list + transaction_mode_list + create_extension_opt_list alter_extension_opt_list +%type <defelt> createdb_opt_item copy_opt_item + transaction_mode_item + create_extension_opt_item alter_extension_opt_item + +%type <ival> opt_lock lock_type cast_context +%type <str> utility_option_name +%type <defelt> utility_option_elem +%type <list> utility_option_list +%type <node> utility_option_arg +%type <defelt> drop_option +%type <boolean> opt_or_replace opt_no + opt_grant_grant_option opt_grant_admin_option + opt_nowait opt_if_exists opt_with_data + opt_transaction_chain +%type <ival> opt_nowait_or_skip + +%type <list> OptRoleList AlterOptRoleList +%type <defelt> CreateOptRoleElem AlterOptRoleElem + +%type <str> opt_type +%type <str> foreign_server_version opt_foreign_server_version +%type <str> opt_in_database + +%type <str> OptSchemaName +%type <list> OptSchemaEltList + +%type <chr> am_type + +%type <boolean> TriggerForSpec TriggerForType +%type <ival> TriggerActionTime +%type <list> TriggerEvents TriggerOneEvent +%type <value> TriggerFuncArg +%type <node> TriggerWhen +%type <str> TransitionRelName +%type <boolean> TransitionRowOrTable TransitionOldOrNew +%type <node> TriggerTransition + +%type <list> event_trigger_when_list event_trigger_value_list +%type <defelt> event_trigger_when_item +%type <chr> enable_trigger + +%type <str> copy_file_name + access_method_clause attr_name + table_access_method_clause name cursor_name file_name + opt_index_name cluster_index_specification + +%type <list> func_name handler_name qual_Op qual_all_Op subquery_Op + opt_class opt_inline_handler opt_validator validator_clause + opt_collate + +%type <range> qualified_name insert_target OptConstrFromTable + +%type <str> all_Op MathOp + +%type <str> row_security_cmd RowSecurityDefaultForCmd +%type <boolean> RowSecurityDefaultPermissive +%type <node> RowSecurityOptionalWithCheck RowSecurityOptionalExpr +%type <list> RowSecurityDefaultToRole RowSecurityOptionalToRole + +%type <str> iso_level opt_encoding +%type <rolespec> grantee +%type <list> grantee_list +%type <accesspriv> privilege +%type <list> privileges privilege_list +%type <privtarget> privilege_target +%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes +%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list +%type <ival> defacl_privilege_target +%type <defelt> DefACLOption +%type <list> DefACLOptionList +%type <ival> import_qualification_type +%type <importqual> import_qualification +%type <node> vacuum_relation +%type <selectlimit> opt_select_limit select_limit limit_clause + +%type <list> parse_toplevel stmtmulti routine_body_stmt_list + OptTableElementList TableElementList OptInherit definition + OptTypedTableElementList TypedTableElementList + reloptions opt_reloptions + OptWith opt_definition func_args func_args_list + func_args_with_defaults func_args_with_defaults_list + aggr_args aggr_args_list + func_as createfunc_opt_list opt_createfunc_opt_list alterfunc_opt_list + old_aggr_definition old_aggr_list + oper_argtypes RuleActionList RuleActionMulti + opt_column_list columnList opt_name_list + sort_clause opt_sort_clause sortby_list index_params stats_params + opt_include opt_c_include index_including_params + name_list role_list from_clause from_list opt_array_bounds + qualified_name_list any_name any_name_list type_name_list + any_operator expr_list attrs + distinct_clause opt_distinct_clause + target_list opt_target_list insert_column_list set_target_list + set_clause_list set_clause + def_list operator_def_list indirection opt_indirection + reloption_list TriggerFuncArgs opclass_item_list opclass_drop_list + opclass_purpose opt_opfamily transaction_mode_list_or_empty + OptTableFuncElementList TableFuncElementList opt_type_modifiers + prep_type_clause + execute_param_clause using_clause returning_clause + opt_enum_val_list enum_val_list table_func_column_list + create_generic_options alter_generic_options + relation_expr_list dostmt_opt_list + transform_element_list transform_type_list + TriggerTransitions TriggerReferencing + vacuum_relation_list opt_vacuum_relation_list + drop_option_list + +%type <node> opt_routine_body +%type <groupclause> group_clause +%type <list> group_by_list +%type <node> group_by_item empty_grouping_set rollup_clause cube_clause +%type <node> grouping_sets_clause +%type <node> opt_publication_for_tables publication_for_tables + +%type <list> opt_fdw_options fdw_options +%type <defelt> fdw_option + +%type <range> OptTempTableName +%type <into> into_clause create_as_target create_mv_target + +%type <defelt> createfunc_opt_item common_func_opt_item dostmt_opt_item +%type <fun_param> func_arg func_arg_with_default table_func_column aggr_arg +%type <fun_param_mode> arg_class +%type <typnam> func_return func_type + +%type <boolean> opt_trusted opt_restart_seqs +%type <ival> OptTemp +%type <ival> OptNoLog +%type <oncommit> OnCommitOption + +%type <ival> for_locking_strength +%type <node> for_locking_item +%type <list> for_locking_clause opt_for_locking_clause for_locking_items +%type <list> locked_rels_list +%type <setquantifier> set_quantifier + +%type <node> join_qual +%type <jtype> join_type + +%type <list> extract_list overlay_list position_list +%type <list> substr_list trim_list +%type <list> opt_interval interval_second +%type <str> unicode_normal_form + +%type <boolean> opt_instead +%type <boolean> opt_unique opt_concurrently opt_verbose opt_full +%type <boolean> opt_freeze opt_analyze opt_default opt_recheck +%type <defelt> opt_binary copy_delimiter + +%type <boolean> copy_from opt_program + +%type <ival> event cursor_options opt_hold opt_set_data +%type <objtype> object_type_any_name object_type_name object_type_name_on_any_name + drop_type_name + +%type <node> fetch_args select_limit_value + offset_clause select_offset_value + select_fetch_first_value I_or_F_const +%type <ival> row_or_rows first_or_next + +%type <list> OptSeqOptList SeqOptList OptParenthesizedSeqOptList +%type <defelt> SeqOptElem + +%type <istmt> insert_rest +%type <infer> opt_conf_expr +%type <onconflict> opt_on_conflict + +%type <vsetstmt> generic_set set_rest set_rest_more generic_reset reset_rest + SetResetClause FunctionSetResetClause + +%type <node> TableElement TypedTableElement ConstraintElem TableFuncElement +%type <node> columnDef columnOptions +%type <defelt> def_elem reloption_elem old_aggr_elem operator_def_elem +%type <node> def_arg columnElem where_clause where_or_current_clause + a_expr b_expr c_expr AexprConst indirection_el opt_slice_bound + columnref in_expr having_clause func_table xmltable array_expr + OptWhereClause operator_def_arg +%type <list> rowsfrom_item rowsfrom_list opt_col_def_list +%type <boolean> opt_ordinality +%type <list> ExclusionConstraintList ExclusionConstraintElem +%type <list> func_arg_list func_arg_list_opt +%type <node> func_arg_expr +%type <list> row explicit_row implicit_row type_list array_expr_list +%type <node> case_expr case_arg when_clause case_default +%type <list> when_clause_list +%type <node> opt_search_clause opt_cycle_clause +%type <ival> sub_type opt_materialized +%type <value> NumericOnly +%type <list> NumericOnly_list +%type <alias> alias_clause opt_alias_clause opt_alias_clause_for_join_using +%type <list> func_alias_clause +%type <sortby> sortby +%type <ielem> index_elem index_elem_options +%type <selem> stats_param +%type <node> table_ref +%type <jexpr> joined_table +%type <range> relation_expr +%type <range> relation_expr_opt_alias +%type <node> tablesample_clause opt_repeatable_clause +%type <target> target_el set_target insert_column_item + +%type <str> generic_option_name +%type <node> generic_option_arg +%type <defelt> generic_option_elem alter_generic_option_elem +%type <list> generic_option_list alter_generic_option_list + +%type <ival> reindex_target_type reindex_target_multitable + +%type <node> copy_generic_opt_arg copy_generic_opt_arg_list_item +%type <defelt> copy_generic_opt_elem +%type <list> copy_generic_opt_list copy_generic_opt_arg_list +%type <list> copy_options + +%type <typnam> Typename SimpleTypename ConstTypename + GenericType Numeric opt_float + Character ConstCharacter + CharacterWithLength CharacterWithoutLength + ConstDatetime ConstInterval + Bit ConstBit BitWithLength BitWithoutLength +%type <str> character +%type <str> extract_arg +%type <boolean> opt_varying opt_timezone opt_no_inherit + +%type <ival> Iconst SignedIconst +%type <str> Sconst comment_text notify_payload +%type <str> RoleId opt_boolean_or_string +%type <list> var_list +%type <str> ColId ColLabel BareColLabel +%type <str> NonReservedWord NonReservedWord_or_Sconst +%type <str> var_name type_function_name param_name +%type <str> createdb_opt_name plassign_target +%type <node> var_value zone_value +%type <rolespec> auth_ident RoleSpec opt_granted_by + +%type <keyword> unreserved_keyword type_func_name_keyword +%type <keyword> col_name_keyword reserved_keyword +%type <keyword> bare_label_keyword + +%type <node> TableConstraint TableLikeClause +%type <ival> TableLikeOptionList TableLikeOption +%type <str> column_compression opt_column_compression +%type <list> ColQualList +%type <node> ColConstraint ColConstraintElem ConstraintAttr +%type <ival> key_actions key_delete key_match key_update key_action +%type <ival> ConstraintAttributeSpec ConstraintAttributeElem +%type <str> ExistingIndex + +%type <list> constraints_set_list +%type <boolean> constraints_set_mode +%type <str> OptTableSpace OptConsTableSpace +%type <rolespec> OptTableSpaceOwner +%type <ival> opt_check_option + +%type <str> opt_provider security_label + +%type <target> xml_attribute_el +%type <list> xml_attribute_list xml_attributes +%type <node> xml_root_version opt_xml_root_standalone +%type <node> xmlexists_argument +%type <ival> document_or_content +%type <boolean> xml_whitespace_option +%type <list> xmltable_column_list xmltable_column_option_list +%type <node> xmltable_column_el +%type <defelt> xmltable_column_option_el +%type <list> xml_namespace_list +%type <target> xml_namespace_el + +%type <node> func_application func_expr_common_subexpr +%type <node> func_expr func_expr_windowless +%type <node> common_table_expr +%type <with> with_clause opt_with_clause +%type <list> cte_list + +%type <list> within_group_clause +%type <node> filter_clause +%type <list> window_clause window_definition_list opt_partition_clause +%type <windef> window_definition over_clause window_specification + opt_frame_clause frame_extent frame_bound +%type <ival> opt_window_exclusion_clause +%type <str> opt_existing_window_name +%type <boolean> opt_if_not_exists +%type <ival> generated_when override_kind +%type <partspec> PartitionSpec OptPartitionSpec +%type <partelem> part_elem +%type <list> part_params +%type <partboundspec> PartitionBoundSpec +%type <list> hash_partbound +%type <defelt> hash_partbound_elem + + +/* + * Non-keyword token types. These are hard-wired into the "flex" lexer. + * They must be listed first so that their numeric codes do not depend on + * the set of keywords. PL/pgSQL depends on this so that it can share the + * same lexer. If you add/change tokens here, fix PL/pgSQL to match! + * + * UIDENT and USCONST are reduced to IDENT and SCONST in parser.c, so that + * they need no productions here; but we must assign token codes to them. + * + * DOT_DOT is unused in the core SQL grammar, and so will always provoke + * parse errors. It is needed by PL/pgSQL. + */ +%token <str> IDENT UIDENT FCONST SCONST USCONST BCONST XCONST Op +%token <ival> ICONST PARAM +%token TYPECAST DOT_DOT COLON_EQUALS EQUALS_GREATER +%token LESS_EQUALS GREATER_EQUALS NOT_EQUALS + +/* + * If you want to make any keyword changes, update the keyword table in + * src/include/parser/kwlist.h and add new keywords to the appropriate one + * of the reserved-or-not-so-reserved keyword lists, below; search + * this file for "Keyword category lists". + */ + +/* ordinary key words in alphabetical order */ +%token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER + AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC + ASENSITIVE ASSERTION ASSIGNMENT ASYMMETRIC ATOMIC AT ATTACH ATTRIBUTE AUTHORIZATION + + BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT + BOOLEAN_P BOTH BREADTH BY + + CACHE CALL CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P + CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE + CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT + COMMITTED COMPRESSION CONCURRENTLY CONFIGURATION CONFLICT + CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY + COST CREATE CROSS CSV CUBE CURRENT_P + CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA + CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE + + DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS + DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DEPENDS DEPTH DESC + DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P + DOUBLE_P DROP + + EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT + EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXPRESSION + EXTENSION EXTERNAL EXTRACT + + FALSE_P FAMILY FETCH FILTER FINALIZE FIRST_P FLOAT_P FOLLOWING FOR + FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS + + GENERATED GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING GROUPS + + HANDLER HAVING HEADER_P HOLD HOUR_P + + IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IMPORT_P IN_P INCLUDE + INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P + INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER + INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION + + JOIN + + KEY + + LABEL LANGUAGE LARGE_P LAST_P LATERAL_P + LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL + LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED + + MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE + + NAME_P NAMES NATIONAL NATURAL NCHAR NEW NEXT NFC NFD NFKC NFKD NO NONE + NORMALIZE NORMALIZED + NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF + NULLS_P NUMERIC + + OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OPTIONS OR + ORDER ORDINALITY OTHERS OUT_P OUTER_P + OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER + + PARALLEL PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POLICY + POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY + PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION + + QUOTE + + RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REFERENCING + REFRESH REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA + RESET RESTART RESTRICT RETURN RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP + ROUTINE ROUTINES ROW ROWS RULE + + SAVEPOINT SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES + SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW + SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P + START STATEMENT STATISTICS STDIN STDOUT STORAGE STORED STRICT_P STRIP_P + SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P + + TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN + TIES TIME TIMESTAMP TO TRAILING TRANSACTION TRANSFORM + TREAT TRIGGER TRIM TRUE_P + TRUNCATE TRUSTED TYPE_P TYPES_P + + UESCAPE UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN + UNLISTEN UNLOGGED UNTIL UPDATE USER USING + + VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING + VERBOSE VERSION_P VIEW VIEWS VOLATILE + + WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE + + XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLNAMESPACES + XMLPARSE XMLPI XMLROOT XMLSERIALIZE XMLTABLE + + YEAR_P YES_P + + ZONE + +/* + * The grammar thinks these are keywords, but they are not in the kwlist.h + * list and so can never be entered directly. The filter in parser.c + * creates these tokens when required (based on looking one token ahead). + * + * NOT_LA exists so that productions such as NOT LIKE can be given the same + * precedence as LIKE; otherwise they'd effectively have the same precedence + * as NOT, at least with respect to their left-hand subexpression. + * NULLS_LA and WITH_LA are needed to make the grammar LALR(1). + */ +%token NOT_LA NULLS_LA WITH_LA + +/* + * The grammar likewise thinks these tokens are keywords, but they are never + * generated by the scanner. Rather, they can be injected by parser.c as + * the initial token of the string (using the lookahead-token mechanism + * implemented there). This provides a way to tell the grammar to parse + * something other than the usual list of SQL commands. + */ +%token MODE_TYPE_NAME +%token MODE_PLPGSQL_EXPR +%token MODE_PLPGSQL_ASSIGN1 +%token MODE_PLPGSQL_ASSIGN2 +%token MODE_PLPGSQL_ASSIGN3 + + +/* Precedence: lowest to highest */ +%nonassoc SET /* see relation_expr_opt_alias */ +%left UNION EXCEPT +%left INTERSECT +%left OR +%left AND +%right NOT +%nonassoc IS ISNULL NOTNULL /* IS sets precedence for IS NULL, etc */ +%nonassoc '<' '>' '=' LESS_EQUALS GREATER_EQUALS NOT_EQUALS +%nonassoc BETWEEN IN_P LIKE ILIKE SIMILAR NOT_LA +%nonassoc ESCAPE /* ESCAPE must be just above LIKE/ILIKE/SIMILAR */ +/* + * To support target_el without AS, it used to be necessary to assign IDENT an + * explicit precedence just less than Op. While that's not really necessary + * since we removed postfix operators, it's still helpful to do so because + * there are some other unreserved keywords that need precedence assignments. + * If those keywords have the same precedence as IDENT then they clearly act + * the same as non-keywords, reducing the risk of unwanted precedence effects. + * + * We need to do this for PARTITION, RANGE, ROWS, and GROUPS to support + * opt_existing_window_name (see comment there). + * + * The frame_bound productions UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING + * are even messier: since UNBOUNDED is an unreserved keyword (per spec!), + * there is no principled way to distinguish these from the productions + * a_expr PRECEDING/FOLLOWING. We hack this up by giving UNBOUNDED slightly + * lower precedence than PRECEDING and FOLLOWING. At present this doesn't + * appear to cause UNBOUNDED to be treated differently from other unreserved + * keywords anywhere else in the grammar, but it's definitely risky. We can + * blame any funny behavior of UNBOUNDED on the SQL standard, though. + * + * To support CUBE and ROLLUP in GROUP BY without reserving them, we give them + * an explicit priority lower than '(', so that a rule with CUBE '(' will shift + * rather than reducing a conflicting rule that takes CUBE as a function name. + * Using the same precedence as IDENT seems right for the reasons given above. + */ +%nonassoc UNBOUNDED /* ideally would have same precedence as IDENT */ +%nonassoc IDENT PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP +%left Op OPERATOR /* multi-character ops and user-defined operators */ +%left '+' '-' +%left '*' '/' '%' +%left '^' +/* Unary Operators */ +%left AT /* sets precedence for AT TIME ZONE */ +%left COLLATE +%right UMINUS +%left '[' ']' +%left '(' ')' +%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 CROSS LEFT FULL RIGHT INNER_P NATURAL + +%% + +/* + * The target production for the whole parse. + * + * Ordinarily we parse a list of statements, but if we see one of the + * special MODE_XXX symbols as first token, we parse something else. + * The options here correspond to enum RawParseMode, which see for details. + */ +parse_toplevel: + stmtmulti + { + pg_yyget_extra(yyscanner)->parsetree = $1; + } + | MODE_TYPE_NAME Typename + { + pg_yyget_extra(yyscanner)->parsetree = list_make1($2); + } + | MODE_PLPGSQL_EXPR PLpgSQL_Expr + { + pg_yyget_extra(yyscanner)->parsetree = + list_make1(makeRawStmt($2, 0)); + } + | MODE_PLPGSQL_ASSIGN1 PLAssignStmt + { + PLAssignStmt *n = (PLAssignStmt *) $2; + n->nnames = 1; + pg_yyget_extra(yyscanner)->parsetree = + list_make1(makeRawStmt((Node *) n, 0)); + } + | MODE_PLPGSQL_ASSIGN2 PLAssignStmt + { + PLAssignStmt *n = (PLAssignStmt *) $2; + n->nnames = 2; + pg_yyget_extra(yyscanner)->parsetree = + list_make1(makeRawStmt((Node *) n, 0)); + } + | MODE_PLPGSQL_ASSIGN3 PLAssignStmt + { + PLAssignStmt *n = (PLAssignStmt *) $2; + n->nnames = 3; + pg_yyget_extra(yyscanner)->parsetree = + list_make1(makeRawStmt((Node *) n, 0)); + } + ; + +/* + * At top level, we wrap each stmt with a RawStmt node carrying start location + * and length of the stmt's text. Notice that the start loc/len are driven + * entirely from semicolon locations (@2). It would seem natural to use + * @1 or @3 to get the true start location of a stmt, but that doesn't work + * for statements that can start with empty nonterminals (opt_with_clause is + * the main offender here); as noted in the comments for YYLLOC_DEFAULT, + * we'd get -1 for the location in such cases. + * We also take care to discard empty statements entirely. + */ +stmtmulti: stmtmulti ';' toplevel_stmt + { + if ($1 != NIL) + { + /* update length of previous stmt */ + updateRawStmtEnd(llast_node(RawStmt, $1), @2); + } + if ($3 != NULL) + $$ = lappend($1, makeRawStmt($3, @2 + 1)); + else + $$ = $1; + } + | toplevel_stmt + { + if ($1 != NULL) + $$ = list_make1(makeRawStmt($1, 0)); + else + $$ = NIL; + } + ; + +/* + * toplevel_stmt includes BEGIN and END. stmt does not include them, because + * those words have different meanings in function bodys. + */ +toplevel_stmt: + stmt + | TransactionStmtLegacy + ; + +stmt: + AlterEventTrigStmt + | AlterCollationStmt + | AlterDatabaseStmt + | AlterDatabaseSetStmt + | AlterDefaultPrivilegesStmt + | AlterDomainStmt + | AlterEnumStmt + | AlterExtensionStmt + | AlterExtensionContentsStmt + | AlterFdwStmt + | AlterForeignServerStmt + | AlterFunctionStmt + | AlterGroupStmt + | AlterObjectDependsStmt + | AlterObjectSchemaStmt + | AlterOwnerStmt + | AlterOperatorStmt + | AlterTypeStmt + | AlterPolicyStmt + | AlterSeqStmt + | AlterSystemStmt + | AlterTableStmt + | AlterTblSpcStmt + | AlterCompositeTypeStmt + | AlterPublicationStmt + | AlterRoleSetStmt + | AlterRoleStmt + | AlterSubscriptionStmt + | AlterStatsStmt + | AlterTSConfigurationStmt + | AlterTSDictionaryStmt + | AlterUserMappingStmt + | AnalyzeStmt + | CallStmt + | CheckPointStmt + | ClosePortalStmt + | ClusterStmt + | CommentStmt + | ConstraintsSetStmt + | CopyStmt + | CreateAmStmt + | CreateAsStmt + | CreateAssertionStmt + | CreateCastStmt + | CreateConversionStmt + | CreateDomainStmt + | CreateExtensionStmt + | CreateFdwStmt + | CreateForeignServerStmt + | CreateForeignTableStmt + | CreateFunctionStmt + | CreateGroupStmt + | CreateMatViewStmt + | CreateOpClassStmt + | CreateOpFamilyStmt + | CreatePublicationStmt + | AlterOpFamilyStmt + | CreatePolicyStmt + | CreatePLangStmt + | CreateSchemaStmt + | CreateSeqStmt + | CreateStmt + | CreateSubscriptionStmt + | CreateStatsStmt + | CreateTableSpaceStmt + | CreateTransformStmt + | CreateTrigStmt + | CreateEventTrigStmt + | CreateRoleStmt + | CreateUserStmt + | CreateUserMappingStmt + | CreatedbStmt + | DeallocateStmt + | DeclareCursorStmt + | DefineStmt + | DeleteStmt + | DiscardStmt + | DoStmt + | DropCastStmt + | DropOpClassStmt + | DropOpFamilyStmt + | DropOwnedStmt + | DropStmt + | DropSubscriptionStmt + | DropTableSpaceStmt + | DropTransformStmt + | DropRoleStmt + | DropUserMappingStmt + | DropdbStmt + | ExecuteStmt + | ExplainStmt + | FetchStmt + | GrantStmt + | GrantRoleStmt + | ImportForeignSchemaStmt + | IndexStmt + | InsertStmt + | ListenStmt + | RefreshMatViewStmt + | LoadStmt + | LockStmt + | NotifyStmt + | PrepareStmt + | ReassignOwnedStmt + | ReindexStmt + | RemoveAggrStmt + | RemoveFuncStmt + | RemoveOperStmt + | RenameStmt + | RevokeStmt + | RevokeRoleStmt + | RuleStmt + | SecLabelStmt + | SelectStmt + | TransactionStmt + | TruncateStmt + | UnlistenStmt + | UpdateStmt + | VacuumStmt + | VariableResetStmt + | VariableSetStmt + | VariableShowStmt + | ViewStmt + | /*EMPTY*/ + { $$ = NULL; } + ; + +/***************************************************************************** + * + * CALL statement + * + *****************************************************************************/ + +CallStmt: CALL func_application + { + CallStmt *n = makeNode(CallStmt); + n->funccall = castNode(FuncCall, $2); + $$ = (Node *)n; + } + ; + +/***************************************************************************** + * + * Create a new Postgres DBMS role + * + *****************************************************************************/ + +CreateRoleStmt: + CREATE ROLE RoleId opt_with OptRoleList + { + CreateRoleStmt *n = makeNode(CreateRoleStmt); + n->stmt_type = ROLESTMT_ROLE; + n->role = $3; + n->options = $5; + $$ = (Node *)n; + } + ; + + +opt_with: WITH + | WITH_LA + | /*EMPTY*/ + ; + +/* + * Options for CREATE ROLE and ALTER ROLE (also used by CREATE/ALTER USER + * for backwards compatibility). Note: the only option required by SQL99 + * is "WITH ADMIN name". + */ +OptRoleList: + OptRoleList CreateOptRoleElem { $$ = lappend($1, $2); } + | /* EMPTY */ { $$ = NIL; } + ; + +AlterOptRoleList: + AlterOptRoleList AlterOptRoleElem { $$ = lappend($1, $2); } + | /* EMPTY */ { $$ = NIL; } + ; + +AlterOptRoleElem: + PASSWORD Sconst + { + $$ = makeDefElem("password", + (Node *)makeString($2), @1); + } + | PASSWORD NULL_P + { + $$ = makeDefElem("password", NULL, @1); + } + | ENCRYPTED PASSWORD Sconst + { + /* + * These days, passwords are always stored in encrypted + * form, so there is no difference between PASSWORD and + * ENCRYPTED PASSWORD. + */ + $$ = makeDefElem("password", + (Node *)makeString($3), @1); + } + | UNENCRYPTED PASSWORD Sconst + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("UNENCRYPTED PASSWORD is no longer supported"), + errhint("Remove UNENCRYPTED to store the password in encrypted form instead."), + parser_errposition(@1))); + } + | INHERIT + { + $$ = makeDefElem("inherit", (Node *)makeInteger(true), @1); + } + | CONNECTION LIMIT SignedIconst + { + $$ = makeDefElem("connectionlimit", (Node *)makeInteger($3), @1); + } + | VALID UNTIL Sconst + { + $$ = makeDefElem("validUntil", (Node *)makeString($3), @1); + } + /* Supported but not documented for roles, for use by ALTER GROUP. */ + | USER role_list + { + $$ = makeDefElem("rolemembers", (Node *)$2, @1); + } + | IDENT + { + /* + * We handle identifiers that aren't parser keywords with + * the following special-case codes, to avoid bloating the + * size of the main parser. + */ + if (strcmp($1, "superuser") == 0) + $$ = makeDefElem("superuser", (Node *)makeInteger(true), @1); + else if (strcmp($1, "nosuperuser") == 0) + $$ = makeDefElem("superuser", (Node *)makeInteger(false), @1); + else if (strcmp($1, "createrole") == 0) + $$ = makeDefElem("createrole", (Node *)makeInteger(true), @1); + else if (strcmp($1, "nocreaterole") == 0) + $$ = makeDefElem("createrole", (Node *)makeInteger(false), @1); + else if (strcmp($1, "replication") == 0) + $$ = makeDefElem("isreplication", (Node *)makeInteger(true), @1); + else if (strcmp($1, "noreplication") == 0) + $$ = makeDefElem("isreplication", (Node *)makeInteger(false), @1); + else if (strcmp($1, "createdb") == 0) + $$ = makeDefElem("createdb", (Node *)makeInteger(true), @1); + else if (strcmp($1, "nocreatedb") == 0) + $$ = makeDefElem("createdb", (Node *)makeInteger(false), @1); + else if (strcmp($1, "login") == 0) + $$ = makeDefElem("canlogin", (Node *)makeInteger(true), @1); + else if (strcmp($1, "nologin") == 0) + $$ = makeDefElem("canlogin", (Node *)makeInteger(false), @1); + else if (strcmp($1, "bypassrls") == 0) + $$ = makeDefElem("bypassrls", (Node *)makeInteger(true), @1); + else if (strcmp($1, "nobypassrls") == 0) + $$ = makeDefElem("bypassrls", (Node *)makeInteger(false), @1); + else if (strcmp($1, "noinherit") == 0) + { + /* + * Note that INHERIT is a keyword, so it's handled by main parser, but + * NOINHERIT is handled here. + */ + $$ = makeDefElem("inherit", (Node *)makeInteger(false), @1); + } + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unrecognized role option \"%s\"", $1), + parser_errposition(@1))); + } + ; + +CreateOptRoleElem: + AlterOptRoleElem { $$ = $1; } + /* The following are not supported by ALTER ROLE/USER/GROUP */ + | SYSID Iconst + { + $$ = makeDefElem("sysid", (Node *)makeInteger($2), @1); + } + | ADMIN role_list + { + $$ = makeDefElem("adminmembers", (Node *)$2, @1); + } + | ROLE role_list + { + $$ = makeDefElem("rolemembers", (Node *)$2, @1); + } + | IN_P ROLE role_list + { + $$ = makeDefElem("addroleto", (Node *)$3, @1); + } + | IN_P GROUP_P role_list + { + $$ = makeDefElem("addroleto", (Node *)$3, @1); + } + ; + + +/***************************************************************************** + * + * Create a new Postgres DBMS user (role with implied login ability) + * + *****************************************************************************/ + +CreateUserStmt: + CREATE USER RoleId opt_with OptRoleList + { + CreateRoleStmt *n = makeNode(CreateRoleStmt); + n->stmt_type = ROLESTMT_USER; + n->role = $3; + n->options = $5; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * + * Alter a postgresql DBMS role + * + *****************************************************************************/ + +AlterRoleStmt: + ALTER ROLE RoleSpec opt_with AlterOptRoleList + { + AlterRoleStmt *n = makeNode(AlterRoleStmt); + n->role = $3; + n->action = +1; /* add, if there are members */ + n->options = $5; + $$ = (Node *)n; + } + | ALTER USER RoleSpec opt_with AlterOptRoleList + { + AlterRoleStmt *n = makeNode(AlterRoleStmt); + n->role = $3; + n->action = +1; /* add, if there are members */ + n->options = $5; + $$ = (Node *)n; + } + ; + +opt_in_database: + /* EMPTY */ { $$ = NULL; } + | IN_P DATABASE name { $$ = $3; } + ; + +AlterRoleSetStmt: + ALTER ROLE RoleSpec opt_in_database SetResetClause + { + AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt); + n->role = $3; + n->database = $4; + n->setstmt = $5; + $$ = (Node *)n; + } + | ALTER ROLE ALL opt_in_database SetResetClause + { + AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt); + n->role = NULL; + n->database = $4; + n->setstmt = $5; + $$ = (Node *)n; + } + | ALTER USER RoleSpec opt_in_database SetResetClause + { + AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt); + n->role = $3; + n->database = $4; + n->setstmt = $5; + $$ = (Node *)n; + } + | ALTER USER ALL opt_in_database SetResetClause + { + AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt); + n->role = NULL; + n->database = $4; + n->setstmt = $5; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * + * Drop a postgresql DBMS role + * + * XXX Ideally this would have CASCADE/RESTRICT options, but a role + * might own objects in multiple databases, and there is presently no way to + * implement cascading to other databases. So we always behave as RESTRICT. + *****************************************************************************/ + +DropRoleStmt: + DROP ROLE role_list + { + DropRoleStmt *n = makeNode(DropRoleStmt); + n->missing_ok = false; + n->roles = $3; + $$ = (Node *)n; + } + | DROP ROLE IF_P EXISTS role_list + { + DropRoleStmt *n = makeNode(DropRoleStmt); + n->missing_ok = true; + n->roles = $5; + $$ = (Node *)n; + } + | DROP USER role_list + { + DropRoleStmt *n = makeNode(DropRoleStmt); + n->missing_ok = false; + n->roles = $3; + $$ = (Node *)n; + } + | DROP USER IF_P EXISTS role_list + { + DropRoleStmt *n = makeNode(DropRoleStmt); + n->roles = $5; + n->missing_ok = true; + $$ = (Node *)n; + } + | DROP GROUP_P role_list + { + DropRoleStmt *n = makeNode(DropRoleStmt); + n->missing_ok = false; + n->roles = $3; + $$ = (Node *)n; + } + | DROP GROUP_P IF_P EXISTS role_list + { + DropRoleStmt *n = makeNode(DropRoleStmt); + n->missing_ok = true; + n->roles = $5; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * + * Create a postgresql group (role without login ability) + * + *****************************************************************************/ + +CreateGroupStmt: + CREATE GROUP_P RoleId opt_with OptRoleList + { + CreateRoleStmt *n = makeNode(CreateRoleStmt); + n->stmt_type = ROLESTMT_GROUP; + n->role = $3; + n->options = $5; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * + * Alter a postgresql group + * + *****************************************************************************/ + +AlterGroupStmt: + ALTER GROUP_P RoleSpec add_drop USER role_list + { + AlterRoleStmt *n = makeNode(AlterRoleStmt); + n->role = $3; + n->action = $4; + n->options = list_make1(makeDefElem("rolemembers", + (Node *)$6, @6)); + $$ = (Node *)n; + } + ; + +add_drop: ADD_P { $$ = +1; } + | DROP { $$ = -1; } + ; + + +/***************************************************************************** + * + * Manipulate a schema + * + *****************************************************************************/ + +CreateSchemaStmt: + CREATE SCHEMA OptSchemaName AUTHORIZATION RoleSpec OptSchemaEltList + { + CreateSchemaStmt *n = makeNode(CreateSchemaStmt); + /* One can omit the schema name or the authorization id. */ + n->schemaname = $3; + n->authrole = $5; + n->schemaElts = $6; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE SCHEMA ColId OptSchemaEltList + { + CreateSchemaStmt *n = makeNode(CreateSchemaStmt); + /* ...but not both */ + n->schemaname = $3; + n->authrole = NULL; + n->schemaElts = $4; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE SCHEMA IF_P NOT EXISTS OptSchemaName AUTHORIZATION RoleSpec OptSchemaEltList + { + CreateSchemaStmt *n = makeNode(CreateSchemaStmt); + /* schema name can be omitted here, too */ + n->schemaname = $6; + n->authrole = $8; + if ($9 != NIL) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("CREATE SCHEMA IF NOT EXISTS cannot include schema elements"), + parser_errposition(@9))); + n->schemaElts = $9; + n->if_not_exists = true; + $$ = (Node *)n; + } + | CREATE SCHEMA IF_P NOT EXISTS ColId OptSchemaEltList + { + CreateSchemaStmt *n = makeNode(CreateSchemaStmt); + /* ...but not here */ + n->schemaname = $6; + n->authrole = NULL; + if ($7 != NIL) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("CREATE SCHEMA IF NOT EXISTS cannot include schema elements"), + parser_errposition(@7))); + n->schemaElts = $7; + n->if_not_exists = true; + $$ = (Node *)n; + } + ; + +OptSchemaName: + ColId { $$ = $1; } + | /* EMPTY */ { $$ = NULL; } + ; + +OptSchemaEltList: + OptSchemaEltList schema_stmt + { + if (@$ < 0) /* see comments for YYLLOC_DEFAULT */ + @$ = @2; + $$ = 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 + | IndexStmt + | CreateSeqStmt + | CreateTrigStmt + | GrantStmt + | ViewStmt + ; + + +/***************************************************************************** + * + * Set PG internal variable + * SET name TO 'var_value' + * Include SQL 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: + TRANSACTION transaction_mode_list + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_MULTI; + n->name = "TRANSACTION"; + n->args = $2; + $$ = n; + } + | SESSION CHARACTERISTICS AS TRANSACTION transaction_mode_list + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_MULTI; + n->name = "SESSION CHARACTERISTICS"; + n->args = $5; + $$ = n; + } + | set_rest_more + ; + +generic_set: + var_name TO var_list + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_VALUE; + n->name = $1; + n->args = $3; + $$ = n; + } + | var_name '=' var_list + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_VALUE; + n->name = $1; + n->args = $3; + $$ = n; + } + | var_name TO DEFAULT + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_DEFAULT; + n->name = $1; + $$ = n; + } + | var_name '=' DEFAULT + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_DEFAULT; + n->name = $1; + $$ = n; + } + ; + +set_rest_more: /* Generic SET syntaxes: */ + generic_set {$$ = $1;} + | var_name FROM CURRENT_P + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_CURRENT; + n->name = $1; + $$ = n; + } + /* Special syntaxes mandated by SQL standard: */ + | TIME ZONE zone_value + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_VALUE; + n->name = "timezone"; + if ($3 != NULL) + n->args = list_make1($3); + else + n->kind = VAR_SET_DEFAULT; + $$ = n; + } + | CATALOG_P Sconst + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("current database cannot be changed"), + parser_errposition(@2))); + $$ = NULL; /*not reached*/ + } + | SCHEMA Sconst + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_VALUE; + n->name = "search_path"; + n->args = list_make1(makeStringConst($2, @2)); + $$ = n; + } + | NAMES opt_encoding + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_VALUE; + n->name = "client_encoding"; + if ($2 != NULL) + n->args = list_make1(makeStringConst($2, @2)); + else + n->kind = VAR_SET_DEFAULT; + $$ = n; + } + | ROLE NonReservedWord_or_Sconst + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_VALUE; + n->name = "role"; + n->args = list_make1(makeStringConst($2, @2)); + $$ = n; + } + | SESSION AUTHORIZATION NonReservedWord_or_Sconst + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_VALUE; + n->name = "session_authorization"; + n->args = list_make1(makeStringConst($3, @3)); + $$ = n; + } + | SESSION AUTHORIZATION DEFAULT + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_DEFAULT; + n->name = "session_authorization"; + $$ = n; + } + | XML_P OPTION document_or_content + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_VALUE; + n->name = "xmloption"; + n->args = list_make1(makeStringConst($3 == XMLOPTION_DOCUMENT ? "DOCUMENT" : "CONTENT", @3)); + $$ = n; + } + /* Special syntaxes invented by PostgreSQL: */ + | TRANSACTION SNAPSHOT Sconst + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_MULTI; + n->name = "TRANSACTION SNAPSHOT"; + n->args = list_make1(makeStringConst($3, @3)); + $$ = n; + } + ; + +var_name: ColId { $$ = $1; } + | var_name '.' ColId + { $$ = psprintf("%s.%s", $1, $3); } + ; + +var_list: var_value { $$ = list_make1($1); } + | var_list ',' var_value { $$ = lappend($1, $3); } + ; + +var_value: opt_boolean_or_string + { $$ = makeStringConst($1, @1); } + | NumericOnly + { $$ = makeAConst($1, @1); } + ; + +iso_level: READ UNCOMMITTED { $$ = "read uncommitted"; } + | READ COMMITTED { $$ = "read committed"; } + | REPEATABLE READ { $$ = "repeatable read"; } + | SERIALIZABLE { $$ = "serializable"; } + ; + +opt_boolean_or_string: + TRUE_P { $$ = "true"; } + | FALSE_P { $$ = "false"; } + | ON { $$ = "on"; } + /* + * OFF is also accepted as a boolean value, but is handled by + * the NonReservedWord rule. The action for booleans and strings + * is the same, so we don't need to distinguish them here. + */ + | NonReservedWord_or_Sconst { $$ = $1; } + ; + +/* 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 (meaning we reject anything that is a key word). + */ +zone_value: + Sconst + { + $$ = makeStringConst($1, @1); + } + | IDENT + { + $$ = makeStringConst($1, @1); + } + | ConstInterval Sconst opt_interval + { + TypeName *t = $1; + if ($3 != NIL) + { + A_Const *n = (A_Const *) linitial($3); + if ((n->val.val.ival & ~(INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE))) != 0) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("time zone interval must be HOUR or HOUR TO MINUTE"), + parser_errposition(@3))); + } + t->typmods = $3; + $$ = makeStringConstCast($2, @2, t); + } + | ConstInterval '(' Iconst ')' Sconst + { + TypeName *t = $1; + t->typmods = list_make2(makeIntConst(INTERVAL_FULL_RANGE, -1), + makeIntConst($3, @3)); + $$ = makeStringConstCast($5, @5, t); + } + | NumericOnly { $$ = makeAConst($1, @1); } + | DEFAULT { $$ = NULL; } + | LOCAL { $$ = NULL; } + ; + +opt_encoding: + Sconst { $$ = $1; } + | DEFAULT { $$ = NULL; } + | /*EMPTY*/ { $$ = NULL; } + ; + +NonReservedWord_or_Sconst: + NonReservedWord { $$ = $1; } + | Sconst { $$ = $1; } + ; + +VariableResetStmt: + RESET reset_rest { $$ = (Node *) $2; } + ; + +reset_rest: + generic_reset { $$ = $1; } + | TIME ZONE + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_RESET; + n->name = "timezone"; + $$ = n; + } + | TRANSACTION ISOLATION LEVEL + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_RESET; + n->name = "transaction_isolation"; + $$ = n; + } + | SESSION AUTHORIZATION + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_RESET; + n->name = "session_authorization"; + $$ = n; + } + ; + +generic_reset: + var_name + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_RESET; + n->name = $1; + $$ = n; + } + | ALL + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_RESET_ALL; + $$ = n; + } + ; + +/* SetResetClause allows SET or RESET without LOCAL */ +SetResetClause: + SET set_rest { $$ = $2; } + | VariableResetStmt { $$ = (VariableSetStmt *) $1; } + ; + +/* SetResetClause allows SET or RESET without LOCAL */ +FunctionSetResetClause: + SET set_rest_more { $$ = $2; } + | VariableResetStmt { $$ = (VariableSetStmt *) $1; } + ; + + +VariableShowStmt: + SHOW var_name + { + 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"; + $$ = (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; + } + ; + + +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; } + | qualified_name_list { $$ = $1; } + ; + +constraints_set_mode: + DEFERRED { $$ = true; } + | IMMEDIATE { $$ = false; } + ; + + +/* + * Checkpoint statement + */ +CheckPointStmt: + CHECKPOINT + { + CheckPointStmt *n = makeNode(CheckPointStmt); + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * + * DISCARD { ALL | TEMP | PLANS | SEQUENCES } + * + *****************************************************************************/ + +DiscardStmt: + DISCARD ALL + { + DiscardStmt *n = makeNode(DiscardStmt); + n->target = DISCARD_ALL; + $$ = (Node *) n; + } + | DISCARD TEMP + { + DiscardStmt *n = makeNode(DiscardStmt); + n->target = DISCARD_TEMP; + $$ = (Node *) n; + } + | DISCARD TEMPORARY + { + DiscardStmt *n = makeNode(DiscardStmt); + n->target = DISCARD_TEMP; + $$ = (Node *) n; + } + | DISCARD PLANS + { + DiscardStmt *n = makeNode(DiscardStmt); + n->target = DISCARD_PLANS; + $$ = (Node *) n; + } + | DISCARD SEQUENCES + { + DiscardStmt *n = makeNode(DiscardStmt); + n->target = DISCARD_SEQUENCES; + $$ = (Node *) n; + } + + ; + + +/***************************************************************************** + * + * ALTER [ TABLE | INDEX | SEQUENCE | VIEW | MATERIALIZED VIEW | FOREIGN TABLE ] variations + * + * Note: we accept all subcommands for each of the variants, and sort + * out what's really legal at execution time. + *****************************************************************************/ + +AlterTableStmt: + ALTER TABLE relation_expr alter_table_cmds + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $3; + n->cmds = $4; + n->objtype = OBJECT_TABLE; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TABLE IF_P EXISTS relation_expr alter_table_cmds + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $5; + n->cmds = $6; + n->objtype = OBJECT_TABLE; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER TABLE relation_expr partition_cmd + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $3; + n->cmds = list_make1($4); + n->objtype = OBJECT_TABLE; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TABLE IF_P EXISTS relation_expr partition_cmd + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $5; + n->cmds = list_make1($6); + n->objtype = OBJECT_TABLE; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER TABLE ALL IN_P TABLESPACE name SET TABLESPACE name opt_nowait + { + AlterTableMoveAllStmt *n = + makeNode(AlterTableMoveAllStmt); + n->orig_tablespacename = $6; + n->objtype = OBJECT_TABLE; + n->roles = NIL; + n->new_tablespacename = $9; + n->nowait = $10; + $$ = (Node *)n; + } + | ALTER TABLE ALL IN_P TABLESPACE name OWNED BY role_list SET TABLESPACE name opt_nowait + { + AlterTableMoveAllStmt *n = + makeNode(AlterTableMoveAllStmt); + n->orig_tablespacename = $6; + n->objtype = OBJECT_TABLE; + n->roles = $9; + n->new_tablespacename = $12; + n->nowait = $13; + $$ = (Node *)n; + } + | ALTER INDEX qualified_name alter_table_cmds + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $3; + n->cmds = $4; + n->objtype = OBJECT_INDEX; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER INDEX IF_P EXISTS qualified_name alter_table_cmds + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $5; + n->cmds = $6; + n->objtype = OBJECT_INDEX; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER INDEX qualified_name index_partition_cmd + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $3; + n->cmds = list_make1($4); + n->objtype = OBJECT_INDEX; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER INDEX ALL IN_P TABLESPACE name SET TABLESPACE name opt_nowait + { + AlterTableMoveAllStmt *n = + makeNode(AlterTableMoveAllStmt); + n->orig_tablespacename = $6; + n->objtype = OBJECT_INDEX; + n->roles = NIL; + n->new_tablespacename = $9; + n->nowait = $10; + $$ = (Node *)n; + } + | ALTER INDEX ALL IN_P TABLESPACE name OWNED BY role_list SET TABLESPACE name opt_nowait + { + AlterTableMoveAllStmt *n = + makeNode(AlterTableMoveAllStmt); + n->orig_tablespacename = $6; + n->objtype = OBJECT_INDEX; + n->roles = $9; + n->new_tablespacename = $12; + n->nowait = $13; + $$ = (Node *)n; + } + | ALTER SEQUENCE qualified_name alter_table_cmds + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $3; + n->cmds = $4; + n->objtype = OBJECT_SEQUENCE; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER SEQUENCE IF_P EXISTS qualified_name alter_table_cmds + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $5; + n->cmds = $6; + n->objtype = OBJECT_SEQUENCE; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER VIEW qualified_name alter_table_cmds + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $3; + n->cmds = $4; + n->objtype = OBJECT_VIEW; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER VIEW IF_P EXISTS qualified_name alter_table_cmds + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $5; + n->cmds = $6; + n->objtype = OBJECT_VIEW; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER MATERIALIZED VIEW qualified_name alter_table_cmds + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $4; + n->cmds = $5; + n->objtype = OBJECT_MATVIEW; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name alter_table_cmds + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $6; + n->cmds = $7; + n->objtype = OBJECT_MATVIEW; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER MATERIALIZED VIEW ALL IN_P TABLESPACE name SET TABLESPACE name opt_nowait + { + AlterTableMoveAllStmt *n = + makeNode(AlterTableMoveAllStmt); + n->orig_tablespacename = $7; + n->objtype = OBJECT_MATVIEW; + n->roles = NIL; + n->new_tablespacename = $10; + n->nowait = $11; + $$ = (Node *)n; + } + | ALTER MATERIALIZED VIEW ALL IN_P TABLESPACE name OWNED BY role_list SET TABLESPACE name opt_nowait + { + AlterTableMoveAllStmt *n = + makeNode(AlterTableMoveAllStmt); + n->orig_tablespacename = $7; + n->objtype = OBJECT_MATVIEW; + n->roles = $10; + n->new_tablespacename = $13; + n->nowait = $14; + $$ = (Node *)n; + } + | ALTER FOREIGN TABLE relation_expr alter_table_cmds + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $4; + n->cmds = $5; + n->objtype = OBJECT_FOREIGN_TABLE; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER FOREIGN TABLE IF_P EXISTS relation_expr alter_table_cmds + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $6; + n->cmds = $7; + n->objtype = OBJECT_FOREIGN_TABLE; + n->missing_ok = true; + $$ = (Node *)n; + } + ; + +alter_table_cmds: + alter_table_cmd { $$ = list_make1($1); } + | alter_table_cmds ',' alter_table_cmd { $$ = lappend($1, $3); } + ; + +partition_cmd: + /* ALTER TABLE <name> ATTACH PARTITION <table_name> FOR VALUES */ + ATTACH PARTITION qualified_name PartitionBoundSpec + { + AlterTableCmd *n = makeNode(AlterTableCmd); + PartitionCmd *cmd = makeNode(PartitionCmd); + + n->subtype = AT_AttachPartition; + cmd->name = $3; + cmd->bound = $4; + cmd->concurrent = false; + n->def = (Node *) cmd; + + $$ = (Node *) n; + } + /* ALTER TABLE <name> DETACH PARTITION <partition_name> [CONCURRENTLY] */ + | DETACH PARTITION qualified_name opt_concurrently + { + AlterTableCmd *n = makeNode(AlterTableCmd); + PartitionCmd *cmd = makeNode(PartitionCmd); + + n->subtype = AT_DetachPartition; + cmd->name = $3; + cmd->bound = NULL; + cmd->concurrent = $4; + n->def = (Node *) cmd; + + $$ = (Node *) n; + } + | DETACH PARTITION qualified_name FINALIZE + { + AlterTableCmd *n = makeNode(AlterTableCmd); + PartitionCmd *cmd = makeNode(PartitionCmd); + + n->subtype = AT_DetachPartitionFinalize; + cmd->name = $3; + cmd->bound = NULL; + cmd->concurrent = false; + n->def = (Node *) cmd; + $$ = (Node *) n; + } + ; + +index_partition_cmd: + /* ALTER INDEX <name> ATTACH PARTITION <index_name> */ + ATTACH PARTITION qualified_name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + PartitionCmd *cmd = makeNode(PartitionCmd); + + n->subtype = AT_AttachPartition; + cmd->name = $3; + cmd->bound = NULL; + cmd->concurrent = false; + n->def = (Node *) cmd; + + $$ = (Node *) n; + } + ; + +alter_table_cmd: + /* ALTER TABLE <name> ADD <coldef> */ + ADD_P columnDef + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_AddColumn; + n->def = $2; + n->missing_ok = false; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ADD IF NOT EXISTS <coldef> */ + | ADD_P IF_P NOT EXISTS columnDef + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_AddColumn; + n->def = $5; + n->missing_ok = true; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ADD COLUMN <coldef> */ + | ADD_P COLUMN columnDef + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_AddColumn; + n->def = $3; + n->missing_ok = false; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ADD COLUMN IF NOT EXISTS <coldef> */ + | ADD_P COLUMN IF_P NOT EXISTS columnDef + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_AddColumn; + n->def = $6; + n->missing_ok = true; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> {SET DEFAULT <expr>|DROP DEFAULT} */ + | ALTER opt_column ColId alter_column_default + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_ColumnDefault; + n->name = $3; + n->def = $4; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> DROP NOT NULL */ + | ALTER opt_column ColId DROP NOT NULL_P + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropNotNull; + n->name = $3; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET NOT NULL */ + | ALTER opt_column ColId SET NOT NULL_P + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetNotNull; + n->name = $3; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> DROP EXPRESSION */ + | ALTER opt_column ColId DROP EXPRESSION + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropExpression; + n->name = $3; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> DROP EXPRESSION IF EXISTS */ + | ALTER opt_column ColId DROP EXPRESSION IF_P EXISTS + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropExpression; + n->name = $3; + n->missing_ok = true; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET STATISTICS <SignedIconst> */ + | ALTER opt_column ColId SET STATISTICS SignedIconst + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetStatistics; + n->name = $3; + n->def = (Node *) makeInteger($6); + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colnum> SET STATISTICS <SignedIconst> */ + | ALTER opt_column Iconst SET STATISTICS SignedIconst + { + AlterTableCmd *n = makeNode(AlterTableCmd); + + if ($3 <= 0 || $3 > PG_INT16_MAX) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("column number must be in range from 1 to %d", PG_INT16_MAX), + parser_errposition(@3))); + + n->subtype = AT_SetStatistics; + n->num = (int16) $3; + n->def = (Node *) makeInteger($6); + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET ( column_parameter = value [, ... ] ) */ + | ALTER opt_column ColId SET reloptions + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetOptions; + n->name = $3; + n->def = (Node *) $5; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> RESET ( column_parameter = value [, ... ] ) */ + | ALTER opt_column ColId RESET reloptions + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_ResetOptions; + n->name = $3; + n->def = (Node *) $5; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET STORAGE <storagemode> */ + | ALTER opt_column ColId SET STORAGE ColId + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetStorage; + n->name = $3; + n->def = (Node *) makeString($6); + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET COMPRESSION <cm> */ + | ALTER opt_column ColId SET column_compression + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetCompression; + n->name = $3; + n->def = (Node *) makeString($5); + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> ADD GENERATED ... AS IDENTITY ... */ + | ALTER opt_column ColId ADD_P GENERATED generated_when AS IDENTITY_P OptParenthesizedSeqOptList + { + AlterTableCmd *n = makeNode(AlterTableCmd); + Constraint *c = makeNode(Constraint); + + c->contype = CONSTR_IDENTITY; + c->generated_when = $6; + c->options = $9; + c->location = @5; + + n->subtype = AT_AddIdentity; + n->name = $3; + n->def = (Node *) c; + + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET <sequence options>/RESET */ + | ALTER opt_column ColId alter_identity_column_option_list + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetIdentity; + n->name = $3; + n->def = (Node *) $4; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> DROP IDENTITY */ + | ALTER opt_column ColId DROP IDENTITY_P + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropIdentity; + n->name = $3; + n->missing_ok = false; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> DROP IDENTITY IF EXISTS */ + | ALTER opt_column ColId DROP IDENTITY_P IF_P EXISTS + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropIdentity; + n->name = $3; + n->missing_ok = true; + $$ = (Node *)n; + } + /* ALTER TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */ + | DROP opt_column IF_P EXISTS ColId opt_drop_behavior + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropColumn; + n->name = $5; + n->behavior = $6; + n->missing_ok = true; + $$ = (Node *)n; + } + /* ALTER TABLE <name> DROP [COLUMN] <colname> [RESTRICT|CASCADE] */ + | DROP opt_column ColId opt_drop_behavior + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropColumn; + n->name = $3; + n->behavior = $4; + n->missing_ok = false; + $$ = (Node *)n; + } + /* + * ALTER TABLE <name> ALTER [COLUMN] <colname> [SET DATA] TYPE <typename> + * [ USING <expression> ] + */ + | ALTER opt_column ColId opt_set_data TYPE_P Typename opt_collate_clause alter_using + { + AlterTableCmd *n = makeNode(AlterTableCmd); + ColumnDef *def = makeNode(ColumnDef); + n->subtype = AT_AlterColumnType; + n->name = $3; + n->def = (Node *) def; + /* We only use these fields of the ColumnDef node */ + def->typeName = $6; + def->collClause = (CollateClause *) $7; + def->raw_default = $8; + def->location = @3; + $$ = (Node *)n; + } + /* ALTER FOREIGN TABLE <name> ALTER [COLUMN] <colname> OPTIONS */ + | ALTER opt_column ColId alter_generic_options + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_AlterColumnGenericOptions; + n->name = $3; + n->def = (Node *) $4; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ADD CONSTRAINT ... */ + | ADD_P TableConstraint + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_AddConstraint; + n->def = $2; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER CONSTRAINT ... */ + | ALTER CONSTRAINT name ConstraintAttributeSpec + { + AlterTableCmd *n = makeNode(AlterTableCmd); + Constraint *c = makeNode(Constraint); + n->subtype = AT_AlterConstraint; + n->def = (Node *) c; + c->contype = CONSTR_FOREIGN; /* others not supported, yet */ + c->conname = $3; + processCASbits($4, @4, "ALTER CONSTRAINT statement", + &c->deferrable, + &c->initdeferred, + NULL, NULL, yyscanner); + $$ = (Node *)n; + } + /* ALTER TABLE <name> VALIDATE CONSTRAINT ... */ + | VALIDATE CONSTRAINT name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_ValidateConstraint; + n->name = $3; + $$ = (Node *)n; + } + /* ALTER TABLE <name> DROP CONSTRAINT IF EXISTS <name> [RESTRICT|CASCADE] */ + | DROP CONSTRAINT IF_P EXISTS name opt_drop_behavior + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropConstraint; + n->name = $5; + n->behavior = $6; + n->missing_ok = true; + $$ = (Node *)n; + } + /* ALTER TABLE <name> DROP CONSTRAINT <name> [RESTRICT|CASCADE] */ + | DROP CONSTRAINT name opt_drop_behavior + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropConstraint; + n->name = $3; + n->behavior = $4; + n->missing_ok = false; + $$ = (Node *)n; + } + /* ALTER TABLE <name> SET WITHOUT OIDS, for backward compat */ + | SET WITHOUT OIDS + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropOids; + $$ = (Node *)n; + } + /* ALTER TABLE <name> CLUSTER ON <indexname> */ + | CLUSTER ON name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_ClusterOn; + n->name = $3; + $$ = (Node *)n; + } + /* ALTER TABLE <name> SET WITHOUT CLUSTER */ + | SET WITHOUT CLUSTER + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropCluster; + n->name = NULL; + $$ = (Node *)n; + } + /* ALTER TABLE <name> SET LOGGED */ + | SET LOGGED + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetLogged; + $$ = (Node *)n; + } + /* ALTER TABLE <name> SET UNLOGGED */ + | SET UNLOGGED + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetUnLogged; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ENABLE TRIGGER <trig> */ + | ENABLE_P TRIGGER name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_EnableTrig; + n->name = $3; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ENABLE ALWAYS TRIGGER <trig> */ + | ENABLE_P ALWAYS TRIGGER name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_EnableAlwaysTrig; + n->name = $4; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ENABLE REPLICA TRIGGER <trig> */ + | ENABLE_P REPLICA TRIGGER name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_EnableReplicaTrig; + n->name = $4; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ENABLE TRIGGER ALL */ + | ENABLE_P TRIGGER ALL + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_EnableTrigAll; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ENABLE TRIGGER USER */ + | ENABLE_P TRIGGER USER + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_EnableTrigUser; + $$ = (Node *)n; + } + /* ALTER TABLE <name> DISABLE TRIGGER <trig> */ + | DISABLE_P TRIGGER name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DisableTrig; + n->name = $3; + $$ = (Node *)n; + } + /* ALTER TABLE <name> DISABLE TRIGGER ALL */ + | DISABLE_P TRIGGER ALL + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DisableTrigAll; + $$ = (Node *)n; + } + /* ALTER TABLE <name> DISABLE TRIGGER USER */ + | DISABLE_P TRIGGER USER + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DisableTrigUser; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ENABLE RULE <rule> */ + | ENABLE_P RULE name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_EnableRule; + n->name = $3; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ENABLE ALWAYS RULE <rule> */ + | ENABLE_P ALWAYS RULE name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_EnableAlwaysRule; + n->name = $4; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ENABLE REPLICA RULE <rule> */ + | ENABLE_P REPLICA RULE name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_EnableReplicaRule; + n->name = $4; + $$ = (Node *)n; + } + /* ALTER TABLE <name> DISABLE RULE <rule> */ + | DISABLE_P RULE name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DisableRule; + n->name = $3; + $$ = (Node *)n; + } + /* ALTER TABLE <name> INHERIT <parent> */ + | INHERIT qualified_name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_AddInherit; + n->def = (Node *) $2; + $$ = (Node *)n; + } + /* ALTER TABLE <name> NO INHERIT <parent> */ + | NO INHERIT qualified_name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropInherit; + n->def = (Node *) $3; + $$ = (Node *)n; + } + /* ALTER TABLE <name> OF <type_name> */ + | OF any_name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + TypeName *def = makeTypeNameFromNameList($2); + def->location = @2; + n->subtype = AT_AddOf; + n->def = (Node *) def; + $$ = (Node *)n; + } + /* ALTER TABLE <name> NOT OF */ + | NOT OF + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropOf; + $$ = (Node *)n; + } + /* ALTER TABLE <name> OWNER TO RoleSpec */ + | OWNER TO RoleSpec + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_ChangeOwner; + n->newowner = $3; + $$ = (Node *)n; + } + /* ALTER TABLE <name> SET TABLESPACE <tablespacename> */ + | SET TABLESPACE name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetTableSpace; + n->name = $3; + $$ = (Node *)n; + } + /* ALTER TABLE <name> SET (...) */ + | SET reloptions + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetRelOptions; + n->def = (Node *)$2; + $$ = (Node *)n; + } + /* ALTER TABLE <name> RESET (...) */ + | RESET reloptions + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_ResetRelOptions; + n->def = (Node *)$2; + $$ = (Node *)n; + } + /* ALTER TABLE <name> REPLICA IDENTITY */ + | REPLICA IDENTITY_P replica_identity + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_ReplicaIdentity; + n->def = $3; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ENABLE ROW LEVEL SECURITY */ + | ENABLE_P ROW LEVEL SECURITY + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_EnableRowSecurity; + $$ = (Node *)n; + } + /* ALTER TABLE <name> DISABLE ROW LEVEL SECURITY */ + | DISABLE_P ROW LEVEL SECURITY + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DisableRowSecurity; + $$ = (Node *)n; + } + /* ALTER TABLE <name> FORCE ROW LEVEL SECURITY */ + | FORCE ROW LEVEL SECURITY + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_ForceRowSecurity; + $$ = (Node *)n; + } + /* ALTER TABLE <name> NO FORCE ROW LEVEL SECURITY */ + | NO FORCE ROW LEVEL SECURITY + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_NoForceRowSecurity; + $$ = (Node *)n; + } + | alter_generic_options + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_GenericOptions; + n->def = (Node *)$1; + $$ = (Node *) n; + } + ; + +alter_column_default: + SET DEFAULT a_expr { $$ = $3; } + | DROP DEFAULT { $$ = NULL; } + ; + +opt_drop_behavior: + CASCADE { $$ = DROP_CASCADE; } + | RESTRICT { $$ = DROP_RESTRICT; } + | /* EMPTY */ { $$ = DROP_RESTRICT; /* default */ } + ; + +opt_collate_clause: + COLLATE any_name + { + CollateClause *n = makeNode(CollateClause); + n->arg = NULL; + n->collname = $2; + n->location = @1; + $$ = (Node *) n; + } + | /* EMPTY */ { $$ = NULL; } + ; + +alter_using: + USING a_expr { $$ = $2; } + | /* EMPTY */ { $$ = NULL; } + ; + +replica_identity: + NOTHING + { + ReplicaIdentityStmt *n = makeNode(ReplicaIdentityStmt); + n->identity_type = REPLICA_IDENTITY_NOTHING; + n->name = NULL; + $$ = (Node *) n; + } + | FULL + { + ReplicaIdentityStmt *n = makeNode(ReplicaIdentityStmt); + n->identity_type = REPLICA_IDENTITY_FULL; + n->name = NULL; + $$ = (Node *) n; + } + | DEFAULT + { + ReplicaIdentityStmt *n = makeNode(ReplicaIdentityStmt); + n->identity_type = REPLICA_IDENTITY_DEFAULT; + n->name = NULL; + $$ = (Node *) n; + } + | USING INDEX name + { + ReplicaIdentityStmt *n = makeNode(ReplicaIdentityStmt); + n->identity_type = REPLICA_IDENTITY_INDEX; + n->name = $3; + $$ = (Node *) n; + } +; + +reloptions: + '(' reloption_list ')' { $$ = $2; } + ; + +opt_reloptions: WITH reloptions { $$ = $2; } + | /* EMPTY */ { $$ = NIL; } + ; + +reloption_list: + reloption_elem { $$ = list_make1($1); } + | reloption_list ',' reloption_elem { $$ = lappend($1, $3); } + ; + +/* This should match def_elem and also allow qualified names */ +reloption_elem: + ColLabel '=' def_arg + { + $$ = makeDefElem($1, (Node *) $3, @1); + } + | ColLabel + { + $$ = makeDefElem($1, NULL, @1); + } + | ColLabel '.' ColLabel '=' def_arg + { + $$ = makeDefElemExtended($1, $3, (Node *) $5, + DEFELEM_UNSPEC, @1); + } + | ColLabel '.' ColLabel + { + $$ = makeDefElemExtended($1, $3, NULL, DEFELEM_UNSPEC, @1); + } + ; + +alter_identity_column_option_list: + alter_identity_column_option + { $$ = list_make1($1); } + | alter_identity_column_option_list alter_identity_column_option + { $$ = lappend($1, $2); } + ; + +alter_identity_column_option: + RESTART + { + $$ = makeDefElem("restart", NULL, @1); + } + | RESTART opt_with NumericOnly + { + $$ = makeDefElem("restart", (Node *)$3, @1); + } + | SET SeqOptElem + { + if (strcmp($2->defname, "as") == 0 || + strcmp($2->defname, "restart") == 0 || + strcmp($2->defname, "owned_by") == 0) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("sequence option \"%s\" not supported here", $2->defname), + parser_errposition(@2))); + $$ = $2; + } + | SET GENERATED generated_when + { + $$ = makeDefElem("generated", (Node *) makeInteger($3), @1); + } + ; + +PartitionBoundSpec: + /* a HASH partition */ + FOR VALUES WITH '(' hash_partbound ')' + { + ListCell *lc; + PartitionBoundSpec *n = makeNode(PartitionBoundSpec); + + n->strategy = PARTITION_STRATEGY_HASH; + n->modulus = n->remainder = -1; + + foreach (lc, $5) + { + DefElem *opt = lfirst_node(DefElem, lc); + + if (strcmp(opt->defname, "modulus") == 0) + { + if (n->modulus != -1) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("modulus for hash partition provided more than once"), + parser_errposition(opt->location))); + n->modulus = defGetInt32(opt); + } + else if (strcmp(opt->defname, "remainder") == 0) + { + if (n->remainder != -1) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("remainder for hash partition provided more than once"), + parser_errposition(opt->location))); + n->remainder = defGetInt32(opt); + } + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unrecognized hash partition bound specification \"%s\"", + opt->defname), + parser_errposition(opt->location))); + } + + if (n->modulus == -1) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("modulus for hash partition must be specified"))); + if (n->remainder == -1) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("remainder for hash partition must be specified"))); + + n->location = @3; + + $$ = n; + } + + /* a LIST partition */ + | FOR VALUES IN_P '(' expr_list ')' + { + PartitionBoundSpec *n = makeNode(PartitionBoundSpec); + + n->strategy = PARTITION_STRATEGY_LIST; + n->is_default = false; + n->listdatums = $5; + n->location = @3; + + $$ = n; + } + + /* a RANGE partition */ + | FOR VALUES FROM '(' expr_list ')' TO '(' expr_list ')' + { + PartitionBoundSpec *n = makeNode(PartitionBoundSpec); + + n->strategy = PARTITION_STRATEGY_RANGE; + n->is_default = false; + n->lowerdatums = $5; + n->upperdatums = $9; + n->location = @3; + + $$ = n; + } + + /* a DEFAULT partition */ + | DEFAULT + { + PartitionBoundSpec *n = makeNode(PartitionBoundSpec); + + n->is_default = true; + n->location = @1; + + $$ = n; + } + ; + +hash_partbound_elem: + NonReservedWord Iconst + { + $$ = makeDefElem($1, (Node *)makeInteger($2), @1); + } + ; + +hash_partbound: + hash_partbound_elem + { + $$ = list_make1($1); + } + | hash_partbound ',' hash_partbound_elem + { + $$ = lappend($1, $3); + } + ; + +/***************************************************************************** + * + * ALTER TYPE + * + * really variants of the ALTER TABLE subcommands with different spellings + *****************************************************************************/ + +AlterCompositeTypeStmt: + ALTER TYPE_P any_name alter_type_cmds + { + AlterTableStmt *n = makeNode(AlterTableStmt); + + /* can't use qualified_name, sigh */ + n->relation = makeRangeVarFromAnyName($3, @3, yyscanner); + n->cmds = $4; + n->objtype = OBJECT_TYPE; + $$ = (Node *)n; + } + ; + +alter_type_cmds: + alter_type_cmd { $$ = list_make1($1); } + | alter_type_cmds ',' alter_type_cmd { $$ = lappend($1, $3); } + ; + +alter_type_cmd: + /* ALTER TYPE <name> ADD ATTRIBUTE <coldef> [RESTRICT|CASCADE] */ + ADD_P ATTRIBUTE TableFuncElement opt_drop_behavior + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_AddColumn; + n->def = $3; + n->behavior = $4; + $$ = (Node *)n; + } + /* ALTER TYPE <name> DROP ATTRIBUTE IF EXISTS <attname> [RESTRICT|CASCADE] */ + | DROP ATTRIBUTE IF_P EXISTS ColId opt_drop_behavior + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropColumn; + n->name = $5; + n->behavior = $6; + n->missing_ok = true; + $$ = (Node *)n; + } + /* ALTER TYPE <name> DROP ATTRIBUTE <attname> [RESTRICT|CASCADE] */ + | DROP ATTRIBUTE ColId opt_drop_behavior + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropColumn; + n->name = $3; + n->behavior = $4; + n->missing_ok = false; + $$ = (Node *)n; + } + /* ALTER TYPE <name> ALTER ATTRIBUTE <attname> [SET DATA] TYPE <typename> [RESTRICT|CASCADE] */ + | ALTER ATTRIBUTE ColId opt_set_data TYPE_P Typename opt_collate_clause opt_drop_behavior + { + AlterTableCmd *n = makeNode(AlterTableCmd); + ColumnDef *def = makeNode(ColumnDef); + n->subtype = AT_AlterColumnType; + n->name = $3; + n->def = (Node *) def; + n->behavior = $8; + /* We only use these fields of the ColumnDef node */ + def->typeName = $6; + def->collClause = (CollateClause *) $7; + def->raw_default = NULL; + def->location = @3; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * + * QUERY : + * close <portalname> + * + *****************************************************************************/ + +ClosePortalStmt: + CLOSE cursor_name + { + ClosePortalStmt *n = makeNode(ClosePortalStmt); + n->portalname = $2; + $$ = (Node *)n; + } + | CLOSE ALL + { + ClosePortalStmt *n = makeNode(ClosePortalStmt); + n->portalname = NULL; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * + * QUERY : + * COPY relname [(columnList)] FROM/TO file [WITH] [(options)] + * COPY ( query ) TO file [WITH] [(options)] + * + * where 'query' can be one of: + * { SELECT | UPDATE | INSERT | DELETE } + * + * and 'file' can be one of: + * { PROGRAM 'command' | STDIN | STDOUT | 'filename' } + * + * In the preferred syntax the options are comma-separated + * and use generic identifiers instead of keywords. The pre-9.0 + * syntax had a hard-wired, space-separated set of options. + * + * Really old syntax, from versions 7.2 and prior: + * COPY [ BINARY ] table FROM/TO file + * [ [ USING ] DELIMITERS 'delimiter' ] ] + * [ WITH NULL AS 'null string' ] + * This option placement is not supported with COPY (query...). + * + *****************************************************************************/ + +CopyStmt: COPY opt_binary qualified_name opt_column_list + copy_from opt_program copy_file_name copy_delimiter opt_with + copy_options where_clause + { + CopyStmt *n = makeNode(CopyStmt); + n->relation = $3; + n->query = NULL; + n->attlist = $4; + n->is_from = $5; + n->is_program = $6; + n->filename = $7; + n->whereClause = $11; + + if (n->is_program && n->filename == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("STDIN/STDOUT not allowed with PROGRAM"), + parser_errposition(@8))); + + if (!n->is_from && n->whereClause != NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("WHERE clause not allowed with COPY TO"), + parser_errposition(@11))); + + n->options = NIL; + /* Concatenate user-supplied flags */ + if ($2) + n->options = lappend(n->options, $2); + if ($8) + n->options = lappend(n->options, $8); + if ($10) + n->options = list_concat(n->options, $10); + $$ = (Node *)n; + } + | COPY '(' PreparableStmt ')' TO opt_program copy_file_name opt_with copy_options + { + CopyStmt *n = makeNode(CopyStmt); + n->relation = NULL; + n->query = $3; + n->attlist = NIL; + n->is_from = false; + n->is_program = $6; + n->filename = $7; + n->options = $9; + + if (n->is_program && n->filename == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("STDIN/STDOUT not allowed with PROGRAM"), + parser_errposition(@5))); + + $$ = (Node *)n; + } + ; + +copy_from: + FROM { $$ = true; } + | TO { $$ = false; } + ; + +opt_program: + PROGRAM { $$ = true; } + | /* EMPTY */ { $$ = 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_options: copy_opt_list { $$ = $1; } + | '(' copy_generic_opt_list ')' { $$ = $2; } + ; + +/* old COPY option syntax */ +copy_opt_list: + copy_opt_list copy_opt_item { $$ = lappend($1, $2); } + | /* EMPTY */ { $$ = NIL; } + ; + +copy_opt_item: + BINARY + { + $$ = makeDefElem("format", (Node *)makeString("binary"), @1); + } + | FREEZE + { + $$ = makeDefElem("freeze", (Node *)makeInteger(true), @1); + } + | DELIMITER opt_as Sconst + { + $$ = makeDefElem("delimiter", (Node *)makeString($3), @1); + } + | NULL_P opt_as Sconst + { + $$ = makeDefElem("null", (Node *)makeString($3), @1); + } + | CSV + { + $$ = makeDefElem("format", (Node *)makeString("csv"), @1); + } + | HEADER_P + { + $$ = makeDefElem("header", (Node *)makeInteger(true), @1); + } + | QUOTE opt_as Sconst + { + $$ = makeDefElem("quote", (Node *)makeString($3), @1); + } + | ESCAPE opt_as Sconst + { + $$ = makeDefElem("escape", (Node *)makeString($3), @1); + } + | FORCE QUOTE columnList + { + $$ = makeDefElem("force_quote", (Node *)$3, @1); + } + | FORCE QUOTE '*' + { + $$ = makeDefElem("force_quote", (Node *)makeNode(A_Star), @1); + } + | FORCE NOT NULL_P columnList + { + $$ = makeDefElem("force_not_null", (Node *)$4, @1); + } + | FORCE NULL_P columnList + { + $$ = makeDefElem("force_null", (Node *)$3, @1); + } + | ENCODING Sconst + { + $$ = makeDefElem("encoding", (Node *)makeString($2), @1); + } + ; + +/* The following exist for backward compatibility with very old versions */ + +opt_binary: + BINARY + { + $$ = makeDefElem("format", (Node *)makeString("binary"), @1); + } + | /*EMPTY*/ { $$ = NULL; } + ; + +copy_delimiter: + opt_using DELIMITERS Sconst + { + $$ = makeDefElem("delimiter", (Node *)makeString($3), @2); + } + | /*EMPTY*/ { $$ = NULL; } + ; + +opt_using: + USING + | /*EMPTY*/ + ; + +/* new COPY option syntax */ +copy_generic_opt_list: + copy_generic_opt_elem + { + $$ = list_make1($1); + } + | copy_generic_opt_list ',' copy_generic_opt_elem + { + $$ = lappend($1, $3); + } + ; + +copy_generic_opt_elem: + ColLabel copy_generic_opt_arg + { + $$ = makeDefElem($1, $2, @1); + } + ; + +copy_generic_opt_arg: + opt_boolean_or_string { $$ = (Node *) makeString($1); } + | NumericOnly { $$ = (Node *) $1; } + | '*' { $$ = (Node *) makeNode(A_Star); } + | '(' copy_generic_opt_arg_list ')' { $$ = (Node *) $2; } + | /* EMPTY */ { $$ = NULL; } + ; + +copy_generic_opt_arg_list: + copy_generic_opt_arg_list_item + { + $$ = list_make1($1); + } + | copy_generic_opt_arg_list ',' copy_generic_opt_arg_list_item + { + $$ = lappend($1, $3); + } + ; + +/* beware of emitting non-string list elements here; see commands/define.c */ +copy_generic_opt_arg_list_item: + opt_boolean_or_string { $$ = (Node *) makeString($1); } + ; + + +/***************************************************************************** + * + * QUERY : + * CREATE TABLE relname + * + *****************************************************************************/ + +CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' + OptInherit OptPartitionSpec table_access_method_clause OptWith + OnCommitOption OptTableSpace + { + CreateStmt *n = makeNode(CreateStmt); + $4->relpersistence = $2; + n->relation = $4; + n->tableElts = $6; + n->inhRelations = $8; + n->partspec = $9; + n->ofTypename = NULL; + n->constraints = NIL; + n->accessMethod = $10; + n->options = $11; + n->oncommit = $12; + n->tablespacename = $13; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name '(' + OptTableElementList ')' OptInherit OptPartitionSpec table_access_method_clause + OptWith OnCommitOption OptTableSpace + { + CreateStmt *n = makeNode(CreateStmt); + $7->relpersistence = $2; + n->relation = $7; + n->tableElts = $9; + n->inhRelations = $11; + n->partspec = $12; + n->ofTypename = NULL; + n->constraints = NIL; + n->accessMethod = $13; + n->options = $14; + n->oncommit = $15; + n->tablespacename = $16; + n->if_not_exists = true; + $$ = (Node *)n; + } + | CREATE OptTemp TABLE qualified_name OF any_name + OptTypedTableElementList OptPartitionSpec table_access_method_clause + OptWith OnCommitOption OptTableSpace + { + CreateStmt *n = makeNode(CreateStmt); + $4->relpersistence = $2; + n->relation = $4; + n->tableElts = $7; + n->inhRelations = NIL; + n->partspec = $8; + n->ofTypename = makeTypeNameFromNameList($6); + n->ofTypename->location = @6; + n->constraints = NIL; + n->accessMethod = $9; + n->options = $10; + n->oncommit = $11; + n->tablespacename = $12; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name OF any_name + OptTypedTableElementList OptPartitionSpec table_access_method_clause + OptWith OnCommitOption OptTableSpace + { + CreateStmt *n = makeNode(CreateStmt); + $7->relpersistence = $2; + n->relation = $7; + n->tableElts = $10; + n->inhRelations = NIL; + n->partspec = $11; + n->ofTypename = makeTypeNameFromNameList($9); + n->ofTypename->location = @9; + n->constraints = NIL; + n->accessMethod = $12; + n->options = $13; + n->oncommit = $14; + n->tablespacename = $15; + n->if_not_exists = true; + $$ = (Node *)n; + } + | CREATE OptTemp TABLE qualified_name PARTITION OF qualified_name + OptTypedTableElementList PartitionBoundSpec OptPartitionSpec + table_access_method_clause OptWith OnCommitOption OptTableSpace + { + CreateStmt *n = makeNode(CreateStmt); + $4->relpersistence = $2; + n->relation = $4; + n->tableElts = $8; + n->inhRelations = list_make1($7); + n->partbound = $9; + n->partspec = $10; + n->ofTypename = NULL; + n->constraints = NIL; + n->accessMethod = $11; + n->options = $12; + n->oncommit = $13; + n->tablespacename = $14; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name PARTITION OF + qualified_name OptTypedTableElementList PartitionBoundSpec OptPartitionSpec + table_access_method_clause OptWith OnCommitOption OptTableSpace + { + CreateStmt *n = makeNode(CreateStmt); + $7->relpersistence = $2; + n->relation = $7; + n->tableElts = $11; + n->inhRelations = list_make1($10); + n->partbound = $12; + n->partspec = $13; + n->ofTypename = NULL; + n->constraints = NIL; + n->accessMethod = $14; + n->options = $15; + n->oncommit = $16; + n->tablespacename = $17; + n->if_not_exists = true; + $$ = (Node *)n; + } + ; + +/* + * Redundancy here is needed to avoid shift/reduce conflicts, + * since TEMP is not a reserved word. See also OptTempTableName. + * + * NOTE: we accept both GLOBAL and LOCAL options. They currently do nothing, + * but future versions might consider GLOBAL to request SQL-spec-compliant + * temp table behavior, so warn about that. Since we have no modules the + * LOCAL keyword is really meaningless; furthermore, some other products + * implement LOCAL as meaning the same as our default temp table behavior, + * so we'll probably continue to treat LOCAL as a noise word. + */ +OptTemp: TEMPORARY { $$ = RELPERSISTENCE_TEMP; } + | TEMP { $$ = RELPERSISTENCE_TEMP; } + | LOCAL TEMPORARY { $$ = RELPERSISTENCE_TEMP; } + | LOCAL TEMP { $$ = RELPERSISTENCE_TEMP; } + | GLOBAL TEMPORARY + { + ereport(WARNING, + (errmsg("GLOBAL is deprecated in temporary table creation"), + parser_errposition(@1))); + $$ = RELPERSISTENCE_TEMP; + } + | GLOBAL TEMP + { + ereport(WARNING, + (errmsg("GLOBAL is deprecated in temporary table creation"), + parser_errposition(@1))); + $$ = RELPERSISTENCE_TEMP; + } + | UNLOGGED { $$ = RELPERSISTENCE_UNLOGGED; } + | /*EMPTY*/ { $$ = RELPERSISTENCE_PERMANENT; } + ; + +OptTableElementList: + TableElementList { $$ = $1; } + | /*EMPTY*/ { $$ = NIL; } + ; + +OptTypedTableElementList: + '(' TypedTableElementList ')' { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + +TableElementList: + TableElement + { + $$ = list_make1($1); + } + | TableElementList ',' TableElement + { + $$ = lappend($1, $3); + } + ; + +TypedTableElementList: + TypedTableElement + { + $$ = list_make1($1); + } + | TypedTableElementList ',' TypedTableElement + { + $$ = lappend($1, $3); + } + ; + +TableElement: + columnDef { $$ = $1; } + | TableLikeClause { $$ = $1; } + | TableConstraint { $$ = $1; } + ; + +TypedTableElement: + columnOptions { $$ = $1; } + | TableConstraint { $$ = $1; } + ; + +columnDef: ColId Typename opt_column_compression create_generic_options ColQualList + { + ColumnDef *n = makeNode(ColumnDef); + n->colname = $1; + n->typeName = $2; + n->compression = $3; + n->inhcount = 0; + n->is_local = true; + n->is_not_null = false; + n->is_from_type = false; + n->storage = 0; + n->raw_default = NULL; + n->cooked_default = NULL; + n->collOid = InvalidOid; + n->fdwoptions = $4; + SplitColQualList($5, &n->constraints, &n->collClause, + yyscanner); + n->location = @1; + $$ = (Node *)n; + } + ; + +columnOptions: ColId ColQualList + { + ColumnDef *n = makeNode(ColumnDef); + n->colname = $1; + n->typeName = NULL; + n->inhcount = 0; + n->is_local = true; + n->is_not_null = false; + n->is_from_type = false; + n->storage = 0; + n->raw_default = NULL; + n->cooked_default = NULL; + n->collOid = InvalidOid; + SplitColQualList($2, &n->constraints, &n->collClause, + yyscanner); + n->location = @1; + $$ = (Node *)n; + } + | ColId WITH OPTIONS ColQualList + { + ColumnDef *n = makeNode(ColumnDef); + n->colname = $1; + n->typeName = NULL; + n->inhcount = 0; + n->is_local = true; + n->is_not_null = false; + n->is_from_type = false; + n->storage = 0; + n->raw_default = NULL; + n->cooked_default = NULL; + n->collOid = InvalidOid; + SplitColQualList($4, &n->constraints, &n->collClause, + yyscanner); + n->location = @1; + $$ = (Node *)n; + } + ; + +column_compression: + COMPRESSION ColId { $$ = $2; } + | COMPRESSION DEFAULT { $$ = pstrdup("default"); } + ; + +opt_column_compression: + column_compression { $$ = $1; } + | /*EMPTY*/ { $$ = NULL; } + ; + +ColQualList: + ColQualList ColConstraint { $$ = lappend($1, $2); } + | /*EMPTY*/ { $$ = NIL; } + ; + +ColConstraint: + CONSTRAINT name ColConstraintElem + { + Constraint *n = castNode(Constraint, $3); + n->conname = $2; + n->location = @1; + $$ = (Node *) n; + } + | ColConstraintElem { $$ = $1; } + | ConstraintAttr { $$ = $1; } + | COLLATE any_name + { + /* + * Note: the CollateClause is momentarily included in + * the list built by ColQualList, but we split it out + * again in SplitColQualList. + */ + CollateClause *n = makeNode(CollateClause); + n->arg = NULL; + n->collname = $2; + n->location = @1; + $$ = (Node *) n; + } + ; + +/* 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 SQL-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->location = @1; + $$ = (Node *)n; + } + | NULL_P + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_NULL; + n->location = @1; + $$ = (Node *)n; + } + | UNIQUE opt_definition OptConsTableSpace + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_UNIQUE; + n->location = @1; + n->keys = NULL; + n->options = $2; + n->indexname = NULL; + n->indexspace = $3; + $$ = (Node *)n; + } + | PRIMARY KEY opt_definition OptConsTableSpace + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_PRIMARY; + n->location = @1; + n->keys = NULL; + n->options = $3; + n->indexname = NULL; + n->indexspace = $4; + $$ = (Node *)n; + } + | CHECK '(' a_expr ')' opt_no_inherit + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_CHECK; + n->location = @1; + n->is_no_inherit = $5; + n->raw_expr = $3; + n->cooked_expr = NULL; + n->skip_validation = false; + n->initially_valid = true; + $$ = (Node *)n; + } + | DEFAULT b_expr + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_DEFAULT; + n->location = @1; + n->raw_expr = $2; + n->cooked_expr = NULL; + $$ = (Node *)n; + } + | GENERATED generated_when AS IDENTITY_P OptParenthesizedSeqOptList + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_IDENTITY; + n->generated_when = $2; + n->options = $5; + n->location = @1; + $$ = (Node *)n; + } + | GENERATED generated_when AS '(' a_expr ')' STORED + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_GENERATED; + n->generated_when = $2; + n->raw_expr = $5; + n->cooked_expr = NULL; + n->location = @1; + + /* + * Can't do this in the grammar because of shift/reduce + * conflicts. (IDENTITY allows both ALWAYS and BY + * DEFAULT, but generated columns only allow ALWAYS.) We + * can also give a more useful error message and location. + */ + if ($2 != ATTRIBUTE_IDENTITY_ALWAYS) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("for a generated column, GENERATED ALWAYS must be specified"), + parser_errposition(@2))); + + $$ = (Node *)n; + } + | REFERENCES qualified_name opt_column_list key_match key_actions + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_FOREIGN; + n->location = @1; + 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->skip_validation = false; + n->initially_valid = true; + $$ = (Node *)n; + } + ; + +generated_when: + ALWAYS { $$ = ATTRIBUTE_IDENTITY_ALWAYS; } + | BY DEFAULT { $$ = ATTRIBUTE_IDENTITY_BY_DEFAULT; } + ; + +/* + * 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). parse_utilcmd.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. (Note: currently, NOT VALID and NO INHERIT + * are allowed clauses in ConstraintAttributeSpec, but not here. Someday we + * might need to allow them here too, but for the moment it doesn't seem + * useful in the statements that use ConstraintAttr.) + */ +ConstraintAttr: + DEFERRABLE + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_ATTR_DEFERRABLE; + n->location = @1; + $$ = (Node *)n; + } + | NOT DEFERRABLE + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_ATTR_NOT_DEFERRABLE; + n->location = @1; + $$ = (Node *)n; + } + | INITIALLY DEFERRED + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_ATTR_DEFERRED; + n->location = @1; + $$ = (Node *)n; + } + | INITIALLY IMMEDIATE + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_ATTR_IMMEDIATE; + n->location = @1; + $$ = (Node *)n; + } + ; + + +TableLikeClause: + LIKE qualified_name TableLikeOptionList + { + TableLikeClause *n = makeNode(TableLikeClause); + n->relation = $2; + n->options = $3; + n->relationOid = InvalidOid; + $$ = (Node *)n; + } + ; + +TableLikeOptionList: + TableLikeOptionList INCLUDING TableLikeOption { $$ = $1 | $3; } + | TableLikeOptionList EXCLUDING TableLikeOption { $$ = $1 & ~$3; } + | /* EMPTY */ { $$ = 0; } + ; + +TableLikeOption: + COMMENTS { $$ = CREATE_TABLE_LIKE_COMMENTS; } + | COMPRESSION { $$ = CREATE_TABLE_LIKE_COMPRESSION; } + | CONSTRAINTS { $$ = CREATE_TABLE_LIKE_CONSTRAINTS; } + | DEFAULTS { $$ = CREATE_TABLE_LIKE_DEFAULTS; } + | IDENTITY_P { $$ = CREATE_TABLE_LIKE_IDENTITY; } + | GENERATED { $$ = CREATE_TABLE_LIKE_GENERATED; } + | INDEXES { $$ = CREATE_TABLE_LIKE_INDEXES; } + | STATISTICS { $$ = CREATE_TABLE_LIKE_STATISTICS; } + | STORAGE { $$ = CREATE_TABLE_LIKE_STORAGE; } + | ALL { $$ = CREATE_TABLE_LIKE_ALL; } + ; + + +/* 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 + { + Constraint *n = castNode(Constraint, $3); + n->conname = $2; + n->location = @1; + $$ = (Node *) n; + } + | ConstraintElem { $$ = $1; } + ; + +ConstraintElem: + CHECK '(' a_expr ')' ConstraintAttributeSpec + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_CHECK; + n->location = @1; + n->raw_expr = $3; + n->cooked_expr = NULL; + processCASbits($5, @5, "CHECK", + NULL, NULL, &n->skip_validation, + &n->is_no_inherit, yyscanner); + n->initially_valid = !n->skip_validation; + $$ = (Node *)n; + } + | UNIQUE '(' columnList ')' opt_c_include opt_definition OptConsTableSpace + ConstraintAttributeSpec + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_UNIQUE; + n->location = @1; + n->keys = $3; + n->including = $5; + n->options = $6; + n->indexname = NULL; + n->indexspace = $7; + processCASbits($8, @8, "UNIQUE", + &n->deferrable, &n->initdeferred, NULL, + NULL, yyscanner); + $$ = (Node *)n; + } + | UNIQUE ExistingIndex ConstraintAttributeSpec + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_UNIQUE; + n->location = @1; + n->keys = NIL; + n->including = NIL; + n->options = NIL; + n->indexname = $2; + n->indexspace = NULL; + processCASbits($3, @3, "UNIQUE", + &n->deferrable, &n->initdeferred, NULL, + NULL, yyscanner); + $$ = (Node *)n; + } + | PRIMARY KEY '(' columnList ')' opt_c_include opt_definition OptConsTableSpace + ConstraintAttributeSpec + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_PRIMARY; + n->location = @1; + n->keys = $4; + n->including = $6; + n->options = $7; + n->indexname = NULL; + n->indexspace = $8; + processCASbits($9, @9, "PRIMARY KEY", + &n->deferrable, &n->initdeferred, NULL, + NULL, yyscanner); + $$ = (Node *)n; + } + | PRIMARY KEY ExistingIndex ConstraintAttributeSpec + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_PRIMARY; + n->location = @1; + n->keys = NIL; + n->including = NIL; + n->options = NIL; + n->indexname = $3; + n->indexspace = NULL; + processCASbits($4, @4, "PRIMARY KEY", + &n->deferrable, &n->initdeferred, NULL, + NULL, yyscanner); + $$ = (Node *)n; + } + | EXCLUDE access_method_clause '(' ExclusionConstraintList ')' + opt_c_include opt_definition OptConsTableSpace OptWhereClause + ConstraintAttributeSpec + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_EXCLUSION; + n->location = @1; + n->access_method = $2; + n->exclusions = $4; + n->including = $6; + n->options = $7; + n->indexname = NULL; + n->indexspace = $8; + n->where_clause = $9; + processCASbits($10, @10, "EXCLUDE", + &n->deferrable, &n->initdeferred, NULL, + NULL, yyscanner); + $$ = (Node *)n; + } + | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name + opt_column_list key_match key_actions ConstraintAttributeSpec + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_FOREIGN; + n->location = @1; + 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); + processCASbits($11, @11, "FOREIGN KEY", + &n->deferrable, &n->initdeferred, + &n->skip_validation, NULL, + yyscanner); + n->initially_valid = !n->skip_validation; + $$ = (Node *)n; + } + ; + +opt_no_inherit: NO INHERIT { $$ = true; } + | /* EMPTY */ { $$ = false; } + ; + +opt_column_list: + '(' columnList ')' { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + +columnList: + columnElem { $$ = list_make1($1); } + | columnList ',' columnElem { $$ = lappend($1, $3); } + ; + +columnElem: ColId + { + $$ = (Node *) makeString($1); + } + ; + +opt_c_include: INCLUDE '(' columnList ')' { $$ = $3; } + | /* EMPTY */ { $$ = NIL; } + ; + +key_match: MATCH FULL + { + $$ = FKCONSTR_MATCH_FULL; + } + | MATCH PARTIAL + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("MATCH PARTIAL not yet implemented"), + parser_errposition(@1))); + $$ = FKCONSTR_MATCH_PARTIAL; + } + | MATCH SIMPLE + { + $$ = FKCONSTR_MATCH_SIMPLE; + } + | /*EMPTY*/ + { + $$ = FKCONSTR_MATCH_SIMPLE; + } + ; + +ExclusionConstraintList: + ExclusionConstraintElem { $$ = list_make1($1); } + | ExclusionConstraintList ',' ExclusionConstraintElem + { $$ = lappend($1, $3); } + ; + +ExclusionConstraintElem: index_elem WITH any_operator + { + $$ = list_make2($1, $3); + } + /* allow OPERATOR() decoration for the benefit of ruleutils.c */ + | index_elem WITH OPERATOR '(' any_operator ')' + { + $$ = list_make2($1, $5); + } + ; + +OptWhereClause: + WHERE '(' a_expr ')' { $$ = $3; } + | /*EMPTY*/ { $$ = NULL; } + ; + +/* + * 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; } + ; + +/* Optional partition key specification */ +OptPartitionSpec: PartitionSpec { $$ = $1; } + | /*EMPTY*/ { $$ = NULL; } + ; + +PartitionSpec: PARTITION BY ColId '(' part_params ')' + { + PartitionSpec *n = makeNode(PartitionSpec); + + n->strategy = $3; + n->partParams = $5; + n->location = @1; + + $$ = n; + } + ; + +part_params: part_elem { $$ = list_make1($1); } + | part_params ',' part_elem { $$ = lappend($1, $3); } + ; + +part_elem: ColId opt_collate opt_class + { + PartitionElem *n = makeNode(PartitionElem); + + n->name = $1; + n->expr = NULL; + n->collation = $2; + n->opclass = $3; + n->location = @1; + $$ = n; + } + | func_expr_windowless opt_collate opt_class + { + PartitionElem *n = makeNode(PartitionElem); + + n->name = NULL; + n->expr = $1; + n->collation = $2; + n->opclass = $3; + n->location = @1; + $$ = n; + } + | '(' a_expr ')' opt_collate opt_class + { + PartitionElem *n = makeNode(PartitionElem); + + n->name = NULL; + n->expr = $2; + n->collation = $4; + n->opclass = $5; + n->location = @1; + $$ = n; + } + ; + +table_access_method_clause: + USING name { $$ = $2; } + | /*EMPTY*/ { $$ = NULL; } + ; + +/* WITHOUT OIDS is legacy only */ +OptWith: + WITH reloptions { $$ = $2; } + | WITHOUT OIDS { $$ = NIL; } + | /*EMPTY*/ { $$ = NIL; } + ; + +OnCommitOption: ON COMMIT DROP { $$ = ONCOMMIT_DROP; } + | ON COMMIT DELETE_P ROWS { $$ = ONCOMMIT_DELETE_ROWS; } + | ON COMMIT PRESERVE ROWS { $$ = ONCOMMIT_PRESERVE_ROWS; } + | /*EMPTY*/ { $$ = ONCOMMIT_NOOP; } + ; + +OptTableSpace: TABLESPACE name { $$ = $2; } + | /*EMPTY*/ { $$ = NULL; } + ; + +OptConsTableSpace: USING INDEX TABLESPACE name { $$ = $4; } + | /*EMPTY*/ { $$ = NULL; } + ; + +ExistingIndex: USING INDEX name { $$ = $3; } + ; + +/***************************************************************************** + * + * QUERY : + * CREATE STATISTICS [IF NOT EXISTS] stats_name [(stat types)] + * ON expression-list FROM from_list + * + * Note: the expectation here is that the clauses after ON are a subset of + * SELECT syntax, allowing for expressions and joined tables, and probably + * someday a WHERE clause. Much less than that is currently implemented, + * but the grammar accepts it and then we'll throw FEATURE_NOT_SUPPORTED + * errors as necessary at execution. + * + *****************************************************************************/ + +CreateStatsStmt: + CREATE STATISTICS any_name + opt_name_list ON stats_params FROM from_list + { + CreateStatsStmt *n = makeNode(CreateStatsStmt); + n->defnames = $3; + n->stat_types = $4; + n->exprs = $6; + n->relations = $8; + n->stxcomment = NULL; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE STATISTICS IF_P NOT EXISTS any_name + opt_name_list ON stats_params FROM from_list + { + CreateStatsStmt *n = makeNode(CreateStatsStmt); + n->defnames = $6; + n->stat_types = $7; + n->exprs = $9; + n->relations = $11; + n->stxcomment = NULL; + n->if_not_exists = true; + $$ = (Node *)n; + } + ; + +/* + * Statistics attributes can be either simple column references, or arbitrary + * expressions in parens. For compatibility with index attributes permitted + * in CREATE INDEX, we allow an expression that's just a function call to be + * written without parens. + */ + +stats_params: stats_param { $$ = list_make1($1); } + | stats_params ',' stats_param { $$ = lappend($1, $3); } + ; + +stats_param: ColId + { + $$ = makeNode(StatsElem); + $$->name = $1; + $$->expr = NULL; + } + | func_expr_windowless + { + $$ = makeNode(StatsElem); + $$->name = NULL; + $$->expr = $1; + } + | '(' a_expr ')' + { + $$ = makeNode(StatsElem); + $$->name = NULL; + $$->expr = $2; + } + ; + +/***************************************************************************** + * + * QUERY : + * ALTER STATISTICS [IF EXISTS] stats_name + * SET STATISTICS <SignedIconst> + * + *****************************************************************************/ + +AlterStatsStmt: + ALTER STATISTICS any_name SET STATISTICS SignedIconst + { + AlterStatsStmt *n = makeNode(AlterStatsStmt); + n->defnames = $3; + n->missing_ok = false; + n->stxstattarget = $6; + $$ = (Node *)n; + } + | ALTER STATISTICS IF_P EXISTS any_name SET STATISTICS SignedIconst + { + AlterStatsStmt *n = makeNode(AlterStatsStmt); + n->defnames = $5; + n->missing_ok = true; + n->stxstattarget = $8; + $$ = (Node *)n; + } + ; + +/***************************************************************************** + * + * QUERY : + * CREATE TABLE relname AS SelectStmt [ WITH [NO] DATA ] + * + * + * Note: SELECT ... INTO is a now-deprecated alternative for this. + * + *****************************************************************************/ + +CreateAsStmt: + CREATE OptTemp TABLE create_as_target AS SelectStmt opt_with_data + { + CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt); + ctas->query = $6; + ctas->into = $4; + ctas->objtype = OBJECT_TABLE; + ctas->is_select_into = false; + ctas->if_not_exists = false; + /* cram additional flags into the IntoClause */ + $4->rel->relpersistence = $2; + $4->skipData = !($7); + $$ = (Node *) ctas; + } + | CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS SelectStmt opt_with_data + { + CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt); + ctas->query = $9; + ctas->into = $7; + ctas->objtype = OBJECT_TABLE; + ctas->is_select_into = false; + ctas->if_not_exists = true; + /* cram additional flags into the IntoClause */ + $7->rel->relpersistence = $2; + $7->skipData = !($10); + $$ = (Node *) ctas; + } + ; + +create_as_target: + qualified_name opt_column_list table_access_method_clause + OptWith OnCommitOption OptTableSpace + { + $$ = makeNode(IntoClause); + $$->rel = $1; + $$->colNames = $2; + $$->accessMethod = $3; + $$->options = $4; + $$->onCommit = $5; + $$->tableSpaceName = $6; + $$->viewQuery = NULL; + $$->skipData = false; /* might get changed later */ + } + ; + +opt_with_data: + WITH DATA_P { $$ = true; } + | WITH NO DATA_P { $$ = false; } + | /*EMPTY*/ { $$ = true; } + ; + + +/***************************************************************************** + * + * QUERY : + * CREATE MATERIALIZED VIEW relname AS SelectStmt + * + *****************************************************************************/ + +CreateMatViewStmt: + CREATE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt opt_with_data + { + CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt); + ctas->query = $7; + ctas->into = $5; + ctas->objtype = OBJECT_MATVIEW; + ctas->is_select_into = false; + ctas->if_not_exists = false; + /* cram additional flags into the IntoClause */ + $5->rel->relpersistence = $2; + $5->skipData = !($8); + $$ = (Node *) ctas; + } + | CREATE OptNoLog MATERIALIZED VIEW IF_P NOT EXISTS create_mv_target AS SelectStmt opt_with_data + { + CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt); + ctas->query = $10; + ctas->into = $8; + ctas->objtype = OBJECT_MATVIEW; + ctas->is_select_into = false; + ctas->if_not_exists = true; + /* cram additional flags into the IntoClause */ + $8->rel->relpersistence = $2; + $8->skipData = !($11); + $$ = (Node *) ctas; + } + ; + +create_mv_target: + qualified_name opt_column_list table_access_method_clause opt_reloptions OptTableSpace + { + $$ = makeNode(IntoClause); + $$->rel = $1; + $$->colNames = $2; + $$->accessMethod = $3; + $$->options = $4; + $$->onCommit = ONCOMMIT_NOOP; + $$->tableSpaceName = $5; + $$->viewQuery = NULL; /* filled at analysis time */ + $$->skipData = false; /* might get changed later */ + } + ; + +OptNoLog: UNLOGGED { $$ = RELPERSISTENCE_UNLOGGED; } + | /*EMPTY*/ { $$ = RELPERSISTENCE_PERMANENT; } + ; + + +/***************************************************************************** + * + * QUERY : + * REFRESH MATERIALIZED VIEW qualified_name + * + *****************************************************************************/ + +RefreshMatViewStmt: + REFRESH MATERIALIZED VIEW opt_concurrently qualified_name opt_with_data + { + RefreshMatViewStmt *n = makeNode(RefreshMatViewStmt); + n->concurrent = $4; + n->relation = $5; + n->skipData = !($6); + $$ = (Node *) n; + } + ; + + +/***************************************************************************** + * + * QUERY : + * CREATE SEQUENCE seqname + * ALTER SEQUENCE seqname + * + *****************************************************************************/ + +CreateSeqStmt: + CREATE OptTemp SEQUENCE qualified_name OptSeqOptList + { + CreateSeqStmt *n = makeNode(CreateSeqStmt); + $4->relpersistence = $2; + n->sequence = $4; + n->options = $5; + n->ownerId = InvalidOid; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE OptTemp SEQUENCE IF_P NOT EXISTS qualified_name OptSeqOptList + { + CreateSeqStmt *n = makeNode(CreateSeqStmt); + $7->relpersistence = $2; + n->sequence = $7; + n->options = $8; + n->ownerId = InvalidOid; + n->if_not_exists = true; + $$ = (Node *)n; + } + ; + +AlterSeqStmt: + ALTER SEQUENCE qualified_name SeqOptList + { + AlterSeqStmt *n = makeNode(AlterSeqStmt); + n->sequence = $3; + n->options = $4; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER SEQUENCE IF_P EXISTS qualified_name SeqOptList + { + AlterSeqStmt *n = makeNode(AlterSeqStmt); + n->sequence = $5; + n->options = $6; + n->missing_ok = true; + $$ = (Node *)n; + } + + ; + +OptSeqOptList: SeqOptList { $$ = $1; } + | /*EMPTY*/ { $$ = NIL; } + ; + +OptParenthesizedSeqOptList: '(' SeqOptList ')' { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + +SeqOptList: SeqOptElem { $$ = list_make1($1); } + | SeqOptList SeqOptElem { $$ = lappend($1, $2); } + ; + +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly + { + $$ = makeDefElem("cache", (Node *)$2, @1); + } + | CYCLE + { + $$ = makeDefElem("cycle", (Node *)makeInteger(true), @1); + } + | NO CYCLE + { + $$ = makeDefElem("cycle", (Node *)makeInteger(false), @1); + } + | INCREMENT opt_by NumericOnly + { + $$ = makeDefElem("increment", (Node *)$3, @1); + } + | MAXVALUE NumericOnly + { + $$ = makeDefElem("maxvalue", (Node *)$2, @1); + } + | MINVALUE NumericOnly + { + $$ = makeDefElem("minvalue", (Node *)$2, @1); + } + | NO MAXVALUE + { + $$ = makeDefElem("maxvalue", NULL, @1); + } + | NO MINVALUE + { + $$ = makeDefElem("minvalue", NULL, @1); + } + | OWNED BY any_name + { + $$ = makeDefElem("owned_by", (Node *)$3, @1); + } + | SEQUENCE NAME_P any_name + { + /* not documented, only used by pg_dump */ + $$ = makeDefElem("sequence_name", (Node *)$3, @1); + } + | START opt_with NumericOnly + { + $$ = makeDefElem("start", (Node *)$3, @1); + } + | RESTART + { + $$ = makeDefElem("restart", NULL, @1); + } + | RESTART opt_with NumericOnly + { + $$ = makeDefElem("restart", (Node *)$3, @1); + } + ; + +opt_by: BY + | /* EMPTY */ + ; + +NumericOnly: + FCONST { $$ = makeFloat($1); } + | '+' FCONST { $$ = makeFloat($2); } + | '-' FCONST + { + $$ = makeFloat($2); + doNegateFloat($$); + } + | SignedIconst { $$ = makeInteger($1); } + ; + +NumericOnly_list: NumericOnly { $$ = list_make1($1); } + | NumericOnly_list ',' NumericOnly { $$ = lappend($1, $3); } + ; + +/***************************************************************************** + * + * QUERIES : + * CREATE [OR REPLACE] [TRUSTED] [PROCEDURAL] LANGUAGE ... + * DROP [PROCEDURAL] LANGUAGE ... + * + *****************************************************************************/ + +CreatePLangStmt: + CREATE opt_or_replace opt_trusted opt_procedural LANGUAGE name + { + /* + * We now interpret parameterless CREATE LANGUAGE as + * CREATE EXTENSION. "OR REPLACE" is silently translated + * to "IF NOT EXISTS", which isn't quite the same, but + * seems more useful than throwing an error. We just + * ignore TRUSTED, as the previous code would have too. + */ + CreateExtensionStmt *n = makeNode(CreateExtensionStmt); + n->if_not_exists = $2; + n->extname = $6; + n->options = NIL; + $$ = (Node *)n; + } + | CREATE opt_or_replace opt_trusted opt_procedural LANGUAGE name + HANDLER handler_name opt_inline_handler opt_validator + { + CreatePLangStmt *n = makeNode(CreatePLangStmt); + n->replace = $2; + n->plname = $6; + n->plhandler = $8; + n->plinline = $9; + n->plvalidator = $10; + n->pltrusted = $3; + $$ = (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 simple names, instead. + */ +handler_name: + name { $$ = list_make1(makeString($1)); } + | name attrs { $$ = lcons(makeString($1), $2); } + ; + +opt_inline_handler: + INLINE_P handler_name { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + +validator_clause: + VALIDATOR handler_name { $$ = $2; } + | NO VALIDATOR { $$ = NIL; } + ; + +opt_validator: + validator_clause { $$ = $1; } + | /*EMPTY*/ { $$ = NIL; } + ; + +opt_procedural: + PROCEDURAL + | /*EMPTY*/ + ; + +/***************************************************************************** + * + * QUERY: + * CREATE TABLESPACE tablespace LOCATION '/path/to/tablespace/' + * + *****************************************************************************/ + +CreateTableSpaceStmt: CREATE TABLESPACE name OptTableSpaceOwner LOCATION Sconst opt_reloptions + { + CreateTableSpaceStmt *n = makeNode(CreateTableSpaceStmt); + n->tablespacename = $3; + n->owner = $4; + n->location = $6; + n->options = $7; + $$ = (Node *) n; + } + ; + +OptTableSpaceOwner: OWNER RoleSpec { $$ = $2; } + | /*EMPTY */ { $$ = NULL; } + ; + +/***************************************************************************** + * + * QUERY : + * DROP TABLESPACE <tablespace> + * + * No need for drop behaviour as we cannot implement dependencies for + * objects in other databases; we can only support RESTRICT. + * + ****************************************************************************/ + +DropTableSpaceStmt: DROP TABLESPACE name + { + DropTableSpaceStmt *n = makeNode(DropTableSpaceStmt); + n->tablespacename = $3; + n->missing_ok = false; + $$ = (Node *) n; + } + | DROP TABLESPACE IF_P EXISTS name + { + DropTableSpaceStmt *n = makeNode(DropTableSpaceStmt); + n->tablespacename = $5; + n->missing_ok = true; + $$ = (Node *) n; + } + ; + +/***************************************************************************** + * + * QUERY: + * CREATE EXTENSION extension + * [ WITH ] [ SCHEMA schema ] [ VERSION version ] + * + *****************************************************************************/ + +CreateExtensionStmt: CREATE EXTENSION name opt_with create_extension_opt_list + { + CreateExtensionStmt *n = makeNode(CreateExtensionStmt); + n->extname = $3; + n->if_not_exists = false; + n->options = $5; + $$ = (Node *) n; + } + | CREATE EXTENSION IF_P NOT EXISTS name opt_with create_extension_opt_list + { + CreateExtensionStmt *n = makeNode(CreateExtensionStmt); + n->extname = $6; + n->if_not_exists = true; + n->options = $8; + $$ = (Node *) n; + } + ; + +create_extension_opt_list: + create_extension_opt_list create_extension_opt_item + { $$ = lappend($1, $2); } + | /* EMPTY */ + { $$ = NIL; } + ; + +create_extension_opt_item: + SCHEMA name + { + $$ = makeDefElem("schema", (Node *)makeString($2), @1); + } + | VERSION_P NonReservedWord_or_Sconst + { + $$ = makeDefElem("new_version", (Node *)makeString($2), @1); + } + | FROM NonReservedWord_or_Sconst + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("CREATE EXTENSION ... FROM is no longer supported"), + parser_errposition(@1))); + } + | CASCADE + { + $$ = makeDefElem("cascade", (Node *)makeInteger(true), @1); + } + ; + +/***************************************************************************** + * + * ALTER EXTENSION name UPDATE [ TO version ] + * + *****************************************************************************/ + +AlterExtensionStmt: ALTER EXTENSION name UPDATE alter_extension_opt_list + { + AlterExtensionStmt *n = makeNode(AlterExtensionStmt); + n->extname = $3; + n->options = $5; + $$ = (Node *) n; + } + ; + +alter_extension_opt_list: + alter_extension_opt_list alter_extension_opt_item + { $$ = lappend($1, $2); } + | /* EMPTY */ + { $$ = NIL; } + ; + +alter_extension_opt_item: + TO NonReservedWord_or_Sconst + { + $$ = makeDefElem("new_version", (Node *)makeString($2), @1); + } + ; + +/***************************************************************************** + * + * ALTER EXTENSION name ADD/DROP object-identifier + * + *****************************************************************************/ + +AlterExtensionContentsStmt: + ALTER EXTENSION name add_drop object_type_name name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = $5; + n->object = (Node *) makeString($6); + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop object_type_any_name any_name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = $5; + n->object = (Node *) $6; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop AGGREGATE aggregate_with_argtypes + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_AGGREGATE; + n->object = (Node *) $6; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop CAST '(' Typename AS Typename ')' + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_CAST; + n->object = (Node *) list_make2($7, $9); + $$ = (Node *) n; + } + | ALTER EXTENSION name add_drop DOMAIN_P Typename + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_DOMAIN; + n->object = (Node *) $6; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop FUNCTION function_with_argtypes + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_FUNCTION; + n->object = (Node *) $6; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop OPERATOR operator_with_argtypes + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_OPERATOR; + n->object = (Node *) $6; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop OPERATOR CLASS any_name USING name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_OPCLASS; + n->object = (Node *) lcons(makeString($9), $7); + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop OPERATOR FAMILY any_name USING name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_OPFAMILY; + n->object = (Node *) lcons(makeString($9), $7); + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop PROCEDURE function_with_argtypes + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_PROCEDURE; + n->object = (Node *) $6; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop ROUTINE function_with_argtypes + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_ROUTINE; + n->object = (Node *) $6; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop TRANSFORM FOR Typename LANGUAGE name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_TRANSFORM; + n->object = (Node *) list_make2($7, makeString($9)); + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop TYPE_P Typename + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_TYPE; + n->object = (Node *) $6; + $$ = (Node *)n; + } + ; + +/***************************************************************************** + * + * QUERY: + * CREATE FOREIGN DATA WRAPPER name options + * + *****************************************************************************/ + +CreateFdwStmt: CREATE FOREIGN DATA_P WRAPPER name opt_fdw_options create_generic_options + { + CreateFdwStmt *n = makeNode(CreateFdwStmt); + n->fdwname = $5; + n->func_options = $6; + n->options = $7; + $$ = (Node *) n; + } + ; + +fdw_option: + HANDLER handler_name { $$ = makeDefElem("handler", (Node *)$2, @1); } + | NO HANDLER { $$ = makeDefElem("handler", NULL, @1); } + | VALIDATOR handler_name { $$ = makeDefElem("validator", (Node *)$2, @1); } + | NO VALIDATOR { $$ = makeDefElem("validator", NULL, @1); } + ; + +fdw_options: + fdw_option { $$ = list_make1($1); } + | fdw_options fdw_option { $$ = lappend($1, $2); } + ; + +opt_fdw_options: + fdw_options { $$ = $1; } + | /*EMPTY*/ { $$ = NIL; } + ; + +/***************************************************************************** + * + * QUERY : + * ALTER FOREIGN DATA WRAPPER name options + * + ****************************************************************************/ + +AlterFdwStmt: ALTER FOREIGN DATA_P WRAPPER name opt_fdw_options alter_generic_options + { + AlterFdwStmt *n = makeNode(AlterFdwStmt); + n->fdwname = $5; + n->func_options = $6; + n->options = $7; + $$ = (Node *) n; + } + | ALTER FOREIGN DATA_P WRAPPER name fdw_options + { + AlterFdwStmt *n = makeNode(AlterFdwStmt); + n->fdwname = $5; + n->func_options = $6; + n->options = NIL; + $$ = (Node *) n; + } + ; + +/* Options definition for CREATE FDW, SERVER and USER MAPPING */ +create_generic_options: + OPTIONS '(' generic_option_list ')' { $$ = $3; } + | /*EMPTY*/ { $$ = NIL; } + ; + +generic_option_list: + generic_option_elem + { + $$ = list_make1($1); + } + | generic_option_list ',' generic_option_elem + { + $$ = lappend($1, $3); + } + ; + +/* Options definition for ALTER FDW, SERVER and USER MAPPING */ +alter_generic_options: + OPTIONS '(' alter_generic_option_list ')' { $$ = $3; } + ; + +alter_generic_option_list: + alter_generic_option_elem + { + $$ = list_make1($1); + } + | alter_generic_option_list ',' alter_generic_option_elem + { + $$ = lappend($1, $3); + } + ; + +alter_generic_option_elem: + generic_option_elem + { + $$ = $1; + } + | SET generic_option_elem + { + $$ = $2; + $$->defaction = DEFELEM_SET; + } + | ADD_P generic_option_elem + { + $$ = $2; + $$->defaction = DEFELEM_ADD; + } + | DROP generic_option_name + { + $$ = makeDefElemExtended(NULL, $2, NULL, DEFELEM_DROP, @2); + } + ; + +generic_option_elem: + generic_option_name generic_option_arg + { + $$ = makeDefElem($1, $2, @1); + } + ; + +generic_option_name: + ColLabel { $$ = $1; } + ; + +/* We could use def_arg here, but the spec only requires string literals */ +generic_option_arg: + Sconst { $$ = (Node *) makeString($1); } + ; + +/***************************************************************************** + * + * QUERY: + * CREATE SERVER name [TYPE] [VERSION] [OPTIONS] + * + *****************************************************************************/ + +CreateForeignServerStmt: CREATE SERVER name opt_type opt_foreign_server_version + FOREIGN DATA_P WRAPPER name create_generic_options + { + CreateForeignServerStmt *n = makeNode(CreateForeignServerStmt); + n->servername = $3; + n->servertype = $4; + n->version = $5; + n->fdwname = $9; + n->options = $10; + n->if_not_exists = false; + $$ = (Node *) n; + } + | CREATE SERVER IF_P NOT EXISTS name opt_type opt_foreign_server_version + FOREIGN DATA_P WRAPPER name create_generic_options + { + CreateForeignServerStmt *n = makeNode(CreateForeignServerStmt); + n->servername = $6; + n->servertype = $7; + n->version = $8; + n->fdwname = $12; + n->options = $13; + n->if_not_exists = true; + $$ = (Node *) n; + } + ; + +opt_type: + TYPE_P Sconst { $$ = $2; } + | /*EMPTY*/ { $$ = NULL; } + ; + + +foreign_server_version: + VERSION_P Sconst { $$ = $2; } + | VERSION_P NULL_P { $$ = NULL; } + ; + +opt_foreign_server_version: + foreign_server_version { $$ = $1; } + | /*EMPTY*/ { $$ = NULL; } + ; + +/***************************************************************************** + * + * QUERY : + * ALTER SERVER name [VERSION] [OPTIONS] + * + ****************************************************************************/ + +AlterForeignServerStmt: ALTER SERVER name foreign_server_version alter_generic_options + { + AlterForeignServerStmt *n = makeNode(AlterForeignServerStmt); + n->servername = $3; + n->version = $4; + n->options = $5; + n->has_version = true; + $$ = (Node *) n; + } + | ALTER SERVER name foreign_server_version + { + AlterForeignServerStmt *n = makeNode(AlterForeignServerStmt); + n->servername = $3; + n->version = $4; + n->has_version = true; + $$ = (Node *) n; + } + | ALTER SERVER name alter_generic_options + { + AlterForeignServerStmt *n = makeNode(AlterForeignServerStmt); + n->servername = $3; + n->options = $4; + $$ = (Node *) n; + } + ; + +/***************************************************************************** + * + * QUERY: + * CREATE FOREIGN TABLE relname (...) SERVER name (...) + * + *****************************************************************************/ + +CreateForeignTableStmt: + CREATE FOREIGN TABLE qualified_name + '(' OptTableElementList ')' + OptInherit SERVER name create_generic_options + { + CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt); + $4->relpersistence = RELPERSISTENCE_PERMANENT; + n->base.relation = $4; + n->base.tableElts = $6; + n->base.inhRelations = $8; + n->base.ofTypename = NULL; + n->base.constraints = NIL; + n->base.options = NIL; + n->base.oncommit = ONCOMMIT_NOOP; + n->base.tablespacename = NULL; + n->base.if_not_exists = false; + /* FDW-specific data */ + n->servername = $10; + n->options = $11; + $$ = (Node *) n; + } + | CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name + '(' OptTableElementList ')' + OptInherit SERVER name create_generic_options + { + CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt); + $7->relpersistence = RELPERSISTENCE_PERMANENT; + n->base.relation = $7; + n->base.tableElts = $9; + n->base.inhRelations = $11; + n->base.ofTypename = NULL; + n->base.constraints = NIL; + n->base.options = NIL; + n->base.oncommit = ONCOMMIT_NOOP; + n->base.tablespacename = NULL; + n->base.if_not_exists = true; + /* FDW-specific data */ + n->servername = $13; + n->options = $14; + $$ = (Node *) n; + } + | CREATE FOREIGN TABLE qualified_name + PARTITION OF qualified_name OptTypedTableElementList PartitionBoundSpec + SERVER name create_generic_options + { + CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt); + $4->relpersistence = RELPERSISTENCE_PERMANENT; + n->base.relation = $4; + n->base.inhRelations = list_make1($7); + n->base.tableElts = $8; + n->base.partbound = $9; + n->base.ofTypename = NULL; + n->base.constraints = NIL; + n->base.options = NIL; + n->base.oncommit = ONCOMMIT_NOOP; + n->base.tablespacename = NULL; + n->base.if_not_exists = false; + /* FDW-specific data */ + n->servername = $11; + n->options = $12; + $$ = (Node *) n; + } + | CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name + PARTITION OF qualified_name OptTypedTableElementList PartitionBoundSpec + SERVER name create_generic_options + { + CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt); + $7->relpersistence = RELPERSISTENCE_PERMANENT; + n->base.relation = $7; + n->base.inhRelations = list_make1($10); + n->base.tableElts = $11; + n->base.partbound = $12; + n->base.ofTypename = NULL; + n->base.constraints = NIL; + n->base.options = NIL; + n->base.oncommit = ONCOMMIT_NOOP; + n->base.tablespacename = NULL; + n->base.if_not_exists = true; + /* FDW-specific data */ + n->servername = $14; + n->options = $15; + $$ = (Node *) n; + } + ; + +/***************************************************************************** + * + * QUERY: + * IMPORT FOREIGN SCHEMA remote_schema + * [ { LIMIT TO | EXCEPT } ( table_list ) ] + * FROM SERVER server_name INTO local_schema [ OPTIONS (...) ] + * + ****************************************************************************/ + +ImportForeignSchemaStmt: + IMPORT_P FOREIGN SCHEMA name import_qualification + FROM SERVER name INTO name create_generic_options + { + ImportForeignSchemaStmt *n = makeNode(ImportForeignSchemaStmt); + n->server_name = $8; + n->remote_schema = $4; + n->local_schema = $10; + n->list_type = $5->type; + n->table_list = $5->table_names; + n->options = $11; + $$ = (Node *) n; + } + ; + +import_qualification_type: + LIMIT TO { $$ = FDW_IMPORT_SCHEMA_LIMIT_TO; } + | EXCEPT { $$ = FDW_IMPORT_SCHEMA_EXCEPT; } + ; + +import_qualification: + import_qualification_type '(' relation_expr_list ')' + { + ImportQual *n = (ImportQual *) palloc(sizeof(ImportQual)); + n->type = $1; + n->table_names = $3; + $$ = n; + } + | /*EMPTY*/ + { + ImportQual *n = (ImportQual *) palloc(sizeof(ImportQual)); + n->type = FDW_IMPORT_SCHEMA_ALL; + n->table_names = NIL; + $$ = n; + } + ; + +/***************************************************************************** + * + * QUERY: + * CREATE USER MAPPING FOR auth_ident SERVER name [OPTIONS] + * + *****************************************************************************/ + +CreateUserMappingStmt: CREATE USER MAPPING FOR auth_ident SERVER name create_generic_options + { + CreateUserMappingStmt *n = makeNode(CreateUserMappingStmt); + n->user = $5; + n->servername = $7; + n->options = $8; + n->if_not_exists = false; + $$ = (Node *) n; + } + | CREATE USER MAPPING IF_P NOT EXISTS FOR auth_ident SERVER name create_generic_options + { + CreateUserMappingStmt *n = makeNode(CreateUserMappingStmt); + n->user = $8; + n->servername = $10; + n->options = $11; + n->if_not_exists = true; + $$ = (Node *) n; + } + ; + +/* User mapping authorization identifier */ +auth_ident: RoleSpec { $$ = $1; } + | USER { $$ = makeRoleSpec(ROLESPEC_CURRENT_USER, @1); } + ; + +/***************************************************************************** + * + * QUERY : + * DROP USER MAPPING FOR auth_ident SERVER name + * + * XXX you'd think this should have a CASCADE/RESTRICT option, even if it's + * only pro forma; but the SQL standard doesn't show one. + ****************************************************************************/ + +DropUserMappingStmt: DROP USER MAPPING FOR auth_ident SERVER name + { + DropUserMappingStmt *n = makeNode(DropUserMappingStmt); + n->user = $5; + n->servername = $7; + n->missing_ok = false; + $$ = (Node *) n; + } + | DROP USER MAPPING IF_P EXISTS FOR auth_ident SERVER name + { + DropUserMappingStmt *n = makeNode(DropUserMappingStmt); + n->user = $7; + n->servername = $9; + n->missing_ok = true; + $$ = (Node *) n; + } + ; + +/***************************************************************************** + * + * QUERY : + * ALTER USER MAPPING FOR auth_ident SERVER name OPTIONS + * + ****************************************************************************/ + +AlterUserMappingStmt: ALTER USER MAPPING FOR auth_ident SERVER name alter_generic_options + { + AlterUserMappingStmt *n = makeNode(AlterUserMappingStmt); + n->user = $5; + n->servername = $7; + n->options = $8; + $$ = (Node *) n; + } + ; + +/***************************************************************************** + * + * QUERIES: + * CREATE POLICY name ON table + * [AS { PERMISSIVE | RESTRICTIVE } ] + * [FOR { SELECT | INSERT | UPDATE | DELETE } ] + * [TO role, ...] + * [USING (qual)] [WITH CHECK (with check qual)] + * ALTER POLICY name ON table [TO role, ...] + * [USING (qual)] [WITH CHECK (with check qual)] + * + *****************************************************************************/ + +CreatePolicyStmt: + CREATE POLICY name ON qualified_name RowSecurityDefaultPermissive + RowSecurityDefaultForCmd RowSecurityDefaultToRole + RowSecurityOptionalExpr RowSecurityOptionalWithCheck + { + CreatePolicyStmt *n = makeNode(CreatePolicyStmt); + n->policy_name = $3; + n->table = $5; + n->permissive = $6; + n->cmd_name = $7; + n->roles = $8; + n->qual = $9; + n->with_check = $10; + $$ = (Node *) n; + } + ; + +AlterPolicyStmt: + ALTER POLICY name ON qualified_name RowSecurityOptionalToRole + RowSecurityOptionalExpr RowSecurityOptionalWithCheck + { + AlterPolicyStmt *n = makeNode(AlterPolicyStmt); + n->policy_name = $3; + n->table = $5; + n->roles = $6; + n->qual = $7; + n->with_check = $8; + $$ = (Node *) n; + } + ; + +RowSecurityOptionalExpr: + USING '(' a_expr ')' { $$ = $3; } + | /* EMPTY */ { $$ = NULL; } + ; + +RowSecurityOptionalWithCheck: + WITH CHECK '(' a_expr ')' { $$ = $4; } + | /* EMPTY */ { $$ = NULL; } + ; + +RowSecurityDefaultToRole: + TO role_list { $$ = $2; } + | /* EMPTY */ { $$ = list_make1(makeRoleSpec(ROLESPEC_PUBLIC, -1)); } + ; + +RowSecurityOptionalToRole: + TO role_list { $$ = $2; } + | /* EMPTY */ { $$ = NULL; } + ; + +RowSecurityDefaultPermissive: + AS IDENT + { + if (strcmp($2, "permissive") == 0) + $$ = true; + else if (strcmp($2, "restrictive") == 0) + $$ = false; + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unrecognized row security option \"%s\"", $2), + errhint("Only PERMISSIVE or RESTRICTIVE policies are supported currently."), + parser_errposition(@2))); + + } + | /* EMPTY */ { $$ = true; } + ; + +RowSecurityDefaultForCmd: + FOR row_security_cmd { $$ = $2; } + | /* EMPTY */ { $$ = "all"; } + ; + +row_security_cmd: + ALL { $$ = "all"; } + | SELECT { $$ = "select"; } + | INSERT { $$ = "insert"; } + | UPDATE { $$ = "update"; } + | DELETE_P { $$ = "delete"; } + ; + +/***************************************************************************** + * + * QUERY: + * CREATE ACCESS METHOD name HANDLER handler_name + * + *****************************************************************************/ + +CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name + { + CreateAmStmt *n = makeNode(CreateAmStmt); + n->amname = $4; + n->handler_name = $8; + n->amtype = $6; + $$ = (Node *) n; + } + ; + +am_type: + INDEX { $$ = AMTYPE_INDEX; } + | TABLE { $$ = AMTYPE_TABLE; } + ; + +/***************************************************************************** + * + * QUERIES : + * CREATE TRIGGER ... + * + *****************************************************************************/ + +CreateTrigStmt: + CREATE opt_or_replace TRIGGER name TriggerActionTime TriggerEvents ON + qualified_name TriggerReferencing TriggerForSpec TriggerWhen + EXECUTE FUNCTION_or_PROCEDURE func_name '(' TriggerFuncArgs ')' + { + CreateTrigStmt *n = makeNode(CreateTrigStmt); + n->replace = $2; + n->isconstraint = false; + n->trigname = $4; + n->relation = $8; + n->funcname = $14; + n->args = $16; + n->row = $10; + n->timing = $5; + n->events = intVal(linitial($6)); + n->columns = (List *) lsecond($6); + n->whenClause = $11; + n->transitionRels = $9; + n->deferrable = false; + n->initdeferred = false; + n->constrrel = NULL; + $$ = (Node *)n; + } + | CREATE opt_or_replace CONSTRAINT TRIGGER name AFTER TriggerEvents ON + qualified_name OptConstrFromTable ConstraintAttributeSpec + FOR EACH ROW TriggerWhen + EXECUTE FUNCTION_or_PROCEDURE func_name '(' TriggerFuncArgs ')' + { + CreateTrigStmt *n = makeNode(CreateTrigStmt); + n->replace = $2; + if (n->replace) /* not supported, see CreateTrigger */ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("CREATE OR REPLACE CONSTRAINT TRIGGER is not supported"))); + n->isconstraint = true; + n->trigname = $5; + n->relation = $9; + n->funcname = $18; + n->args = $20; + n->row = true; + n->timing = TRIGGER_TYPE_AFTER; + n->events = intVal(linitial($7)); + n->columns = (List *) lsecond($7); + n->whenClause = $15; + n->transitionRels = NIL; + processCASbits($11, @11, "TRIGGER", + &n->deferrable, &n->initdeferred, NULL, + NULL, yyscanner); + n->constrrel = $10; + $$ = (Node *)n; + } + ; + +TriggerActionTime: + BEFORE { $$ = TRIGGER_TYPE_BEFORE; } + | AFTER { $$ = TRIGGER_TYPE_AFTER; } + | INSTEAD OF { $$ = TRIGGER_TYPE_INSTEAD; } + ; + +TriggerEvents: + TriggerOneEvent + { $$ = $1; } + | TriggerEvents OR TriggerOneEvent + { + int events1 = intVal(linitial($1)); + int events2 = intVal(linitial($3)); + List *columns1 = (List *) lsecond($1); + List *columns2 = (List *) lsecond($3); + + if (events1 & events2) + parser_yyerror("duplicate trigger events specified"); + /* + * concat'ing the columns lists loses information about + * which columns went with which event, but so long as + * only UPDATE carries columns and we disallow multiple + * UPDATE items, it doesn't matter. Command execution + * should just ignore the columns for non-UPDATE events. + */ + $$ = list_make2(makeInteger(events1 | events2), + list_concat(columns1, columns2)); + } + ; + +TriggerOneEvent: + INSERT + { $$ = list_make2(makeInteger(TRIGGER_TYPE_INSERT), NIL); } + | DELETE_P + { $$ = list_make2(makeInteger(TRIGGER_TYPE_DELETE), NIL); } + | UPDATE + { $$ = list_make2(makeInteger(TRIGGER_TYPE_UPDATE), NIL); } + | UPDATE OF columnList + { $$ = list_make2(makeInteger(TRIGGER_TYPE_UPDATE), $3); } + | TRUNCATE + { $$ = list_make2(makeInteger(TRIGGER_TYPE_TRUNCATE), NIL); } + ; + +TriggerReferencing: + REFERENCING TriggerTransitions { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + +TriggerTransitions: + TriggerTransition { $$ = list_make1($1); } + | TriggerTransitions TriggerTransition { $$ = lappend($1, $2); } + ; + +TriggerTransition: + TransitionOldOrNew TransitionRowOrTable opt_as TransitionRelName + { + TriggerTransition *n = makeNode(TriggerTransition); + n->name = $4; + n->isNew = $1; + n->isTable = $2; + $$ = (Node *)n; + } + ; + +TransitionOldOrNew: + NEW { $$ = true; } + | OLD { $$ = false; } + ; + +TransitionRowOrTable: + TABLE { $$ = true; } + /* + * According to the standard, lack of a keyword here implies ROW. + * Support for that would require prohibiting ROW entirely here, + * reserving the keyword ROW, and/or requiring AS (instead of + * allowing it to be optional, as the standard specifies) as the + * next token. Requiring ROW seems cleanest and easiest to + * explain. + */ + | ROW { $$ = false; } + ; + +TransitionRelName: + ColId { $$ = $1; } + ; + +TriggerForSpec: + FOR TriggerForOptEach TriggerForType + { + $$ = $3; + } + | /* EMPTY */ + { + /* + * If ROW/STATEMENT not specified, default to + * STATEMENT, per SQL + */ + $$ = false; + } + ; + +TriggerForOptEach: + EACH + | /*EMPTY*/ + ; + +TriggerForType: + ROW { $$ = true; } + | STATEMENT { $$ = false; } + ; + +TriggerWhen: + WHEN '(' a_expr ')' { $$ = $3; } + | /*EMPTY*/ { $$ = NULL; } + ; + +FUNCTION_or_PROCEDURE: + FUNCTION + | PROCEDURE + ; + +TriggerFuncArgs: + TriggerFuncArg { $$ = list_make1($1); } + | TriggerFuncArgs ',' TriggerFuncArg { $$ = lappend($1, $3); } + | /*EMPTY*/ { $$ = NIL; } + ; + +TriggerFuncArg: + Iconst + { + $$ = makeString(psprintf("%d", $1)); + } + | FCONST { $$ = makeString($1); } + | Sconst { $$ = makeString($1); } + | ColLabel { $$ = makeString($1); } + ; + +OptConstrFromTable: + FROM qualified_name { $$ = $2; } + | /*EMPTY*/ { $$ = NULL; } + ; + +ConstraintAttributeSpec: + /*EMPTY*/ + { $$ = 0; } + | ConstraintAttributeSpec ConstraintAttributeElem + { + /* + * We must complain about conflicting options. + * We could, but choose not to, complain about redundant + * options (ie, where $2's bit is already set in $1). + */ + int newspec = $1 | $2; + + /* special message for this case */ + if ((newspec & (CAS_NOT_DEFERRABLE | CAS_INITIALLY_DEFERRED)) == (CAS_NOT_DEFERRABLE | CAS_INITIALLY_DEFERRED)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"), + parser_errposition(@2))); + /* generic message for other conflicts */ + if ((newspec & (CAS_NOT_DEFERRABLE | CAS_DEFERRABLE)) == (CAS_NOT_DEFERRABLE | CAS_DEFERRABLE) || + (newspec & (CAS_INITIALLY_IMMEDIATE | CAS_INITIALLY_DEFERRED)) == (CAS_INITIALLY_IMMEDIATE | CAS_INITIALLY_DEFERRED)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting constraint properties"), + parser_errposition(@2))); + $$ = newspec; + } + ; + +ConstraintAttributeElem: + NOT DEFERRABLE { $$ = CAS_NOT_DEFERRABLE; } + | DEFERRABLE { $$ = CAS_DEFERRABLE; } + | INITIALLY IMMEDIATE { $$ = CAS_INITIALLY_IMMEDIATE; } + | INITIALLY DEFERRED { $$ = CAS_INITIALLY_DEFERRED; } + | NOT VALID { $$ = CAS_NOT_VALID; } + | NO INHERIT { $$ = CAS_NO_INHERIT; } + ; + + +/***************************************************************************** + * + * QUERIES : + * CREATE EVENT TRIGGER ... + * ALTER EVENT TRIGGER ... + * + *****************************************************************************/ + +CreateEventTrigStmt: + CREATE EVENT TRIGGER name ON ColLabel + EXECUTE FUNCTION_or_PROCEDURE func_name '(' ')' + { + CreateEventTrigStmt *n = makeNode(CreateEventTrigStmt); + n->trigname = $4; + n->eventname = $6; + n->whenclause = NULL; + n->funcname = $9; + $$ = (Node *)n; + } + | CREATE EVENT TRIGGER name ON ColLabel + WHEN event_trigger_when_list + EXECUTE FUNCTION_or_PROCEDURE func_name '(' ')' + { + CreateEventTrigStmt *n = makeNode(CreateEventTrigStmt); + n->trigname = $4; + n->eventname = $6; + n->whenclause = $8; + n->funcname = $11; + $$ = (Node *)n; + } + ; + +event_trigger_when_list: + event_trigger_when_item + { $$ = list_make1($1); } + | event_trigger_when_list AND event_trigger_when_item + { $$ = lappend($1, $3); } + ; + +event_trigger_when_item: + ColId IN_P '(' event_trigger_value_list ')' + { $$ = makeDefElem($1, (Node *) $4, @1); } + ; + +event_trigger_value_list: + SCONST + { $$ = list_make1(makeString($1)); } + | event_trigger_value_list ',' SCONST + { $$ = lappend($1, makeString($3)); } + ; + +AlterEventTrigStmt: + ALTER EVENT TRIGGER name enable_trigger + { + AlterEventTrigStmt *n = makeNode(AlterEventTrigStmt); + n->trigname = $4; + n->tgenabled = $5; + $$ = (Node *) n; + } + ; + +enable_trigger: + ENABLE_P { $$ = TRIGGER_FIRES_ON_ORIGIN; } + | ENABLE_P REPLICA { $$ = TRIGGER_FIRES_ON_REPLICA; } + | ENABLE_P ALWAYS { $$ = TRIGGER_FIRES_ALWAYS; } + | DISABLE_P { $$ = TRIGGER_DISABLED; } + ; + +/***************************************************************************** + * + * QUERY : + * CREATE ASSERTION ... + * + *****************************************************************************/ + +CreateAssertionStmt: + CREATE ASSERTION any_name CHECK '(' a_expr ')' ConstraintAttributeSpec + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("CREATE ASSERTION is not yet implemented"))); + + $$ = NULL; + } + ; + + +/***************************************************************************** + * + * QUERY : + * define (aggregate,operator,type) + * + *****************************************************************************/ + +DefineStmt: + CREATE opt_or_replace AGGREGATE func_name aggr_args definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_AGGREGATE; + n->oldstyle = false; + n->replace = $2; + n->defnames = $4; + n->args = $5; + n->definition = $6; + $$ = (Node *)n; + } + | CREATE opt_or_replace AGGREGATE func_name old_aggr_definition + { + /* old-style (pre-8.2) syntax for CREATE AGGREGATE */ + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_AGGREGATE; + n->oldstyle = true; + n->replace = $2; + n->defnames = $4; + n->args = NIL; + n->definition = $5; + $$ = (Node *)n; + } + | CREATE OPERATOR any_operator definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_OPERATOR; + n->oldstyle = false; + n->defnames = $3; + n->args = NIL; + n->definition = $4; + $$ = (Node *)n; + } + | CREATE TYPE_P any_name definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_TYPE; + n->oldstyle = false; + n->defnames = $3; + n->args = NIL; + n->definition = $4; + $$ = (Node *)n; + } + | CREATE TYPE_P any_name + { + /* Shell type (identified by lack of definition) */ + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_TYPE; + n->oldstyle = false; + n->defnames = $3; + n->args = NIL; + n->definition = NIL; + $$ = (Node *)n; + } + | CREATE TYPE_P any_name AS '(' OptTableFuncElementList ')' + { + CompositeTypeStmt *n = makeNode(CompositeTypeStmt); + + /* can't use qualified_name, sigh */ + n->typevar = makeRangeVarFromAnyName($3, @3, yyscanner); + n->coldeflist = $6; + $$ = (Node *)n; + } + | CREATE TYPE_P any_name AS ENUM_P '(' opt_enum_val_list ')' + { + CreateEnumStmt *n = makeNode(CreateEnumStmt); + n->typeName = $3; + n->vals = $7; + $$ = (Node *)n; + } + | CREATE TYPE_P any_name AS RANGE definition + { + CreateRangeStmt *n = makeNode(CreateRangeStmt); + n->typeName = $3; + n->params = $6; + $$ = (Node *)n; + } + | CREATE TEXT_P SEARCH PARSER any_name definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_TSPARSER; + n->args = NIL; + n->defnames = $5; + n->definition = $6; + $$ = (Node *)n; + } + | CREATE TEXT_P SEARCH DICTIONARY any_name definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_TSDICTIONARY; + n->args = NIL; + n->defnames = $5; + n->definition = $6; + $$ = (Node *)n; + } + | CREATE TEXT_P SEARCH TEMPLATE any_name definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_TSTEMPLATE; + n->args = NIL; + n->defnames = $5; + n->definition = $6; + $$ = (Node *)n; + } + | CREATE TEXT_P SEARCH CONFIGURATION any_name definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_TSCONFIGURATION; + n->args = NIL; + n->defnames = $5; + n->definition = $6; + $$ = (Node *)n; + } + | CREATE COLLATION any_name definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_COLLATION; + n->args = NIL; + n->defnames = $3; + n->definition = $4; + $$ = (Node *)n; + } + | CREATE COLLATION IF_P NOT EXISTS any_name definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_COLLATION; + n->args = NIL; + n->defnames = $6; + n->definition = $7; + n->if_not_exists = true; + $$ = (Node *)n; + } + | CREATE COLLATION any_name FROM any_name + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_COLLATION; + n->args = NIL; + n->defnames = $3; + n->definition = list_make1(makeDefElem("from", (Node *) $5, @5)); + $$ = (Node *)n; + } + | CREATE COLLATION IF_P NOT EXISTS any_name FROM any_name + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_COLLATION; + n->args = NIL; + n->defnames = $6; + n->definition = list_make1(makeDefElem("from", (Node *) $8, @8)); + n->if_not_exists = true; + $$ = (Node *)n; + } + ; + +definition: '(' def_list ')' { $$ = $2; } + ; + +def_list: def_elem { $$ = list_make1($1); } + | def_list ',' def_elem { $$ = lappend($1, $3); } + ; + +def_elem: ColLabel '=' def_arg + { + $$ = makeDefElem($1, (Node *) $3, @1); + } + | ColLabel + { + $$ = makeDefElem($1, NULL, @1); + } + ; + +/* Note: any simple identifier will be returned as a type name! */ +def_arg: func_type { $$ = (Node *)$1; } + | reserved_keyword { $$ = (Node *)makeString(pstrdup($1)); } + | qual_all_Op { $$ = (Node *)$1; } + | NumericOnly { $$ = (Node *)$1; } + | Sconst { $$ = (Node *)makeString($1); } + | NONE { $$ = (Node *)makeString(pstrdup($1)); } + ; + +old_aggr_definition: '(' old_aggr_list ')' { $$ = $2; } + ; + +old_aggr_list: old_aggr_elem { $$ = list_make1($1); } + | old_aggr_list ',' old_aggr_elem { $$ = lappend($1, $3); } + ; + +/* + * Must use IDENT here to avoid reduce/reduce conflicts; fortunately none of + * the item names needed in old aggregate definitions are likely to become + * SQL keywords. + */ +old_aggr_elem: IDENT '=' def_arg + { + $$ = makeDefElem($1, (Node *)$3, @1); + } + ; + +opt_enum_val_list: + enum_val_list { $$ = $1; } + | /*EMPTY*/ { $$ = NIL; } + ; + +enum_val_list: Sconst + { $$ = list_make1(makeString($1)); } + | enum_val_list ',' Sconst + { $$ = lappend($1, makeString($3)); } + ; + +/***************************************************************************** + * + * ALTER TYPE enumtype ADD ... + * + *****************************************************************************/ + +AlterEnumStmt: + ALTER TYPE_P any_name ADD_P VALUE_P opt_if_not_exists Sconst + { + AlterEnumStmt *n = makeNode(AlterEnumStmt); + n->typeName = $3; + n->oldVal = NULL; + n->newVal = $7; + n->newValNeighbor = NULL; + n->newValIsAfter = true; + n->skipIfNewValExists = $6; + $$ = (Node *) n; + } + | ALTER TYPE_P any_name ADD_P VALUE_P opt_if_not_exists Sconst BEFORE Sconst + { + AlterEnumStmt *n = makeNode(AlterEnumStmt); + n->typeName = $3; + n->oldVal = NULL; + n->newVal = $7; + n->newValNeighbor = $9; + n->newValIsAfter = false; + n->skipIfNewValExists = $6; + $$ = (Node *) n; + } + | ALTER TYPE_P any_name ADD_P VALUE_P opt_if_not_exists Sconst AFTER Sconst + { + AlterEnumStmt *n = makeNode(AlterEnumStmt); + n->typeName = $3; + n->oldVal = NULL; + n->newVal = $7; + n->newValNeighbor = $9; + n->newValIsAfter = true; + n->skipIfNewValExists = $6; + $$ = (Node *) n; + } + | ALTER TYPE_P any_name RENAME VALUE_P Sconst TO Sconst + { + AlterEnumStmt *n = makeNode(AlterEnumStmt); + n->typeName = $3; + n->oldVal = $6; + n->newVal = $8; + n->newValNeighbor = NULL; + n->newValIsAfter = false; + n->skipIfNewValExists = false; + $$ = (Node *) n; + } + ; + +opt_if_not_exists: IF_P NOT EXISTS { $$ = true; } + | /* EMPTY */ { $$ = false; } + ; + + +/***************************************************************************** + * + * QUERIES : + * CREATE OPERATOR CLASS ... + * CREATE OPERATOR FAMILY ... + * ALTER OPERATOR FAMILY ... + * DROP OPERATOR CLASS ... + * DROP OPERATOR FAMILY ... + * + *****************************************************************************/ + +CreateOpClassStmt: + CREATE OPERATOR CLASS any_name opt_default FOR TYPE_P Typename + USING name opt_opfamily AS opclass_item_list + { + CreateOpClassStmt *n = makeNode(CreateOpClassStmt); + n->opclassname = $4; + n->isDefault = $5; + n->datatype = $8; + n->amname = $10; + n->opfamilyname = $11; + n->items = $13; + $$ = (Node *) n; + } + ; + +opclass_item_list: + opclass_item { $$ = list_make1($1); } + | opclass_item_list ',' opclass_item { $$ = lappend($1, $3); } + ; + +opclass_item: + OPERATOR Iconst any_operator opclass_purpose opt_recheck + { + CreateOpClassItem *n = makeNode(CreateOpClassItem); + ObjectWithArgs *owa = makeNode(ObjectWithArgs); + owa->objname = $3; + owa->objargs = NIL; + n->itemtype = OPCLASS_ITEM_OPERATOR; + n->name = owa; + n->number = $2; + n->order_family = $4; + $$ = (Node *) n; + } + | OPERATOR Iconst operator_with_argtypes opclass_purpose + opt_recheck + { + CreateOpClassItem *n = makeNode(CreateOpClassItem); + n->itemtype = OPCLASS_ITEM_OPERATOR; + n->name = $3; + n->number = $2; + n->order_family = $4; + $$ = (Node *) n; + } + | FUNCTION Iconst function_with_argtypes + { + CreateOpClassItem *n = makeNode(CreateOpClassItem); + n->itemtype = OPCLASS_ITEM_FUNCTION; + n->name = $3; + n->number = $2; + $$ = (Node *) n; + } + | FUNCTION Iconst '(' type_list ')' function_with_argtypes + { + CreateOpClassItem *n = makeNode(CreateOpClassItem); + n->itemtype = OPCLASS_ITEM_FUNCTION; + n->name = $6; + n->number = $2; + n->class_args = $4; + $$ = (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_opfamily: FAMILY any_name { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + +opclass_purpose: FOR SEARCH { $$ = NIL; } + | FOR ORDER BY any_name { $$ = $4; } + | /*EMPTY*/ { $$ = NIL; } + ; + +opt_recheck: RECHECK + { + /* + * RECHECK no longer does anything in opclass definitions, + * but we still accept it to ease porting of old database + * dumps. + */ + ereport(NOTICE, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("RECHECK is no longer required"), + errhint("Update your data type."), + parser_errposition(@1))); + $$ = true; + } + | /*EMPTY*/ { $$ = false; } + ; + + +CreateOpFamilyStmt: + CREATE OPERATOR FAMILY any_name USING name + { + CreateOpFamilyStmt *n = makeNode(CreateOpFamilyStmt); + n->opfamilyname = $4; + n->amname = $6; + $$ = (Node *) n; + } + ; + +AlterOpFamilyStmt: + ALTER OPERATOR FAMILY any_name USING name ADD_P opclass_item_list + { + AlterOpFamilyStmt *n = makeNode(AlterOpFamilyStmt); + n->opfamilyname = $4; + n->amname = $6; + n->isDrop = false; + n->items = $8; + $$ = (Node *) n; + } + | ALTER OPERATOR FAMILY any_name USING name DROP opclass_drop_list + { + AlterOpFamilyStmt *n = makeNode(AlterOpFamilyStmt); + n->opfamilyname = $4; + n->amname = $6; + n->isDrop = true; + n->items = $8; + $$ = (Node *) n; + } + ; + +opclass_drop_list: + opclass_drop { $$ = list_make1($1); } + | opclass_drop_list ',' opclass_drop { $$ = lappend($1, $3); } + ; + +opclass_drop: + OPERATOR Iconst '(' type_list ')' + { + CreateOpClassItem *n = makeNode(CreateOpClassItem); + n->itemtype = OPCLASS_ITEM_OPERATOR; + n->number = $2; + n->class_args = $4; + $$ = (Node *) n; + } + | FUNCTION Iconst '(' type_list ')' + { + CreateOpClassItem *n = makeNode(CreateOpClassItem); + n->itemtype = OPCLASS_ITEM_FUNCTION; + n->number = $2; + n->class_args = $4; + $$ = (Node *) n; + } + ; + + +DropOpClassStmt: + DROP OPERATOR CLASS any_name USING name opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->objects = list_make1(lcons(makeString($6), $4)); + n->removeType = OBJECT_OPCLASS; + n->behavior = $7; + n->missing_ok = false; + n->concurrent = false; + $$ = (Node *) n; + } + | DROP OPERATOR CLASS IF_P EXISTS any_name USING name opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->objects = list_make1(lcons(makeString($8), $6)); + n->removeType = OBJECT_OPCLASS; + n->behavior = $9; + n->missing_ok = true; + n->concurrent = false; + $$ = (Node *) n; + } + ; + +DropOpFamilyStmt: + DROP OPERATOR FAMILY any_name USING name opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->objects = list_make1(lcons(makeString($6), $4)); + n->removeType = OBJECT_OPFAMILY; + n->behavior = $7; + n->missing_ok = false; + n->concurrent = false; + $$ = (Node *) n; + } + | DROP OPERATOR FAMILY IF_P EXISTS any_name USING name opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->objects = list_make1(lcons(makeString($8), $6)); + n->removeType = OBJECT_OPFAMILY; + n->behavior = $9; + n->missing_ok = true; + n->concurrent = false; + $$ = (Node *) n; + } + ; + + +/***************************************************************************** + * + * QUERY: + * + * DROP OWNED BY username [, username ...] [ RESTRICT | CASCADE ] + * REASSIGN OWNED BY username [, username ...] TO username + * + *****************************************************************************/ +DropOwnedStmt: + DROP OWNED BY role_list opt_drop_behavior + { + DropOwnedStmt *n = makeNode(DropOwnedStmt); + n->roles = $4; + n->behavior = $5; + $$ = (Node *)n; + } + ; + +ReassignOwnedStmt: + REASSIGN OWNED BY role_list TO RoleSpec + { + ReassignOwnedStmt *n = makeNode(ReassignOwnedStmt); + n->roles = $4; + n->newrole = $6; + $$ = (Node *)n; + } + ; + +/***************************************************************************** + * + * QUERY: + * + * DROP itemtype [ IF EXISTS ] itemname [, itemname ...] + * [ RESTRICT | CASCADE ] + * + *****************************************************************************/ + +DropStmt: DROP object_type_any_name IF_P EXISTS any_name_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = $2; + n->missing_ok = true; + n->objects = $5; + n->behavior = $6; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP object_type_any_name any_name_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = $2; + n->missing_ok = false; + n->objects = $3; + n->behavior = $4; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP drop_type_name IF_P EXISTS name_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = $2; + n->missing_ok = true; + n->objects = $5; + n->behavior = $6; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP drop_type_name name_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = $2; + n->missing_ok = false; + n->objects = $3; + n->behavior = $4; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP object_type_name_on_any_name name ON any_name opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = $2; + n->objects = list_make1(lappend($5, makeString($3))); + n->behavior = $6; + n->missing_ok = false; + n->concurrent = false; + $$ = (Node *) n; + } + | DROP object_type_name_on_any_name IF_P EXISTS name ON any_name opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = $2; + n->objects = list_make1(lappend($7, makeString($5))); + n->behavior = $8; + n->missing_ok = true; + n->concurrent = false; + $$ = (Node *) n; + } + | DROP TYPE_P type_name_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_TYPE; + n->missing_ok = false; + n->objects = $3; + n->behavior = $4; + n->concurrent = false; + $$ = (Node *) n; + } + | DROP TYPE_P IF_P EXISTS type_name_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_TYPE; + n->missing_ok = true; + n->objects = $5; + n->behavior = $6; + n->concurrent = false; + $$ = (Node *) n; + } + | DROP DOMAIN_P type_name_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_DOMAIN; + n->missing_ok = false; + n->objects = $3; + n->behavior = $4; + n->concurrent = false; + $$ = (Node *) n; + } + | DROP DOMAIN_P IF_P EXISTS type_name_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_DOMAIN; + n->missing_ok = true; + n->objects = $5; + n->behavior = $6; + n->concurrent = false; + $$ = (Node *) n; + } + | DROP INDEX CONCURRENTLY any_name_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_INDEX; + n->missing_ok = false; + n->objects = $4; + n->behavior = $5; + n->concurrent = true; + $$ = (Node *)n; + } + | DROP INDEX CONCURRENTLY IF_P EXISTS any_name_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_INDEX; + n->missing_ok = true; + n->objects = $6; + n->behavior = $7; + n->concurrent = true; + $$ = (Node *)n; + } + ; + +/* object types taking any_name/any_name_list */ +object_type_any_name: + TABLE { $$ = OBJECT_TABLE; } + | SEQUENCE { $$ = OBJECT_SEQUENCE; } + | VIEW { $$ = OBJECT_VIEW; } + | MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; } + | INDEX { $$ = OBJECT_INDEX; } + | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } + | COLLATION { $$ = OBJECT_COLLATION; } + | CONVERSION_P { $$ = OBJECT_CONVERSION; } + | STATISTICS { $$ = OBJECT_STATISTIC_EXT; } + | TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; } + | TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; } + | TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; } + | TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; } + ; + +/* + * object types taking name/name_list + * + * DROP handles some of them separately + */ + +object_type_name: + drop_type_name { $$ = $1; } + | DATABASE { $$ = OBJECT_DATABASE; } + | ROLE { $$ = OBJECT_ROLE; } + | SUBSCRIPTION { $$ = OBJECT_SUBSCRIPTION; } + | TABLESPACE { $$ = OBJECT_TABLESPACE; } + ; + +drop_type_name: + ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; } + | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } + | EXTENSION { $$ = OBJECT_EXTENSION; } + | FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; } + | opt_procedural LANGUAGE { $$ = OBJECT_LANGUAGE; } + | PUBLICATION { $$ = OBJECT_PUBLICATION; } + | SCHEMA { $$ = OBJECT_SCHEMA; } + | SERVER { $$ = OBJECT_FOREIGN_SERVER; } + ; + +/* object types attached to a table */ +object_type_name_on_any_name: + POLICY { $$ = OBJECT_POLICY; } + | RULE { $$ = OBJECT_RULE; } + | TRIGGER { $$ = OBJECT_TRIGGER; } + ; + +any_name_list: + any_name { $$ = list_make1($1); } + | any_name_list ',' any_name { $$ = lappend($1, $3); } + ; + +any_name: ColId { $$ = list_make1(makeString($1)); } + | ColId attrs { $$ = lcons(makeString($1), $2); } + ; + +attrs: '.' attr_name + { $$ = list_make1(makeString($2)); } + | attrs '.' attr_name + { $$ = lappend($1, makeString($3)); } + ; + +type_name_list: + Typename { $$ = list_make1($1); } + | type_name_list ',' Typename { $$ = lappend($1, $3); } + ; + +/***************************************************************************** + * + * QUERY: + * truncate table relname1, relname2, ... + * + *****************************************************************************/ + +TruncateStmt: + TRUNCATE opt_table relation_expr_list opt_restart_seqs opt_drop_behavior + { + TruncateStmt *n = makeNode(TruncateStmt); + n->relations = $3; + n->restart_seqs = $4; + n->behavior = $5; + $$ = (Node *)n; + } + ; + +opt_restart_seqs: + CONTINUE_P IDENTITY_P { $$ = false; } + | RESTART IDENTITY_P { $$ = true; } + | /* EMPTY */ { $$ = false; } + ; + +/***************************************************************************** + * + * COMMENT ON <object> IS <text> + * + *****************************************************************************/ + +CommentStmt: + COMMENT ON object_type_any_name any_name IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = $3; + n->object = (Node *) $4; + n->comment = $6; + $$ = (Node *) n; + } + | COMMENT ON COLUMN any_name IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_COLUMN; + n->object = (Node *) $4; + n->comment = $6; + $$ = (Node *) n; + } + | COMMENT ON object_type_name name IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = $3; + n->object = (Node *) makeString($4); + n->comment = $6; + $$ = (Node *) n; + } + | COMMENT ON TYPE_P Typename IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_TYPE; + n->object = (Node *) $4; + n->comment = $6; + $$ = (Node *) n; + } + | COMMENT ON DOMAIN_P Typename IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_DOMAIN; + n->object = (Node *) $4; + n->comment = $6; + $$ = (Node *) n; + } + | COMMENT ON AGGREGATE aggregate_with_argtypes IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_AGGREGATE; + n->object = (Node *) $4; + n->comment = $6; + $$ = (Node *) n; + } + | COMMENT ON FUNCTION function_with_argtypes IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_FUNCTION; + n->object = (Node *) $4; + n->comment = $6; + $$ = (Node *) n; + } + | COMMENT ON OPERATOR operator_with_argtypes IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_OPERATOR; + n->object = (Node *) $4; + n->comment = $6; + $$ = (Node *) n; + } + | COMMENT ON CONSTRAINT name ON any_name IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_TABCONSTRAINT; + n->object = (Node *) lappend($6, makeString($4)); + n->comment = $8; + $$ = (Node *) n; + } + | COMMENT ON CONSTRAINT name ON DOMAIN_P any_name IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_DOMCONSTRAINT; + /* + * should use Typename not any_name in the production, but + * there's a shift/reduce conflict if we do that, so fix it + * up here. + */ + n->object = (Node *) list_make2(makeTypeNameFromNameList($7), makeString($4)); + n->comment = $9; + $$ = (Node *) n; + } + | COMMENT ON object_type_name_on_any_name name ON any_name IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = $3; + n->object = (Node *) lappend($6, makeString($4)); + n->comment = $8; + $$ = (Node *) n; + } + | COMMENT ON PROCEDURE function_with_argtypes IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_PROCEDURE; + n->object = (Node *) $4; + n->comment = $6; + $$ = (Node *) n; + } + | COMMENT ON ROUTINE function_with_argtypes IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_ROUTINE; + n->object = (Node *) $4; + n->comment = $6; + $$ = (Node *) n; + } + | COMMENT ON TRANSFORM FOR Typename LANGUAGE name IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_TRANSFORM; + n->object = (Node *) list_make2($5, makeString($7)); + n->comment = $9; + $$ = (Node *) n; + } + | COMMENT ON OPERATOR CLASS any_name USING name IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_OPCLASS; + n->object = (Node *) lcons(makeString($7), $5); + n->comment = $9; + $$ = (Node *) n; + } + | COMMENT ON OPERATOR FAMILY any_name USING name IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_OPFAMILY; + n->object = (Node *) lcons(makeString($7), $5); + n->comment = $9; + $$ = (Node *) n; + } + | COMMENT ON LARGE_P OBJECT_P NumericOnly IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_LARGEOBJECT; + n->object = (Node *) $5; + n->comment = $7; + $$ = (Node *) n; + } + | COMMENT ON CAST '(' Typename AS Typename ')' IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_CAST; + n->object = (Node *) list_make2($5, $7); + n->comment = $10; + $$ = (Node *) n; + } + ; + +comment_text: + Sconst { $$ = $1; } + | NULL_P { $$ = NULL; } + ; + + +/***************************************************************************** + * + * SECURITY LABEL [FOR <provider>] ON <object> IS <label> + * + * As with COMMENT ON, <object> can refer to various types of database + * objects (e.g. TABLE, COLUMN, etc.). + * + *****************************************************************************/ + +SecLabelStmt: + SECURITY LABEL opt_provider ON object_type_any_name any_name + IS security_label + { + SecLabelStmt *n = makeNode(SecLabelStmt); + n->provider = $3; + n->objtype = $5; + n->object = (Node *) $6; + n->label = $8; + $$ = (Node *) n; + } + | SECURITY LABEL opt_provider ON COLUMN any_name + IS security_label + { + SecLabelStmt *n = makeNode(SecLabelStmt); + n->provider = $3; + n->objtype = OBJECT_COLUMN; + n->object = (Node *) $6; + n->label = $8; + $$ = (Node *) n; + } + | SECURITY LABEL opt_provider ON object_type_name name + IS security_label + { + SecLabelStmt *n = makeNode(SecLabelStmt); + n->provider = $3; + n->objtype = $5; + n->object = (Node *) makeString($6); + n->label = $8; + $$ = (Node *) n; + } + | SECURITY LABEL opt_provider ON TYPE_P Typename + IS security_label + { + SecLabelStmt *n = makeNode(SecLabelStmt); + n->provider = $3; + n->objtype = OBJECT_TYPE; + n->object = (Node *) $6; + n->label = $8; + $$ = (Node *) n; + } + | SECURITY LABEL opt_provider ON DOMAIN_P Typename + IS security_label + { + SecLabelStmt *n = makeNode(SecLabelStmt); + n->provider = $3; + n->objtype = OBJECT_DOMAIN; + n->object = (Node *) $6; + n->label = $8; + $$ = (Node *) n; + } + | SECURITY LABEL opt_provider ON AGGREGATE aggregate_with_argtypes + IS security_label + { + SecLabelStmt *n = makeNode(SecLabelStmt); + n->provider = $3; + n->objtype = OBJECT_AGGREGATE; + n->object = (Node *) $6; + n->label = $8; + $$ = (Node *) n; + } + | SECURITY LABEL opt_provider ON FUNCTION function_with_argtypes + IS security_label + { + SecLabelStmt *n = makeNode(SecLabelStmt); + n->provider = $3; + n->objtype = OBJECT_FUNCTION; + n->object = (Node *) $6; + n->label = $8; + $$ = (Node *) n; + } + | SECURITY LABEL opt_provider ON LARGE_P OBJECT_P NumericOnly + IS security_label + { + SecLabelStmt *n = makeNode(SecLabelStmt); + n->provider = $3; + n->objtype = OBJECT_LARGEOBJECT; + n->object = (Node *) $7; + n->label = $9; + $$ = (Node *) n; + } + | SECURITY LABEL opt_provider ON PROCEDURE function_with_argtypes + IS security_label + { + SecLabelStmt *n = makeNode(SecLabelStmt); + n->provider = $3; + n->objtype = OBJECT_PROCEDURE; + n->object = (Node *) $6; + n->label = $8; + $$ = (Node *) n; + } + | SECURITY LABEL opt_provider ON ROUTINE function_with_argtypes + IS security_label + { + SecLabelStmt *n = makeNode(SecLabelStmt); + n->provider = $3; + n->objtype = OBJECT_ROUTINE; + n->object = (Node *) $6; + n->label = $8; + $$ = (Node *) n; + } + ; + +opt_provider: FOR NonReservedWord_or_Sconst { $$ = $2; } + | /* EMPTY */ { $$ = NULL; } + ; + +security_label: Sconst { $$ = $1; } + | NULL_P { $$ = NULL; } + ; + +/***************************************************************************** + * + * QUERY: + * fetch/move + * + *****************************************************************************/ + +FetchStmt: FETCH fetch_args + { + FetchStmt *n = (FetchStmt *) $2; + n->ismove = false; + $$ = (Node *)n; + } + | MOVE fetch_args + { + FetchStmt *n = (FetchStmt *) $2; + n->ismove = true; + $$ = (Node *)n; + } + ; + +fetch_args: cursor_name + { + FetchStmt *n = makeNode(FetchStmt); + n->portalname = $1; + n->direction = FETCH_FORWARD; + n->howMany = 1; + $$ = (Node *)n; + } + | from_in cursor_name + { + FetchStmt *n = makeNode(FetchStmt); + n->portalname = $2; + n->direction = FETCH_FORWARD; + n->howMany = 1; + $$ = (Node *)n; + } + | NEXT opt_from_in cursor_name + { + FetchStmt *n = makeNode(FetchStmt); + n->portalname = $3; + n->direction = FETCH_FORWARD; + n->howMany = 1; + $$ = (Node *)n; + } + | PRIOR opt_from_in cursor_name + { + FetchStmt *n = makeNode(FetchStmt); + n->portalname = $3; + n->direction = FETCH_BACKWARD; + n->howMany = 1; + $$ = (Node *)n; + } + | FIRST_P opt_from_in cursor_name + { + FetchStmt *n = makeNode(FetchStmt); + n->portalname = $3; + n->direction = FETCH_ABSOLUTE; + n->howMany = 1; + $$ = (Node *)n; + } + | LAST_P opt_from_in cursor_name + { + FetchStmt *n = makeNode(FetchStmt); + n->portalname = $3; + n->direction = FETCH_ABSOLUTE; + n->howMany = -1; + $$ = (Node *)n; + } + | ABSOLUTE_P SignedIconst opt_from_in cursor_name + { + FetchStmt *n = makeNode(FetchStmt); + n->portalname = $4; + n->direction = FETCH_ABSOLUTE; + n->howMany = $2; + $$ = (Node *)n; + } + | RELATIVE_P SignedIconst opt_from_in cursor_name + { + FetchStmt *n = makeNode(FetchStmt); + n->portalname = $4; + n->direction = FETCH_RELATIVE; + n->howMany = $2; + $$ = (Node *)n; + } + | SignedIconst opt_from_in cursor_name + { + FetchStmt *n = makeNode(FetchStmt); + n->portalname = $3; + n->direction = FETCH_FORWARD; + n->howMany = $1; + $$ = (Node *)n; + } + | ALL opt_from_in cursor_name + { + FetchStmt *n = makeNode(FetchStmt); + n->portalname = $3; + n->direction = FETCH_FORWARD; + n->howMany = FETCH_ALL; + $$ = (Node *)n; + } + | FORWARD opt_from_in cursor_name + { + FetchStmt *n = makeNode(FetchStmt); + n->portalname = $3; + n->direction = FETCH_FORWARD; + n->howMany = 1; + $$ = (Node *)n; + } + | FORWARD SignedIconst opt_from_in cursor_name + { + FetchStmt *n = makeNode(FetchStmt); + n->portalname = $4; + n->direction = FETCH_FORWARD; + n->howMany = $2; + $$ = (Node *)n; + } + | FORWARD ALL opt_from_in cursor_name + { + FetchStmt *n = makeNode(FetchStmt); + n->portalname = $4; + n->direction = FETCH_FORWARD; + n->howMany = FETCH_ALL; + $$ = (Node *)n; + } + | BACKWARD opt_from_in cursor_name + { + FetchStmt *n = makeNode(FetchStmt); + n->portalname = $3; + n->direction = FETCH_BACKWARD; + n->howMany = 1; + $$ = (Node *)n; + } + | BACKWARD SignedIconst opt_from_in cursor_name + { + FetchStmt *n = makeNode(FetchStmt); + n->portalname = $4; + n->direction = FETCH_BACKWARD; + n->howMany = $2; + $$ = (Node *)n; + } + | BACKWARD ALL opt_from_in cursor_name + { + FetchStmt *n = makeNode(FetchStmt); + n->portalname = $4; + n->direction = FETCH_BACKWARD; + n->howMany = FETCH_ALL; + $$ = (Node *)n; + } + ; + +from_in: FROM + | IN_P + ; + +opt_from_in: from_in + | /* EMPTY */ + ; + + +/***************************************************************************** + * + * GRANT and REVOKE statements + * + *****************************************************************************/ + +GrantStmt: GRANT privileges ON privilege_target TO grantee_list + opt_grant_grant_option opt_granted_by + { + GrantStmt *n = makeNode(GrantStmt); + n->is_grant = true; + n->privileges = $2; + n->targtype = ($4)->targtype; + n->objtype = ($4)->objtype; + n->objects = ($4)->objs; + n->grantees = $6; + n->grant_option = $7; + n->grantor = $8; + $$ = (Node*)n; + } + ; + +RevokeStmt: + REVOKE privileges ON privilege_target + FROM grantee_list opt_granted_by opt_drop_behavior + { + GrantStmt *n = makeNode(GrantStmt); + n->is_grant = false; + n->grant_option = false; + n->privileges = $2; + n->targtype = ($4)->targtype; + n->objtype = ($4)->objtype; + n->objects = ($4)->objs; + n->grantees = $6; + n->grantor = $7; + n->behavior = $8; + $$ = (Node *)n; + } + | REVOKE GRANT OPTION FOR privileges ON privilege_target + FROM grantee_list opt_granted_by opt_drop_behavior + { + GrantStmt *n = makeNode(GrantStmt); + n->is_grant = false; + n->grant_option = true; + n->privileges = $5; + n->targtype = ($7)->targtype; + n->objtype = ($7)->objtype; + n->objects = ($7)->objs; + n->grantees = $9; + n->grantor = $10; + n->behavior = $11; + $$ = (Node *)n; + } + ; + + +/* + * Privilege names are represented as strings; the validity of the privilege + * names gets checked at execution. This is a bit annoying but we have little + * choice because of the syntactic conflict with lists of role names in + * GRANT/REVOKE. What's more, we have to call out in the "privilege" + * production any reserved keywords that need to be usable as privilege names. + */ + +/* either ALL [PRIVILEGES] or a list of individual privileges */ +privileges: privilege_list + { $$ = $1; } + | ALL + { $$ = NIL; } + | ALL PRIVILEGES + { $$ = NIL; } + | ALL '(' columnList ')' + { + AccessPriv *n = makeNode(AccessPriv); + n->priv_name = NULL; + n->cols = $3; + $$ = list_make1(n); + } + | ALL PRIVILEGES '(' columnList ')' + { + AccessPriv *n = makeNode(AccessPriv); + n->priv_name = NULL; + n->cols = $4; + $$ = list_make1(n); + } + ; + +privilege_list: privilege { $$ = list_make1($1); } + | privilege_list ',' privilege { $$ = lappend($1, $3); } + ; + +privilege: SELECT opt_column_list + { + AccessPriv *n = makeNode(AccessPriv); + n->priv_name = pstrdup($1); + n->cols = $2; + $$ = n; + } + | REFERENCES opt_column_list + { + AccessPriv *n = makeNode(AccessPriv); + n->priv_name = pstrdup($1); + n->cols = $2; + $$ = n; + } + | CREATE opt_column_list + { + AccessPriv *n = makeNode(AccessPriv); + n->priv_name = pstrdup($1); + n->cols = $2; + $$ = n; + } + | ColId opt_column_list + { + AccessPriv *n = makeNode(AccessPriv); + n->priv_name = $1; + n->cols = $2; + $$ = n; + } + ; + + +/* 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 = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = OBJECT_TABLE; + n->objs = $1; + $$ = n; + } + | TABLE qualified_name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = OBJECT_TABLE; + n->objs = $2; + $$ = n; + } + | SEQUENCE qualified_name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = OBJECT_SEQUENCE; + n->objs = $2; + $$ = n; + } + | FOREIGN DATA_P WRAPPER name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = OBJECT_FDW; + n->objs = $4; + $$ = n; + } + | FOREIGN SERVER name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = OBJECT_FOREIGN_SERVER; + n->objs = $3; + $$ = n; + } + | FUNCTION function_with_argtypes_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = OBJECT_FUNCTION; + n->objs = $2; + $$ = n; + } + | PROCEDURE function_with_argtypes_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = OBJECT_PROCEDURE; + n->objs = $2; + $$ = n; + } + | ROUTINE function_with_argtypes_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = OBJECT_ROUTINE; + n->objs = $2; + $$ = n; + } + | DATABASE name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = OBJECT_DATABASE; + n->objs = $2; + $$ = n; + } + | DOMAIN_P any_name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = OBJECT_DOMAIN; + n->objs = $2; + $$ = n; + } + | LANGUAGE name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = OBJECT_LANGUAGE; + n->objs = $2; + $$ = n; + } + | LARGE_P OBJECT_P NumericOnly_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = OBJECT_LARGEOBJECT; + n->objs = $3; + $$ = n; + } + | SCHEMA name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = OBJECT_SCHEMA; + n->objs = $2; + $$ = n; + } + | TABLESPACE name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = OBJECT_TABLESPACE; + n->objs = $2; + $$ = n; + } + | TYPE_P any_name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = OBJECT_TYPE; + n->objs = $2; + $$ = n; + } + | ALL TABLES IN_P SCHEMA name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_ALL_IN_SCHEMA; + n->objtype = OBJECT_TABLE; + n->objs = $5; + $$ = n; + } + | ALL SEQUENCES IN_P SCHEMA name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_ALL_IN_SCHEMA; + n->objtype = OBJECT_SEQUENCE; + n->objs = $5; + $$ = n; + } + | ALL FUNCTIONS IN_P SCHEMA name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_ALL_IN_SCHEMA; + n->objtype = OBJECT_FUNCTION; + n->objs = $5; + $$ = n; + } + | ALL PROCEDURES IN_P SCHEMA name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_ALL_IN_SCHEMA; + n->objtype = OBJECT_PROCEDURE; + n->objs = $5; + $$ = n; + } + | ALL ROUTINES IN_P SCHEMA name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_ALL_IN_SCHEMA; + n->objtype = OBJECT_ROUTINE; + n->objs = $5; + $$ = n; + } + ; + + +grantee_list: + grantee { $$ = list_make1($1); } + | grantee_list ',' grantee { $$ = lappend($1, $3); } + ; + +grantee: + RoleSpec { $$ = $1; } + | GROUP_P RoleSpec { $$ = $2; } + ; + + +opt_grant_grant_option: + WITH GRANT OPTION { $$ = true; } + | /*EMPTY*/ { $$ = false; } + ; + +/***************************************************************************** + * + * GRANT and REVOKE ROLE statements + * + *****************************************************************************/ + +GrantRoleStmt: + GRANT privilege_list TO role_list opt_grant_admin_option opt_granted_by + { + GrantRoleStmt *n = makeNode(GrantRoleStmt); + n->is_grant = true; + n->granted_roles = $2; + n->grantee_roles = $4; + n->admin_opt = $5; + n->grantor = $6; + $$ = (Node*)n; + } + ; + +RevokeRoleStmt: + REVOKE privilege_list FROM role_list opt_granted_by opt_drop_behavior + { + GrantRoleStmt *n = makeNode(GrantRoleStmt); + n->is_grant = false; + n->admin_opt = false; + n->granted_roles = $2; + n->grantee_roles = $4; + n->behavior = $6; + $$ = (Node*)n; + } + | REVOKE ADMIN OPTION FOR privilege_list FROM role_list opt_granted_by opt_drop_behavior + { + GrantRoleStmt *n = makeNode(GrantRoleStmt); + n->is_grant = false; + n->admin_opt = true; + n->granted_roles = $5; + n->grantee_roles = $7; + n->behavior = $9; + $$ = (Node*)n; + } + ; + +opt_grant_admin_option: WITH ADMIN OPTION { $$ = true; } + | /*EMPTY*/ { $$ = false; } + ; + +opt_granted_by: GRANTED BY RoleSpec { $$ = $3; } + | /*EMPTY*/ { $$ = NULL; } + ; + +/***************************************************************************** + * + * ALTER DEFAULT PRIVILEGES statement + * + *****************************************************************************/ + +AlterDefaultPrivilegesStmt: + ALTER DEFAULT PRIVILEGES DefACLOptionList DefACLAction + { + AlterDefaultPrivilegesStmt *n = makeNode(AlterDefaultPrivilegesStmt); + n->options = $4; + n->action = (GrantStmt *) $5; + $$ = (Node*)n; + } + ; + +DefACLOptionList: + DefACLOptionList DefACLOption { $$ = lappend($1, $2); } + | /* EMPTY */ { $$ = NIL; } + ; + +DefACLOption: + IN_P SCHEMA name_list + { + $$ = makeDefElem("schemas", (Node *)$3, @1); + } + | FOR ROLE role_list + { + $$ = makeDefElem("roles", (Node *)$3, @1); + } + | FOR USER role_list + { + $$ = makeDefElem("roles", (Node *)$3, @1); + } + ; + +/* + * This should match GRANT/REVOKE, except that individual target objects + * are not mentioned and we only allow a subset of object types. + */ +DefACLAction: + GRANT privileges ON defacl_privilege_target TO grantee_list + opt_grant_grant_option + { + GrantStmt *n = makeNode(GrantStmt); + n->is_grant = true; + n->privileges = $2; + n->targtype = ACL_TARGET_DEFAULTS; + n->objtype = $4; + n->objects = NIL; + n->grantees = $6; + n->grant_option = $7; + $$ = (Node*)n; + } + | REVOKE privileges ON defacl_privilege_target + FROM grantee_list opt_drop_behavior + { + GrantStmt *n = makeNode(GrantStmt); + n->is_grant = false; + n->grant_option = false; + n->privileges = $2; + n->targtype = ACL_TARGET_DEFAULTS; + n->objtype = $4; + n->objects = NIL; + n->grantees = $6; + n->behavior = $7; + $$ = (Node *)n; + } + | REVOKE GRANT OPTION FOR privileges ON defacl_privilege_target + FROM grantee_list opt_drop_behavior + { + GrantStmt *n = makeNode(GrantStmt); + n->is_grant = false; + n->grant_option = true; + n->privileges = $5; + n->targtype = ACL_TARGET_DEFAULTS; + n->objtype = $7; + n->objects = NIL; + n->grantees = $9; + n->behavior = $10; + $$ = (Node *)n; + } + ; + +defacl_privilege_target: + TABLES { $$ = OBJECT_TABLE; } + | FUNCTIONS { $$ = OBJECT_FUNCTION; } + | ROUTINES { $$ = OBJECT_FUNCTION; } + | SEQUENCES { $$ = OBJECT_SEQUENCE; } + | TYPES_P { $$ = OBJECT_TYPE; } + | SCHEMAS { $$ = OBJECT_SCHEMA; } + ; + + +/***************************************************************************** + * + * QUERY: CREATE INDEX + * + * Note: we cannot put TABLESPACE clause after WHERE clause unless we are + * willing to make TABLESPACE a fully reserved word. + *****************************************************************************/ + +IndexStmt: CREATE opt_unique INDEX opt_concurrently opt_index_name + ON relation_expr access_method_clause '(' index_params ')' + opt_include opt_reloptions OptTableSpace where_clause + { + IndexStmt *n = makeNode(IndexStmt); + n->unique = $2; + n->concurrent = $4; + n->idxname = $5; + n->relation = $7; + n->accessMethod = $8; + n->indexParams = $10; + n->indexIncludingParams = $12; + n->options = $13; + n->tableSpace = $14; + n->whereClause = $15; + n->excludeOpNames = NIL; + n->idxcomment = NULL; + n->indexOid = InvalidOid; + n->oldNode = InvalidOid; + n->oldCreateSubid = InvalidSubTransactionId; + n->oldFirstRelfilenodeSubid = InvalidSubTransactionId; + n->primary = false; + n->isconstraint = false; + n->deferrable = false; + n->initdeferred = false; + n->transformed = false; + n->if_not_exists = false; + n->reset_default_tblspc = false; + $$ = (Node *)n; + } + | CREATE opt_unique INDEX opt_concurrently IF_P NOT EXISTS name + ON relation_expr access_method_clause '(' index_params ')' + opt_include opt_reloptions OptTableSpace where_clause + { + IndexStmt *n = makeNode(IndexStmt); + n->unique = $2; + n->concurrent = $4; + n->idxname = $8; + n->relation = $10; + n->accessMethod = $11; + n->indexParams = $13; + n->indexIncludingParams = $15; + n->options = $16; + n->tableSpace = $17; + n->whereClause = $18; + n->excludeOpNames = NIL; + n->idxcomment = NULL; + n->indexOid = InvalidOid; + n->oldNode = InvalidOid; + n->oldCreateSubid = InvalidSubTransactionId; + n->oldFirstRelfilenodeSubid = InvalidSubTransactionId; + n->primary = false; + n->isconstraint = false; + n->deferrable = false; + n->initdeferred = false; + n->transformed = false; + n->if_not_exists = true; + n->reset_default_tblspc = false; + $$ = (Node *)n; + } + ; + +opt_unique: + UNIQUE { $$ = true; } + | /*EMPTY*/ { $$ = false; } + ; + +opt_concurrently: + CONCURRENTLY { $$ = true; } + | /*EMPTY*/ { $$ = false; } + ; + +opt_index_name: + name { $$ = $1; } + | /*EMPTY*/ { $$ = NULL; } + ; + +access_method_clause: + USING name { $$ = $2; } + | /*EMPTY*/ { $$ = DEFAULT_INDEX_TYPE; } + ; + +index_params: index_elem { $$ = list_make1($1); } + | index_params ',' index_elem { $$ = lappend($1, $3); } + ; + + +index_elem_options: + opt_collate opt_class opt_asc_desc opt_nulls_order + { + $$ = makeNode(IndexElem); + $$->name = NULL; + $$->expr = NULL; + $$->indexcolname = NULL; + $$->collation = $1; + $$->opclass = $2; + $$->opclassopts = NIL; + $$->ordering = $3; + $$->nulls_ordering = $4; + } + | opt_collate any_name reloptions opt_asc_desc opt_nulls_order + { + $$ = makeNode(IndexElem); + $$->name = NULL; + $$->expr = NULL; + $$->indexcolname = NULL; + $$->collation = $1; + $$->opclass = $2; + $$->opclassopts = $3; + $$->ordering = $4; + $$->nulls_ordering = $5; + } + ; + +/* + * Index attributes can be either simple column references, or arbitrary + * expressions in parens. For backwards-compatibility reasons, we allow + * an expression that's just a function call to be written without parens. + */ +index_elem: ColId index_elem_options + { + $$ = $2; + $$->name = $1; + } + | func_expr_windowless index_elem_options + { + $$ = $2; + $$->expr = $1; + } + | '(' a_expr ')' index_elem_options + { + $$ = $4; + $$->expr = $2; + } + ; + +opt_include: INCLUDE '(' index_including_params ')' { $$ = $3; } + | /* EMPTY */ { $$ = NIL; } + ; + +index_including_params: index_elem { $$ = list_make1($1); } + | index_including_params ',' index_elem { $$ = lappend($1, $3); } + ; + +opt_collate: COLLATE any_name { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + +opt_class: any_name { $$ = $1; } + | /*EMPTY*/ { $$ = NIL; } + ; + +opt_asc_desc: ASC { $$ = SORTBY_ASC; } + | DESC { $$ = SORTBY_DESC; } + | /*EMPTY*/ { $$ = SORTBY_DEFAULT; } + ; + +opt_nulls_order: NULLS_LA FIRST_P { $$ = SORTBY_NULLS_FIRST; } + | NULLS_LA LAST_P { $$ = SORTBY_NULLS_LAST; } + | /*EMPTY*/ { $$ = SORTBY_NULLS_DEFAULT; } + ; + + +/***************************************************************************** + * + * QUERY: + * create [or replace] function <fname> + * [(<type-1> { , <type-n>})] + * returns <type-r> + * as <filename or code in language as appropriate> + * language <lang> [with parameters] + * + *****************************************************************************/ + +CreateFunctionStmt: + CREATE opt_or_replace FUNCTION func_name func_args_with_defaults + RETURNS func_return opt_createfunc_opt_list opt_routine_body + { + CreateFunctionStmt *n = makeNode(CreateFunctionStmt); + n->is_procedure = false; + n->replace = $2; + n->funcname = $4; + n->parameters = $5; + n->returnType = $7; + n->options = $8; + n->sql_body = $9; + $$ = (Node *)n; + } + | CREATE opt_or_replace FUNCTION func_name func_args_with_defaults + RETURNS TABLE '(' table_func_column_list ')' opt_createfunc_opt_list opt_routine_body + { + CreateFunctionStmt *n = makeNode(CreateFunctionStmt); + n->is_procedure = false; + n->replace = $2; + n->funcname = $4; + n->parameters = mergeTableFuncParameters($5, $9); + n->returnType = TableFuncTypeName($9); + n->returnType->location = @7; + n->options = $11; + n->sql_body = $12; + $$ = (Node *)n; + } + | CREATE opt_or_replace FUNCTION func_name func_args_with_defaults + opt_createfunc_opt_list opt_routine_body + { + CreateFunctionStmt *n = makeNode(CreateFunctionStmt); + n->is_procedure = false; + n->replace = $2; + n->funcname = $4; + n->parameters = $5; + n->returnType = NULL; + n->options = $6; + n->sql_body = $7; + $$ = (Node *)n; + } + | CREATE opt_or_replace PROCEDURE func_name func_args_with_defaults + opt_createfunc_opt_list opt_routine_body + { + CreateFunctionStmt *n = makeNode(CreateFunctionStmt); + n->is_procedure = true; + n->replace = $2; + n->funcname = $4; + n->parameters = $5; + n->returnType = NULL; + n->options = $6; + n->sql_body = $7; + $$ = (Node *)n; + } + ; + +opt_or_replace: + OR REPLACE { $$ = true; } + | /*EMPTY*/ { $$ = false; } + ; + +func_args: '(' func_args_list ')' { $$ = $2; } + | '(' ')' { $$ = NIL; } + ; + +func_args_list: + func_arg { $$ = list_make1($1); } + | func_args_list ',' func_arg { $$ = lappend($1, $3); } + ; + +function_with_argtypes_list: + function_with_argtypes { $$ = list_make1($1); } + | function_with_argtypes_list ',' function_with_argtypes + { $$ = lappend($1, $3); } + ; + +function_with_argtypes: + func_name func_args + { + ObjectWithArgs *n = makeNode(ObjectWithArgs); + n->objname = $1; + n->objargs = extractArgTypes($2); + n->objfuncargs = $2; + $$ = n; + } + /* + * Because of reduce/reduce conflicts, we can't use func_name + * below, but we can write it out the long way, which actually + * allows more cases. + */ + | type_func_name_keyword + { + ObjectWithArgs *n = makeNode(ObjectWithArgs); + n->objname = list_make1(makeString(pstrdup($1))); + n->args_unspecified = true; + $$ = n; + } + | ColId + { + ObjectWithArgs *n = makeNode(ObjectWithArgs); + n->objname = list_make1(makeString($1)); + n->args_unspecified = true; + $$ = n; + } + | ColId indirection + { + ObjectWithArgs *n = makeNode(ObjectWithArgs); + n->objname = check_func_name(lcons(makeString($1), $2), + yyscanner); + n->args_unspecified = true; + $$ = n; + } + ; + +/* + * func_args_with_defaults is separate because we only want to accept + * defaults in CREATE FUNCTION, not in ALTER etc. + */ +func_args_with_defaults: + '(' func_args_with_defaults_list ')' { $$ = $2; } + | '(' ')' { $$ = NIL; } + ; + +func_args_with_defaults_list: + func_arg_with_default { $$ = list_make1($1); } + | func_args_with_defaults_list ',' func_arg_with_default + { $$ = lappend($1, $3); } + ; + +/* + * The style with arg_class first is SQL99 standard, but Oracle puts + * param_name first; accept both since it's likely people will try both + * anyway. Don't bother trying to save productions by letting arg_class + * have an empty alternative ... you'll get shift/reduce conflicts. + * + * We can catch over-specified arguments here if we want to, + * but for now better to silently swallow typmod, etc. + * - thomas 2000-03-22 + */ +func_arg: + arg_class param_name func_type + { + FunctionParameter *n = makeNode(FunctionParameter); + n->name = $2; + n->argType = $3; + n->mode = $1; + n->defexpr = NULL; + $$ = n; + } + | param_name arg_class func_type + { + FunctionParameter *n = makeNode(FunctionParameter); + n->name = $1; + n->argType = $3; + n->mode = $2; + n->defexpr = NULL; + $$ = n; + } + | param_name func_type + { + FunctionParameter *n = makeNode(FunctionParameter); + n->name = $1; + n->argType = $2; + n->mode = FUNC_PARAM_DEFAULT; + n->defexpr = NULL; + $$ = n; + } + | arg_class func_type + { + FunctionParameter *n = makeNode(FunctionParameter); + n->name = NULL; + n->argType = $2; + n->mode = $1; + n->defexpr = NULL; + $$ = n; + } + | func_type + { + FunctionParameter *n = makeNode(FunctionParameter); + n->name = NULL; + n->argType = $1; + n->mode = FUNC_PARAM_DEFAULT; + n->defexpr = NULL; + $$ = n; + } + ; + +/* INOUT is SQL99 standard, IN OUT is for Oracle compatibility */ +arg_class: IN_P { $$ = FUNC_PARAM_IN; } + | OUT_P { $$ = FUNC_PARAM_OUT; } + | INOUT { $$ = FUNC_PARAM_INOUT; } + | IN_P OUT_P { $$ = FUNC_PARAM_INOUT; } + | VARIADIC { $$ = FUNC_PARAM_VARIADIC; } + ; + +/* + * Ideally param_name should be ColId, but that causes too many conflicts. + */ +param_name: type_function_name + ; + +func_return: + func_type + { + /* We can catch over-specified results 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 %TYPE productions here be ColId attrs etc, + * but that causes reduce/reduce conflicts. type_function_name + * is next best choice. + */ +func_type: Typename { $$ = $1; } + | type_function_name attrs '%' TYPE_P + { + $$ = makeTypeNameFromNameList(lcons(makeString($1), $2)); + $$->pct_type = true; + $$->location = @1; + } + | SETOF type_function_name attrs '%' TYPE_P + { + $$ = makeTypeNameFromNameList(lcons(makeString($2), $3)); + $$->pct_type = true; + $$->setof = true; + $$->location = @2; + } + ; + +func_arg_with_default: + func_arg + { + $$ = $1; + } + | func_arg DEFAULT a_expr + { + $$ = $1; + $$->defexpr = $3; + } + | func_arg '=' a_expr + { + $$ = $1; + $$->defexpr = $3; + } + ; + +/* Aggregate args can be most things that function args can be */ +aggr_arg: func_arg + { + if (!($1->mode == FUNC_PARAM_DEFAULT || + $1->mode == FUNC_PARAM_IN || + $1->mode == FUNC_PARAM_VARIADIC)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("aggregates cannot have output arguments"), + parser_errposition(@1))); + $$ = $1; + } + ; + +/* + * The SQL standard offers no guidance on how to declare aggregate argument + * lists, since it doesn't have CREATE AGGREGATE etc. We accept these cases: + * + * (*) - normal agg with no args + * (aggr_arg,...) - normal agg with args + * (ORDER BY aggr_arg,...) - ordered-set agg with no direct args + * (aggr_arg,... ORDER BY aggr_arg,...) - ordered-set agg with direct args + * + * The zero-argument case is spelled with '*' for consistency with COUNT(*). + * + * An additional restriction is that if the direct-args list ends in a + * VARIADIC item, the ordered-args list must contain exactly one item that + * is also VARIADIC with the same type. This allows us to collapse the two + * VARIADIC items into one, which is necessary to represent the aggregate in + * pg_proc. We check this at the grammar stage so that we can return a list + * in which the second VARIADIC item is already discarded, avoiding extra work + * in cases such as DROP AGGREGATE. + * + * The return value of this production is a two-element list, in which the + * first item is a sublist of FunctionParameter nodes (with any duplicate + * VARIADIC item already dropped, as per above) and the second is an integer + * Value node, containing -1 if there was no ORDER BY and otherwise the number + * of argument declarations before the ORDER BY. (If this number is equal + * to the first sublist's length, then we dropped a duplicate VARIADIC item.) + * This representation is passed as-is to CREATE AGGREGATE; for operations + * on existing aggregates, we can just apply extractArgTypes to the first + * sublist. + */ +aggr_args: '(' '*' ')' + { + $$ = list_make2(NIL, makeInteger(-1)); + } + | '(' aggr_args_list ')' + { + $$ = list_make2($2, makeInteger(-1)); + } + | '(' ORDER BY aggr_args_list ')' + { + $$ = list_make2($4, makeInteger(0)); + } + | '(' aggr_args_list ORDER BY aggr_args_list ')' + { + /* this is the only case requiring consistency checking */ + $$ = makeOrderedSetArgs($2, $5, yyscanner); + } + ; + +aggr_args_list: + aggr_arg { $$ = list_make1($1); } + | aggr_args_list ',' aggr_arg { $$ = lappend($1, $3); } + ; + +aggregate_with_argtypes: + func_name aggr_args + { + ObjectWithArgs *n = makeNode(ObjectWithArgs); + n->objname = $1; + n->objargs = extractAggrArgTypes($2); + n->objfuncargs = (List *) linitial($2); + $$ = n; + } + ; + +aggregate_with_argtypes_list: + aggregate_with_argtypes { $$ = list_make1($1); } + | aggregate_with_argtypes_list ',' aggregate_with_argtypes + { $$ = lappend($1, $3); } + ; + +opt_createfunc_opt_list: + createfunc_opt_list + | /*EMPTY*/ { $$ = NIL; } + ; + +createfunc_opt_list: + /* Must be at least one to prevent conflict */ + createfunc_opt_item { $$ = list_make1($1); } + | createfunc_opt_list createfunc_opt_item { $$ = lappend($1, $2); } + ; + +/* + * Options common to both CREATE FUNCTION and ALTER FUNCTION + */ +common_func_opt_item: + CALLED ON NULL_P INPUT_P + { + $$ = makeDefElem("strict", (Node *)makeInteger(false), @1); + } + | RETURNS NULL_P ON NULL_P INPUT_P + { + $$ = makeDefElem("strict", (Node *)makeInteger(true), @1); + } + | STRICT_P + { + $$ = makeDefElem("strict", (Node *)makeInteger(true), @1); + } + | IMMUTABLE + { + $$ = makeDefElem("volatility", (Node *)makeString("immutable"), @1); + } + | STABLE + { + $$ = makeDefElem("volatility", (Node *)makeString("stable"), @1); + } + | VOLATILE + { + $$ = makeDefElem("volatility", (Node *)makeString("volatile"), @1); + } + | EXTERNAL SECURITY DEFINER + { + $$ = makeDefElem("security", (Node *)makeInteger(true), @1); + } + | EXTERNAL SECURITY INVOKER + { + $$ = makeDefElem("security", (Node *)makeInteger(false), @1); + } + | SECURITY DEFINER + { + $$ = makeDefElem("security", (Node *)makeInteger(true), @1); + } + | SECURITY INVOKER + { + $$ = makeDefElem("security", (Node *)makeInteger(false), @1); + } + | LEAKPROOF + { + $$ = makeDefElem("leakproof", (Node *)makeInteger(true), @1); + } + | NOT LEAKPROOF + { + $$ = makeDefElem("leakproof", (Node *)makeInteger(false), @1); + } + | COST NumericOnly + { + $$ = makeDefElem("cost", (Node *)$2, @1); + } + | ROWS NumericOnly + { + $$ = makeDefElem("rows", (Node *)$2, @1); + } + | SUPPORT any_name + { + $$ = makeDefElem("support", (Node *)$2, @1); + } + | FunctionSetResetClause + { + /* we abuse the normal content of a DefElem here */ + $$ = makeDefElem("set", (Node *)$1, @1); + } + | PARALLEL ColId + { + $$ = makeDefElem("parallel", (Node *)makeString($2), @1); + } + ; + +createfunc_opt_item: + AS func_as + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | LANGUAGE NonReservedWord_or_Sconst + { + $$ = makeDefElem("language", (Node *)makeString($2), @1); + } + | TRANSFORM transform_type_list + { + $$ = makeDefElem("transform", (Node *)$2, @1); + } + | WINDOW + { + $$ = makeDefElem("window", (Node *)makeInteger(true), @1); + } + | common_func_opt_item + { + $$ = $1; + } + ; + +func_as: Sconst { $$ = list_make1(makeString($1)); } + | Sconst ',' Sconst + { + $$ = list_make2(makeString($1), makeString($3)); + } + ; + +ReturnStmt: RETURN a_expr + { + ReturnStmt *r = makeNode(ReturnStmt); + r->returnval = (Node *) $2; + $$ = (Node *) r; + } + ; + +opt_routine_body: + ReturnStmt + { + $$ = $1; + } + | BEGIN_P ATOMIC routine_body_stmt_list END_P + { + /* + * A compound statement is stored as a single-item list + * containing the list of statements as its member. That + * way, the parse analysis code can tell apart an empty + * body from no body at all. + */ + $$ = (Node *) list_make1($3); + } + | /*EMPTY*/ + { + $$ = NULL; + } + ; + +routine_body_stmt_list: + routine_body_stmt_list routine_body_stmt ';' + { + /* As in stmtmulti, discard empty statements */ + if ($2 != NULL) + $$ = lappend($1, $2); + else + $$ = $1; + } + | /*EMPTY*/ + { + $$ = NIL; + } + ; + +routine_body_stmt: + stmt + | ReturnStmt + ; + +transform_type_list: + FOR TYPE_P Typename { $$ = list_make1($3); } + | transform_type_list ',' FOR TYPE_P Typename { $$ = lappend($1, $5); } + ; + +opt_definition: + WITH definition { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + +table_func_column: param_name func_type + { + FunctionParameter *n = makeNode(FunctionParameter); + n->name = $1; + n->argType = $2; + n->mode = FUNC_PARAM_TABLE; + n->defexpr = NULL; + $$ = n; + } + ; + +table_func_column_list: + table_func_column + { + $$ = list_make1($1); + } + | table_func_column_list ',' table_func_column + { + $$ = lappend($1, $3); + } + ; + +/***************************************************************************** + * ALTER FUNCTION / ALTER PROCEDURE / ALTER ROUTINE + * + * RENAME and OWNER subcommands are already provided by the generic + * ALTER infrastructure, here we just specify alterations that can + * only be applied to functions. + * + *****************************************************************************/ +AlterFunctionStmt: + ALTER FUNCTION function_with_argtypes alterfunc_opt_list opt_restrict + { + AlterFunctionStmt *n = makeNode(AlterFunctionStmt); + n->objtype = OBJECT_FUNCTION; + n->func = $3; + n->actions = $4; + $$ = (Node *) n; + } + | ALTER PROCEDURE function_with_argtypes alterfunc_opt_list opt_restrict + { + AlterFunctionStmt *n = makeNode(AlterFunctionStmt); + n->objtype = OBJECT_PROCEDURE; + n->func = $3; + n->actions = $4; + $$ = (Node *) n; + } + | ALTER ROUTINE function_with_argtypes alterfunc_opt_list opt_restrict + { + AlterFunctionStmt *n = makeNode(AlterFunctionStmt); + n->objtype = OBJECT_ROUTINE; + n->func = $3; + n->actions = $4; + $$ = (Node *) n; + } + ; + +alterfunc_opt_list: + /* At least one option must be specified */ + common_func_opt_item { $$ = list_make1($1); } + | alterfunc_opt_list common_func_opt_item { $$ = lappend($1, $2); } + ; + +/* Ignored, merely for SQL compliance */ +opt_restrict: + RESTRICT + | /* EMPTY */ + ; + + +/***************************************************************************** + * + * QUERY: + * + * DROP FUNCTION funcname (arg1, arg2, ...) [ RESTRICT | CASCADE ] + * DROP PROCEDURE procname (arg1, arg2, ...) [ RESTRICT | CASCADE ] + * DROP ROUTINE routname (arg1, arg2, ...) [ RESTRICT | CASCADE ] + * DROP AGGREGATE aggname (arg1, ...) [ RESTRICT | CASCADE ] + * DROP OPERATOR opname (leftoperand_typ, rightoperand_typ) [ RESTRICT | CASCADE ] + * + *****************************************************************************/ + +RemoveFuncStmt: + DROP FUNCTION function_with_argtypes_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_FUNCTION; + n->objects = $3; + n->behavior = $4; + n->missing_ok = false; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP FUNCTION IF_P EXISTS function_with_argtypes_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_FUNCTION; + n->objects = $5; + n->behavior = $6; + n->missing_ok = true; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP PROCEDURE function_with_argtypes_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_PROCEDURE; + n->objects = $3; + n->behavior = $4; + n->missing_ok = false; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP PROCEDURE IF_P EXISTS function_with_argtypes_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_PROCEDURE; + n->objects = $5; + n->behavior = $6; + n->missing_ok = true; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP ROUTINE function_with_argtypes_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_ROUTINE; + n->objects = $3; + n->behavior = $4; + n->missing_ok = false; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP ROUTINE IF_P EXISTS function_with_argtypes_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_ROUTINE; + n->objects = $5; + n->behavior = $6; + n->missing_ok = true; + n->concurrent = false; + $$ = (Node *)n; + } + ; + +RemoveAggrStmt: + DROP AGGREGATE aggregate_with_argtypes_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_AGGREGATE; + n->objects = $3; + n->behavior = $4; + n->missing_ok = false; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP AGGREGATE IF_P EXISTS aggregate_with_argtypes_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_AGGREGATE; + n->objects = $5; + n->behavior = $6; + n->missing_ok = true; + n->concurrent = false; + $$ = (Node *)n; + } + ; + +RemoveOperStmt: + DROP OPERATOR operator_with_argtypes_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_OPERATOR; + n->objects = $3; + n->behavior = $4; + n->missing_ok = false; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP OPERATOR IF_P EXISTS operator_with_argtypes_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_OPERATOR; + n->objects = $5; + n->behavior = $6; + n->missing_ok = true; + n->concurrent = false; + $$ = (Node *)n; + } + ; + +oper_argtypes: + '(' Typename ')' + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("missing argument"), + errhint("Use NONE to denote the missing argument of a unary operator."), + parser_errposition(@3))); + } + | '(' Typename ',' Typename ')' + { $$ = list_make2($2, $4); } + | '(' NONE ',' Typename ')' /* left unary */ + { $$ = list_make2(NULL, $4); } + | '(' Typename ',' NONE ')' /* right unary */ + { $$ = list_make2($2, NULL); } + ; + +any_operator: + all_Op + { $$ = list_make1(makeString($1)); } + | ColId '.' any_operator + { $$ = lcons(makeString($1), $3); } + ; + +operator_with_argtypes_list: + operator_with_argtypes { $$ = list_make1($1); } + | operator_with_argtypes_list ',' operator_with_argtypes + { $$ = lappend($1, $3); } + ; + +operator_with_argtypes: + any_operator oper_argtypes + { + ObjectWithArgs *n = makeNode(ObjectWithArgs); + n->objname = $1; + n->objargs = $2; + $$ = n; + } + ; + +/***************************************************************************** + * + * DO <anonymous code block> [ LANGUAGE language ] + * + * We use a DefElem list for future extensibility, and to allow flexibility + * in the clause order. + * + *****************************************************************************/ + +DoStmt: DO dostmt_opt_list + { + DoStmt *n = makeNode(DoStmt); + n->args = $2; + $$ = (Node *)n; + } + ; + +dostmt_opt_list: + dostmt_opt_item { $$ = list_make1($1); } + | dostmt_opt_list dostmt_opt_item { $$ = lappend($1, $2); } + ; + +dostmt_opt_item: + Sconst + { + $$ = makeDefElem("as", (Node *)makeString($1), @1); + } + | LANGUAGE NonReservedWord_or_Sconst + { + $$ = makeDefElem("language", (Node *)makeString($2), @1); + } + ; + +/***************************************************************************** + * + * CREATE CAST / DROP CAST + * + *****************************************************************************/ + +CreateCastStmt: CREATE CAST '(' Typename AS Typename ')' + WITH FUNCTION function_with_argtypes cast_context + { + CreateCastStmt *n = makeNode(CreateCastStmt); + n->sourcetype = $4; + n->targettype = $6; + n->func = $10; + n->context = (CoercionContext) $11; + n->inout = false; + $$ = (Node *)n; + } + | CREATE CAST '(' Typename AS Typename ')' + WITHOUT FUNCTION cast_context + { + CreateCastStmt *n = makeNode(CreateCastStmt); + n->sourcetype = $4; + n->targettype = $6; + n->func = NULL; + n->context = (CoercionContext) $10; + n->inout = false; + $$ = (Node *)n; + } + | CREATE CAST '(' Typename AS Typename ')' + WITH INOUT cast_context + { + CreateCastStmt *n = makeNode(CreateCastStmt); + n->sourcetype = $4; + n->targettype = $6; + n->func = NULL; + n->context = (CoercionContext) $10; + n->inout = true; + $$ = (Node *)n; + } + ; + +cast_context: AS IMPLICIT_P { $$ = COERCION_IMPLICIT; } + | AS ASSIGNMENT { $$ = COERCION_ASSIGNMENT; } + | /*EMPTY*/ { $$ = COERCION_EXPLICIT; } + ; + + +DropCastStmt: DROP CAST opt_if_exists '(' Typename AS Typename ')' opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_CAST; + n->objects = list_make1(list_make2($5, $7)); + n->behavior = $9; + n->missing_ok = $3; + n->concurrent = false; + $$ = (Node *)n; + } + ; + +opt_if_exists: IF_P EXISTS { $$ = true; } + | /*EMPTY*/ { $$ = false; } + ; + + +/***************************************************************************** + * + * CREATE TRANSFORM / DROP TRANSFORM + * + *****************************************************************************/ + +CreateTransformStmt: CREATE opt_or_replace TRANSFORM FOR Typename LANGUAGE name '(' transform_element_list ')' + { + CreateTransformStmt *n = makeNode(CreateTransformStmt); + n->replace = $2; + n->type_name = $5; + n->lang = $7; + n->fromsql = linitial($9); + n->tosql = lsecond($9); + $$ = (Node *)n; + } + ; + +transform_element_list: FROM SQL_P WITH FUNCTION function_with_argtypes ',' TO SQL_P WITH FUNCTION function_with_argtypes + { + $$ = list_make2($5, $11); + } + | TO SQL_P WITH FUNCTION function_with_argtypes ',' FROM SQL_P WITH FUNCTION function_with_argtypes + { + $$ = list_make2($11, $5); + } + | FROM SQL_P WITH FUNCTION function_with_argtypes + { + $$ = list_make2($5, NULL); + } + | TO SQL_P WITH FUNCTION function_with_argtypes + { + $$ = list_make2(NULL, $5); + } + ; + + +DropTransformStmt: DROP TRANSFORM opt_if_exists FOR Typename LANGUAGE name opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_TRANSFORM; + n->objects = list_make1(list_make2($5, makeString($7))); + n->behavior = $8; + n->missing_ok = $3; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * + * QUERY: + * + * REINDEX [ (options) ] type [CONCURRENTLY] <name> + *****************************************************************************/ + +ReindexStmt: + REINDEX reindex_target_type opt_concurrently qualified_name + { + ReindexStmt *n = makeNode(ReindexStmt); + n->kind = $2; + n->relation = $4; + n->name = NULL; + n->params = NIL; + if ($3) + n->params = lappend(n->params, + makeDefElem("concurrently", NULL, @3)); + $$ = (Node *)n; + } + | REINDEX reindex_target_multitable opt_concurrently name + { + ReindexStmt *n = makeNode(ReindexStmt); + n->kind = $2; + n->name = $4; + n->relation = NULL; + n->params = NIL; + if ($3) + n->params = lappend(n->params, + makeDefElem("concurrently", NULL, @3)); + $$ = (Node *)n; + } + | REINDEX '(' utility_option_list ')' reindex_target_type opt_concurrently qualified_name + { + ReindexStmt *n = makeNode(ReindexStmt); + n->kind = $5; + n->relation = $7; + n->name = NULL; + n->params = $3; + if ($6) + n->params = lappend(n->params, + makeDefElem("concurrently", NULL, @6)); + $$ = (Node *)n; + } + | REINDEX '(' utility_option_list ')' reindex_target_multitable opt_concurrently name + { + ReindexStmt *n = makeNode(ReindexStmt); + n->kind = $5; + n->name = $7; + n->relation = NULL; + n->params = $3; + if ($6) + n->params = lappend(n->params, + makeDefElem("concurrently", NULL, @6)); + $$ = (Node *)n; + } + ; +reindex_target_type: + INDEX { $$ = REINDEX_OBJECT_INDEX; } + | TABLE { $$ = REINDEX_OBJECT_TABLE; } + ; +reindex_target_multitable: + SCHEMA { $$ = REINDEX_OBJECT_SCHEMA; } + | SYSTEM_P { $$ = REINDEX_OBJECT_SYSTEM; } + | DATABASE { $$ = REINDEX_OBJECT_DATABASE; } + ; + +/***************************************************************************** + * + * ALTER TABLESPACE + * + *****************************************************************************/ + +AlterTblSpcStmt: + ALTER TABLESPACE name SET reloptions + { + AlterTableSpaceOptionsStmt *n = + makeNode(AlterTableSpaceOptionsStmt); + n->tablespacename = $3; + n->options = $5; + n->isReset = false; + $$ = (Node *)n; + } + | ALTER TABLESPACE name RESET reloptions + { + AlterTableSpaceOptionsStmt *n = + makeNode(AlterTableSpaceOptionsStmt); + n->tablespacename = $3; + n->options = $5; + n->isReset = true; + $$ = (Node *)n; + } + ; + +/***************************************************************************** + * + * ALTER THING name RENAME TO newname + * + *****************************************************************************/ + +RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_AGGREGATE; + n->object = (Node *) $3; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER COLLATION any_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_COLLATION; + n->object = (Node *) $3; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER CONVERSION_P any_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_CONVERSION; + n->object = (Node *) $3; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER DATABASE name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_DATABASE; + n->subname = $3; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER DOMAIN_P any_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_DOMAIN; + n->object = (Node *) $3; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER DOMAIN_P any_name RENAME CONSTRAINT name TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_DOMCONSTRAINT; + n->object = (Node *) $3; + n->subname = $6; + n->newname = $8; + $$ = (Node *)n; + } + | ALTER FOREIGN DATA_P WRAPPER name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_FDW; + n->object = (Node *) makeString($5); + n->newname = $8; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER FUNCTION function_with_argtypes RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_FUNCTION; + n->object = (Node *) $3; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER GROUP_P RoleId RENAME TO RoleId + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_ROLE; + n->subname = $3; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER opt_procedural LANGUAGE name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_LANGUAGE; + n->object = (Node *) makeString($4); + n->newname = $7; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER OPERATOR CLASS any_name USING name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_OPCLASS; + n->object = (Node *) lcons(makeString($6), $4); + n->newname = $9; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER OPERATOR FAMILY any_name USING name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_OPFAMILY; + n->object = (Node *) lcons(makeString($6), $4); + n->newname = $9; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER POLICY name ON qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_POLICY; + n->relation = $5; + n->subname = $3; + n->newname = $8; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER POLICY IF_P EXISTS name ON qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_POLICY; + n->relation = $7; + n->subname = $5; + n->newname = $10; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER PROCEDURE function_with_argtypes RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_PROCEDURE; + n->object = (Node *) $3; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER PUBLICATION name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_PUBLICATION; + n->object = (Node *) makeString($3); + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER ROUTINE function_with_argtypes RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_ROUTINE; + n->object = (Node *) $3; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER SCHEMA name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_SCHEMA; + n->subname = $3; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER SERVER name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_FOREIGN_SERVER; + n->object = (Node *) makeString($3); + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_SUBSCRIPTION; + n->object = (Node *) makeString($3); + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TABLE relation_expr RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_TABLE; + n->relation = $3; + n->subname = NULL; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TABLE IF_P EXISTS relation_expr RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_TABLE; + n->relation = $5; + n->subname = NULL; + n->newname = $8; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER SEQUENCE qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_SEQUENCE; + n->relation = $3; + n->subname = NULL; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER SEQUENCE IF_P EXISTS qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_SEQUENCE; + n->relation = $5; + n->subname = NULL; + n->newname = $8; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER VIEW qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_VIEW; + n->relation = $3; + n->subname = NULL; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER VIEW IF_P EXISTS qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_VIEW; + n->relation = $5; + n->subname = NULL; + n->newname = $8; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER MATERIALIZED VIEW qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_MATVIEW; + n->relation = $4; + n->subname = NULL; + n->newname = $7; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_MATVIEW; + n->relation = $6; + n->subname = NULL; + n->newname = $9; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER INDEX qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_INDEX; + n->relation = $3; + n->subname = NULL; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER INDEX IF_P EXISTS qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_INDEX; + n->relation = $5; + n->subname = NULL; + n->newname = $8; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER FOREIGN TABLE relation_expr RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_FOREIGN_TABLE; + n->relation = $4; + n->subname = NULL; + n->newname = $7; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER FOREIGN TABLE IF_P EXISTS relation_expr RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_FOREIGN_TABLE; + n->relation = $6; + n->subname = NULL; + n->newname = $9; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER TABLE relation_expr RENAME opt_column name TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_COLUMN; + n->relationType = OBJECT_TABLE; + n->relation = $3; + n->subname = $6; + n->newname = $8; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TABLE IF_P EXISTS relation_expr RENAME opt_column name TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_COLUMN; + n->relationType = OBJECT_TABLE; + n->relation = $5; + n->subname = $8; + n->newname = $10; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER VIEW qualified_name RENAME opt_column name TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_COLUMN; + n->relationType = OBJECT_VIEW; + n->relation = $3; + n->subname = $6; + n->newname = $8; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER VIEW IF_P EXISTS qualified_name RENAME opt_column name TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_COLUMN; + n->relationType = OBJECT_VIEW; + n->relation = $5; + n->subname = $8; + n->newname = $10; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER MATERIALIZED VIEW qualified_name RENAME opt_column name TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_COLUMN; + n->relationType = OBJECT_MATVIEW; + n->relation = $4; + n->subname = $7; + n->newname = $9; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name RENAME opt_column name TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_COLUMN; + n->relationType = OBJECT_MATVIEW; + n->relation = $6; + n->subname = $9; + n->newname = $11; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER TABLE relation_expr RENAME CONSTRAINT name TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_TABCONSTRAINT; + n->relation = $3; + n->subname = $6; + n->newname = $8; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TABLE IF_P EXISTS relation_expr RENAME CONSTRAINT name TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_TABCONSTRAINT; + n->relation = $5; + n->subname = $8; + n->newname = $10; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER FOREIGN TABLE relation_expr RENAME opt_column name TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_COLUMN; + n->relationType = OBJECT_FOREIGN_TABLE; + n->relation = $4; + n->subname = $7; + n->newname = $9; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER FOREIGN TABLE IF_P EXISTS relation_expr RENAME opt_column name TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_COLUMN; + n->relationType = OBJECT_FOREIGN_TABLE; + n->relation = $6; + n->subname = $9; + n->newname = $11; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER RULE name ON qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_RULE; + n->relation = $5; + n->subname = $3; + n->newname = $8; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TRIGGER name ON qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_TRIGGER; + n->relation = $5; + n->subname = $3; + n->newname = $8; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER EVENT TRIGGER name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_EVENT_TRIGGER; + n->object = (Node *) makeString($4); + n->newname = $7; + $$ = (Node *)n; + } + | ALTER ROLE RoleId RENAME TO RoleId + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_ROLE; + n->subname = $3; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER USER RoleId RENAME TO RoleId + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_ROLE; + n->subname = $3; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TABLESPACE name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_TABLESPACE; + n->subname = $3; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER STATISTICS any_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_STATISTIC_EXT; + n->object = (Node *) $3; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TEXT_P SEARCH PARSER any_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_TSPARSER; + n->object = (Node *) $5; + n->newname = $8; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TEXT_P SEARCH DICTIONARY any_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_TSDICTIONARY; + n->object = (Node *) $5; + n->newname = $8; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TEXT_P SEARCH TEMPLATE any_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_TSTEMPLATE; + n->object = (Node *) $5; + n->newname = $8; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TEXT_P SEARCH CONFIGURATION any_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_TSCONFIGURATION; + n->object = (Node *) $5; + n->newname = $8; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TYPE_P any_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_TYPE; + n->object = (Node *) $3; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TYPE_P any_name RENAME ATTRIBUTE name TO name opt_drop_behavior + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_ATTRIBUTE; + n->relationType = OBJECT_TYPE; + n->relation = makeRangeVarFromAnyName($3, @3, yyscanner); + n->subname = $6; + n->newname = $8; + n->behavior = $9; + n->missing_ok = false; + $$ = (Node *)n; + } + ; + +opt_column: COLUMN + | /*EMPTY*/ + ; + +opt_set_data: SET DATA_P { $$ = 1; } + | /*EMPTY*/ { $$ = 0; } + ; + +/***************************************************************************** + * + * ALTER THING name DEPENDS ON EXTENSION name + * + *****************************************************************************/ + +AlterObjectDependsStmt: + ALTER FUNCTION function_with_argtypes opt_no DEPENDS ON EXTENSION name + { + AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt); + n->objectType = OBJECT_FUNCTION; + n->object = (Node *) $3; + n->extname = makeString($8); + n->remove = $4; + $$ = (Node *)n; + } + | ALTER PROCEDURE function_with_argtypes opt_no DEPENDS ON EXTENSION name + { + AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt); + n->objectType = OBJECT_PROCEDURE; + n->object = (Node *) $3; + n->extname = makeString($8); + n->remove = $4; + $$ = (Node *)n; + } + | ALTER ROUTINE function_with_argtypes opt_no DEPENDS ON EXTENSION name + { + AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt); + n->objectType = OBJECT_ROUTINE; + n->object = (Node *) $3; + n->extname = makeString($8); + n->remove = $4; + $$ = (Node *)n; + } + | ALTER TRIGGER name ON qualified_name opt_no DEPENDS ON EXTENSION name + { + AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt); + n->objectType = OBJECT_TRIGGER; + n->relation = $5; + n->object = (Node *) list_make1(makeString($3)); + n->extname = makeString($10); + n->remove = $6; + $$ = (Node *)n; + } + | ALTER MATERIALIZED VIEW qualified_name opt_no DEPENDS ON EXTENSION name + { + AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt); + n->objectType = OBJECT_MATVIEW; + n->relation = $4; + n->extname = makeString($9); + n->remove = $5; + $$ = (Node *)n; + } + | ALTER INDEX qualified_name opt_no DEPENDS ON EXTENSION name + { + AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt); + n->objectType = OBJECT_INDEX; + n->relation = $3; + n->extname = makeString($8); + n->remove = $4; + $$ = (Node *)n; + } + ; + +opt_no: NO { $$ = true; } + | /* EMPTY */ { $$ = false; } + ; + +/***************************************************************************** + * + * ALTER THING name SET SCHEMA name + * + *****************************************************************************/ + +AlterObjectSchemaStmt: + ALTER AGGREGATE aggregate_with_argtypes SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_AGGREGATE; + n->object = (Node *) $3; + n->newschema = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER COLLATION any_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_COLLATION; + n->object = (Node *) $3; + n->newschema = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER CONVERSION_P any_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_CONVERSION; + n->object = (Node *) $3; + n->newschema = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER DOMAIN_P any_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_DOMAIN; + n->object = (Node *) $3; + n->newschema = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER EXTENSION name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_EXTENSION; + n->object = (Node *) makeString($3); + n->newschema = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER FUNCTION function_with_argtypes SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_FUNCTION; + n->object = (Node *) $3; + n->newschema = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER OPERATOR operator_with_argtypes SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_OPERATOR; + n->object = (Node *) $3; + n->newschema = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER OPERATOR CLASS any_name USING name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_OPCLASS; + n->object = (Node *) lcons(makeString($6), $4); + n->newschema = $9; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER OPERATOR FAMILY any_name USING name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_OPFAMILY; + n->object = (Node *) lcons(makeString($6), $4); + n->newschema = $9; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER PROCEDURE function_with_argtypes SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_PROCEDURE; + n->object = (Node *) $3; + n->newschema = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER ROUTINE function_with_argtypes SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_ROUTINE; + n->object = (Node *) $3; + n->newschema = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TABLE relation_expr SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_TABLE; + n->relation = $3; + n->newschema = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TABLE IF_P EXISTS relation_expr SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_TABLE; + n->relation = $5; + n->newschema = $8; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER STATISTICS any_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_STATISTIC_EXT; + n->object = (Node *) $3; + n->newschema = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TEXT_P SEARCH PARSER any_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_TSPARSER; + n->object = (Node *) $5; + n->newschema = $8; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TEXT_P SEARCH DICTIONARY any_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_TSDICTIONARY; + n->object = (Node *) $5; + n->newschema = $8; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TEXT_P SEARCH TEMPLATE any_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_TSTEMPLATE; + n->object = (Node *) $5; + n->newschema = $8; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TEXT_P SEARCH CONFIGURATION any_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_TSCONFIGURATION; + n->object = (Node *) $5; + n->newschema = $8; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER SEQUENCE qualified_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_SEQUENCE; + n->relation = $3; + n->newschema = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER SEQUENCE IF_P EXISTS qualified_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_SEQUENCE; + n->relation = $5; + n->newschema = $8; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER VIEW qualified_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_VIEW; + n->relation = $3; + n->newschema = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER VIEW IF_P EXISTS qualified_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_VIEW; + n->relation = $5; + n->newschema = $8; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER MATERIALIZED VIEW qualified_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_MATVIEW; + n->relation = $4; + n->newschema = $7; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_MATVIEW; + n->relation = $6; + n->newschema = $9; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER FOREIGN TABLE relation_expr SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_FOREIGN_TABLE; + n->relation = $4; + n->newschema = $7; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER FOREIGN TABLE IF_P EXISTS relation_expr SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_FOREIGN_TABLE; + n->relation = $6; + n->newschema = $9; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER TYPE_P any_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_TYPE; + n->object = (Node *) $3; + n->newschema = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + ; + +/***************************************************************************** + * + * ALTER OPERATOR name SET define + * + *****************************************************************************/ + +AlterOperatorStmt: + ALTER OPERATOR operator_with_argtypes SET '(' operator_def_list ')' + { + AlterOperatorStmt *n = makeNode(AlterOperatorStmt); + n->opername = $3; + n->options = $6; + $$ = (Node *)n; + } + ; + +operator_def_list: operator_def_elem { $$ = list_make1($1); } + | operator_def_list ',' operator_def_elem { $$ = lappend($1, $3); } + ; + +operator_def_elem: ColLabel '=' NONE + { $$ = makeDefElem($1, NULL, @1); } + | ColLabel '=' operator_def_arg + { $$ = makeDefElem($1, (Node *) $3, @1); } + ; + +/* must be similar enough to def_arg to avoid reduce/reduce conflicts */ +operator_def_arg: + func_type { $$ = (Node *)$1; } + | reserved_keyword { $$ = (Node *)makeString(pstrdup($1)); } + | qual_all_Op { $$ = (Node *)$1; } + | NumericOnly { $$ = (Node *)$1; } + | Sconst { $$ = (Node *)makeString($1); } + ; + +/***************************************************************************** + * + * ALTER TYPE name SET define + * + * We repurpose ALTER OPERATOR's version of "definition" here + * + *****************************************************************************/ + +AlterTypeStmt: + ALTER TYPE_P any_name SET '(' operator_def_list ')' + { + AlterTypeStmt *n = makeNode(AlterTypeStmt); + n->typeName = $3; + n->options = $6; + $$ = (Node *)n; + } + ; + +/***************************************************************************** + * + * ALTER THING name OWNER TO newname + * + *****************************************************************************/ + +AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_AGGREGATE; + n->object = (Node *) $3; + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER COLLATION any_name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_COLLATION; + n->object = (Node *) $3; + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER CONVERSION_P any_name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_CONVERSION; + n->object = (Node *) $3; + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER DATABASE name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_DATABASE; + n->object = (Node *) makeString($3); + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER DOMAIN_P any_name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_DOMAIN; + n->object = (Node *) $3; + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER FUNCTION function_with_argtypes OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_FUNCTION; + n->object = (Node *) $3; + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER opt_procedural LANGUAGE name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_LANGUAGE; + n->object = (Node *) makeString($4); + n->newowner = $7; + $$ = (Node *)n; + } + | ALTER LARGE_P OBJECT_P NumericOnly OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_LARGEOBJECT; + n->object = (Node *) $4; + n->newowner = $7; + $$ = (Node *)n; + } + | ALTER OPERATOR operator_with_argtypes OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_OPERATOR; + n->object = (Node *) $3; + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER OPERATOR CLASS any_name USING name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_OPCLASS; + n->object = (Node *) lcons(makeString($6), $4); + n->newowner = $9; + $$ = (Node *)n; + } + | ALTER OPERATOR FAMILY any_name USING name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_OPFAMILY; + n->object = (Node *) lcons(makeString($6), $4); + n->newowner = $9; + $$ = (Node *)n; + } + | ALTER PROCEDURE function_with_argtypes OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_PROCEDURE; + n->object = (Node *) $3; + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER ROUTINE function_with_argtypes OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_ROUTINE; + n->object = (Node *) $3; + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER SCHEMA name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_SCHEMA; + n->object = (Node *) makeString($3); + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER TYPE_P any_name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_TYPE; + n->object = (Node *) $3; + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER TABLESPACE name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_TABLESPACE; + n->object = (Node *) makeString($3); + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER STATISTICS any_name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_STATISTIC_EXT; + n->object = (Node *) $3; + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER TEXT_P SEARCH DICTIONARY any_name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_TSDICTIONARY; + n->object = (Node *) $5; + n->newowner = $8; + $$ = (Node *)n; + } + | ALTER TEXT_P SEARCH CONFIGURATION any_name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_TSCONFIGURATION; + n->object = (Node *) $5; + n->newowner = $8; + $$ = (Node *)n; + } + | ALTER FOREIGN DATA_P WRAPPER name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_FDW; + n->object = (Node *) makeString($5); + n->newowner = $8; + $$ = (Node *)n; + } + | ALTER SERVER name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_FOREIGN_SERVER; + n->object = (Node *) makeString($3); + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER EVENT TRIGGER name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_EVENT_TRIGGER; + n->object = (Node *) makeString($4); + n->newowner = $7; + $$ = (Node *)n; + } + | ALTER PUBLICATION name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_PUBLICATION; + n->object = (Node *) makeString($3); + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_SUBSCRIPTION; + n->object = (Node *) makeString($3); + n->newowner = $6; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * + * CREATE PUBLICATION name [ FOR TABLE ] [ WITH options ] + * + *****************************************************************************/ + +CreatePublicationStmt: + CREATE PUBLICATION name opt_publication_for_tables opt_definition + { + CreatePublicationStmt *n = makeNode(CreatePublicationStmt); + n->pubname = $3; + n->options = $5; + if ($4 != NULL) + { + /* FOR TABLE */ + if (IsA($4, List)) + n->tables = (List *)$4; + /* FOR ALL TABLES */ + else + n->for_all_tables = true; + } + $$ = (Node *)n; + } + ; + +opt_publication_for_tables: + publication_for_tables { $$ = $1; } + | /* EMPTY */ { $$ = NULL; } + ; + +publication_for_tables: + FOR TABLE relation_expr_list + { + $$ = (Node *) $3; + } + | FOR ALL TABLES + { + $$ = (Node *) makeInteger(true); + } + ; + + +/***************************************************************************** + * + * ALTER PUBLICATION name SET ( options ) + * + * ALTER PUBLICATION name ADD TABLE table [, table2] + * + * ALTER PUBLICATION name DROP TABLE table [, table2] + * + * ALTER PUBLICATION name SET TABLE table [, table2] + * + *****************************************************************************/ + +AlterPublicationStmt: + ALTER PUBLICATION name SET definition + { + AlterPublicationStmt *n = makeNode(AlterPublicationStmt); + n->pubname = $3; + n->options = $5; + $$ = (Node *)n; + } + | ALTER PUBLICATION name ADD_P TABLE relation_expr_list + { + AlterPublicationStmt *n = makeNode(AlterPublicationStmt); + n->pubname = $3; + n->tables = $6; + n->tableAction = DEFELEM_ADD; + $$ = (Node *)n; + } + | ALTER PUBLICATION name SET TABLE relation_expr_list + { + AlterPublicationStmt *n = makeNode(AlterPublicationStmt); + n->pubname = $3; + n->tables = $6; + n->tableAction = DEFELEM_SET; + $$ = (Node *)n; + } + | ALTER PUBLICATION name DROP TABLE relation_expr_list + { + AlterPublicationStmt *n = makeNode(AlterPublicationStmt); + n->pubname = $3; + n->tables = $6; + n->tableAction = DEFELEM_DROP; + $$ = (Node *)n; + } + ; + +/***************************************************************************** + * + * CREATE SUBSCRIPTION name ... + * + *****************************************************************************/ + +CreateSubscriptionStmt: + CREATE SUBSCRIPTION name CONNECTION Sconst PUBLICATION name_list opt_definition + { + CreateSubscriptionStmt *n = + makeNode(CreateSubscriptionStmt); + n->subname = $3; + n->conninfo = $5; + n->publication = $7; + n->options = $8; + $$ = (Node *)n; + } + ; + +/***************************************************************************** + * + * ALTER SUBSCRIPTION name ... + * + *****************************************************************************/ + +AlterSubscriptionStmt: + ALTER SUBSCRIPTION name SET definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_OPTIONS; + n->subname = $3; + n->options = $5; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name CONNECTION Sconst + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_CONNECTION; + n->subname = $3; + n->conninfo = $5; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name REFRESH PUBLICATION opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_REFRESH; + n->subname = $3; + n->options = $6; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_SET_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name ENABLE_P + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ENABLED; + n->subname = $3; + n->options = list_make1(makeDefElem("enabled", + (Node *)makeInteger(true), @1)); + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DISABLE_P + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ENABLED; + n->subname = $3; + n->options = list_make1(makeDefElem("enabled", + (Node *)makeInteger(false), @1)); + $$ = (Node *)n; + } + ; + +/***************************************************************************** + * + * DROP SUBSCRIPTION [ IF EXISTS ] name + * + *****************************************************************************/ + +DropSubscriptionStmt: DROP SUBSCRIPTION name opt_drop_behavior + { + DropSubscriptionStmt *n = makeNode(DropSubscriptionStmt); + n->subname = $3; + n->missing_ok = false; + n->behavior = $4; + $$ = (Node *) n; + } + | DROP SUBSCRIPTION IF_P EXISTS name opt_drop_behavior + { + DropSubscriptionStmt *n = makeNode(DropSubscriptionStmt); + n->subname = $5; + n->missing_ok = true; + n->behavior = $6; + $$ = (Node *) n; + } + ; + +/***************************************************************************** + * + * QUERY: Define Rewrite Rule + * + *****************************************************************************/ + +RuleStmt: CREATE opt_or_replace RULE name AS + ON event TO qualified_name where_clause + DO opt_instead RuleActionList + { + RuleStmt *n = makeNode(RuleStmt); + n->replace = $2; + n->relation = $9; + n->rulename = $4; + n->whereClause = $10; + n->event = $7; + n->instead = $12; + n->actions = $13; + $$ = (Node *)n; + } + ; + +RuleActionList: + NOTHING { $$ = NIL; } + | RuleActionStmt { $$ = list_make1($1); } + | '(' RuleActionMulti ')' { $$ = $2; } + ; + +/* the thrashing around here is to discard "empty" statements... */ +RuleActionMulti: + RuleActionMulti ';' RuleActionStmtOrEmpty + { if ($3 != NULL) + $$ = lappend($1, $3); + else + $$ = $1; + } + | RuleActionStmtOrEmpty + { if ($1 != NULL) + $$ = list_make1($1); + else + $$ = NIL; + } + ; + +RuleActionStmt: + SelectStmt + | InsertStmt + | UpdateStmt + | DeleteStmt + | NotifyStmt + ; + +RuleActionStmtOrEmpty: + RuleActionStmt { $$ = $1; } + | /*EMPTY*/ { $$ = NULL; } + ; + +event: SELECT { $$ = CMD_SELECT; } + | UPDATE { $$ = CMD_UPDATE; } + | DELETE_P { $$ = CMD_DELETE; } + | INSERT { $$ = CMD_INSERT; } + ; + +opt_instead: + INSTEAD { $$ = true; } + | ALSO { $$ = false; } + | /*EMPTY*/ { $$ = false; } + ; + + +/***************************************************************************** + * + * QUERY: + * NOTIFY <identifier> can appear both in rule bodies and + * as a query-level command + * + *****************************************************************************/ + +NotifyStmt: NOTIFY ColId notify_payload + { + NotifyStmt *n = makeNode(NotifyStmt); + n->conditionname = $2; + n->payload = $3; + $$ = (Node *)n; + } + ; + +notify_payload: + ',' Sconst { $$ = $2; } + | /*EMPTY*/ { $$ = NULL; } + ; + +ListenStmt: LISTEN ColId + { + ListenStmt *n = makeNode(ListenStmt); + n->conditionname = $2; + $$ = (Node *)n; + } + ; + +UnlistenStmt: + UNLISTEN ColId + { + UnlistenStmt *n = makeNode(UnlistenStmt); + n->conditionname = $2; + $$ = (Node *)n; + } + | UNLISTEN '*' + { + UnlistenStmt *n = makeNode(UnlistenStmt); + n->conditionname = NULL; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * + * Transactions: + * + * BEGIN / COMMIT / ROLLBACK + * (also older versions END / ABORT) + * + *****************************************************************************/ + +TransactionStmt: + ABORT_P opt_transaction opt_transaction_chain + { + TransactionStmt *n = makeNode(TransactionStmt); + n->kind = TRANS_STMT_ROLLBACK; + n->options = NIL; + n->chain = $3; + $$ = (Node *)n; + } + | START TRANSACTION transaction_mode_list_or_empty + { + TransactionStmt *n = makeNode(TransactionStmt); + n->kind = TRANS_STMT_START; + n->options = $3; + $$ = (Node *)n; + } + | COMMIT opt_transaction opt_transaction_chain + { + TransactionStmt *n = makeNode(TransactionStmt); + n->kind = TRANS_STMT_COMMIT; + n->options = NIL; + n->chain = $3; + $$ = (Node *)n; + } + | ROLLBACK opt_transaction opt_transaction_chain + { + TransactionStmt *n = makeNode(TransactionStmt); + n->kind = TRANS_STMT_ROLLBACK; + n->options = NIL; + n->chain = $3; + $$ = (Node *)n; + } + | SAVEPOINT ColId + { + TransactionStmt *n = makeNode(TransactionStmt); + n->kind = TRANS_STMT_SAVEPOINT; + n->savepoint_name = $2; + $$ = (Node *)n; + } + | RELEASE SAVEPOINT ColId + { + TransactionStmt *n = makeNode(TransactionStmt); + n->kind = TRANS_STMT_RELEASE; + n->savepoint_name = $3; + $$ = (Node *)n; + } + | RELEASE ColId + { + TransactionStmt *n = makeNode(TransactionStmt); + n->kind = TRANS_STMT_RELEASE; + n->savepoint_name = $2; + $$ = (Node *)n; + } + | ROLLBACK opt_transaction TO SAVEPOINT ColId + { + TransactionStmt *n = makeNode(TransactionStmt); + n->kind = TRANS_STMT_ROLLBACK_TO; + n->savepoint_name = $5; + $$ = (Node *)n; + } + | ROLLBACK opt_transaction TO ColId + { + TransactionStmt *n = makeNode(TransactionStmt); + n->kind = TRANS_STMT_ROLLBACK_TO; + n->savepoint_name = $4; + $$ = (Node *)n; + } + | PREPARE TRANSACTION Sconst + { + TransactionStmt *n = makeNode(TransactionStmt); + n->kind = TRANS_STMT_PREPARE; + n->gid = $3; + $$ = (Node *)n; + } + | COMMIT PREPARED Sconst + { + TransactionStmt *n = makeNode(TransactionStmt); + n->kind = TRANS_STMT_COMMIT_PREPARED; + n->gid = $3; + $$ = (Node *)n; + } + | ROLLBACK PREPARED Sconst + { + TransactionStmt *n = makeNode(TransactionStmt); + n->kind = TRANS_STMT_ROLLBACK_PREPARED; + n->gid = $3; + $$ = (Node *)n; + } + ; + +TransactionStmtLegacy: + BEGIN_P opt_transaction transaction_mode_list_or_empty + { + TransactionStmt *n = makeNode(TransactionStmt); + n->kind = TRANS_STMT_BEGIN; + n->options = $3; + $$ = (Node *)n; + } + | END_P opt_transaction opt_transaction_chain + { + TransactionStmt *n = makeNode(TransactionStmt); + n->kind = TRANS_STMT_COMMIT; + n->options = NIL; + n->chain = $3; + $$ = (Node *)n; + } + ; + +opt_transaction: WORK + | TRANSACTION + | /*EMPTY*/ + ; + +transaction_mode_item: + ISOLATION LEVEL iso_level + { $$ = makeDefElem("transaction_isolation", + makeStringConst($3, @3), @1); } + | READ ONLY + { $$ = makeDefElem("transaction_read_only", + makeIntConst(true, @1), @1); } + | READ WRITE + { $$ = makeDefElem("transaction_read_only", + makeIntConst(false, @1), @1); } + | DEFERRABLE + { $$ = makeDefElem("transaction_deferrable", + makeIntConst(true, @1), @1); } + | NOT DEFERRABLE + { $$ = makeDefElem("transaction_deferrable", + makeIntConst(false, @1), @1); } + ; + +/* Syntax with commas is SQL-spec, without commas is Postgres historical */ +transaction_mode_list: + transaction_mode_item + { $$ = list_make1($1); } + | transaction_mode_list ',' transaction_mode_item + { $$ = lappend($1, $3); } + | transaction_mode_list transaction_mode_item + { $$ = lappend($1, $2); } + ; + +transaction_mode_list_or_empty: + transaction_mode_list + | /* EMPTY */ + { $$ = NIL; } + ; + +opt_transaction_chain: + AND CHAIN { $$ = true; } + | AND NO CHAIN { $$ = false; } + | /* EMPTY */ { $$ = false; } + ; + + +/***************************************************************************** + * + * QUERY: + * CREATE [ OR REPLACE ] [ TEMP ] VIEW <viewname> '('target-list ')' + * AS <query> [ WITH [ CASCADED | LOCAL ] CHECK OPTION ] + * + *****************************************************************************/ + +ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list opt_reloptions + AS SelectStmt opt_check_option + { + ViewStmt *n = makeNode(ViewStmt); + n->view = $4; + n->view->relpersistence = $2; + n->aliases = $5; + n->query = $8; + n->replace = false; + n->options = $6; + n->withCheckOption = $9; + $$ = (Node *) n; + } + | CREATE OR REPLACE OptTemp VIEW qualified_name opt_column_list opt_reloptions + AS SelectStmt opt_check_option + { + ViewStmt *n = makeNode(ViewStmt); + n->view = $6; + n->view->relpersistence = $4; + n->aliases = $7; + n->query = $10; + n->replace = true; + n->options = $8; + n->withCheckOption = $11; + $$ = (Node *) n; + } + | CREATE OptTemp RECURSIVE VIEW qualified_name '(' columnList ')' opt_reloptions + AS SelectStmt opt_check_option + { + ViewStmt *n = makeNode(ViewStmt); + n->view = $5; + n->view->relpersistence = $2; + n->aliases = $7; + n->query = makeRecursiveViewSelect(n->view->relname, n->aliases, $11); + n->replace = false; + n->options = $9; + n->withCheckOption = $12; + if (n->withCheckOption != NO_CHECK_OPTION) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("WITH CHECK OPTION not supported on recursive views"), + parser_errposition(@12))); + $$ = (Node *) n; + } + | CREATE OR REPLACE OptTemp RECURSIVE VIEW qualified_name '(' columnList ')' opt_reloptions + AS SelectStmt opt_check_option + { + ViewStmt *n = makeNode(ViewStmt); + n->view = $7; + n->view->relpersistence = $4; + n->aliases = $9; + n->query = makeRecursiveViewSelect(n->view->relname, n->aliases, $13); + n->replace = true; + n->options = $11; + n->withCheckOption = $14; + if (n->withCheckOption != NO_CHECK_OPTION) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("WITH CHECK OPTION not supported on recursive views"), + parser_errposition(@14))); + $$ = (Node *) n; + } + ; + +opt_check_option: + WITH CHECK OPTION { $$ = CASCADED_CHECK_OPTION; } + | WITH CASCADED CHECK OPTION { $$ = CASCADED_CHECK_OPTION; } + | WITH LOCAL CHECK OPTION { $$ = LOCAL_CHECK_OPTION; } + | /* EMPTY */ { $$ = NO_CHECK_OPTION; } + ; + +/***************************************************************************** + * + * QUERY: + * LOAD "filename" + * + *****************************************************************************/ + +LoadStmt: LOAD file_name + { + LoadStmt *n = makeNode(LoadStmt); + n->filename = $2; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * + * CREATE DATABASE + * + *****************************************************************************/ + +CreatedbStmt: + CREATE DATABASE name opt_with createdb_opt_list + { + CreatedbStmt *n = makeNode(CreatedbStmt); + n->dbname = $3; + n->options = $5; + $$ = (Node *)n; + } + ; + +createdb_opt_list: + createdb_opt_items { $$ = $1; } + | /* EMPTY */ { $$ = NIL; } + ; + +createdb_opt_items: + createdb_opt_item { $$ = list_make1($1); } + | createdb_opt_items createdb_opt_item { $$ = lappend($1, $2); } + ; + +createdb_opt_item: + createdb_opt_name opt_equal SignedIconst + { + $$ = makeDefElem($1, (Node *)makeInteger($3), @1); + } + | createdb_opt_name opt_equal opt_boolean_or_string + { + $$ = makeDefElem($1, (Node *)makeString($3), @1); + } + | createdb_opt_name opt_equal DEFAULT + { + $$ = makeDefElem($1, NULL, @1); + } + ; + +/* + * Ideally we'd use ColId here, but that causes shift/reduce conflicts against + * the ALTER DATABASE SET/RESET syntaxes. Instead call out specific keywords + * we need, and allow IDENT so that database option names don't have to be + * parser keywords unless they are already keywords for other reasons. + * + * XXX this coding technique is fragile since if someone makes a formerly + * non-keyword option name into a keyword and forgets to add it here, the + * option will silently break. Best defense is to provide a regression test + * exercising every such option, at least at the syntax level. + */ +createdb_opt_name: + IDENT { $$ = $1; } + | CONNECTION LIMIT { $$ = pstrdup("connection_limit"); } + | ENCODING { $$ = pstrdup($1); } + | LOCATION { $$ = pstrdup($1); } + | OWNER { $$ = pstrdup($1); } + | TABLESPACE { $$ = pstrdup($1); } + | TEMPLATE { $$ = pstrdup($1); } + ; + +/* + * Though the equals sign doesn't match other WITH options, pg_dump uses + * equals for backward compatibility, and it doesn't seem worth removing it. + */ +opt_equal: '=' + | /*EMPTY*/ + ; + + +/***************************************************************************** + * + * ALTER DATABASE + * + *****************************************************************************/ + +AlterDatabaseStmt: + ALTER DATABASE name WITH createdb_opt_list + { + AlterDatabaseStmt *n = makeNode(AlterDatabaseStmt); + n->dbname = $3; + n->options = $5; + $$ = (Node *)n; + } + | ALTER DATABASE name createdb_opt_list + { + AlterDatabaseStmt *n = makeNode(AlterDatabaseStmt); + n->dbname = $3; + n->options = $4; + $$ = (Node *)n; + } + | ALTER DATABASE name SET TABLESPACE name + { + AlterDatabaseStmt *n = makeNode(AlterDatabaseStmt); + n->dbname = $3; + n->options = list_make1(makeDefElem("tablespace", + (Node *)makeString($6), @6)); + $$ = (Node *)n; + } + ; + +AlterDatabaseSetStmt: + ALTER DATABASE name SetResetClause + { + AlterDatabaseSetStmt *n = makeNode(AlterDatabaseSetStmt); + n->dbname = $3; + n->setstmt = $4; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * + * DROP DATABASE [ IF EXISTS ] dbname [ [ WITH ] ( options ) ] + * + * This is implicitly CASCADE, no need for drop behavior + *****************************************************************************/ + +DropdbStmt: DROP DATABASE name + { + DropdbStmt *n = makeNode(DropdbStmt); + n->dbname = $3; + n->missing_ok = false; + n->options = NULL; + $$ = (Node *)n; + } + | DROP DATABASE IF_P EXISTS name + { + DropdbStmt *n = makeNode(DropdbStmt); + n->dbname = $5; + n->missing_ok = true; + n->options = NULL; + $$ = (Node *)n; + } + | DROP DATABASE name opt_with '(' drop_option_list ')' + { + DropdbStmt *n = makeNode(DropdbStmt); + n->dbname = $3; + n->missing_ok = false; + n->options = $6; + $$ = (Node *)n; + } + | DROP DATABASE IF_P EXISTS name opt_with '(' drop_option_list ')' + { + DropdbStmt *n = makeNode(DropdbStmt); + n->dbname = $5; + n->missing_ok = true; + n->options = $8; + $$ = (Node *)n; + } + ; + +drop_option_list: + drop_option + { + $$ = list_make1((Node *) $1); + } + | drop_option_list ',' drop_option + { + $$ = lappend($1, (Node *) $3); + } + ; + +/* + * Currently only the FORCE option is supported, but the syntax is designed + * to be extensible so that we can add more options in the future if required. + */ +drop_option: + FORCE + { + $$ = makeDefElem("force", NULL, @1); + } + ; + +/***************************************************************************** + * + * ALTER COLLATION + * + *****************************************************************************/ + +AlterCollationStmt: ALTER COLLATION any_name REFRESH VERSION_P + { + AlterCollationStmt *n = makeNode(AlterCollationStmt); + n->collname = $3; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * + * ALTER SYSTEM + * + * This is used to change configuration parameters persistently. + *****************************************************************************/ + +AlterSystemStmt: + ALTER SYSTEM_P SET generic_set + { + AlterSystemStmt *n = makeNode(AlterSystemStmt); + n->setstmt = $4; + $$ = (Node *)n; + } + | ALTER SYSTEM_P RESET generic_reset + { + AlterSystemStmt *n = makeNode(AlterSystemStmt); + n->setstmt = $4; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * + * Manipulate a domain + * + *****************************************************************************/ + +CreateDomainStmt: + CREATE DOMAIN_P any_name opt_as Typename ColQualList + { + CreateDomainStmt *n = makeNode(CreateDomainStmt); + n->domainname = $3; + n->typeName = $5; + SplitColQualList($6, &n->constraints, &n->collClause, + yyscanner); + $$ = (Node *)n; + } + ; + +AlterDomainStmt: + /* ALTER DOMAIN <domain> {SET DEFAULT <expr>|DROP DEFAULT} */ + ALTER DOMAIN_P any_name alter_column_default + { + AlterDomainStmt *n = makeNode(AlterDomainStmt); + n->subtype = 'T'; + n->typeName = $3; + n->def = $4; + $$ = (Node *)n; + } + /* ALTER DOMAIN <domain> DROP NOT NULL */ + | ALTER DOMAIN_P any_name DROP NOT NULL_P + { + AlterDomainStmt *n = makeNode(AlterDomainStmt); + n->subtype = 'N'; + n->typeName = $3; + $$ = (Node *)n; + } + /* ALTER DOMAIN <domain> SET NOT NULL */ + | ALTER DOMAIN_P any_name SET NOT NULL_P + { + AlterDomainStmt *n = makeNode(AlterDomainStmt); + n->subtype = 'O'; + n->typeName = $3; + $$ = (Node *)n; + } + /* ALTER DOMAIN <domain> ADD CONSTRAINT ... */ + | ALTER DOMAIN_P any_name ADD_P TableConstraint + { + AlterDomainStmt *n = makeNode(AlterDomainStmt); + n->subtype = 'C'; + n->typeName = $3; + n->def = $5; + $$ = (Node *)n; + } + /* ALTER DOMAIN <domain> DROP CONSTRAINT <name> [RESTRICT|CASCADE] */ + | ALTER DOMAIN_P any_name DROP CONSTRAINT name opt_drop_behavior + { + AlterDomainStmt *n = makeNode(AlterDomainStmt); + n->subtype = 'X'; + n->typeName = $3; + n->name = $6; + n->behavior = $7; + n->missing_ok = false; + $$ = (Node *)n; + } + /* ALTER DOMAIN <domain> DROP CONSTRAINT IF EXISTS <name> [RESTRICT|CASCADE] */ + | ALTER DOMAIN_P any_name DROP CONSTRAINT IF_P EXISTS name opt_drop_behavior + { + AlterDomainStmt *n = makeNode(AlterDomainStmt); + n->subtype = 'X'; + n->typeName = $3; + n->name = $8; + n->behavior = $9; + n->missing_ok = true; + $$ = (Node *)n; + } + /* ALTER DOMAIN <domain> VALIDATE CONSTRAINT <name> */ + | ALTER DOMAIN_P any_name VALIDATE CONSTRAINT name + { + AlterDomainStmt *n = makeNode(AlterDomainStmt); + n->subtype = 'V'; + n->typeName = $3; + n->name = $6; + $$ = (Node *)n; + } + ; + +opt_as: AS + | /* EMPTY */ + ; + + +/***************************************************************************** + * + * Manipulate a text search dictionary or configuration + * + *****************************************************************************/ + +AlterTSDictionaryStmt: + ALTER TEXT_P SEARCH DICTIONARY any_name definition + { + AlterTSDictionaryStmt *n = makeNode(AlterTSDictionaryStmt); + n->dictname = $5; + n->options = $6; + $$ = (Node *)n; + } + ; + +AlterTSConfigurationStmt: + ALTER TEXT_P SEARCH CONFIGURATION any_name ADD_P MAPPING FOR name_list any_with any_name_list + { + AlterTSConfigurationStmt *n = makeNode(AlterTSConfigurationStmt); + n->kind = ALTER_TSCONFIG_ADD_MAPPING; + n->cfgname = $5; + n->tokentype = $9; + n->dicts = $11; + n->override = false; + n->replace = false; + $$ = (Node*)n; + } + | ALTER TEXT_P SEARCH CONFIGURATION any_name ALTER MAPPING FOR name_list any_with any_name_list + { + AlterTSConfigurationStmt *n = makeNode(AlterTSConfigurationStmt); + n->kind = ALTER_TSCONFIG_ALTER_MAPPING_FOR_TOKEN; + n->cfgname = $5; + n->tokentype = $9; + n->dicts = $11; + n->override = true; + n->replace = false; + $$ = (Node*)n; + } + | ALTER TEXT_P SEARCH CONFIGURATION any_name ALTER MAPPING REPLACE any_name any_with any_name + { + AlterTSConfigurationStmt *n = makeNode(AlterTSConfigurationStmt); + n->kind = ALTER_TSCONFIG_REPLACE_DICT; + n->cfgname = $5; + n->tokentype = NIL; + n->dicts = list_make2($9,$11); + n->override = false; + n->replace = true; + $$ = (Node*)n; + } + | ALTER TEXT_P SEARCH CONFIGURATION any_name ALTER MAPPING FOR name_list REPLACE any_name any_with any_name + { + AlterTSConfigurationStmt *n = makeNode(AlterTSConfigurationStmt); + n->kind = ALTER_TSCONFIG_REPLACE_DICT_FOR_TOKEN; + n->cfgname = $5; + n->tokentype = $9; + n->dicts = list_make2($11,$13); + n->override = false; + n->replace = true; + $$ = (Node*)n; + } + | ALTER TEXT_P SEARCH CONFIGURATION any_name DROP MAPPING FOR name_list + { + AlterTSConfigurationStmt *n = makeNode(AlterTSConfigurationStmt); + n->kind = ALTER_TSCONFIG_DROP_MAPPING; + n->cfgname = $5; + n->tokentype = $9; + n->missing_ok = false; + $$ = (Node*)n; + } + | ALTER TEXT_P SEARCH CONFIGURATION any_name DROP MAPPING IF_P EXISTS FOR name_list + { + AlterTSConfigurationStmt *n = makeNode(AlterTSConfigurationStmt); + n->kind = ALTER_TSCONFIG_DROP_MAPPING; + n->cfgname = $5; + n->tokentype = $11; + n->missing_ok = true; + $$ = (Node*)n; + } + ; + +/* Use this if TIME or ORDINALITY after WITH should be taken as an identifier */ +any_with: WITH + | WITH_LA + ; + + +/***************************************************************************** + * + * Manipulate a conversion + * + * CREATE [DEFAULT] CONVERSION <conversion_name> + * FOR <encoding_name> TO <encoding_name> FROM <func_name> + * + *****************************************************************************/ + +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 [VERBOSE] <qualified_name> [ USING <index_name> ] + * CLUSTER [ (options) ] <qualified_name> [ USING <index_name> ] + * CLUSTER [VERBOSE] + * CLUSTER [VERBOSE] <index_name> ON <qualified_name> (for pre-8.3) + * + *****************************************************************************/ + +ClusterStmt: + CLUSTER opt_verbose qualified_name cluster_index_specification + { + ClusterStmt *n = makeNode(ClusterStmt); + n->relation = $3; + n->indexname = $4; + n->params = NIL; + if ($2) + n->params = lappend(n->params, makeDefElem("verbose", NULL, @2)); + $$ = (Node*)n; + } + + | CLUSTER '(' utility_option_list ')' qualified_name cluster_index_specification + { + ClusterStmt *n = makeNode(ClusterStmt); + n->relation = $5; + n->indexname = $6; + n->params = $3; + $$ = (Node*)n; + } + | CLUSTER opt_verbose + { + ClusterStmt *n = makeNode(ClusterStmt); + n->relation = NULL; + n->indexname = NULL; + n->params = NIL; + if ($2) + n->params = lappend(n->params, makeDefElem("verbose", NULL, @2)); + $$ = (Node*)n; + } + /* kept for pre-8.3 compatibility */ + | CLUSTER opt_verbose name ON qualified_name + { + ClusterStmt *n = makeNode(ClusterStmt); + n->relation = $5; + n->indexname = $3; + n->params = NIL; + if ($2) + n->params = lappend(n->params, makeDefElem("verbose", NULL, @2)); + $$ = (Node*)n; + } + ; + +cluster_index_specification: + USING name { $$ = $2; } + | /*EMPTY*/ { $$ = NULL; } + ; + + +/***************************************************************************** + * + * QUERY: + * VACUUM + * ANALYZE + * + *****************************************************************************/ + +VacuumStmt: VACUUM opt_full opt_freeze opt_verbose opt_analyze opt_vacuum_relation_list + { + VacuumStmt *n = makeNode(VacuumStmt); + n->options = NIL; + if ($2) + n->options = lappend(n->options, + makeDefElem("full", NULL, @2)); + if ($3) + n->options = lappend(n->options, + makeDefElem("freeze", NULL, @3)); + if ($4) + n->options = lappend(n->options, + makeDefElem("verbose", NULL, @4)); + if ($5) + n->options = lappend(n->options, + makeDefElem("analyze", NULL, @5)); + n->rels = $6; + n->is_vacuumcmd = true; + $$ = (Node *)n; + } + | VACUUM '(' utility_option_list ')' opt_vacuum_relation_list + { + VacuumStmt *n = makeNode(VacuumStmt); + n->options = $3; + n->rels = $5; + n->is_vacuumcmd = true; + $$ = (Node *) n; + } + ; + +AnalyzeStmt: analyze_keyword opt_verbose opt_vacuum_relation_list + { + VacuumStmt *n = makeNode(VacuumStmt); + n->options = NIL; + if ($2) + n->options = lappend(n->options, + makeDefElem("verbose", NULL, @2)); + n->rels = $3; + n->is_vacuumcmd = false; + $$ = (Node *)n; + } + | analyze_keyword '(' utility_option_list ')' opt_vacuum_relation_list + { + VacuumStmt *n = makeNode(VacuumStmt); + n->options = $3; + n->rels = $5; + n->is_vacuumcmd = false; + $$ = (Node *) n; + } + ; + +utility_option_list: + utility_option_elem + { + $$ = list_make1($1); + } + | utility_option_list ',' utility_option_elem + { + $$ = lappend($1, $3); + } + ; + +analyze_keyword: + ANALYZE + | ANALYSE /* British */ + ; + +utility_option_elem: + utility_option_name utility_option_arg + { + $$ = makeDefElem($1, $2, @1); + } + ; + +utility_option_name: + NonReservedWord { $$ = $1; } + | analyze_keyword { $$ = "analyze"; } + ; + +utility_option_arg: + opt_boolean_or_string { $$ = (Node *) makeString($1); } + | NumericOnly { $$ = (Node *) $1; } + | /* EMPTY */ { $$ = NULL; } + ; + +opt_analyze: + analyze_keyword { $$ = true; } + | /*EMPTY*/ { $$ = false; } + ; + +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; } + ; + +vacuum_relation: + qualified_name opt_name_list + { + $$ = (Node *) makeVacuumRelation($1, InvalidOid, $2); + } + ; + +vacuum_relation_list: + vacuum_relation + { $$ = list_make1($1); } + | vacuum_relation_list ',' vacuum_relation + { $$ = lappend($1, $3); } + ; + +opt_vacuum_relation_list: + vacuum_relation_list { $$ = $1; } + | /*EMPTY*/ { $$ = NIL; } + ; + + +/***************************************************************************** + * + * QUERY: + * EXPLAIN [ANALYZE] [VERBOSE] query + * EXPLAIN ( options ) query + * + *****************************************************************************/ + +ExplainStmt: + EXPLAIN ExplainableStmt + { + ExplainStmt *n = makeNode(ExplainStmt); + n->query = $2; + n->options = NIL; + $$ = (Node *) n; + } + | EXPLAIN analyze_keyword opt_verbose ExplainableStmt + { + ExplainStmt *n = makeNode(ExplainStmt); + n->query = $4; + n->options = list_make1(makeDefElem("analyze", NULL, @2)); + if ($3) + n->options = lappend(n->options, + makeDefElem("verbose", NULL, @3)); + $$ = (Node *) n; + } + | EXPLAIN VERBOSE ExplainableStmt + { + ExplainStmt *n = makeNode(ExplainStmt); + n->query = $3; + n->options = list_make1(makeDefElem("verbose", NULL, @2)); + $$ = (Node *) n; + } + | EXPLAIN '(' utility_option_list ')' ExplainableStmt + { + ExplainStmt *n = makeNode(ExplainStmt); + n->query = $5; + n->options = $3; + $$ = (Node *) n; + } + ; + +ExplainableStmt: + SelectStmt + | InsertStmt + | UpdateStmt + | DeleteStmt + | DeclareCursorStmt + | CreateAsStmt + | CreateMatViewStmt + | RefreshMatViewStmt + | ExecuteStmt /* by default all are $$=$1 */ + ; + +/***************************************************************************** + * + * QUERY: + * PREPARE <plan_name> [(args, ...)] AS <query> + * + *****************************************************************************/ + +PrepareStmt: PREPARE name prep_type_clause AS PreparableStmt + { + PrepareStmt *n = makeNode(PrepareStmt); + n->name = $2; + n->argtypes = $3; + n->query = $5; + $$ = (Node *) n; + } + ; + +prep_type_clause: '(' type_list ')' { $$ = $2; } + | /* EMPTY */ { $$ = NIL; } + ; + +PreparableStmt: + SelectStmt + | InsertStmt + | UpdateStmt + | DeleteStmt /* by default all are $$=$1 */ + ; + +/***************************************************************************** + * + * EXECUTE <plan_name> [(params, ...)] + * CREATE TABLE <name> AS EXECUTE <plan_name> [(params, ...)] + * + *****************************************************************************/ + +ExecuteStmt: EXECUTE name execute_param_clause + { + ExecuteStmt *n = makeNode(ExecuteStmt); + n->name = $2; + n->params = $3; + $$ = (Node *) n; + } + | CREATE OptTemp TABLE create_as_target AS + EXECUTE name execute_param_clause opt_with_data + { + CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt); + ExecuteStmt *n = makeNode(ExecuteStmt); + n->name = $7; + n->params = $8; + ctas->query = (Node *) n; + ctas->into = $4; + ctas->objtype = OBJECT_TABLE; + ctas->is_select_into = false; + ctas->if_not_exists = false; + /* cram additional flags into the IntoClause */ + $4->rel->relpersistence = $2; + $4->skipData = !($9); + $$ = (Node *) ctas; + } + | CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS + EXECUTE name execute_param_clause opt_with_data + { + CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt); + ExecuteStmt *n = makeNode(ExecuteStmt); + n->name = $10; + n->params = $11; + ctas->query = (Node *) n; + ctas->into = $7; + ctas->objtype = OBJECT_TABLE; + ctas->is_select_into = false; + ctas->if_not_exists = true; + /* cram additional flags into the IntoClause */ + $7->rel->relpersistence = $2; + $7->skipData = !($12); + $$ = (Node *) ctas; + } + ; + +execute_param_clause: '(' expr_list ')' { $$ = $2; } + | /* EMPTY */ { $$ = NIL; } + ; + +/***************************************************************************** + * + * QUERY: + * DEALLOCATE [PREPARE] <plan_name> + * + *****************************************************************************/ + +DeallocateStmt: DEALLOCATE name + { + DeallocateStmt *n = makeNode(DeallocateStmt); + n->name = $2; + $$ = (Node *) n; + } + | DEALLOCATE PREPARE name + { + DeallocateStmt *n = makeNode(DeallocateStmt); + n->name = $3; + $$ = (Node *) n; + } + | DEALLOCATE ALL + { + DeallocateStmt *n = makeNode(DeallocateStmt); + n->name = NULL; + $$ = (Node *) n; + } + | DEALLOCATE PREPARE ALL + { + DeallocateStmt *n = makeNode(DeallocateStmt); + n->name = NULL; + $$ = (Node *) n; + } + ; + +/***************************************************************************** + * + * QUERY: + * INSERT STATEMENTS + * + *****************************************************************************/ + +InsertStmt: + opt_with_clause INSERT INTO insert_target insert_rest + opt_on_conflict returning_clause + { + $5->relation = $4; + $5->onConflictClause = $6; + $5->returningList = $7; + $5->withClause = $1; + $$ = (Node *) $5; + } + ; + +/* + * Can't easily make AS optional here, because VALUES in insert_rest would + * have a shift/reduce conflict with VALUES as an optional alias. We could + * easily allow unreserved_keywords as optional aliases, but that'd be an odd + * divergence from other places. So just require AS for now. + */ +insert_target: + qualified_name + { + $$ = $1; + } + | qualified_name AS ColId + { + $1->alias = makeAlias($3, NIL); + $$ = $1; + } + ; + +insert_rest: + SelectStmt + { + $$ = makeNode(InsertStmt); + $$->cols = NIL; + $$->selectStmt = $1; + } + | OVERRIDING override_kind VALUE_P SelectStmt + { + $$ = makeNode(InsertStmt); + $$->cols = NIL; + $$->override = $2; + $$->selectStmt = $4; + } + | '(' insert_column_list ')' SelectStmt + { + $$ = makeNode(InsertStmt); + $$->cols = $2; + $$->selectStmt = $4; + } + | '(' insert_column_list ')' OVERRIDING override_kind VALUE_P SelectStmt + { + $$ = makeNode(InsertStmt); + $$->cols = $2; + $$->override = $5; + $$->selectStmt = $7; + } + | DEFAULT VALUES + { + $$ = makeNode(InsertStmt); + $$->cols = NIL; + $$->selectStmt = NULL; + } + ; + +override_kind: + USER { $$ = OVERRIDING_USER_VALUE; } + | SYSTEM_P { $$ = OVERRIDING_SYSTEM_VALUE; } + ; + +insert_column_list: + insert_column_item + { $$ = list_make1($1); } + | insert_column_list ',' insert_column_item + { $$ = lappend($1, $3); } + ; + +insert_column_item: + ColId opt_indirection + { + $$ = makeNode(ResTarget); + $$->name = $1; + $$->indirection = check_indirection($2, yyscanner); + $$->val = NULL; + $$->location = @1; + } + ; + +opt_on_conflict: + ON CONFLICT opt_conf_expr DO UPDATE SET set_clause_list where_clause + { + $$ = makeNode(OnConflictClause); + $$->action = ONCONFLICT_UPDATE; + $$->infer = $3; + $$->targetList = $7; + $$->whereClause = $8; + $$->location = @1; + } + | + ON CONFLICT opt_conf_expr DO NOTHING + { + $$ = makeNode(OnConflictClause); + $$->action = ONCONFLICT_NOTHING; + $$->infer = $3; + $$->targetList = NIL; + $$->whereClause = NULL; + $$->location = @1; + } + | /*EMPTY*/ + { + $$ = NULL; + } + ; + +opt_conf_expr: + '(' index_params ')' where_clause + { + $$ = makeNode(InferClause); + $$->indexElems = $2; + $$->whereClause = $4; + $$->conname = NULL; + $$->location = @1; + } + | + ON CONSTRAINT name + { + $$ = makeNode(InferClause); + $$->indexElems = NIL; + $$->whereClause = NULL; + $$->conname = $3; + $$->location = @1; + } + | /*EMPTY*/ + { + $$ = NULL; + } + ; + +returning_clause: + RETURNING target_list { $$ = $2; } + | /* EMPTY */ { $$ = NIL; } + ; + + +/***************************************************************************** + * + * QUERY: + * DELETE STATEMENTS + * + *****************************************************************************/ + +DeleteStmt: opt_with_clause DELETE_P FROM relation_expr_opt_alias + using_clause where_or_current_clause returning_clause + { + DeleteStmt *n = makeNode(DeleteStmt); + n->relation = $4; + n->usingClause = $5; + n->whereClause = $6; + n->returningList = $7; + n->withClause = $1; + $$ = (Node *)n; + } + ; + +using_clause: + USING from_list { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + + +/***************************************************************************** + * + * QUERY: + * LOCK TABLE + * + *****************************************************************************/ + +LockStmt: LOCK_P opt_table relation_expr_list opt_lock opt_nowait + { + LockStmt *n = makeNode(LockStmt); + + n->relations = $3; + n->mode = $4; + n->nowait = $5; + $$ = (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; } + ; + +opt_nowait: NOWAIT { $$ = true; } + | /*EMPTY*/ { $$ = false; } + ; + +opt_nowait_or_skip: + NOWAIT { $$ = LockWaitError; } + | SKIP LOCKED { $$ = LockWaitSkip; } + | /*EMPTY*/ { $$ = LockWaitBlock; } + ; + + +/***************************************************************************** + * + * QUERY: + * UpdateStmt (UPDATE) + * + *****************************************************************************/ + +UpdateStmt: opt_with_clause UPDATE relation_expr_opt_alias + SET set_clause_list + from_clause + where_or_current_clause + returning_clause + { + UpdateStmt *n = makeNode(UpdateStmt); + n->relation = $3; + n->targetList = $5; + n->fromClause = $6; + n->whereClause = $7; + n->returningList = $8; + n->withClause = $1; + $$ = (Node *)n; + } + ; + +set_clause_list: + set_clause { $$ = $1; } + | set_clause_list ',' set_clause { $$ = list_concat($1,$3); } + ; + +set_clause: + set_target '=' a_expr + { + $1->val = (Node *) $3; + $$ = list_make1($1); + } + | '(' set_target_list ')' '=' a_expr + { + int ncolumns = list_length($2); + int i = 1; + ListCell *col_cell; + + /* Create a MultiAssignRef source for each target */ + foreach(col_cell, $2) + { + ResTarget *res_col = (ResTarget *) lfirst(col_cell); + MultiAssignRef *r = makeNode(MultiAssignRef); + + r->source = (Node *) $5; + r->colno = i; + r->ncolumns = ncolumns; + res_col->val = (Node *) r; + i++; + } + + $$ = $2; + } + ; + +set_target: + ColId opt_indirection + { + $$ = makeNode(ResTarget); + $$->name = $1; + $$->indirection = check_indirection($2, yyscanner); + $$->val = NULL; /* upper production sets this */ + $$->location = @1; + } + ; + +set_target_list: + set_target { $$ = list_make1($1); } + | set_target_list ',' set_target { $$ = lappend($1,$3); } + ; + + +/***************************************************************************** + * + * QUERY: + * CURSOR STATEMENTS + * + *****************************************************************************/ +DeclareCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR SelectStmt + { + DeclareCursorStmt *n = makeNode(DeclareCursorStmt); + n->portalname = $2; + /* currently we always set FAST_PLAN option */ + n->options = $3 | $5 | CURSOR_OPT_FAST_PLAN; + n->query = $7; + $$ = (Node *)n; + } + ; + +cursor_name: name { $$ = $1; } + ; + +cursor_options: /*EMPTY*/ { $$ = 0; } + | cursor_options NO SCROLL { $$ = $1 | CURSOR_OPT_NO_SCROLL; } + | cursor_options SCROLL { $$ = $1 | CURSOR_OPT_SCROLL; } + | cursor_options BINARY { $$ = $1 | CURSOR_OPT_BINARY; } + | cursor_options ASENSITIVE { $$ = $1 | CURSOR_OPT_ASENSITIVE; } + | cursor_options INSENSITIVE { $$ = $1 | CURSOR_OPT_INSENSITIVE; } + ; + +opt_hold: /* EMPTY */ { $$ = 0; } + | WITH HOLD { $$ = CURSOR_OPT_HOLD; } + | WITHOUT HOLD { $$ = 0; } + ; + +/***************************************************************************** + * + * 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 bison 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 '(' <select> ')' as + * a select_with_parens. To resolve the conflicts, the productions that + * conflict with the select_with_parens productions are manually given + * precedences lower than the precedence of ')', thereby ensuring that we + * shift ')' (and then reduce to select_with_parens) rather than trying to + * reduce the inner <select> 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; } + ; + +/* + * This rule parses the equivalent of the standard's <query expression>. + * The duplicative productions are annoying, but hard to get rid of without + * creating shift/reduce conflicts. + * + * The locking clause (FOR UPDATE etc) 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 the locking + * clause. + * 2002-08-28 bjm + */ +select_no_parens: + simple_select { $$ = $1; } + | select_clause sort_clause + { + insertSelectOptions((SelectStmt *) $1, $2, NIL, + NULL, NULL, + yyscanner); + $$ = $1; + } + | select_clause opt_sort_clause for_locking_clause opt_select_limit + { + insertSelectOptions((SelectStmt *) $1, $2, $3, + $4, + NULL, + yyscanner); + $$ = $1; + } + | select_clause opt_sort_clause select_limit opt_for_locking_clause + { + insertSelectOptions((SelectStmt *) $1, $2, $4, + $3, + NULL, + yyscanner); + $$ = $1; + } + | with_clause select_clause + { + insertSelectOptions((SelectStmt *) $2, NULL, NIL, + NULL, + $1, + yyscanner); + $$ = $2; + } + | with_clause select_clause sort_clause + { + insertSelectOptions((SelectStmt *) $2, $3, NIL, + NULL, + $1, + yyscanner); + $$ = $2; + } + | with_clause select_clause opt_sort_clause for_locking_clause opt_select_limit + { + insertSelectOptions((SelectStmt *) $2, $3, $4, + $5, + $1, + yyscanner); + $$ = $2; + } + | with_clause select_clause opt_sort_clause select_limit opt_for_locking_clause + { + insertSelectOptions((SelectStmt *) $2, $3, $5, + $4, + $1, + yyscanner); + $$ = $2; + } + ; + +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. + * + * It might appear that we could fold the first two alternatives into one + * by using opt_distinct_clause. However, that causes a shift/reduce conflict + * against INSERT ... SELECT ... ON CONFLICT. We avoid the ambiguity by + * requiring SELECT DISTINCT [ON] to be followed by a non-empty target_list. + * + * Note that sort clauses cannot be included at this level --- SQL 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 WITH, 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 these + * 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_all_clause opt_target_list + into_clause from_clause where_clause + group_clause having_clause window_clause + { + SelectStmt *n = makeNode(SelectStmt); + n->targetList = $3; + n->intoClause = $4; + n->fromClause = $5; + n->whereClause = $6; + n->groupClause = ($7)->list; + n->groupDistinct = ($7)->distinct; + n->havingClause = $8; + n->windowClause = $9; + $$ = (Node *)n; + } + | SELECT distinct_clause target_list + into_clause from_clause where_clause + group_clause having_clause window_clause + { + SelectStmt *n = makeNode(SelectStmt); + n->distinctClause = $2; + n->targetList = $3; + n->intoClause = $4; + n->fromClause = $5; + n->whereClause = $6; + n->groupClause = ($7)->list; + n->groupDistinct = ($7)->distinct; + n->havingClause = $8; + n->windowClause = $9; + $$ = (Node *)n; + } + | values_clause { $$ = $1; } + | TABLE relation_expr + { + /* same as SELECT * FROM relation_expr */ + ColumnRef *cr = makeNode(ColumnRef); + ResTarget *rt = makeNode(ResTarget); + SelectStmt *n = makeNode(SelectStmt); + + cr->fields = list_make1(makeNode(A_Star)); + cr->location = -1; + + rt->name = NULL; + rt->indirection = NIL; + rt->val = (Node *)cr; + rt->location = -1; + + n->targetList = list_make1(rt); + n->fromClause = list_make1($2); + $$ = (Node *)n; + } + | select_clause UNION set_quantifier select_clause + { + $$ = makeSetOp(SETOP_UNION, $3 == SET_QUANTIFIER_ALL, $1, $4); + } + | select_clause INTERSECT set_quantifier select_clause + { + $$ = makeSetOp(SETOP_INTERSECT, $3 == SET_QUANTIFIER_ALL, $1, $4); + } + | select_clause EXCEPT set_quantifier select_clause + { + $$ = makeSetOp(SETOP_EXCEPT, $3 == SET_QUANTIFIER_ALL, $1, $4); + } + ; + +/* + * SQL standard WITH clause looks like: + * + * WITH [ RECURSIVE ] <query name> [ (<column>,...) ] + * AS (query) [ SEARCH or CYCLE clause ] + * + * Recognizing WITH_LA here allows a CTE to be named TIME or ORDINALITY. + */ +with_clause: + WITH cte_list + { + $$ = makeNode(WithClause); + $$->ctes = $2; + $$->recursive = false; + $$->location = @1; + } + | WITH_LA cte_list + { + $$ = makeNode(WithClause); + $$->ctes = $2; + $$->recursive = false; + $$->location = @1; + } + | WITH RECURSIVE cte_list + { + $$ = makeNode(WithClause); + $$->ctes = $3; + $$->recursive = true; + $$->location = @1; + } + ; + +cte_list: + common_table_expr { $$ = list_make1($1); } + | cte_list ',' common_table_expr { $$ = lappend($1, $3); } + ; + +common_table_expr: name opt_name_list AS opt_materialized '(' PreparableStmt ')' opt_search_clause opt_cycle_clause + { + CommonTableExpr *n = makeNode(CommonTableExpr); + n->ctename = $1; + n->aliascolnames = $2; + n->ctematerialized = $4; + n->ctequery = $6; + n->search_clause = castNode(CTESearchClause, $8); + n->cycle_clause = castNode(CTECycleClause, $9); + n->location = @1; + $$ = (Node *) n; + } + ; + +opt_materialized: + MATERIALIZED { $$ = CTEMaterializeAlways; } + | NOT MATERIALIZED { $$ = CTEMaterializeNever; } + | /*EMPTY*/ { $$ = CTEMaterializeDefault; } + ; + +opt_search_clause: + SEARCH DEPTH FIRST_P BY columnList SET ColId + { + CTESearchClause *n = makeNode(CTESearchClause); + n->search_col_list = $5; + n->search_breadth_first = false; + n->search_seq_column = $7; + n->location = @1; + $$ = (Node *) n; + } + | SEARCH BREADTH FIRST_P BY columnList SET ColId + { + CTESearchClause *n = makeNode(CTESearchClause); + n->search_col_list = $5; + n->search_breadth_first = true; + n->search_seq_column = $7; + n->location = @1; + $$ = (Node *) n; + } + | /*EMPTY*/ + { + $$ = NULL; + } + ; + +opt_cycle_clause: + CYCLE columnList SET ColId TO AexprConst DEFAULT AexprConst USING ColId + { + CTECycleClause *n = makeNode(CTECycleClause); + n->cycle_col_list = $2; + n->cycle_mark_column = $4; + n->cycle_mark_value = $6; + n->cycle_mark_default = $8; + n->cycle_path_column = $10; + n->location = @1; + $$ = (Node *) n; + } + | CYCLE columnList SET ColId USING ColId + { + CTECycleClause *n = makeNode(CTECycleClause); + n->cycle_col_list = $2; + n->cycle_mark_column = $4; + n->cycle_mark_value = makeBoolAConst(true, -1); + n->cycle_mark_default = makeBoolAConst(false, -1); + n->cycle_path_column = $6; + n->location = @1; + $$ = (Node *) n; + } + | /*EMPTY*/ + { + $$ = NULL; + } + ; + +opt_with_clause: + with_clause { $$ = $1; } + | /*EMPTY*/ { $$ = NULL; } + ; + +into_clause: + INTO OptTempTableName + { + $$ = makeNode(IntoClause); + $$->rel = $2; + $$->colNames = NIL; + $$->options = NIL; + $$->onCommit = ONCOMMIT_NOOP; + $$->tableSpaceName = NULL; + $$->viewQuery = NULL; + $$->skipData = false; + } + | /*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; + $$->relpersistence = RELPERSISTENCE_TEMP; + } + | TEMP opt_table qualified_name + { + $$ = $3; + $$->relpersistence = RELPERSISTENCE_TEMP; + } + | LOCAL TEMPORARY opt_table qualified_name + { + $$ = $4; + $$->relpersistence = RELPERSISTENCE_TEMP; + } + | LOCAL TEMP opt_table qualified_name + { + $$ = $4; + $$->relpersistence = RELPERSISTENCE_TEMP; + } + | GLOBAL TEMPORARY opt_table qualified_name + { + ereport(WARNING, + (errmsg("GLOBAL is deprecated in temporary table creation"), + parser_errposition(@1))); + $$ = $4; + $$->relpersistence = RELPERSISTENCE_TEMP; + } + | GLOBAL TEMP opt_table qualified_name + { + ereport(WARNING, + (errmsg("GLOBAL is deprecated in temporary table creation"), + parser_errposition(@1))); + $$ = $4; + $$->relpersistence = RELPERSISTENCE_TEMP; + } + | UNLOGGED opt_table qualified_name + { + $$ = $3; + $$->relpersistence = RELPERSISTENCE_UNLOGGED; + } + | TABLE qualified_name + { + $$ = $2; + $$->relpersistence = RELPERSISTENCE_PERMANENT; + } + | qualified_name + { + $$ = $1; + $$->relpersistence = RELPERSISTENCE_PERMANENT; + } + ; + +opt_table: TABLE + | /*EMPTY*/ + ; + +set_quantifier: + ALL { $$ = SET_QUANTIFIER_ALL; } + | DISTINCT { $$ = SET_QUANTIFIER_DISTINCT; } + | /*EMPTY*/ { $$ = SET_QUANTIFIER_DEFAULT; } + ; + +/* We use (NIL) as a placeholder to indicate that all target expressions + * should be placed in the DISTINCT list during parsetree analysis. + */ +distinct_clause: + DISTINCT { $$ = list_make1(NIL); } + | DISTINCT ON '(' expr_list ')' { $$ = $4; } + ; + +opt_all_clause: + ALL + | /*EMPTY*/ + ; + +opt_distinct_clause: + distinct_clause { $$ = $1; } + | opt_all_clause { $$ = NIL; } + ; + +opt_sort_clause: + sort_clause { $$ = $1; } + | /*EMPTY*/ { $$ = NIL; } + ; + +sort_clause: + ORDER BY sortby_list { $$ = $3; } + ; + +sortby_list: + sortby { $$ = list_make1($1); } + | sortby_list ',' sortby { $$ = lappend($1, $3); } + ; + +sortby: a_expr USING qual_all_Op opt_nulls_order + { + $$ = makeNode(SortBy); + $$->node = $1; + $$->sortby_dir = SORTBY_USING; + $$->sortby_nulls = $4; + $$->useOp = $3; + $$->location = @3; + } + | a_expr opt_asc_desc opt_nulls_order + { + $$ = makeNode(SortBy); + $$->node = $1; + $$->sortby_dir = $2; + $$->sortby_nulls = $3; + $$->useOp = NIL; + $$->location = -1; /* no operator */ + } + ; + + +select_limit: + limit_clause offset_clause + { + $$ = $1; + ($$)->limitOffset = $2; + } + | offset_clause limit_clause + { + $$ = $2; + ($$)->limitOffset = $1; + } + | limit_clause + { + $$ = $1; + } + | offset_clause + { + SelectLimit *n = (SelectLimit *) palloc(sizeof(SelectLimit)); + n->limitOffset = $1; + n->limitCount = NULL; + n->limitOption = LIMIT_OPTION_COUNT; + $$ = n; + } + ; + +opt_select_limit: + select_limit { $$ = $1; } + | /* EMPTY */ { $$ = NULL; } + ; + +limit_clause: + LIMIT select_limit_value + { + SelectLimit *n = (SelectLimit *) palloc(sizeof(SelectLimit)); + n->limitOffset = NULL; + n->limitCount = $2; + n->limitOption = LIMIT_OPTION_COUNT; + $$ = n; + } + | LIMIT select_limit_value ',' select_offset_value + { + /* Disabled because it was too confusing, bjm 2002-02-18 */ + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("LIMIT #,# syntax is not supported"), + errhint("Use separate LIMIT and OFFSET clauses."), + parser_errposition(@1))); + } + /* SQL:2008 syntax */ + /* to avoid shift/reduce conflicts, handle the optional value with + * a separate production rather than an opt_ expression. The fact + * that ONLY is fully reserved means that this way, we defer any + * decision about what rule reduces ROW or ROWS to the point where + * we can see the ONLY token in the lookahead slot. + */ + | FETCH first_or_next select_fetch_first_value row_or_rows ONLY + { + SelectLimit *n = (SelectLimit *) palloc(sizeof(SelectLimit)); + n->limitOffset = NULL; + n->limitCount = $3; + n->limitOption = LIMIT_OPTION_COUNT; + $$ = n; + } + | FETCH first_or_next select_fetch_first_value row_or_rows WITH TIES + { + SelectLimit *n = (SelectLimit *) palloc(sizeof(SelectLimit)); + n->limitOffset = NULL; + n->limitCount = $3; + n->limitOption = LIMIT_OPTION_WITH_TIES; + $$ = n; + } + | FETCH first_or_next row_or_rows ONLY + { + SelectLimit *n = (SelectLimit *) palloc(sizeof(SelectLimit)); + n->limitOffset = NULL; + n->limitCount = makeIntConst(1, -1); + n->limitOption = LIMIT_OPTION_COUNT; + $$ = n; + } + | FETCH first_or_next row_or_rows WITH TIES + { + SelectLimit *n = (SelectLimit *) palloc(sizeof(SelectLimit)); + n->limitOffset = NULL; + n->limitCount = makeIntConst(1, -1); + n->limitOption = LIMIT_OPTION_WITH_TIES; + $$ = n; + } + ; + +offset_clause: + OFFSET select_offset_value + { $$ = $2; } + /* SQL:2008 syntax */ + | OFFSET select_fetch_first_value row_or_rows + { $$ = $2; } + ; + +select_limit_value: + a_expr { $$ = $1; } + | ALL + { + /* LIMIT ALL is represented as a NULL constant */ + $$ = makeNullAConst(@1); + } + ; + +select_offset_value: + a_expr { $$ = $1; } + ; + +/* + * Allowing full expressions without parentheses causes various parsing + * problems with the trailing ROW/ROWS key words. SQL spec only calls for + * <simple value specification>, which is either a literal or a parameter (but + * an <SQL parameter reference> could be an identifier, bringing up conflicts + * with ROW/ROWS). We solve this by leveraging the presence of ONLY (see above) + * to determine whether the expression is missing rather than trying to make it + * optional in this rule. + * + * c_expr covers almost all the spec-required cases (and more), but it doesn't + * cover signed numeric literals, which are allowed by the spec. So we include + * those here explicitly. We need FCONST as well as ICONST because values that + * don't fit in the platform's "long", but do fit in bigint, should still be + * accepted here. (This is possible in 64-bit Windows as well as all 32-bit + * builds.) + */ +select_fetch_first_value: + c_expr { $$ = $1; } + | '+' I_or_F_const + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", NULL, $2, @1); } + | '-' I_or_F_const + { $$ = doNegate($2, @1); } + ; + +I_or_F_const: + Iconst { $$ = makeIntConst($1,@1); } + | FCONST { $$ = makeFloatConst($1,@1); } + ; + +/* noise words */ +row_or_rows: ROW { $$ = 0; } + | ROWS { $$ = 0; } + ; + +first_or_next: FIRST_P { $$ = 0; } + | NEXT { $$ = 0; } + ; + + +/* + * This syntax for group_clause tries to follow the spec quite closely. + * However, the spec allows only column references, not expressions, + * which introduces an ambiguity between implicit row constructors + * (a,b) and lists of column references. + * + * We handle this by using the a_expr production for what the spec calls + * <ordinary grouping set>, which in the spec represents either one column + * reference or a parenthesized list of column references. Then, we check the + * top node of the a_expr to see if it's an implicit RowExpr, and if so, just + * grab and use the list, discarding the node. (this is done in parse analysis, + * not here) + * + * (we abuse the row_format field of RowExpr to distinguish implicit and + * explicit row constructors; it's debatable if anyone sanely wants to use them + * in a group clause, but if they have a reason to, we make it possible.) + * + * Each item in the group_clause list is either an expression tree or a + * GroupingSet node of some type. + */ +group_clause: + GROUP_P BY set_quantifier group_by_list + { + GroupClause *n = (GroupClause *) palloc(sizeof(GroupClause)); + n->distinct = $3 == SET_QUANTIFIER_DISTINCT; + n->list = $4; + $$ = n; + } + | /*EMPTY*/ + { + GroupClause *n = (GroupClause *) palloc(sizeof(GroupClause)); + n->distinct = false; + n->list = NIL; + $$ = n; + } + ; + +group_by_list: + group_by_item { $$ = list_make1($1); } + | group_by_list ',' group_by_item { $$ = lappend($1,$3); } + ; + +group_by_item: + a_expr { $$ = $1; } + | empty_grouping_set { $$ = $1; } + | cube_clause { $$ = $1; } + | rollup_clause { $$ = $1; } + | grouping_sets_clause { $$ = $1; } + ; + +empty_grouping_set: + '(' ')' + { + $$ = (Node *) makeGroupingSet(GROUPING_SET_EMPTY, NIL, @1); + } + ; + +/* + * These hacks rely on setting precedence of CUBE and ROLLUP below that of '(', + * so that they shift in these rules rather than reducing the conflicting + * unreserved_keyword rule. + */ + +rollup_clause: + ROLLUP '(' expr_list ')' + { + $$ = (Node *) makeGroupingSet(GROUPING_SET_ROLLUP, $3, @1); + } + ; + +cube_clause: + CUBE '(' expr_list ')' + { + $$ = (Node *) makeGroupingSet(GROUPING_SET_CUBE, $3, @1); + } + ; + +grouping_sets_clause: + GROUPING SETS '(' group_by_list ')' + { + $$ = (Node *) makeGroupingSet(GROUPING_SET_SETS, $4, @1); + } + ; + +having_clause: + HAVING a_expr { $$ = $2; } + | /*EMPTY*/ { $$ = NULL; } + ; + +for_locking_clause: + for_locking_items { $$ = $1; } + | FOR READ ONLY { $$ = NIL; } + ; + +opt_for_locking_clause: + for_locking_clause { $$ = $1; } + | /* EMPTY */ { $$ = NIL; } + ; + +for_locking_items: + for_locking_item { $$ = list_make1($1); } + | for_locking_items for_locking_item { $$ = lappend($1, $2); } + ; + +for_locking_item: + for_locking_strength locked_rels_list opt_nowait_or_skip + { + LockingClause *n = makeNode(LockingClause); + n->lockedRels = $2; + n->strength = $1; + n->waitPolicy = $3; + $$ = (Node *) n; + } + ; + +for_locking_strength: + FOR UPDATE { $$ = LCS_FORUPDATE; } + | FOR NO KEY UPDATE { $$ = LCS_FORNOKEYUPDATE; } + | FOR SHARE { $$ = LCS_FORSHARE; } + | FOR KEY SHARE { $$ = LCS_FORKEYSHARE; } + ; + +locked_rels_list: + OF qualified_name_list { $$ = $2; } + | /* EMPTY */ { $$ = NIL; } + ; + + +/* + * We should allow ROW '(' expr_list ')' too, but that seems to require + * making VALUES a fully reserved word, which will probably break more apps + * than allowing the noise-word is worth. + */ +values_clause: + VALUES '(' expr_list ')' + { + SelectStmt *n = makeNode(SelectStmt); + n->valuesLists = list_make1($3); + $$ = (Node *) n; + } + | values_clause ',' '(' expr_list ')' + { + SelectStmt *n = (SelectStmt *) $1; + n->valuesLists = lappend(n->valuesLists, $4); + $$ = (Node *) n; + } + ; + + +/***************************************************************************** + * + * 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 { $$ = list_make1($1); } + | from_list ',' table_ref { $$ = lappend($1, $3); } + ; + +/* + * table_ref is where an alias clause can be attached. + */ +table_ref: relation_expr opt_alias_clause + { + $1->alias = $2; + $$ = (Node *) $1; + } + | relation_expr opt_alias_clause tablesample_clause + { + RangeTableSample *n = (RangeTableSample *) $3; + $1->alias = $2; + /* relation_expr goes inside the RangeTableSample node */ + n->relation = (Node *) $1; + $$ = (Node *) n; + } + | func_table func_alias_clause + { + RangeFunction *n = (RangeFunction *) $1; + n->alias = linitial($2); + n->coldeflist = lsecond($2); + $$ = (Node *) n; + } + | LATERAL_P func_table func_alias_clause + { + RangeFunction *n = (RangeFunction *) $2; + n->lateral = true; + n->alias = linitial($3); + n->coldeflist = lsecond($3); + $$ = (Node *) n; + } + | xmltable opt_alias_clause + { + RangeTableFunc *n = (RangeTableFunc *) $1; + n->alias = $2; + $$ = (Node *) n; + } + | LATERAL_P xmltable opt_alias_clause + { + RangeTableFunc *n = (RangeTableFunc *) $2; + n->lateral = true; + n->alias = $3; + $$ = (Node *) n; + } + | select_with_parens opt_alias_clause + { + RangeSubselect *n = makeNode(RangeSubselect); + n->lateral = false; + n->subquery = $1; + n->alias = $2; + /* + * The SQL spec does not permit a subselect + * (<derived_table>) 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 "syntax error". + */ + if ($2 == NULL) + { + if (IsA($1, SelectStmt) && + ((SelectStmt *) $1)->valuesLists) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("VALUES in FROM must have an alias"), + errhint("For example, FROM (VALUES ...) [AS] foo."), + parser_errposition(@1))); + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subquery in FROM must have an alias"), + errhint("For example, FROM (SELECT ...) [AS] foo."), + parser_errposition(@1))); + } + $$ = (Node *) n; + } + | LATERAL_P select_with_parens opt_alias_clause + { + RangeSubselect *n = makeNode(RangeSubselect); + n->lateral = true; + n->subquery = $2; + n->alias = $3; + /* same comment as above */ + if ($3 == NULL) + { + if (IsA($2, SelectStmt) && + ((SelectStmt *) $2)->valuesLists) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("VALUES in FROM must have an alias"), + errhint("For example, FROM (VALUES ...) [AS] foo."), + parser_errposition(@2))); + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subquery in FROM must have an alias"), + errhint("For example, FROM (SELECT ...) [AS] foo."), + parser_errposition(@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 SQL'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->usingClause = NIL; + n->join_using_alias = NULL; + 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)) + { + /* USING clause */ + n->usingClause = linitial_node(List, castNode(List, $5)); + n->join_using_alias = lsecond_node(Alias, castNode(List, $5)); + } + else + { + /* ON clause */ + n->quals = $5; + } + $$ = 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)) + { + /* USING clause */ + n->usingClause = linitial_node(List, castNode(List, $4)); + n->join_using_alias = lsecond_node(Alias, castNode(List, $4)); + } + else + { + /* ON clause */ + n->quals = $4; + } + $$ = 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->usingClause = NIL; /* figure out which columns later... */ + n->join_using_alias = NULL; + 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->usingClause = NIL; /* figure out which columns later... */ + n->join_using_alias = NULL; + 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; + } + ; + +opt_alias_clause: alias_clause { $$ = $1; } + | /*EMPTY*/ { $$ = NULL; } + ; + +/* + * The alias clause after JOIN ... USING only accepts the AS ColId spelling, + * per SQL standard. (The grammar could parse the other variants, but they + * don't seem to be useful, and it might lead to parser problems in the + * future.) + */ +opt_alias_clause_for_join_using: + AS ColId + { + $$ = makeNode(Alias); + $$->aliasname = $2; + /* the column name list will be inserted later */ + } + | /*EMPTY*/ { $$ = NULL; } + ; + +/* + * func_alias_clause can include both an Alias and a coldeflist, so we make it + * return a 2-element list that gets disassembled by calling production. + */ +func_alias_clause: + alias_clause + { + $$ = list_make2($1, NIL); + } + | AS '(' TableFuncElementList ')' + { + $$ = list_make2(NULL, $3); + } + | AS ColId '(' TableFuncElementList ')' + { + Alias *a = makeNode(Alias); + a->aliasname = $2; + $$ = list_make2(a, $4); + } + | ColId '(' TableFuncElementList ')' + { + Alias *a = makeNode(Alias); + a->aliasname = $1; + $$ = list_make2(a, $3); + } + | /*EMPTY*/ + { + $$ = list_make2(NULL, NIL); + } + ; + +join_type: FULL opt_outer { $$ = JOIN_FULL; } + | LEFT opt_outer { $$ = JOIN_LEFT; } + | RIGHT opt_outer { $$ = JOIN_RIGHT; } + | INNER_P { $$ = JOIN_INNER; } + ; + +/* OUTER is just noise... */ +opt_outer: OUTER_P + | /*EMPTY*/ + ; + +/* JOIN qualification clauses + * Possibilities are: + * USING ( column list ) [ AS alias ] + * allows only unqualified column names, + * which must match between tables. + * ON expr allows more general qualifications. + * + * We return USING as a two-element List (the first item being a sub-List + * of the common column names, and the second either an Alias item or NULL). + * An ON-expr will not be a List, so it can be told apart that way. + */ + +join_qual: USING '(' name_list ')' opt_alias_clause_for_join_using + { + $$ = (Node *) list_make2($3, $5); + } + | ON a_expr + { + $$ = $2; + } + ; + + +relation_expr: + qualified_name + { + /* inheritance query, implicitly */ + $$ = $1; + $$->inh = true; + $$->alias = NULL; + } + | qualified_name '*' + { + /* inheritance query, explicitly */ + $$ = $1; + $$->inh = true; + $$->alias = NULL; + } + | ONLY qualified_name + { + /* no inheritance */ + $$ = $2; + $$->inh = false; + $$->alias = NULL; + } + | ONLY '(' qualified_name ')' + { + /* no inheritance, SQL99-style syntax */ + $$ = $3; + $$->inh = false; + $$->alias = NULL; + } + ; + + +relation_expr_list: + relation_expr { $$ = list_make1($1); } + | relation_expr_list ',' relation_expr { $$ = lappend($1, $3); } + ; + + +/* + * Given "UPDATE foo set set ...", we have to decide without looking any + * further ahead whether the first "set" is an alias or the UPDATE's SET + * keyword. Since "set" is allowed as a column name both interpretations + * are feasible. We resolve the shift/reduce conflict by giving the first + * relation_expr_opt_alias production a higher precedence than the SET token + * has, causing the parser to prefer to reduce, in effect assuming that the + * SET is not an alias. + */ +relation_expr_opt_alias: relation_expr %prec UMINUS + { + $$ = $1; + } + | relation_expr ColId + { + Alias *alias = makeNode(Alias); + alias->aliasname = $2; + $1->alias = alias; + $$ = $1; + } + | relation_expr AS ColId + { + Alias *alias = makeNode(Alias); + alias->aliasname = $3; + $1->alias = alias; + $$ = $1; + } + ; + +/* + * TABLESAMPLE decoration in a FROM item + */ +tablesample_clause: + TABLESAMPLE func_name '(' expr_list ')' opt_repeatable_clause + { + RangeTableSample *n = makeNode(RangeTableSample); + /* n->relation will be filled in later */ + n->method = $2; + n->args = $4; + n->repeatable = $6; + n->location = @2; + $$ = (Node *) n; + } + ; + +opt_repeatable_clause: + REPEATABLE '(' a_expr ')' { $$ = (Node *) $3; } + | /*EMPTY*/ { $$ = NULL; } + ; + +/* + * func_table represents a function invocation in a FROM list. It can be + * a plain function call, like "foo(...)", or a ROWS FROM expression with + * one or more function calls, "ROWS FROM (foo(...), bar(...))", + * optionally with WITH ORDINALITY attached. + * In the ROWS FROM syntax, a column definition list can be given for each + * function, for example: + * ROWS FROM (foo() AS (foo_res_a text, foo_res_b text), + * bar() AS (bar_res_a text, bar_res_b text)) + * It's also possible to attach a column definition list to the RangeFunction + * as a whole, but that's handled by the table_ref production. + */ +func_table: func_expr_windowless opt_ordinality + { + RangeFunction *n = makeNode(RangeFunction); + n->lateral = false; + n->ordinality = $2; + n->is_rowsfrom = false; + n->functions = list_make1(list_make2($1, NIL)); + /* alias and coldeflist are set by table_ref production */ + $$ = (Node *) n; + } + | ROWS FROM '(' rowsfrom_list ')' opt_ordinality + { + RangeFunction *n = makeNode(RangeFunction); + n->lateral = false; + n->ordinality = $6; + n->is_rowsfrom = true; + n->functions = $4; + /* alias and coldeflist are set by table_ref production */ + $$ = (Node *) n; + } + ; + +rowsfrom_item: func_expr_windowless opt_col_def_list + { $$ = list_make2($1, $2); } + ; + +rowsfrom_list: + rowsfrom_item { $$ = list_make1($1); } + | rowsfrom_list ',' rowsfrom_item { $$ = lappend($1, $3); } + ; + +opt_col_def_list: AS '(' TableFuncElementList ')' { $$ = $3; } + | /*EMPTY*/ { $$ = NIL; } + ; + +opt_ordinality: WITH_LA ORDINALITY { $$ = true; } + | /*EMPTY*/ { $$ = false; } + ; + + +where_clause: + WHERE a_expr { $$ = $2; } + | /*EMPTY*/ { $$ = NULL; } + ; + +/* variant for UPDATE and DELETE */ +where_or_current_clause: + WHERE a_expr { $$ = $2; } + | WHERE CURRENT_P OF cursor_name + { + CurrentOfExpr *n = makeNode(CurrentOfExpr); + /* cvarno is filled in by parse analysis */ + n->cursor_name = $4; + n->cursor_param = 0; + $$ = (Node *) n; + } + | /*EMPTY*/ { $$ = NULL; } + ; + + +OptTableFuncElementList: + TableFuncElementList { $$ = $1; } + | /*EMPTY*/ { $$ = NIL; } + ; + +TableFuncElementList: + TableFuncElement + { + $$ = list_make1($1); + } + | TableFuncElementList ',' TableFuncElement + { + $$ = lappend($1, $3); + } + ; + +TableFuncElement: ColId Typename opt_collate_clause + { + ColumnDef *n = makeNode(ColumnDef); + n->colname = $1; + n->typeName = $2; + n->inhcount = 0; + n->is_local = true; + n->is_not_null = false; + n->is_from_type = false; + n->storage = 0; + n->raw_default = NULL; + n->cooked_default = NULL; + n->collClause = (CollateClause *) $3; + n->collOid = InvalidOid; + n->constraints = NIL; + n->location = @1; + $$ = (Node *)n; + } + ; + +/* + * XMLTABLE + */ +xmltable: + XMLTABLE '(' c_expr xmlexists_argument COLUMNS xmltable_column_list ')' + { + RangeTableFunc *n = makeNode(RangeTableFunc); + n->rowexpr = $3; + n->docexpr = $4; + n->columns = $6; + n->namespaces = NIL; + n->location = @1; + $$ = (Node *)n; + } + | XMLTABLE '(' XMLNAMESPACES '(' xml_namespace_list ')' ',' + c_expr xmlexists_argument COLUMNS xmltable_column_list ')' + { + RangeTableFunc *n = makeNode(RangeTableFunc); + n->rowexpr = $8; + n->docexpr = $9; + n->columns = $11; + n->namespaces = $5; + n->location = @1; + $$ = (Node *)n; + } + ; + +xmltable_column_list: xmltable_column_el { $$ = list_make1($1); } + | xmltable_column_list ',' xmltable_column_el { $$ = lappend($1, $3); } + ; + +xmltable_column_el: + ColId Typename + { + RangeTableFuncCol *fc = makeNode(RangeTableFuncCol); + + fc->colname = $1; + fc->for_ordinality = false; + fc->typeName = $2; + fc->is_not_null = false; + fc->colexpr = NULL; + fc->coldefexpr = NULL; + fc->location = @1; + + $$ = (Node *) fc; + } + | ColId Typename xmltable_column_option_list + { + RangeTableFuncCol *fc = makeNode(RangeTableFuncCol); + ListCell *option; + bool nullability_seen = false; + + fc->colname = $1; + fc->typeName = $2; + fc->for_ordinality = false; + fc->is_not_null = false; + fc->colexpr = NULL; + fc->coldefexpr = NULL; + fc->location = @1; + + foreach(option, $3) + { + DefElem *defel = (DefElem *) lfirst(option); + + if (strcmp(defel->defname, "default") == 0) + { + if (fc->coldefexpr != NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("only one DEFAULT value is allowed"), + parser_errposition(defel->location))); + fc->coldefexpr = defel->arg; + } + else if (strcmp(defel->defname, "path") == 0) + { + if (fc->colexpr != NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("only one PATH value per column is allowed"), + parser_errposition(defel->location))); + fc->colexpr = defel->arg; + } + else if (strcmp(defel->defname, "is_not_null") == 0) + { + if (nullability_seen) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant NULL / NOT NULL declarations for column \"%s\"", fc->colname), + parser_errposition(defel->location))); + fc->is_not_null = intVal(defel->arg); + nullability_seen = true; + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unrecognized column option \"%s\"", + defel->defname), + parser_errposition(defel->location))); + } + } + $$ = (Node *) fc; + } + | ColId FOR ORDINALITY + { + RangeTableFuncCol *fc = makeNode(RangeTableFuncCol); + + fc->colname = $1; + fc->for_ordinality = true; + /* other fields are ignored, initialized by makeNode */ + fc->location = @1; + + $$ = (Node *) fc; + } + ; + +xmltable_column_option_list: + xmltable_column_option_el + { $$ = list_make1($1); } + | xmltable_column_option_list xmltable_column_option_el + { $$ = lappend($1, $2); } + ; + +xmltable_column_option_el: + IDENT b_expr + { $$ = makeDefElem($1, $2, @1); } + | DEFAULT b_expr + { $$ = makeDefElem("default", $2, @1); } + | NOT NULL_P + { $$ = makeDefElem("is_not_null", (Node *) makeInteger(true), @1); } + | NULL_P + { $$ = makeDefElem("is_not_null", (Node *) makeInteger(false), @1); } + ; + +xml_namespace_list: + xml_namespace_el + { $$ = list_make1($1); } + | xml_namespace_list ',' xml_namespace_el + { $$ = lappend($1, $3); } + ; + +xml_namespace_el: + b_expr AS ColLabel + { + $$ = makeNode(ResTarget); + $$->name = $3; + $$->indirection = NIL; + $$->val = $1; + $$->location = @1; + } + | DEFAULT b_expr + { + $$ = makeNode(ResTarget); + $$->name = NULL; + $$->indirection = NIL; + $$->val = $2; + $$->location = @1; + } + ; + +/***************************************************************************** + * + * Type syntax + * SQL 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 opt_array_bounds + { + $$ = $2; + $$->arrayBounds = $3; + $$->setof = true; + } + /* SQL standard syntax, currently only one-dimensional */ + | SimpleTypename ARRAY '[' Iconst ']' + { + $$ = $1; + $$->arrayBounds = list_make1(makeInteger($4)); + } + | SETOF SimpleTypename ARRAY '[' Iconst ']' + { + $$ = $2; + $$->arrayBounds = list_make1(makeInteger($5)); + $$->setof = true; + } + | SimpleTypename ARRAY + { + $$ = $1; + $$->arrayBounds = list_make1(makeInteger(-1)); + } + | SETOF SimpleTypename ARRAY + { + $$ = $2; + $$->arrayBounds = list_make1(makeInteger(-1)); + $$->setof = true; + } + ; + +opt_array_bounds: + opt_array_bounds '[' ']' + { $$ = lappend($1, makeInteger(-1)); } + | opt_array_bounds '[' Iconst ']' + { $$ = lappend($1, makeInteger($3)); } + | /*EMPTY*/ + { $$ = NIL; } + ; + +SimpleTypename: + GenericType { $$ = $1; } + | Numeric { $$ = $1; } + | Bit { $$ = $1; } + | Character { $$ = $1; } + | ConstDatetime { $$ = $1; } + | ConstInterval opt_interval + { + $$ = $1; + $$->typmods = $2; + } + | ConstInterval '(' Iconst ')' + { + $$ = $1; + $$->typmods = list_make2(makeIntConst(INTERVAL_FULL_RANGE, -1), + makeIntConst($3, @3)); + } + ; + +/* 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 accommodate the postfix + * options (e.g. INTERVAL '1' YEAR). Likewise, we have to handle + * the generic-type-name case in AexprConst to avoid premature + * reduce/reduce conflicts against function names. + */ +ConstTypename: + Numeric { $$ = $1; } + | ConstBit { $$ = $1; } + | ConstCharacter { $$ = $1; } + | ConstDatetime { $$ = $1; } + ; + +/* + * GenericType covers all type names that don't have special syntax mandated + * by the standard, including qualified names. We also allow type modifiers. + * To avoid parsing conflicts against function invocations, the modifiers + * have to be shown as expr_list here, but parse analysis will only accept + * constants for them. + */ +GenericType: + type_function_name opt_type_modifiers + { + $$ = makeTypeName($1); + $$->typmods = $2; + $$->location = @1; + } + | type_function_name attrs opt_type_modifiers + { + $$ = makeTypeNameFromNameList(lcons(makeString($1), $2)); + $$->typmods = $3; + $$->location = @1; + } + ; + +opt_type_modifiers: '(' expr_list ')' { $$ = $2; } + | /* EMPTY */ { $$ = NIL; } + ; + +/* + * SQL numeric data types + */ +Numeric: INT_P + { + $$ = SystemTypeName("int4"); + $$->location = @1; + } + | INTEGER + { + $$ = SystemTypeName("int4"); + $$->location = @1; + } + | SMALLINT + { + $$ = SystemTypeName("int2"); + $$->location = @1; + } + | BIGINT + { + $$ = SystemTypeName("int8"); + $$->location = @1; + } + | REAL + { + $$ = SystemTypeName("float4"); + $$->location = @1; + } + | FLOAT_P opt_float + { + $$ = $2; + $$->location = @1; + } + | DOUBLE_P PRECISION + { + $$ = SystemTypeName("float8"); + $$->location = @1; + } + | DECIMAL_P opt_type_modifiers + { + $$ = SystemTypeName("numeric"); + $$->typmods = $2; + $$->location = @1; + } + | DEC opt_type_modifiers + { + $$ = SystemTypeName("numeric"); + $$->typmods = $2; + $$->location = @1; + } + | NUMERIC opt_type_modifiers + { + $$ = SystemTypeName("numeric"); + $$->typmods = $2; + $$->location = @1; + } + | BOOLEAN_P + { + $$ = SystemTypeName("bool"); + $$->location = @1; + } + ; + +opt_float: '(' Iconst ')' + { + /* + * Check FLOAT() precision limits assuming IEEE floating + * types - thomas 1997-09-18 + */ + if ($2 < 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("precision for type float must be at least 1 bit"), + parser_errposition(@2))); + else if ($2 <= 24) + $$ = SystemTypeName("float4"); + else if ($2 <= 53) + $$ = SystemTypeName("float8"); + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("precision for type float must be less than 54 bits"), + parser_errposition(@2))); + } + | /*EMPTY*/ + { + $$ = SystemTypeName("float8"); + } + ; + +/* + * SQL bit-field data types + * The following implements BIT() and BIT VARYING(). + */ +Bit: BitWithLength + { + $$ = $1; + } + | BitWithoutLength + { + $$ = $1; + } + ; + +/* ConstBit is like Bit except "BIT" defaults to unspecified length */ +/* See notes for ConstCharacter, which addresses same issue for "CHAR" */ +ConstBit: BitWithLength + { + $$ = $1; + } + | BitWithoutLength + { + $$ = $1; + $$->typmods = NIL; + } + ; + +BitWithLength: + BIT opt_varying '(' expr_list ')' + { + char *typname; + + typname = $2 ? "varbit" : "bit"; + $$ = SystemTypeName(typname); + $$->typmods = $4; + $$->location = @1; + } + ; + +BitWithoutLength: + BIT opt_varying + { + /* bit defaults to bit(1), varbit to no limit */ + if ($2) + { + $$ = SystemTypeName("varbit"); + } + else + { + $$ = SystemTypeName("bit"); + $$->typmods = list_make1(makeIntConst(1, -1)); + } + $$->location = @1; + } + ; + + +/* + * SQL 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; + $$->typmods = NIL; + } + ; + +CharacterWithLength: character '(' Iconst ')' + { + $$ = SystemTypeName($1); + $$->typmods = list_make1(makeIntConst($3, @3)); + $$->location = @1; + } + ; + +CharacterWithoutLength: character + { + $$ = SystemTypeName($1); + /* char defaults to char(1), varchar to no limit */ + if (strcmp($1, "bpchar") == 0) + $$->typmods = list_make1(makeIntConst(1, -1)); + $$->location = @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; } + ; + +/* + * SQL date/time types + */ +ConstDatetime: + TIMESTAMP '(' Iconst ')' opt_timezone + { + if ($5) + $$ = SystemTypeName("timestamptz"); + else + $$ = SystemTypeName("timestamp"); + $$->typmods = list_make1(makeIntConst($3, @3)); + $$->location = @1; + } + | TIMESTAMP opt_timezone + { + if ($2) + $$ = SystemTypeName("timestamptz"); + else + $$ = SystemTypeName("timestamp"); + $$->location = @1; + } + | TIME '(' Iconst ')' opt_timezone + { + if ($5) + $$ = SystemTypeName("timetz"); + else + $$ = SystemTypeName("time"); + $$->typmods = list_make1(makeIntConst($3, @3)); + $$->location = @1; + } + | TIME opt_timezone + { + if ($2) + $$ = SystemTypeName("timetz"); + else + $$ = SystemTypeName("time"); + $$->location = @1; + } + ; + +ConstInterval: + INTERVAL + { + $$ = SystemTypeName("interval"); + $$->location = @1; + } + ; + +opt_timezone: + WITH_LA TIME ZONE { $$ = true; } + | WITHOUT TIME ZONE { $$ = false; } + | /*EMPTY*/ { $$ = false; } + ; + +opt_interval: + YEAR_P + { $$ = list_make1(makeIntConst(INTERVAL_MASK(YEAR), @1)); } + | MONTH_P + { $$ = list_make1(makeIntConst(INTERVAL_MASK(MONTH), @1)); } + | DAY_P + { $$ = list_make1(makeIntConst(INTERVAL_MASK(DAY), @1)); } + | HOUR_P + { $$ = list_make1(makeIntConst(INTERVAL_MASK(HOUR), @1)); } + | MINUTE_P + { $$ = list_make1(makeIntConst(INTERVAL_MASK(MINUTE), @1)); } + | interval_second + { $$ = $1; } + | YEAR_P TO MONTH_P + { + $$ = list_make1(makeIntConst(INTERVAL_MASK(YEAR) | + INTERVAL_MASK(MONTH), @1)); + } + | DAY_P TO HOUR_P + { + $$ = list_make1(makeIntConst(INTERVAL_MASK(DAY) | + INTERVAL_MASK(HOUR), @1)); + } + | DAY_P TO MINUTE_P + { + $$ = list_make1(makeIntConst(INTERVAL_MASK(DAY) | + INTERVAL_MASK(HOUR) | + INTERVAL_MASK(MINUTE), @1)); + } + | DAY_P TO interval_second + { + $$ = $3; + linitial($$) = makeIntConst(INTERVAL_MASK(DAY) | + INTERVAL_MASK(HOUR) | + INTERVAL_MASK(MINUTE) | + INTERVAL_MASK(SECOND), @1); + } + | HOUR_P TO MINUTE_P + { + $$ = list_make1(makeIntConst(INTERVAL_MASK(HOUR) | + INTERVAL_MASK(MINUTE), @1)); + } + | HOUR_P TO interval_second + { + $$ = $3; + linitial($$) = makeIntConst(INTERVAL_MASK(HOUR) | + INTERVAL_MASK(MINUTE) | + INTERVAL_MASK(SECOND), @1); + } + | MINUTE_P TO interval_second + { + $$ = $3; + linitial($$) = makeIntConst(INTERVAL_MASK(MINUTE) | + INTERVAL_MASK(SECOND), @1); + } + | /*EMPTY*/ + { $$ = NIL; } + ; + +interval_second: + SECOND_P + { + $$ = list_make1(makeIntConst(INTERVAL_MASK(SECOND), @1)); + } + | SECOND_P '(' Iconst ')' + { + $$ = list_make2(makeIntConst(INTERVAL_MASK(SECOND), @1), + makeIntConst($3, @3)); + } + ; + + +/***************************************************************************** + * + * expression grammar + * + *****************************************************************************/ + +/* + * 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. + * + * Be careful of productions involving more than one terminal token. + * By default, bison will assign such productions the precedence of their + * last terminal, but in nearly all cases you want it to be the precedence + * of the first terminal instead; otherwise you will not get the behavior + * you expect! So we use %prec annotations freely to set precedences. + */ +a_expr: c_expr { $$ = $1; } + | a_expr TYPECAST Typename + { $$ = makeTypeCast($1, $3, @2); } + | a_expr COLLATE any_name + { + CollateClause *n = makeNode(CollateClause); + n->arg = $1; + n->collname = $3; + n->location = @2; + $$ = (Node *) n; + } + | a_expr AT TIME ZONE a_expr %prec AT + { + $$ = (Node *) makeFuncCall(SystemFuncName("timezone"), + list_make2($5, $1), + COERCE_SQL_SYNTAX, + @2); + } + /* + * These operators must be called out explicitly in order to make use + * of 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 below. + */ + | '+' a_expr %prec UMINUS + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", NULL, $2, @1); } + | '-' a_expr %prec UMINUS + { $$ = doNegate($2, @1); } + | a_expr '+' a_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", $1, $3, @2); } + | a_expr '-' a_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "-", $1, $3, @2); } + | a_expr '*' a_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "*", $1, $3, @2); } + | a_expr '/' a_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "/", $1, $3, @2); } + | a_expr '%' a_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "%", $1, $3, @2); } + | a_expr '^' a_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "^", $1, $3, @2); } + | a_expr '<' a_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $3, @2); } + | a_expr '>' a_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $3, @2); } + | a_expr '=' a_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", $1, $3, @2); } + | a_expr LESS_EQUALS a_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $3, @2); } + | a_expr GREATER_EQUALS a_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $3, @2); } + | a_expr NOT_EQUALS a_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<>", $1, $3, @2); } + + | a_expr qual_Op a_expr %prec Op + { $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); } + | qual_Op a_expr %prec Op + { $$ = (Node *) makeA_Expr(AEXPR_OP, $1, NULL, $2, @1); } + + | a_expr AND a_expr + { $$ = makeAndExpr($1, $3, @2); } + | a_expr OR a_expr + { $$ = makeOrExpr($1, $3, @2); } + | NOT a_expr + { $$ = makeNotExpr($2, @1); } + | NOT_LA a_expr %prec NOT + { $$ = makeNotExpr($2, @1); } + + | a_expr LIKE a_expr + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_LIKE, "~~", + $1, $3, @2); + } + | a_expr LIKE a_expr ESCAPE a_expr %prec LIKE + { + FuncCall *n = makeFuncCall(SystemFuncName("like_escape"), + list_make2($3, $5), + COERCE_EXPLICIT_CALL, + @2); + $$ = (Node *) makeSimpleA_Expr(AEXPR_LIKE, "~~", + $1, (Node *) n, @2); + } + | a_expr NOT_LA LIKE a_expr %prec NOT_LA + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_LIKE, "!~~", + $1, $4, @2); + } + | a_expr NOT_LA LIKE a_expr ESCAPE a_expr %prec NOT_LA + { + FuncCall *n = makeFuncCall(SystemFuncName("like_escape"), + list_make2($4, $6), + COERCE_EXPLICIT_CALL, + @2); + $$ = (Node *) makeSimpleA_Expr(AEXPR_LIKE, "!~~", + $1, (Node *) n, @2); + } + | a_expr ILIKE a_expr + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_ILIKE, "~~*", + $1, $3, @2); + } + | a_expr ILIKE a_expr ESCAPE a_expr %prec ILIKE + { + FuncCall *n = makeFuncCall(SystemFuncName("like_escape"), + list_make2($3, $5), + COERCE_EXPLICIT_CALL, + @2); + $$ = (Node *) makeSimpleA_Expr(AEXPR_ILIKE, "~~*", + $1, (Node *) n, @2); + } + | a_expr NOT_LA ILIKE a_expr %prec NOT_LA + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_ILIKE, "!~~*", + $1, $4, @2); + } + | a_expr NOT_LA ILIKE a_expr ESCAPE a_expr %prec NOT_LA + { + FuncCall *n = makeFuncCall(SystemFuncName("like_escape"), + list_make2($4, $6), + COERCE_EXPLICIT_CALL, + @2); + $$ = (Node *) makeSimpleA_Expr(AEXPR_ILIKE, "!~~*", + $1, (Node *) n, @2); + } + + | a_expr SIMILAR TO a_expr %prec SIMILAR + { + FuncCall *n = makeFuncCall(SystemFuncName("similar_to_escape"), + list_make1($4), + COERCE_EXPLICIT_CALL, + @2); + $$ = (Node *) makeSimpleA_Expr(AEXPR_SIMILAR, "~", + $1, (Node *) n, @2); + } + | a_expr SIMILAR TO a_expr ESCAPE a_expr %prec SIMILAR + { + FuncCall *n = makeFuncCall(SystemFuncName("similar_to_escape"), + list_make2($4, $6), + COERCE_EXPLICIT_CALL, + @2); + $$ = (Node *) makeSimpleA_Expr(AEXPR_SIMILAR, "~", + $1, (Node *) n, @2); + } + | a_expr NOT_LA SIMILAR TO a_expr %prec NOT_LA + { + FuncCall *n = makeFuncCall(SystemFuncName("similar_to_escape"), + list_make1($5), + COERCE_EXPLICIT_CALL, + @2); + $$ = (Node *) makeSimpleA_Expr(AEXPR_SIMILAR, "!~", + $1, (Node *) n, @2); + } + | a_expr NOT_LA SIMILAR TO a_expr ESCAPE a_expr %prec NOT_LA + { + FuncCall *n = makeFuncCall(SystemFuncName("similar_to_escape"), + list_make2($5, $7), + COERCE_EXPLICIT_CALL, + @2); + $$ = (Node *) makeSimpleA_Expr(AEXPR_SIMILAR, "!~", + $1, (Node *) n, @2); + } + + /* NullTest clause + * Define SQL-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 IS NULL_P %prec IS + { + NullTest *n = makeNode(NullTest); + n->arg = (Expr *) $1; + n->nulltesttype = IS_NULL; + n->location = @2; + $$ = (Node *)n; + } + | a_expr ISNULL + { + NullTest *n = makeNode(NullTest); + n->arg = (Expr *) $1; + n->nulltesttype = IS_NULL; + n->location = @2; + $$ = (Node *)n; + } + | a_expr IS NOT NULL_P %prec IS + { + NullTest *n = makeNode(NullTest); + n->arg = (Expr *) $1; + n->nulltesttype = IS_NOT_NULL; + n->location = @2; + $$ = (Node *)n; + } + | a_expr NOTNULL + { + NullTest *n = makeNode(NullTest); + n->arg = (Expr *) $1; + n->nulltesttype = IS_NOT_NULL; + n->location = @2; + $$ = (Node *)n; + } + | row OVERLAPS row + { + if (list_length($1) != 2) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("wrong number of parameters on left side of OVERLAPS expression"), + parser_errposition(@1))); + if (list_length($3) != 2) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("wrong number of parameters on right side of OVERLAPS expression"), + parser_errposition(@3))); + $$ = (Node *) makeFuncCall(SystemFuncName("overlaps"), + list_concat($1, $3), + COERCE_SQL_SYNTAX, + @2); + } + | a_expr IS TRUE_P %prec IS + { + BooleanTest *b = makeNode(BooleanTest); + b->arg = (Expr *) $1; + b->booltesttype = IS_TRUE; + b->location = @2; + $$ = (Node *)b; + } + | a_expr IS NOT TRUE_P %prec IS + { + BooleanTest *b = makeNode(BooleanTest); + b->arg = (Expr *) $1; + b->booltesttype = IS_NOT_TRUE; + b->location = @2; + $$ = (Node *)b; + } + | a_expr IS FALSE_P %prec IS + { + BooleanTest *b = makeNode(BooleanTest); + b->arg = (Expr *) $1; + b->booltesttype = IS_FALSE; + b->location = @2; + $$ = (Node *)b; + } + | a_expr IS NOT FALSE_P %prec IS + { + BooleanTest *b = makeNode(BooleanTest); + b->arg = (Expr *) $1; + b->booltesttype = IS_NOT_FALSE; + b->location = @2; + $$ = (Node *)b; + } + | a_expr IS UNKNOWN %prec IS + { + BooleanTest *b = makeNode(BooleanTest); + b->arg = (Expr *) $1; + b->booltesttype = IS_UNKNOWN; + b->location = @2; + $$ = (Node *)b; + } + | a_expr IS NOT UNKNOWN %prec IS + { + BooleanTest *b = makeNode(BooleanTest); + b->arg = (Expr *) $1; + b->booltesttype = IS_NOT_UNKNOWN; + b->location = @2; + $$ = (Node *)b; + } + | a_expr IS DISTINCT FROM a_expr %prec IS + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5, @2); + } + | a_expr IS NOT DISTINCT FROM a_expr %prec IS + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_NOT_DISTINCT, "=", $1, $6, @2); + } + | a_expr BETWEEN opt_asymmetric b_expr AND a_expr %prec BETWEEN + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_BETWEEN, + "BETWEEN", + $1, + (Node *) list_make2($4, $6), + @2); + } + | a_expr NOT_LA BETWEEN opt_asymmetric b_expr AND a_expr %prec NOT_LA + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_NOT_BETWEEN, + "NOT BETWEEN", + $1, + (Node *) list_make2($5, $7), + @2); + } + | a_expr BETWEEN SYMMETRIC b_expr AND a_expr %prec BETWEEN + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_BETWEEN_SYM, + "BETWEEN SYMMETRIC", + $1, + (Node *) list_make2($4, $6), + @2); + } + | a_expr NOT_LA BETWEEN SYMMETRIC b_expr AND a_expr %prec NOT_LA + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_NOT_BETWEEN_SYM, + "NOT BETWEEN SYMMETRIC", + $1, + (Node *) list_make2($5, $7), + @2); + } + | a_expr IN_P in_expr + { + /* in_expr returns a SubLink or a list of a_exprs */ + if (IsA($3, SubLink)) + { + /* generate foo = ANY (subquery) */ + SubLink *n = (SubLink *) $3; + n->subLinkType = ANY_SUBLINK; + n->subLinkId = 0; + n->testexpr = $1; + n->operName = NIL; /* show it's IN not = ANY */ + n->location = @2; + $$ = (Node *)n; + } + else + { + /* generate scalar IN expression */ + $$ = (Node *) makeSimpleA_Expr(AEXPR_IN, "=", $1, $3, @2); + } + } + | a_expr NOT_LA IN_P in_expr %prec NOT_LA + { + /* in_expr returns a SubLink or a list of a_exprs */ + if (IsA($4, SubLink)) + { + /* generate NOT (foo = ANY (subquery)) */ + /* Make an = ANY node */ + SubLink *n = (SubLink *) $4; + n->subLinkType = ANY_SUBLINK; + n->subLinkId = 0; + n->testexpr = $1; + n->operName = NIL; /* show it's IN not = ANY */ + n->location = @2; + /* Stick a NOT on top; must have same parse location */ + $$ = makeNotExpr((Node *) n, @2); + } + else + { + /* generate scalar NOT IN expression */ + $$ = (Node *) makeSimpleA_Expr(AEXPR_IN, "<>", $1, $4, @2); + } + } + | a_expr subquery_Op sub_type select_with_parens %prec Op + { + SubLink *n = makeNode(SubLink); + n->subLinkType = $3; + n->subLinkId = 0; + n->testexpr = $1; + n->operName = $2; + n->subselect = $4; + n->location = @2; + $$ = (Node *)n; + } + | a_expr subquery_Op sub_type '(' a_expr ')' %prec Op + { + if ($3 == ANY_SUBLINK) + $$ = (Node *) makeA_Expr(AEXPR_OP_ANY, $2, $1, $5, @2); + else + $$ = (Node *) makeA_Expr(AEXPR_OP_ALL, $2, $1, $5, @2); + } + | UNIQUE select_with_parens + { + /* 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. + */ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("UNIQUE predicate is not yet implemented"), + parser_errposition(@1))); + } + | a_expr IS DOCUMENT_P %prec IS + { + $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, + list_make1($1), @2); + } + | a_expr IS NOT DOCUMENT_P %prec IS + { + $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL, + list_make1($1), @2), + @2); + } + | a_expr IS NORMALIZED %prec IS + { + $$ = (Node *) makeFuncCall(SystemFuncName("is_normalized"), + list_make1($1), + COERCE_SQL_SYNTAX, + @2); + } + | a_expr IS unicode_normal_form NORMALIZED %prec IS + { + $$ = (Node *) makeFuncCall(SystemFuncName("is_normalized"), + list_make2($1, makeStringConst($3, @3)), + COERCE_SQL_SYNTAX, + @2); + } + | a_expr IS NOT NORMALIZED %prec IS + { + $$ = makeNotExpr((Node *) makeFuncCall(SystemFuncName("is_normalized"), + list_make1($1), + COERCE_SQL_SYNTAX, + @2), + @2); + } + | a_expr IS NOT unicode_normal_form NORMALIZED %prec IS + { + $$ = makeNotExpr((Node *) makeFuncCall(SystemFuncName("is_normalized"), + list_make2($1, makeStringConst($4, @4)), + COERCE_SQL_SYNTAX, + @2), + @2); + } + | DEFAULT + { + /* + * The SQL spec only allows DEFAULT in "contextually typed + * expressions", but for us, it's easier to allow it in + * any a_expr and then throw error during parse analysis + * if it's in an inappropriate context. This way also + * lets us say something smarter than "syntax error". + */ + SetToDefault *n = makeNode(SetToDefault); + /* parse analysis will fill in the rest */ + n->location = @1; + $$ = (Node *)n; + } + ; + +/* + * 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, @2); } + | '+' b_expr %prec UMINUS + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", NULL, $2, @1); } + | '-' b_expr %prec UMINUS + { $$ = doNegate($2, @1); } + | b_expr '+' b_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", $1, $3, @2); } + | b_expr '-' b_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "-", $1, $3, @2); } + | b_expr '*' b_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "*", $1, $3, @2); } + | b_expr '/' b_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "/", $1, $3, @2); } + | b_expr '%' b_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "%", $1, $3, @2); } + | b_expr '^' b_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "^", $1, $3, @2); } + | b_expr '<' b_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $3, @2); } + | b_expr '>' b_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $3, @2); } + | b_expr '=' b_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", $1, $3, @2); } + | b_expr LESS_EQUALS b_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $3, @2); } + | b_expr GREATER_EQUALS b_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $3, @2); } + | b_expr NOT_EQUALS b_expr + { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<>", $1, $3, @2); } + | b_expr qual_Op b_expr %prec Op + { $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); } + | qual_Op b_expr %prec Op + { $$ = (Node *) makeA_Expr(AEXPR_OP, $1, NULL, $2, @1); } + | b_expr IS DISTINCT FROM b_expr %prec IS + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5, @2); + } + | b_expr IS NOT DISTINCT FROM b_expr %prec IS + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_NOT_DISTINCT, "=", $1, $6, @2); + } + | b_expr IS DOCUMENT_P %prec IS + { + $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, + list_make1($1), @2); + } + | b_expr IS NOT DOCUMENT_P %prec IS + { + $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL, + list_make1($1), @2), + @2); + } + ; + +/* + * 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 { $$ = $1; } + | AexprConst { $$ = $1; } + | PARAM opt_indirection + { + ParamRef *p = makeNode(ParamRef); + p->number = $1; + p->location = @1; + if ($2) + { + A_Indirection *n = makeNode(A_Indirection); + n->arg = (Node *) p; + n->indirection = check_indirection($2, yyscanner); + $$ = (Node *) n; + } + else + $$ = (Node *) p; + } + | '(' a_expr ')' opt_indirection + { + if ($4) + { + A_Indirection *n = makeNode(A_Indirection); + n->arg = $2; + n->indirection = check_indirection($4, yyscanner); + $$ = (Node *)n; + } + else + $$ = $2; + } + | case_expr + { $$ = $1; } + | func_expr + { $$ = $1; } + | select_with_parens %prec UMINUS + { + SubLink *n = makeNode(SubLink); + n->subLinkType = EXPR_SUBLINK; + n->subLinkId = 0; + n->testexpr = NULL; + n->operName = NIL; + n->subselect = $1; + n->location = @1; + $$ = (Node *)n; + } + | select_with_parens indirection + { + /* + * Because the select_with_parens nonterminal is designed + * to "eat" as many levels of parens as possible, the + * '(' a_expr ')' opt_indirection production above will + * fail to match a sub-SELECT with indirection decoration; + * the sub-SELECT won't be regarded as an a_expr as long + * as there are parens around it. To support applying + * subscripting or field selection to a sub-SELECT result, + * we need this redundant-looking production. + */ + SubLink *n = makeNode(SubLink); + A_Indirection *a = makeNode(A_Indirection); + n->subLinkType = EXPR_SUBLINK; + n->subLinkId = 0; + n->testexpr = NULL; + n->operName = NIL; + n->subselect = $1; + n->location = @1; + a->arg = (Node *)n; + a->indirection = check_indirection($2, yyscanner); + $$ = (Node *)a; + } + | EXISTS select_with_parens + { + SubLink *n = makeNode(SubLink); + n->subLinkType = EXISTS_SUBLINK; + n->subLinkId = 0; + n->testexpr = NULL; + n->operName = NIL; + n->subselect = $2; + n->location = @1; + $$ = (Node *)n; + } + | ARRAY select_with_parens + { + SubLink *n = makeNode(SubLink); + n->subLinkType = ARRAY_SUBLINK; + n->subLinkId = 0; + n->testexpr = NULL; + n->operName = NIL; + n->subselect = $2; + n->location = @1; + $$ = (Node *)n; + } + | ARRAY array_expr + { + A_ArrayExpr *n = castNode(A_ArrayExpr, $2); + /* point outermost A_ArrayExpr to the ARRAY keyword */ + n->location = @1; + $$ = (Node *)n; + } + | explicit_row + { + RowExpr *r = makeNode(RowExpr); + r->args = $1; + r->row_typeid = InvalidOid; /* not analyzed yet */ + r->colnames = NIL; /* to be filled in during analysis */ + r->row_format = COERCE_EXPLICIT_CALL; /* abuse */ + r->location = @1; + $$ = (Node *)r; + } + | implicit_row + { + RowExpr *r = makeNode(RowExpr); + r->args = $1; + r->row_typeid = InvalidOid; /* not analyzed yet */ + r->colnames = NIL; /* to be filled in during analysis */ + r->row_format = COERCE_IMPLICIT_CAST; /* abuse */ + r->location = @1; + $$ = (Node *)r; + } + | GROUPING '(' expr_list ')' + { + GroupingFunc *g = makeNode(GroupingFunc); + g->args = $3; + g->location = @1; + $$ = (Node *)g; + } + ; + +func_application: func_name '(' ')' + { + $$ = (Node *) makeFuncCall($1, NIL, + COERCE_EXPLICIT_CALL, + @1); + } + | func_name '(' func_arg_list opt_sort_clause ')' + { + FuncCall *n = makeFuncCall($1, $3, + COERCE_EXPLICIT_CALL, + @1); + n->agg_order = $4; + $$ = (Node *)n; + } + | func_name '(' VARIADIC func_arg_expr opt_sort_clause ')' + { + FuncCall *n = makeFuncCall($1, list_make1($4), + COERCE_EXPLICIT_CALL, + @1); + n->func_variadic = true; + n->agg_order = $5; + $$ = (Node *)n; + } + | func_name '(' func_arg_list ',' VARIADIC func_arg_expr opt_sort_clause ')' + { + FuncCall *n = makeFuncCall($1, lappend($3, $6), + COERCE_EXPLICIT_CALL, + @1); + n->func_variadic = true; + n->agg_order = $7; + $$ = (Node *)n; + } + | func_name '(' ALL func_arg_list opt_sort_clause ')' + { + FuncCall *n = makeFuncCall($1, $4, + COERCE_EXPLICIT_CALL, + @1); + n->agg_order = $5; + /* 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 func_arg_list opt_sort_clause ')' + { + FuncCall *n = makeFuncCall($1, $4, + COERCE_EXPLICIT_CALL, + @1); + n->agg_order = $5; + n->agg_distinct = true; + $$ = (Node *)n; + } + | func_name '(' '*' ')' + { + /* + * We consider AGGREGATE(*) to invoke a parameterless + * aggregate. This does the right thing for COUNT(*), + * and there are no other aggregates in SQL 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 = makeFuncCall($1, NIL, + COERCE_EXPLICIT_CALL, + @1); + n->agg_star = true; + $$ = (Node *)n; + } + ; + + +/* + * func_expr and its cousin func_expr_windowless are split out from c_expr just + * so that we have classifications for "everything that is a function call or + * looks like one". This isn't very important, but it saves us having to + * document which variants are legal in places like "FROM function()" or the + * backwards-compatible functional-index syntax for CREATE INDEX. + * (Note that many of the special SQL functions wouldn't actually make any + * sense as functional index entries, but we ignore that consideration here.) + */ +func_expr: func_application within_group_clause filter_clause over_clause + { + FuncCall *n = (FuncCall *) $1; + /* + * The order clause for WITHIN GROUP and the one for + * plain-aggregate ORDER BY share a field, so we have to + * check here that at most one is present. We also check + * for DISTINCT and VARIADIC here to give a better error + * location. Other consistency checks are deferred to + * parse analysis. + */ + if ($2 != NIL) + { + if (n->agg_order != NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("cannot use multiple ORDER BY clauses with WITHIN GROUP"), + parser_errposition(@2))); + if (n->agg_distinct) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("cannot use DISTINCT with WITHIN GROUP"), + parser_errposition(@2))); + if (n->func_variadic) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("cannot use VARIADIC with WITHIN GROUP"), + parser_errposition(@2))); + n->agg_order = $2; + n->agg_within_group = true; + } + n->agg_filter = $3; + n->over = $4; + $$ = (Node *) n; + } + | func_expr_common_subexpr + { $$ = $1; } + ; + +/* + * As func_expr but does not accept WINDOW functions directly + * (but they can still be contained in arguments for functions etc). + * Use this when window expressions are not allowed, where needed to + * disambiguate the grammar (e.g. in CREATE INDEX). + */ +func_expr_windowless: + func_application { $$ = $1; } + | func_expr_common_subexpr { $$ = $1; } + ; + +/* + * Special expressions that are considered to be functions. + */ +func_expr_common_subexpr: + COLLATION FOR '(' a_expr ')' + { + $$ = (Node *) makeFuncCall(SystemFuncName("pg_collation_for"), + list_make1($4), + COERCE_SQL_SYNTAX, + @1); + } + | CURRENT_DATE + { + $$ = makeSQLValueFunction(SVFOP_CURRENT_DATE, -1, @1); + } + | CURRENT_TIME + { + $$ = makeSQLValueFunction(SVFOP_CURRENT_TIME, -1, @1); + } + | CURRENT_TIME '(' Iconst ')' + { + $$ = makeSQLValueFunction(SVFOP_CURRENT_TIME_N, $3, @1); + } + | CURRENT_TIMESTAMP + { + $$ = makeSQLValueFunction(SVFOP_CURRENT_TIMESTAMP, -1, @1); + } + | CURRENT_TIMESTAMP '(' Iconst ')' + { + $$ = makeSQLValueFunction(SVFOP_CURRENT_TIMESTAMP_N, $3, @1); + } + | LOCALTIME + { + $$ = makeSQLValueFunction(SVFOP_LOCALTIME, -1, @1); + } + | LOCALTIME '(' Iconst ')' + { + $$ = makeSQLValueFunction(SVFOP_LOCALTIME_N, $3, @1); + } + | LOCALTIMESTAMP + { + $$ = makeSQLValueFunction(SVFOP_LOCALTIMESTAMP, -1, @1); + } + | LOCALTIMESTAMP '(' Iconst ')' + { + $$ = makeSQLValueFunction(SVFOP_LOCALTIMESTAMP_N, $3, @1); + } + | CURRENT_ROLE + { + $$ = makeSQLValueFunction(SVFOP_CURRENT_ROLE, -1, @1); + } + | CURRENT_USER + { + $$ = makeSQLValueFunction(SVFOP_CURRENT_USER, -1, @1); + } + | SESSION_USER + { + $$ = makeSQLValueFunction(SVFOP_SESSION_USER, -1, @1); + } + | USER + { + $$ = makeSQLValueFunction(SVFOP_USER, -1, @1); + } + | CURRENT_CATALOG + { + $$ = makeSQLValueFunction(SVFOP_CURRENT_CATALOG, -1, @1); + } + | CURRENT_SCHEMA + { + $$ = makeSQLValueFunction(SVFOP_CURRENT_SCHEMA, -1, @1); + } + | CAST '(' a_expr AS Typename ')' + { $$ = makeTypeCast($3, $5, @1); } + | EXTRACT '(' extract_list ')' + { + $$ = (Node *) makeFuncCall(SystemFuncName("extract"), + $3, + COERCE_SQL_SYNTAX, + @1); + } + | NORMALIZE '(' a_expr ')' + { + $$ = (Node *) makeFuncCall(SystemFuncName("normalize"), + list_make1($3), + COERCE_SQL_SYNTAX, + @1); + } + | NORMALIZE '(' a_expr ',' unicode_normal_form ')' + { + $$ = (Node *) makeFuncCall(SystemFuncName("normalize"), + list_make2($3, makeStringConst($5, @5)), + COERCE_SQL_SYNTAX, + @1); + } + | OVERLAY '(' overlay_list ')' + { + $$ = (Node *) makeFuncCall(SystemFuncName("overlay"), + $3, + COERCE_SQL_SYNTAX, + @1); + } + | OVERLAY '(' func_arg_list_opt ')' + { + /* + * allow functions named overlay() to be called without + * special syntax + */ + $$ = (Node *) makeFuncCall(list_make1(makeString("overlay")), + $3, + COERCE_EXPLICIT_CALL, + @1); + } + | POSITION '(' position_list ')' + { + /* + * position(A in B) is converted to position(B, A) + * + * We deliberately don't offer a "plain syntax" option + * for position(), because the reversal of the arguments + * creates too much risk of confusion. + */ + $$ = (Node *) makeFuncCall(SystemFuncName("position"), + $3, + COERCE_SQL_SYNTAX, + @1); + } + | SUBSTRING '(' substr_list ')' + { + /* substring(A from B for C) is converted to + * substring(A, B, C) - thomas 2000-11-28 + */ + $$ = (Node *) makeFuncCall(SystemFuncName("substring"), + $3, + COERCE_SQL_SYNTAX, + @1); + } + | SUBSTRING '(' func_arg_list_opt ')' + { + /* + * allow functions named substring() to be called without + * special syntax + */ + $$ = (Node *) makeFuncCall(list_make1(makeString("substring")), + $3, + COERCE_EXPLICIT_CALL, + @1); + } + | 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 + * coercions than are handled by implicit casting. + * + * Convert SystemTypeName() to SystemFuncName() even though + * at the moment they result in the same thing. + */ + $$ = (Node *) makeFuncCall(SystemFuncName(strVal(llast($5->names))), + list_make1($3), + COERCE_EXPLICIT_CALL, + @1); + } + | TRIM '(' BOTH trim_list ')' + { + /* various trim expressions are defined in SQL + * - thomas 1997-07-19 + */ + $$ = (Node *) makeFuncCall(SystemFuncName("btrim"), + $4, + COERCE_SQL_SYNTAX, + @1); + } + | TRIM '(' LEADING trim_list ')' + { + $$ = (Node *) makeFuncCall(SystemFuncName("ltrim"), + $4, + COERCE_SQL_SYNTAX, + @1); + } + | TRIM '(' TRAILING trim_list ')' + { + $$ = (Node *) makeFuncCall(SystemFuncName("rtrim"), + $4, + COERCE_SQL_SYNTAX, + @1); + } + | TRIM '(' trim_list ')' + { + $$ = (Node *) makeFuncCall(SystemFuncName("btrim"), + $3, + COERCE_SQL_SYNTAX, + @1); + } + | NULLIF '(' a_expr ',' a_expr ')' + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_NULLIF, "=", $3, $5, @1); + } + | COALESCE '(' expr_list ')' + { + CoalesceExpr *c = makeNode(CoalesceExpr); + c->args = $3; + c->location = @1; + $$ = (Node *)c; + } + | GREATEST '(' expr_list ')' + { + MinMaxExpr *v = makeNode(MinMaxExpr); + v->args = $3; + v->op = IS_GREATEST; + v->location = @1; + $$ = (Node *)v; + } + | LEAST '(' expr_list ')' + { + MinMaxExpr *v = makeNode(MinMaxExpr); + v->args = $3; + v->op = IS_LEAST; + v->location = @1; + $$ = (Node *)v; + } + | XMLCONCAT '(' expr_list ')' + { + $$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, $3, @1); + } + | XMLELEMENT '(' NAME_P ColLabel ')' + { + $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, @1); + } + | XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ')' + { + $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, @1); + } + | XMLELEMENT '(' NAME_P ColLabel ',' expr_list ')' + { + $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, @1); + } + | XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' expr_list ')' + { + $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, @1); + } + | XMLEXISTS '(' c_expr xmlexists_argument ')' + { + /* xmlexists(A PASSING [BY REF] B [BY REF]) is + * converted to xmlexists(A, B)*/ + $$ = (Node *) makeFuncCall(SystemFuncName("xmlexists"), + list_make2($3, $4), + COERCE_SQL_SYNTAX, + @1); + } + | XMLFOREST '(' xml_attribute_list ')' + { + $$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NIL, @1); + } + | XMLPARSE '(' document_or_content a_expr xml_whitespace_option ')' + { + XmlExpr *x = (XmlExpr *) + makeXmlExpr(IS_XMLPARSE, NULL, NIL, + list_make2($4, makeBoolAConst($5, -1)), + @1); + x->xmloption = $3; + $$ = (Node *)x; + } + | XMLPI '(' NAME_P ColLabel ')' + { + $$ = makeXmlExpr(IS_XMLPI, $4, NULL, NIL, @1); + } + | XMLPI '(' NAME_P ColLabel ',' a_expr ')' + { + $$ = makeXmlExpr(IS_XMLPI, $4, NULL, list_make1($6), @1); + } + | XMLROOT '(' a_expr ',' xml_root_version opt_xml_root_standalone ')' + { + $$ = makeXmlExpr(IS_XMLROOT, NULL, NIL, + list_make3($3, $5, $6), @1); + } + | XMLSERIALIZE '(' document_or_content a_expr AS SimpleTypename ')' + { + XmlSerialize *n = makeNode(XmlSerialize); + n->xmloption = $3; + n->expr = $4; + n->typeName = $6; + n->location = @1; + $$ = (Node *)n; + } + ; + +/* + * SQL/XML support + */ +xml_root_version: VERSION_P a_expr + { $$ = $2; } + | VERSION_P NO VALUE_P + { $$ = makeNullAConst(-1); } + ; + +opt_xml_root_standalone: ',' STANDALONE_P YES_P + { $$ = makeIntConst(XML_STANDALONE_YES, -1); } + | ',' STANDALONE_P NO + { $$ = makeIntConst(XML_STANDALONE_NO, -1); } + | ',' STANDALONE_P NO VALUE_P + { $$ = makeIntConst(XML_STANDALONE_NO_VALUE, -1); } + | /*EMPTY*/ + { $$ = makeIntConst(XML_STANDALONE_OMITTED, -1); } + ; + +xml_attributes: XMLATTRIBUTES '(' xml_attribute_list ')' { $$ = $3; } + ; + +xml_attribute_list: xml_attribute_el { $$ = list_make1($1); } + | xml_attribute_list ',' xml_attribute_el { $$ = lappend($1, $3); } + ; + +xml_attribute_el: a_expr AS ColLabel + { + $$ = makeNode(ResTarget); + $$->name = $3; + $$->indirection = NIL; + $$->val = (Node *) $1; + $$->location = @1; + } + | a_expr + { + $$ = makeNode(ResTarget); + $$->name = NULL; + $$->indirection = NIL; + $$->val = (Node *) $1; + $$->location = @1; + } + ; + +document_or_content: DOCUMENT_P { $$ = XMLOPTION_DOCUMENT; } + | CONTENT_P { $$ = XMLOPTION_CONTENT; } + ; + +xml_whitespace_option: PRESERVE WHITESPACE_P { $$ = true; } + | STRIP_P WHITESPACE_P { $$ = false; } + | /*EMPTY*/ { $$ = false; } + ; + +/* We allow several variants for SQL and other compatibility. */ +xmlexists_argument: + PASSING c_expr + { + $$ = $2; + } + | PASSING c_expr xml_passing_mech + { + $$ = $2; + } + | PASSING xml_passing_mech c_expr + { + $$ = $3; + } + | PASSING xml_passing_mech c_expr xml_passing_mech + { + $$ = $3; + } + ; + +xml_passing_mech: + BY REF + | BY VALUE_P + ; + + +/* + * Aggregate decoration clauses + */ +within_group_clause: + WITHIN GROUP_P '(' sort_clause ')' { $$ = $4; } + | /*EMPTY*/ { $$ = NIL; } + ; + +filter_clause: + FILTER '(' WHERE a_expr ')' { $$ = $4; } + | /*EMPTY*/ { $$ = NULL; } + ; + + +/* + * Window Definitions + */ +window_clause: + WINDOW window_definition_list { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + +window_definition_list: + window_definition { $$ = list_make1($1); } + | window_definition_list ',' window_definition + { $$ = lappend($1, $3); } + ; + +window_definition: + ColId AS window_specification + { + WindowDef *n = $3; + n->name = $1; + $$ = n; + } + ; + +over_clause: OVER window_specification + { $$ = $2; } + | OVER ColId + { + WindowDef *n = makeNode(WindowDef); + n->name = $2; + n->refname = NULL; + n->partitionClause = NIL; + n->orderClause = NIL; + n->frameOptions = FRAMEOPTION_DEFAULTS; + n->startOffset = NULL; + n->endOffset = NULL; + n->location = @2; + $$ = n; + } + | /*EMPTY*/ + { $$ = NULL; } + ; + +window_specification: '(' opt_existing_window_name opt_partition_clause + opt_sort_clause opt_frame_clause ')' + { + WindowDef *n = makeNode(WindowDef); + n->name = NULL; + n->refname = $2; + n->partitionClause = $3; + n->orderClause = $4; + /* copy relevant fields of opt_frame_clause */ + n->frameOptions = $5->frameOptions; + n->startOffset = $5->startOffset; + n->endOffset = $5->endOffset; + n->location = @1; + $$ = n; + } + ; + +/* + * If we see PARTITION, RANGE, ROWS or GROUPS as the first token after the '(' + * of a window_specification, we want the assumption to be that there is + * no existing_window_name; but those keywords are unreserved and so could + * be ColIds. We fix this by making them have the same precedence as IDENT + * and giving the empty production here a slightly higher precedence, so + * that the shift/reduce conflict is resolved in favor of reducing the rule. + * These keywords are thus precluded from being an existing_window_name but + * are not reserved for any other purpose. + */ +opt_existing_window_name: ColId { $$ = $1; } + | /*EMPTY*/ %prec Op { $$ = NULL; } + ; + +opt_partition_clause: PARTITION BY expr_list { $$ = $3; } + | /*EMPTY*/ { $$ = NIL; } + ; + +/* + * For frame clauses, we return a WindowDef, but only some fields are used: + * frameOptions, startOffset, and endOffset. + */ +opt_frame_clause: + RANGE frame_extent opt_window_exclusion_clause + { + WindowDef *n = $2; + n->frameOptions |= FRAMEOPTION_NONDEFAULT | FRAMEOPTION_RANGE; + n->frameOptions |= $3; + $$ = n; + } + | ROWS frame_extent opt_window_exclusion_clause + { + WindowDef *n = $2; + n->frameOptions |= FRAMEOPTION_NONDEFAULT | FRAMEOPTION_ROWS; + n->frameOptions |= $3; + $$ = n; + } + | GROUPS frame_extent opt_window_exclusion_clause + { + WindowDef *n = $2; + n->frameOptions |= FRAMEOPTION_NONDEFAULT | FRAMEOPTION_GROUPS; + n->frameOptions |= $3; + $$ = n; + } + | /*EMPTY*/ + { + WindowDef *n = makeNode(WindowDef); + n->frameOptions = FRAMEOPTION_DEFAULTS; + n->startOffset = NULL; + n->endOffset = NULL; + $$ = n; + } + ; + +frame_extent: frame_bound + { + WindowDef *n = $1; + /* reject invalid cases */ + if (n->frameOptions & FRAMEOPTION_START_UNBOUNDED_FOLLOWING) + ereport(ERROR, + (errcode(ERRCODE_WINDOWING_ERROR), + errmsg("frame start cannot be UNBOUNDED FOLLOWING"), + parser_errposition(@1))); + if (n->frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING) + ereport(ERROR, + (errcode(ERRCODE_WINDOWING_ERROR), + errmsg("frame starting from following row cannot end with current row"), + parser_errposition(@1))); + n->frameOptions |= FRAMEOPTION_END_CURRENT_ROW; + $$ = n; + } + | BETWEEN frame_bound AND frame_bound + { + WindowDef *n1 = $2; + WindowDef *n2 = $4; + /* form merged options */ + int frameOptions = n1->frameOptions; + /* shift converts START_ options to END_ options */ + frameOptions |= n2->frameOptions << 1; + frameOptions |= FRAMEOPTION_BETWEEN; + /* reject invalid cases */ + if (frameOptions & FRAMEOPTION_START_UNBOUNDED_FOLLOWING) + ereport(ERROR, + (errcode(ERRCODE_WINDOWING_ERROR), + errmsg("frame start cannot be UNBOUNDED FOLLOWING"), + parser_errposition(@2))); + if (frameOptions & FRAMEOPTION_END_UNBOUNDED_PRECEDING) + ereport(ERROR, + (errcode(ERRCODE_WINDOWING_ERROR), + errmsg("frame end cannot be UNBOUNDED PRECEDING"), + parser_errposition(@4))); + if ((frameOptions & FRAMEOPTION_START_CURRENT_ROW) && + (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)) + ereport(ERROR, + (errcode(ERRCODE_WINDOWING_ERROR), + errmsg("frame starting from current row cannot have preceding rows"), + parser_errposition(@4))); + if ((frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING) && + (frameOptions & (FRAMEOPTION_END_OFFSET_PRECEDING | + FRAMEOPTION_END_CURRENT_ROW))) + ereport(ERROR, + (errcode(ERRCODE_WINDOWING_ERROR), + errmsg("frame starting from following row cannot have preceding rows"), + parser_errposition(@4))); + n1->frameOptions = frameOptions; + n1->endOffset = n2->startOffset; + $$ = n1; + } + ; + +/* + * This is used for both frame start and frame end, with output set up on + * the assumption it's frame start; the frame_extent productions must reject + * invalid cases. + */ +frame_bound: + UNBOUNDED PRECEDING + { + WindowDef *n = makeNode(WindowDef); + n->frameOptions = FRAMEOPTION_START_UNBOUNDED_PRECEDING; + n->startOffset = NULL; + n->endOffset = NULL; + $$ = n; + } + | UNBOUNDED FOLLOWING + { + WindowDef *n = makeNode(WindowDef); + n->frameOptions = FRAMEOPTION_START_UNBOUNDED_FOLLOWING; + n->startOffset = NULL; + n->endOffset = NULL; + $$ = n; + } + | CURRENT_P ROW + { + WindowDef *n = makeNode(WindowDef); + n->frameOptions = FRAMEOPTION_START_CURRENT_ROW; + n->startOffset = NULL; + n->endOffset = NULL; + $$ = n; + } + | a_expr PRECEDING + { + WindowDef *n = makeNode(WindowDef); + n->frameOptions = FRAMEOPTION_START_OFFSET_PRECEDING; + n->startOffset = $1; + n->endOffset = NULL; + $$ = n; + } + | a_expr FOLLOWING + { + WindowDef *n = makeNode(WindowDef); + n->frameOptions = FRAMEOPTION_START_OFFSET_FOLLOWING; + n->startOffset = $1; + n->endOffset = NULL; + $$ = n; + } + ; + +opt_window_exclusion_clause: + EXCLUDE CURRENT_P ROW { $$ = FRAMEOPTION_EXCLUDE_CURRENT_ROW; } + | EXCLUDE GROUP_P { $$ = FRAMEOPTION_EXCLUDE_GROUP; } + | EXCLUDE TIES { $$ = FRAMEOPTION_EXCLUDE_TIES; } + | EXCLUDE NO OTHERS { $$ = 0; } + | /*EMPTY*/ { $$ = 0; } + ; + + +/* + * Supporting nonterminals for expressions. + */ + +/* Explicit row production. + * + * SQL99 allows an optional ROW keyword, so we can now do single-element rows + * without conflicting with the parenthesized a_expr production. Without the + * ROW keyword, there must be more than one a_expr inside the parens. + */ +row: ROW '(' expr_list ')' { $$ = $3; } + | ROW '(' ')' { $$ = NIL; } + | '(' expr_list ',' a_expr ')' { $$ = lappend($2, $4); } + ; + +explicit_row: ROW '(' expr_list ')' { $$ = $3; } + | ROW '(' ')' { $$ = NIL; } + ; + +implicit_row: '(' expr_list ',' a_expr ')' { $$ = lappend($2, $4); } + ; + +sub_type: ANY { $$ = ANY_SUBLINK; } + | SOME { $$ = ANY_SUBLINK; } + | ALL { $$ = ALL_SUBLINK; } + ; + +all_Op: Op { $$ = $1; } + | MathOp { $$ = $1; } + ; + +MathOp: '+' { $$ = "+"; } + | '-' { $$ = "-"; } + | '*' { $$ = "*"; } + | '/' { $$ = "/"; } + | '%' { $$ = "%"; } + | '^' { $$ = "^"; } + | '<' { $$ = "<"; } + | '>' { $$ = ">"; } + | '=' { $$ = "="; } + | LESS_EQUALS { $$ = "<="; } + | GREATER_EQUALS { $$ = ">="; } + | NOT_EQUALS { $$ = "<>"; } + ; + +qual_Op: Op + { $$ = list_make1(makeString($1)); } + | OPERATOR '(' any_operator ')' + { $$ = $3; } + ; + +qual_all_Op: + all_Op + { $$ = list_make1(makeString($1)); } + | OPERATOR '(' any_operator ')' + { $$ = $3; } + ; + +subquery_Op: + all_Op + { $$ = list_make1(makeString($1)); } + | OPERATOR '(' any_operator ')' + { $$ = $3; } + | LIKE + { $$ = list_make1(makeString("~~")); } + | NOT_LA LIKE + { $$ = list_make1(makeString("!~~")); } + | ILIKE + { $$ = list_make1(makeString("~~*")); } + | NOT_LA ILIKE + { $$ = list_make1(makeString("!~~*")); } +/* cannot put SIMILAR TO here, because SIMILAR TO is a hack. + * the regular expression is preprocessed by a function (similar_to_escape), + * and the ~ operator for posix regular expressions is used. + * x SIMILAR TO y -> x ~ similar_to_escape(y) + * this transformation is made on the fly by the parser upwards. + * however the SubLink structure which handles any/some/all stuff + * is not ready for such a thing. + */ + ; + +expr_list: a_expr + { + $$ = list_make1($1); + } + | expr_list ',' a_expr + { + $$ = lappend($1, $3); + } + ; + +/* function arguments can have names */ +func_arg_list: func_arg_expr + { + $$ = list_make1($1); + } + | func_arg_list ',' func_arg_expr + { + $$ = lappend($1, $3); + } + ; + +func_arg_expr: a_expr + { + $$ = $1; + } + | param_name COLON_EQUALS a_expr + { + NamedArgExpr *na = makeNode(NamedArgExpr); + na->name = $1; + na->arg = (Expr *) $3; + na->argnumber = -1; /* until determined */ + na->location = @1; + $$ = (Node *) na; + } + | param_name EQUALS_GREATER a_expr + { + NamedArgExpr *na = makeNode(NamedArgExpr); + na->name = $1; + na->arg = (Expr *) $3; + na->argnumber = -1; /* until determined */ + na->location = @1; + $$ = (Node *) na; + } + ; + +func_arg_list_opt: func_arg_list { $$ = $1; } + | /*EMPTY*/ { $$ = NIL; } + ; + +type_list: Typename { $$ = list_make1($1); } + | type_list ',' Typename { $$ = lappend($1, $3); } + ; + +array_expr: '[' expr_list ']' + { + $$ = makeAArrayExpr($2, @1); + } + | '[' array_expr_list ']' + { + $$ = makeAArrayExpr($2, @1); + } + | '[' ']' + { + $$ = makeAArrayExpr(NIL, @1); + } + ; + +array_expr_list: array_expr { $$ = list_make1($1); } + | array_expr_list ',' array_expr { $$ = lappend($1, $3); } + ; + + +extract_list: + extract_arg FROM a_expr + { + $$ = list_make2(makeStringConst($1, @1), $3); + } + ; + +/* 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; } + ; + +unicode_normal_form: + NFC { $$ = "NFC"; } + | NFD { $$ = "NFD"; } + | NFKC { $$ = "NFKC"; } + | NFKD { $$ = "NFKD"; } + ; + +/* OVERLAY() arguments */ +overlay_list: + a_expr PLACING a_expr FROM a_expr FOR a_expr + { + /* overlay(A PLACING B FROM C FOR D) is converted to overlay(A, B, C, D) */ + $$ = list_make4($1, $3, $5, $7); + } + | a_expr PLACING a_expr FROM a_expr + { + /* overlay(A PLACING B FROM C) is converted to overlay(A, B, C) */ + $$ = list_make3($1, $3, $5); + } + ; + +/* position_list uses b_expr not a_expr to avoid conflict with general IN */ +position_list: + b_expr IN_P b_expr { $$ = list_make2($3, $1); } + ; + +/* + * SUBSTRING() arguments + * + * Note that SQL:1999 has both + * text FROM int FOR int + * and + * text FROM pattern FOR escape + * + * In the parser we map them both to a call to the substring() function and + * rely on type resolution to pick the right one. + * + * In SQL:2003, the second variant was changed to + * text SIMILAR pattern ESCAPE escape + * We could in theory map that to a different function internally, but + * since we still support the SQL:1999 version, we don't. However, + * ruleutils.c will reverse-list the call in the newer style. + */ +substr_list: + a_expr FROM a_expr FOR a_expr + { + $$ = list_make3($1, $3, $5); + } + | a_expr FOR a_expr FROM a_expr + { + /* not legal per SQL, but might as well allow it */ + $$ = list_make3($1, $5, $3); + } + | a_expr FROM a_expr + { + /* + * Because we aren't restricting data types here, this + * syntax can end up resolving to textregexsubstr(). + * We've historically allowed that to happen, so continue + * to accept it. However, ruleutils.c will reverse-list + * such a call in regular function call syntax. + */ + $$ = list_make2($1, $3); + } + | a_expr FOR a_expr + { + /* not legal per SQL */ + + /* + * Since there are no cases where this syntax allows + * a textual FOR value, we forcibly cast the argument + * to int4. The possible matches in pg_proc are + * substring(text,int4) and substring(text,text), + * and we don't want the parser to choose the latter, + * which it is likely to do if the second argument + * is unknown or doesn't have an implicit cast to int4. + */ + $$ = list_make3($1, makeIntConst(1, -1), + makeTypeCast($3, + SystemTypeName("int4"), -1)); + } + | a_expr SIMILAR a_expr ESCAPE a_expr + { + $$ = list_make3($1, $3, $5); + } + ; + +trim_list: a_expr FROM expr_list { $$ = lappend($3, $1); } + | FROM expr_list { $$ = $2; } + | expr_list { $$ = $1; } + ; + +in_expr: select_with_parens + { + SubLink *n = makeNode(SubLink); + n->subselect = $1; + /* other fields will be filled later */ + $$ = (Node *)n; + } + | '(' expr_list ')' { $$ = (Node *)$2; } + ; + +/* + * Define SQL-style CASE clause. + * - Full specification + * CASE WHEN a = b THEN c ... ELSE d END + * - Implicit argument + * CASE a WHEN b THEN c ... ELSE d END + */ +case_expr: CASE case_arg when_clause_list case_default END_P + { + CaseExpr *c = makeNode(CaseExpr); + c->casetype = InvalidOid; /* not analyzed yet */ + c->arg = (Expr *) $2; + c->args = $3; + c->defresult = (Expr *) $4; + c->location = @1; + $$ = (Node *)c; + } + ; + +when_clause_list: + /* There must be at least one */ + when_clause { $$ = list_make1($1); } + | when_clause_list when_clause { $$ = lappend($1, $2); } + ; + +when_clause: + WHEN a_expr THEN a_expr + { + CaseWhen *w = makeNode(CaseWhen); + w->expr = (Expr *) $2; + w->result = (Expr *) $4; + w->location = @1; + $$ = (Node *)w; + } + ; + +case_default: + ELSE a_expr { $$ = $2; } + | /*EMPTY*/ { $$ = NULL; } + ; + +case_arg: a_expr { $$ = $1; } + | /*EMPTY*/ { $$ = NULL; } + ; + +columnref: ColId + { + $$ = makeColumnRef($1, NIL, @1, yyscanner); + } + | ColId indirection + { + $$ = makeColumnRef($1, $2, @1, yyscanner); + } + ; + +indirection_el: + '.' attr_name + { + $$ = (Node *) makeString($2); + } + | '.' '*' + { + $$ = (Node *) makeNode(A_Star); + } + | '[' a_expr ']' + { + A_Indices *ai = makeNode(A_Indices); + ai->is_slice = false; + ai->lidx = NULL; + ai->uidx = $2; + $$ = (Node *) ai; + } + | '[' opt_slice_bound ':' opt_slice_bound ']' + { + A_Indices *ai = makeNode(A_Indices); + ai->is_slice = true; + ai->lidx = $2; + ai->uidx = $4; + $$ = (Node *) ai; + } + ; + +opt_slice_bound: + a_expr { $$ = $1; } + | /*EMPTY*/ { $$ = NULL; } + ; + +indirection: + indirection_el { $$ = list_make1($1); } + | indirection indirection_el { $$ = lappend($1, $2); } + ; + +opt_indirection: + /*EMPTY*/ { $$ = NIL; } + | opt_indirection indirection_el { $$ = lappend($1, $2); } + ; + +opt_asymmetric: ASYMMETRIC + | /*EMPTY*/ + ; + + +/***************************************************************************** + * + * target list for SELECT + * + *****************************************************************************/ + +opt_target_list: target_list { $$ = $1; } + | /* EMPTY */ { $$ = NIL; } + ; + +target_list: + target_el { $$ = list_make1($1); } + | target_list ',' target_el { $$ = lappend($1, $3); } + ; + +target_el: a_expr AS ColLabel + { + $$ = makeNode(ResTarget); + $$->name = $3; + $$->indirection = NIL; + $$->val = (Node *)$1; + $$->location = @1; + } + | a_expr BareColLabel + { + $$ = makeNode(ResTarget); + $$->name = $2; + $$->indirection = NIL; + $$->val = (Node *)$1; + $$->location = @1; + } + | a_expr + { + $$ = makeNode(ResTarget); + $$->name = NULL; + $$->indirection = NIL; + $$->val = (Node *)$1; + $$->location = @1; + } + | '*' + { + ColumnRef *n = makeNode(ColumnRef); + n->fields = list_make1(makeNode(A_Star)); + n->location = @1; + + $$ = makeNode(ResTarget); + $$->name = NULL; + $$->indirection = NIL; + $$->val = (Node *)n; + $$->location = @1; + } + ; + + +/***************************************************************************** + * + * Names and constants + * + *****************************************************************************/ + +qualified_name_list: + qualified_name { $$ = list_make1($1); } + | qualified_name_list ',' qualified_name { $$ = lappend($1, $3); } + ; + +/* + * The production for a qualified relation name has to exactly match the + * production for a qualified func_name, because in a FROM clause we cannot + * tell which we are parsing until we see what comes after it ('(' for a + * func_name, something else for a relation). Therefore we allow 'indirection' + * which may contain subscripts, and reject that case in the C code. + */ +qualified_name: + ColId + { + $$ = makeRangeVar(NULL, $1, @1); + } + | ColId indirection + { + check_qualified_name($2, yyscanner); + $$ = makeRangeVar(NULL, NULL, @1); + switch (list_length($2)) + { + case 1: + $$->catalogname = NULL; + $$->schemaname = $1; + $$->relname = strVal(linitial($2)); + break; + case 2: + $$->catalogname = $1; + $$->schemaname = strVal(linitial($2)); + $$->relname = strVal(lsecond($2)); + break; + default: + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("improper qualified name (too many dotted names): %s", + NameListToString(lcons(makeString($1), $2))), + parser_errposition(@1))); + break; + } + } + ; + +name_list: name + { $$ = list_make1(makeString($1)); } + | name_list ',' name + { $$ = lappend($1, makeString($3)); } + ; + + +name: ColId { $$ = $1; }; + +attr_name: ColLabel { $$ = $1; }; + +file_name: Sconst { $$ = $1; }; + +/* + * The production for a qualified func_name has to exactly match the + * production for a qualified columnref, because we cannot tell which we + * are parsing until we see what comes after it ('(' or Sconst for a func_name, + * anything else for a columnref). Therefore we allow 'indirection' which + * may contain subscripts, and reject that case in the C code. (If we + * ever implement SQL99-like methods, such syntax may actually become legal!) + */ +func_name: type_function_name + { $$ = list_make1(makeString($1)); } + | ColId indirection + { + $$ = check_func_name(lcons(makeString($1), $2), + yyscanner); + } + ; + + +/* + * Constants + */ +AexprConst: Iconst + { + $$ = makeIntConst($1, @1); + } + | FCONST + { + $$ = makeFloatConst($1, @1); + } + | Sconst + { + $$ = makeStringConst($1, @1); + } + | BCONST + { + $$ = makeBitStringConst($1, @1); + } + | XCONST + { + /* This is a bit constant per SQL99: + * Without Feature F511, "BIT data type", + * a <general literal> shall not be a + * <bit string literal> or a <hex string literal>. + */ + $$ = makeBitStringConst($1, @1); + } + | func_name Sconst + { + /* generic type 'literal' syntax */ + TypeName *t = makeTypeNameFromNameList($1); + t->location = @1; + $$ = makeStringConstCast($2, @2, t); + } + | func_name '(' func_arg_list opt_sort_clause ')' Sconst + { + /* generic syntax with a type modifier */ + TypeName *t = makeTypeNameFromNameList($1); + ListCell *lc; + + /* + * We must use func_arg_list and opt_sort_clause in the + * production to avoid reduce/reduce conflicts, but we + * don't actually wish to allow NamedArgExpr in this + * context, nor ORDER BY. + */ + foreach(lc, $3) + { + NamedArgExpr *arg = (NamedArgExpr *) lfirst(lc); + + if (IsA(arg, NamedArgExpr)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("type modifier cannot have parameter name"), + parser_errposition(arg->location))); + } + if ($4 != NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("type modifier cannot have ORDER BY"), + parser_errposition(@4))); + + t->typmods = $3; + t->location = @1; + $$ = makeStringConstCast($6, @6, t); + } + | ConstTypename Sconst + { + $$ = makeStringConstCast($2, @2, $1); + } + | ConstInterval Sconst opt_interval + { + TypeName *t = $1; + t->typmods = $3; + $$ = makeStringConstCast($2, @2, t); + } + | ConstInterval '(' Iconst ')' Sconst + { + TypeName *t = $1; + t->typmods = list_make2(makeIntConst(INTERVAL_FULL_RANGE, -1), + makeIntConst($3, @3)); + $$ = makeStringConstCast($5, @5, t); + } + | TRUE_P + { + $$ = makeBoolAConst(true, @1); + } + | FALSE_P + { + $$ = makeBoolAConst(false, @1); + } + | NULL_P + { + $$ = makeNullAConst(@1); + } + ; + +Iconst: ICONST { $$ = $1; }; +Sconst: SCONST { $$ = $1; }; + +SignedIconst: Iconst { $$ = $1; } + | '+' Iconst { $$ = + $2; } + | '-' Iconst { $$ = - $2; } + ; + +/* Role specifications */ +RoleId: RoleSpec + { + RoleSpec *spc = (RoleSpec *) $1; + switch (spc->roletype) + { + case ROLESPEC_CSTRING: + $$ = spc->rolename; + break; + case ROLESPEC_PUBLIC: + ereport(ERROR, + (errcode(ERRCODE_RESERVED_NAME), + errmsg("role name \"%s\" is reserved", + "public"), + parser_errposition(@1))); + break; + case ROLESPEC_SESSION_USER: + ereport(ERROR, + (errcode(ERRCODE_RESERVED_NAME), + errmsg("%s cannot be used as a role name here", + "SESSION_USER"), + parser_errposition(@1))); + break; + case ROLESPEC_CURRENT_USER: + ereport(ERROR, + (errcode(ERRCODE_RESERVED_NAME), + errmsg("%s cannot be used as a role name here", + "CURRENT_USER"), + parser_errposition(@1))); + break; + case ROLESPEC_CURRENT_ROLE: + ereport(ERROR, + (errcode(ERRCODE_RESERVED_NAME), + errmsg("%s cannot be used as a role name here", + "CURRENT_ROLE"), + parser_errposition(@1))); + break; + } + } + ; + +RoleSpec: NonReservedWord + { + /* + * "public" and "none" are not keywords, but they must + * be treated specially here. + */ + RoleSpec *n; + if (strcmp($1, "public") == 0) + { + n = (RoleSpec *) makeRoleSpec(ROLESPEC_PUBLIC, @1); + n->roletype = ROLESPEC_PUBLIC; + } + else if (strcmp($1, "none") == 0) + { + ereport(ERROR, + (errcode(ERRCODE_RESERVED_NAME), + errmsg("role name \"%s\" is reserved", + "none"), + parser_errposition(@1))); + } + else + { + n = makeRoleSpec(ROLESPEC_CSTRING, @1); + n->rolename = pstrdup($1); + } + $$ = n; + } + | CURRENT_ROLE + { + $$ = makeRoleSpec(ROLESPEC_CURRENT_ROLE, @1); + } + | CURRENT_USER + { + $$ = makeRoleSpec(ROLESPEC_CURRENT_USER, @1); + } + | SESSION_USER + { + $$ = makeRoleSpec(ROLESPEC_SESSION_USER, @1); + } + ; + +role_list: RoleSpec + { $$ = list_make1($1); } + | role_list ',' RoleSpec + { $$ = lappend($1, $3); } + ; + + +/***************************************************************************** + * + * PL/pgSQL extensions + * + * You'd think a PL/pgSQL "expression" should be just an a_expr, but + * historically it can include just about anything that can follow SELECT. + * Therefore the returned struct is a SelectStmt. + *****************************************************************************/ + +PLpgSQL_Expr: opt_distinct_clause opt_target_list + from_clause where_clause + group_clause having_clause window_clause + opt_sort_clause opt_select_limit opt_for_locking_clause + { + SelectStmt *n = makeNode(SelectStmt); + + n->distinctClause = $1; + n->targetList = $2; + n->fromClause = $3; + n->whereClause = $4; + n->groupClause = ($5)->list; + n->groupDistinct = ($5)->distinct; + n->havingClause = $6; + n->windowClause = $7; + n->sortClause = $8; + if ($9) + { + n->limitOffset = $9->limitOffset; + n->limitCount = $9->limitCount; + if (!n->sortClause && + $9->limitOption == LIMIT_OPTION_WITH_TIES) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("WITH TIES cannot be specified without ORDER BY clause"))); + n->limitOption = $9->limitOption; + } + n->lockingClause = $10; + $$ = (Node *) n; + } + ; + +/* + * PL/pgSQL Assignment statement: name opt_indirection := PLpgSQL_Expr + */ + +PLAssignStmt: plassign_target opt_indirection plassign_equals PLpgSQL_Expr + { + PLAssignStmt *n = makeNode(PLAssignStmt); + + n->name = $1; + n->indirection = check_indirection($2, yyscanner); + /* nnames will be filled by calling production */ + n->val = (SelectStmt *) $4; + n->location = @1; + $$ = (Node *) n; + } + ; + +plassign_target: ColId { $$ = $1; } + | PARAM { $$ = psprintf("$%d", $1); } + ; + +plassign_equals: COLON_EQUALS + | '=' + ; + + +/* + * 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/function identifier --- names that can be type or function names. + */ +type_function_name: IDENT { $$ = $1; } + | unreserved_keyword { $$ = pstrdup($1); } + | type_func_name_keyword { $$ = pstrdup($1); } + ; + +/* Any not-fully-reserved word --- these names can be, eg, role names. + */ +NonReservedWord: IDENT { $$ = $1; } + | unreserved_keyword { $$ = pstrdup($1); } + | col_name_keyword { $$ = pstrdup($1); } + | type_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); } + | type_func_name_keyword { $$ = pstrdup($1); } + | reserved_keyword { $$ = pstrdup($1); } + ; + +/* Bare column label --- names that can be column labels without writing "AS". + * This classification is orthogonal to the other keyword categories. + */ +BareColLabel: IDENT { $$ = $1; } + | bare_label_keyword { $$ = pstrdup($1); } + ; + + +/* + * Keyword category 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. + * + * Make sure that each keyword's category in kwlist.h matches where + * it is listed here. (Someday we may be able to generate these lists and + * kwlist.h's table from one source of truth.) + */ + +/* "Unreserved" keywords --- available for use as any kind of name. + */ +unreserved_keyword: + ABORT_P + | ABSOLUTE_P + | ACCESS + | ACTION + | ADD_P + | ADMIN + | AFTER + | AGGREGATE + | ALSO + | ALTER + | ALWAYS + | ASENSITIVE + | ASSERTION + | ASSIGNMENT + | AT + | ATOMIC + | ATTACH + | ATTRIBUTE + | BACKWARD + | BEFORE + | BEGIN_P + | BREADTH + | BY + | CACHE + | CALL + | CALLED + | CASCADE + | CASCADED + | CATALOG_P + | CHAIN + | CHARACTERISTICS + | CHECKPOINT + | CLASS + | CLOSE + | CLUSTER + | COLUMNS + | COMMENT + | COMMENTS + | COMMIT + | COMMITTED + | COMPRESSION + | CONFIGURATION + | CONFLICT + | CONNECTION + | CONSTRAINTS + | CONTENT_P + | CONTINUE_P + | CONVERSION_P + | COPY + | COST + | CSV + | CUBE + | CURRENT_P + | CURSOR + | CYCLE + | DATA_P + | DATABASE + | DAY_P + | DEALLOCATE + | DECLARE + | DEFAULTS + | DEFERRED + | DEFINER + | DELETE_P + | DELIMITER + | DELIMITERS + | DEPENDS + | DEPTH + | DETACH + | DICTIONARY + | DISABLE_P + | DISCARD + | DOCUMENT_P + | DOMAIN_P + | DOUBLE_P + | DROP + | EACH + | ENABLE_P + | ENCODING + | ENCRYPTED + | ENUM_P + | ESCAPE + | EVENT + | EXCLUDE + | EXCLUDING + | EXCLUSIVE + | EXECUTE + | EXPLAIN + | EXPRESSION + | EXTENSION + | EXTERNAL + | FAMILY + | FILTER + | FINALIZE + | FIRST_P + | FOLLOWING + | FORCE + | FORWARD + | FUNCTION + | FUNCTIONS + | GENERATED + | GLOBAL + | GRANTED + | GROUPS + | HANDLER + | HEADER_P + | HOLD + | HOUR_P + | IDENTITY_P + | IF_P + | IMMEDIATE + | IMMUTABLE + | IMPLICIT_P + | IMPORT_P + | INCLUDE + | INCLUDING + | INCREMENT + | INDEX + | INDEXES + | INHERIT + | INHERITS + | INLINE_P + | INPUT_P + | INSENSITIVE + | INSERT + | INSTEAD + | INVOKER + | ISOLATION + | KEY + | LABEL + | LANGUAGE + | LARGE_P + | LAST_P + | LEAKPROOF + | LEVEL + | LISTEN + | LOAD + | LOCAL + | LOCATION + | LOCK_P + | LOCKED + | LOGGED + | MAPPING + | MATCH + | MATERIALIZED + | MAXVALUE + | METHOD + | MINUTE_P + | MINVALUE + | MODE + | MONTH_P + | MOVE + | NAME_P + | NAMES + | NEW + | NEXT + | NFC + | NFD + | NFKC + | NFKD + | NO + | NORMALIZED + | NOTHING + | NOTIFY + | NOWAIT + | NULLS_P + | OBJECT_P + | OF + | OFF + | OIDS + | OLD + | OPERATOR + | OPTION + | OPTIONS + | ORDINALITY + | OTHERS + | OVER + | OVERRIDING + | OWNED + | OWNER + | PARALLEL + | PARSER + | PARTIAL + | PARTITION + | PASSING + | PASSWORD + | PLANS + | POLICY + | PRECEDING + | PREPARE + | PREPARED + | PRESERVE + | PRIOR + | PRIVILEGES + | PROCEDURAL + | PROCEDURE + | PROCEDURES + | PROGRAM + | PUBLICATION + | QUOTE + | RANGE + | READ + | REASSIGN + | RECHECK + | RECURSIVE + | REF + | REFERENCING + | REFRESH + | REINDEX + | RELATIVE_P + | RELEASE + | RENAME + | REPEATABLE + | REPLACE + | REPLICA + | RESET + | RESTART + | RESTRICT + | RETURN + | RETURNS + | REVOKE + | ROLE + | ROLLBACK + | ROLLUP + | ROUTINE + | ROUTINES + | ROWS + | RULE + | SAVEPOINT + | SCHEMA + | SCHEMAS + | SCROLL + | SEARCH + | SECOND_P + | SECURITY + | SEQUENCE + | SEQUENCES + | SERIALIZABLE + | SERVER + | SESSION + | SET + | SETS + | SHARE + | SHOW + | SIMPLE + | SKIP + | SNAPSHOT + | SQL_P + | STABLE + | STANDALONE_P + | START + | STATEMENT + | STATISTICS + | STDIN + | STDOUT + | STORAGE + | STORED + | STRICT_P + | STRIP_P + | SUBSCRIPTION + | SUPPORT + | SYSID + | SYSTEM_P + | TABLES + | TABLESPACE + | TEMP + | TEMPLATE + | TEMPORARY + | TEXT_P + | TIES + | TRANSACTION + | TRANSFORM + | TRIGGER + | TRUNCATE + | TRUSTED + | TYPE_P + | TYPES_P + | UESCAPE + | UNBOUNDED + | UNCOMMITTED + | UNENCRYPTED + | UNKNOWN + | UNLISTEN + | UNLOGGED + | UNTIL + | UPDATE + | VACUUM + | VALID + | VALIDATE + | VALIDATOR + | VALUE_P + | VARYING + | VERSION_P + | VIEW + | VIEWS + | VOLATILE + | WHITESPACE_P + | WITHIN + | WITHOUT + | WORK + | WRAPPER + | WRITE + | XML_P + | YEAR_P + | YES_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: + BETWEEN + | BIGINT + | BIT + | BOOLEAN_P + | CHAR_P + | CHARACTER + | COALESCE + | DEC + | DECIMAL_P + | EXISTS + | EXTRACT + | FLOAT_P + | GREATEST + | GROUPING + | INOUT + | INT_P + | INTEGER + | INTERVAL + | LEAST + | NATIONAL + | NCHAR + | NONE + | NORMALIZE + | NULLIF + | NUMERIC + | OUT_P + | OVERLAY + | POSITION + | PRECISION + | REAL + | ROW + | SETOF + | SMALLINT + | SUBSTRING + | TIME + | TIMESTAMP + | TREAT + | TRIM + | VALUES + | VARCHAR + | XMLATTRIBUTES + | XMLCONCAT + | XMLELEMENT + | XMLEXISTS + | XMLFOREST + | XMLNAMESPACES + | XMLPARSE + | XMLPI + | XMLROOT + | XMLSERIALIZE + | XMLTABLE + ; + +/* Type/function identifier --- keywords that can be type or 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 + */ +type_func_name_keyword: + AUTHORIZATION + | BINARY + | COLLATION + | CONCURRENTLY + | CROSS + | CURRENT_SCHEMA + | FREEZE + | FULL + | ILIKE + | INNER_P + | IS + | ISNULL + | JOIN + | LEFT + | LIKE + | NATURAL + | NOTNULL + | OUTER_P + | OVERLAPS + | RIGHT + | SIMILAR + | TABLESAMPLE + | 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 + | ARRAY + | AS + | ASC + | ASYMMETRIC + | BOTH + | CASE + | CAST + | CHECK + | COLLATE + | COLUMN + | CONSTRAINT + | CREATE + | CURRENT_CATALOG + | CURRENT_DATE + | CURRENT_ROLE + | CURRENT_TIME + | CURRENT_TIMESTAMP + | CURRENT_USER + | DEFAULT + | DEFERRABLE + | DESC + | DISTINCT + | DO + | ELSE + | END_P + | EXCEPT + | FALSE_P + | FETCH + | FOR + | FOREIGN + | FROM + | GRANT + | GROUP_P + | HAVING + | IN_P + | INITIALLY + | INTERSECT + | INTO + | LATERAL_P + | LEADING + | LIMIT + | LOCALTIME + | LOCALTIMESTAMP + | NOT + | NULL_P + | OFFSET + | ON + | ONLY + | OR + | ORDER + | PLACING + | PRIMARY + | REFERENCES + | RETURNING + | SELECT + | SESSION_USER + | SOME + | SYMMETRIC + | TABLE + | THEN + | TO + | TRAILING + | TRUE_P + | UNION + | UNIQUE + | USER + | USING + | VARIADIC + | WHEN + | WHERE + | WINDOW + | WITH + ; + +/* + * While all keywords can be used as column labels when preceded by AS, + * not all of them can be used as a "bare" column label without AS. + * Those that can be used as a bare label must be listed here, + * in addition to appearing in one of the category lists above. + * + * Always add a new keyword to this list if possible. Mark it BARE_LABEL + * in kwlist.h if it is included here, or AS_LABEL if it is not. + */ +bare_label_keyword: + ABORT_P + | ABSOLUTE_P + | ACCESS + | ACTION + | ADD_P + | ADMIN + | AFTER + | AGGREGATE + | ALL + | ALSO + | ALTER + | ALWAYS + | ANALYSE + | ANALYZE + | AND + | ANY + | ASC + | ASENSITIVE + | ASSERTION + | ASSIGNMENT + | ASYMMETRIC + | AT + | ATOMIC + | ATTACH + | ATTRIBUTE + | AUTHORIZATION + | BACKWARD + | BEFORE + | BEGIN_P + | BETWEEN + | BIGINT + | BINARY + | BIT + | BOOLEAN_P + | BOTH + | BREADTH + | BY + | CACHE + | CALL + | CALLED + | CASCADE + | CASCADED + | CASE + | CAST + | CATALOG_P + | CHAIN + | CHARACTERISTICS + | CHECK + | CHECKPOINT + | CLASS + | CLOSE + | CLUSTER + | COALESCE + | COLLATE + | COLLATION + | COLUMN + | COLUMNS + | COMMENT + | COMMENTS + | COMMIT + | COMMITTED + | COMPRESSION + | CONCURRENTLY + | CONFIGURATION + | CONFLICT + | CONNECTION + | CONSTRAINT + | CONSTRAINTS + | CONTENT_P + | CONTINUE_P + | CONVERSION_P + | COPY + | COST + | CROSS + | CSV + | CUBE + | CURRENT_P + | CURRENT_CATALOG + | CURRENT_DATE + | CURRENT_ROLE + | CURRENT_SCHEMA + | CURRENT_TIME + | CURRENT_TIMESTAMP + | CURRENT_USER + | CURSOR + | CYCLE + | DATA_P + | DATABASE + | DEALLOCATE + | DEC + | DECIMAL_P + | DECLARE + | DEFAULT + | DEFAULTS + | DEFERRABLE + | DEFERRED + | DEFINER + | DELETE_P + | DELIMITER + | DELIMITERS + | DEPENDS + | DEPTH + | DESC + | DETACH + | DICTIONARY + | DISABLE_P + | DISCARD + | DISTINCT + | DO + | DOCUMENT_P + | DOMAIN_P + | DOUBLE_P + | DROP + | EACH + | ELSE + | ENABLE_P + | ENCODING + | ENCRYPTED + | END_P + | ENUM_P + | ESCAPE + | EVENT + | EXCLUDE + | EXCLUDING + | EXCLUSIVE + | EXECUTE + | EXISTS + | EXPLAIN + | EXPRESSION + | EXTENSION + | EXTERNAL + | EXTRACT + | FALSE_P + | FAMILY + | FINALIZE + | FIRST_P + | FLOAT_P + | FOLLOWING + | FORCE + | FOREIGN + | FORWARD + | FREEZE + | FULL + | FUNCTION + | FUNCTIONS + | GENERATED + | GLOBAL + | GRANTED + | GREATEST + | GROUPING + | GROUPS + | HANDLER + | HEADER_P + | HOLD + | IDENTITY_P + | IF_P + | ILIKE + | IMMEDIATE + | IMMUTABLE + | IMPLICIT_P + | IMPORT_P + | IN_P + | INCLUDE + | INCLUDING + | INCREMENT + | INDEX + | INDEXES + | INHERIT + | INHERITS + | INITIALLY + | INLINE_P + | INNER_P + | INOUT + | INPUT_P + | INSENSITIVE + | INSERT + | INSTEAD + | INT_P + | INTEGER + | INTERVAL + | INVOKER + | IS + | ISOLATION + | JOIN + | KEY + | LABEL + | LANGUAGE + | LARGE_P + | LAST_P + | LATERAL_P + | LEADING + | LEAKPROOF + | LEAST + | LEFT + | LEVEL + | LIKE + | LISTEN + | LOAD + | LOCAL + | LOCALTIME + | LOCALTIMESTAMP + | LOCATION + | LOCK_P + | LOCKED + | LOGGED + | MAPPING + | MATCH + | MATERIALIZED + | MAXVALUE + | METHOD + | MINVALUE + | MODE + | MOVE + | NAME_P + | NAMES + | NATIONAL + | NATURAL + | NCHAR + | NEW + | NEXT + | NFC + | NFD + | NFKC + | NFKD + | NO + | NONE + | NORMALIZE + | NORMALIZED + | NOT + | NOTHING + | NOTIFY + | NOWAIT + | NULL_P + | NULLIF + | NULLS_P + | NUMERIC + | OBJECT_P + | OF + | OFF + | OIDS + | OLD + | ONLY + | OPERATOR + | OPTION + | OPTIONS + | OR + | ORDINALITY + | OTHERS + | OUT_P + | OUTER_P + | OVERLAY + | OVERRIDING + | OWNED + | OWNER + | PARALLEL + | PARSER + | PARTIAL + | PARTITION + | PASSING + | PASSWORD + | PLACING + | PLANS + | POLICY + | POSITION + | PRECEDING + | PREPARE + | PREPARED + | PRESERVE + | PRIMARY + | PRIOR + | PRIVILEGES + | PROCEDURAL + | PROCEDURE + | PROCEDURES + | PROGRAM + | PUBLICATION + | QUOTE + | RANGE + | READ + | REAL + | REASSIGN + | RECHECK + | RECURSIVE + | REF + | REFERENCES + | REFERENCING + | REFRESH + | REINDEX + | RELATIVE_P + | RELEASE + | RENAME + | REPEATABLE + | REPLACE + | REPLICA + | RESET + | RESTART + | RESTRICT + | RETURN + | RETURNS + | REVOKE + | RIGHT + | ROLE + | ROLLBACK + | ROLLUP + | ROUTINE + | ROUTINES + | ROW + | ROWS + | RULE + | SAVEPOINT + | SCHEMA + | SCHEMAS + | SCROLL + | SEARCH + | SECURITY + | SELECT + | SEQUENCE + | SEQUENCES + | SERIALIZABLE + | SERVER + | SESSION + | SESSION_USER + | SET + | SETOF + | SETS + | SHARE + | SHOW + | SIMILAR + | SIMPLE + | SKIP + | SMALLINT + | SNAPSHOT + | SOME + | SQL_P + | STABLE + | STANDALONE_P + | START + | STATEMENT + | STATISTICS + | STDIN + | STDOUT + | STORAGE + | STORED + | STRICT_P + | STRIP_P + | SUBSCRIPTION + | SUBSTRING + | SUPPORT + | SYMMETRIC + | SYSID + | SYSTEM_P + | TABLE + | TABLES + | TABLESAMPLE + | TABLESPACE + | TEMP + | TEMPLATE + | TEMPORARY + | TEXT_P + | THEN + | TIES + | TIME + | TIMESTAMP + | TRAILING + | TRANSACTION + | TRANSFORM + | TREAT + | TRIGGER + | TRIM + | TRUE_P + | TRUNCATE + | TRUSTED + | TYPE_P + | TYPES_P + | UESCAPE + | UNBOUNDED + | UNCOMMITTED + | UNENCRYPTED + | UNIQUE + | UNKNOWN + | UNLISTEN + | UNLOGGED + | UNTIL + | UPDATE + | USER + | USING + | VACUUM + | VALID + | VALIDATE + | VALIDATOR + | VALUE_P + | VALUES + | VARCHAR + | VARIADIC + | VERBOSE + | VERSION_P + | VIEW + | VIEWS + | VOLATILE + | WHEN + | WHITESPACE_P + | WORK + | WRAPPER + | WRITE + | XML_P + | XMLATTRIBUTES + | XMLCONCAT + | XMLELEMENT + | XMLEXISTS + | XMLFOREST + | XMLNAMESPACES + | XMLPARSE + | XMLPI + | XMLROOT + | XMLSERIALIZE + | XMLTABLE + | YES_P + | ZONE + ; + +%% + +/* + * The signature of this function is required by bison. However, we + * ignore the passed yylloc and instead use the last token position + * available from the scanner. + */ +static void +base_yyerror(YYLTYPE *yylloc, core_yyscan_t yyscanner, const char *msg) +{ + parser_yyerror(msg); +} + +static RawStmt * +makeRawStmt(Node *stmt, int stmt_location) +{ + RawStmt *rs = makeNode(RawStmt); + + rs->stmt = stmt; + rs->stmt_location = stmt_location; + rs->stmt_len = 0; /* might get changed later */ + return rs; +} + +/* Adjust a RawStmt to reflect that it doesn't run to the end of the string */ +static void +updateRawStmtEnd(RawStmt *rs, int end_location) +{ + /* + * If we already set the length, don't change it. This is for situations + * like "select foo ;; select bar" where the same statement will be last + * in the string for more than one semicolon. + */ + if (rs->stmt_len > 0) + return; + + /* OK, update length of RawStmt */ + rs->stmt_len = end_location - rs->stmt_location; +} + +static Node * +makeColumnRef(char *colname, List *indirection, + int location, core_yyscan_t yyscanner) +{ + /* + * Generate a ColumnRef node, with an A_Indirection node added if there + * is any subscripting in the specified indirection list. However, + * any field selection at the start of the indirection list must be + * transposed into the "fields" part of the ColumnRef node. + */ + ColumnRef *c = makeNode(ColumnRef); + int nfields = 0; + ListCell *l; + + c->location = location; + foreach(l, indirection) + { + if (IsA(lfirst(l), A_Indices)) + { + A_Indirection *i = makeNode(A_Indirection); + + if (nfields == 0) + { + /* easy case - all indirection goes to A_Indirection */ + c->fields = list_make1(makeString(colname)); + i->indirection = check_indirection(indirection, yyscanner); + } + else + { + /* got to split the list in two */ + i->indirection = check_indirection(list_copy_tail(indirection, + nfields), + yyscanner); + indirection = list_truncate(indirection, nfields); + c->fields = lcons(makeString(colname), indirection); + } + i->arg = (Node *) c; + return (Node *) i; + } + else if (IsA(lfirst(l), A_Star)) + { + /* We only allow '*' at the end of a ColumnRef */ + if (lnext(indirection, l) != NULL) + parser_yyerror("improper use of \"*\""); + } + nfields++; + } + /* No subscripting, so all indirection gets added to field list */ + c->fields = lcons(makeString(colname), indirection); + return (Node *) c; +} + +static Node * +makeTypeCast(Node *arg, TypeName *typename, int location) +{ + TypeCast *n = makeNode(TypeCast); + n->arg = arg; + n->typeName = typename; + n->location = location; + return (Node *) n; +} + +static Node * +makeStringConst(char *str, int location) +{ + A_Const *n = makeNode(A_Const); + + n->val.type = T_String; + n->val.val.str = str; + n->location = location; + + return (Node *)n; +} + +static Node * +makeStringConstCast(char *str, int location, TypeName *typename) +{ + Node *s = makeStringConst(str, location); + + return makeTypeCast(s, typename, -1); +} + +static Node * +makeIntConst(int val, int location) +{ + A_Const *n = makeNode(A_Const); + + n->val.type = T_Integer; + n->val.val.ival = val; + n->location = location; + + return (Node *)n; +} + +static Node * +makeFloatConst(char *str, int location) +{ + A_Const *n = makeNode(A_Const); + + n->val.type = T_Float; + n->val.val.str = str; + n->location = location; + + return (Node *)n; +} + +static Node * +makeBitStringConst(char *str, int location) +{ + A_Const *n = makeNode(A_Const); + + n->val.type = T_BitString; + n->val.val.str = str; + n->location = location; + + return (Node *)n; +} + +static Node * +makeNullAConst(int location) +{ + A_Const *n = makeNode(A_Const); + + n->val.type = T_Null; + n->location = location; + + return (Node *)n; +} + +static Node * +makeAConst(Value *v, int location) +{ + Node *n; + + switch (v->type) + { + case T_Float: + n = makeFloatConst(v->val.str, location); + break; + + case T_Integer: + n = makeIntConst(v->val.ival, location); + break; + + case T_String: + default: + n = makeStringConst(v->val.str, location); + break; + } + + return n; +} + +/* makeBoolAConst() + * Create an A_Const string node and put it inside a boolean cast. + */ +static Node * +makeBoolAConst(bool state, int location) +{ + A_Const *n = makeNode(A_Const); + + n->val.type = T_String; + n->val.val.str = (state ? "t" : "f"); + n->location = location; + + return makeTypeCast((Node *)n, SystemTypeName("bool"), -1); +} + +/* makeRoleSpec + * Create a RoleSpec with the given type + */ +static RoleSpec * +makeRoleSpec(RoleSpecType type, int location) +{ + RoleSpec *spec = makeNode(RoleSpec); + + spec->roletype = type; + spec->location = location; + + return spec; +} + +/* check_qualified_name --- check the result of qualified_name production + * + * It's easiest to let the grammar production for qualified_name allow + * subscripts and '*', which we then must reject here. + */ +static void +check_qualified_name(List *names, core_yyscan_t yyscanner) +{ + ListCell *i; + + foreach(i, names) + { + if (!IsA(lfirst(i), String)) + parser_yyerror("syntax error"); + } +} + +/* check_func_name --- check the result of func_name production + * + * It's easiest to let the grammar production for func_name allow subscripts + * and '*', which we then must reject here. + */ +static List * +check_func_name(List *names, core_yyscan_t yyscanner) +{ + ListCell *i; + + foreach(i, names) + { + if (!IsA(lfirst(i), String)) + parser_yyerror("syntax error"); + } + return names; +} + +/* check_indirection --- check the result of indirection production + * + * We only allow '*' at the end of the list, but it's hard to enforce that + * in the grammar, so do it here. + */ +static List * +check_indirection(List *indirection, core_yyscan_t yyscanner) +{ + ListCell *l; + + foreach(l, indirection) + { + if (IsA(lfirst(l), A_Star)) + { + if (lnext(indirection, l) != NULL) + parser_yyerror("improper use of \"*\""); + } + } + return indirection; +} + +/* extractArgTypes() + * Given a list of FunctionParameter nodes, extract a list of just the + * argument types (TypeNames) for input parameters only. This is what + * is needed to look up an existing function, which is what is wanted by + * the productions that use this call. + */ +static List * +extractArgTypes(List *parameters) +{ + List *result = NIL; + ListCell *i; + + foreach(i, parameters) + { + FunctionParameter *p = (FunctionParameter *) lfirst(i); + + if (p->mode != FUNC_PARAM_OUT && p->mode != FUNC_PARAM_TABLE) + result = lappend(result, p->argType); + } + return result; +} + +/* extractAggrArgTypes() + * As above, but work from the output of the aggr_args production. + */ +static List * +extractAggrArgTypes(List *aggrargs) +{ + Assert(list_length(aggrargs) == 2); + return extractArgTypes((List *) linitial(aggrargs)); +} + +/* makeOrderedSetArgs() + * Build the result of the aggr_args production (which see the comments for). + * This handles only the case where both given lists are nonempty, so that + * we have to deal with multiple VARIADIC arguments. + */ +static List * +makeOrderedSetArgs(List *directargs, List *orderedargs, + core_yyscan_t yyscanner) +{ + FunctionParameter *lastd = (FunctionParameter *) llast(directargs); + Value *ndirectargs; + + /* No restriction unless last direct arg is VARIADIC */ + if (lastd->mode == FUNC_PARAM_VARIADIC) + { + FunctionParameter *firsto = (FunctionParameter *) linitial(orderedargs); + + /* + * We ignore the names, though the aggr_arg production allows them; + * it doesn't allow default values, so those need not be checked. + */ + if (list_length(orderedargs) != 1 || + firsto->mode != FUNC_PARAM_VARIADIC || + !equal(lastd->argType, firsto->argType)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("an ordered-set aggregate with a VARIADIC direct argument must have one VARIADIC aggregated argument of the same data type"), + parser_errposition(exprLocation((Node *) firsto)))); + + /* OK, drop the duplicate VARIADIC argument from the internal form */ + orderedargs = NIL; + } + + /* don't merge into the next line, as list_concat changes directargs */ + ndirectargs = makeInteger(list_length(directargs)); + + return list_make2(list_concat(directargs, orderedargs), + ndirectargs); +} + +/* 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 *lockingClause, + SelectLimit *limitClause, + WithClause *withClause, + core_yyscan_t yyscanner) +{ + Assert(IsA(stmt, SelectStmt)); + + /* + * Tests here are to reject constructs like + * (SELECT foo ORDER BY bar) ORDER BY baz + */ + if (sortClause) + { + if (stmt->sortClause) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("multiple ORDER BY clauses not allowed"), + parser_errposition(exprLocation((Node *) sortClause)))); + stmt->sortClause = sortClause; + } + /* We can handle multiple locking clauses, though */ + stmt->lockingClause = list_concat(stmt->lockingClause, lockingClause); + if (limitClause && limitClause->limitOffset) + { + if (stmt->limitOffset) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("multiple OFFSET clauses not allowed"), + parser_errposition(exprLocation(limitClause->limitOffset)))); + stmt->limitOffset = limitClause->limitOffset; + } + if (limitClause && limitClause->limitCount) + { + if (stmt->limitCount) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("multiple LIMIT clauses not allowed"), + parser_errposition(exprLocation(limitClause->limitCount)))); + stmt->limitCount = limitClause->limitCount; + } + if (limitClause && limitClause->limitOption != LIMIT_OPTION_DEFAULT) + { + if (stmt->limitOption) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("multiple limit options not allowed"))); + if (!stmt->sortClause && limitClause->limitOption == LIMIT_OPTION_WITH_TIES) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("WITH TIES cannot be specified without ORDER BY clause"))); + if (limitClause->limitOption == LIMIT_OPTION_WITH_TIES && stmt->lockingClause) + { + ListCell *lc; + + foreach(lc, stmt->lockingClause) + { + LockingClause *lock = lfirst_node(LockingClause, lc); + + if (lock->waitPolicy == LockWaitSkip) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s and %s options cannot be used together", + "SKIP LOCKED", "WITH TIES"))); + } + } + stmt->limitOption = limitClause->limitOption; + } + if (withClause) + { + if (stmt->withClause) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("multiple WITH clauses not allowed"), + parser_errposition(exprLocation((Node *) withClause)))); + stmt->withClause = withClause; + } +} + +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 list_make2(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. + * Likewise for the location. + */ +TypeName * +SystemTypeName(char *name) +{ + return makeTypeNameFromNameList(list_make2(makeString("pg_catalog"), + makeString(name))); +} + +/* 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, int location) +{ + if (IsA(n, A_Const)) + { + A_Const *con = (A_Const *)n; + + /* report the constant's location as that of the '-' sign */ + con->location = location; + + 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(AEXPR_OP, "-", NULL, n, location); +} + +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 + v->val.str = psprintf("-%s", oldval); +} + +static Node * +makeAndExpr(Node *lexpr, Node *rexpr, int location) +{ + /* Flatten "a AND b AND c ..." to a single BoolExpr on sight */ + if (IsA(lexpr, BoolExpr)) + { + BoolExpr *blexpr = (BoolExpr *) lexpr; + + if (blexpr->boolop == AND_EXPR) + { + blexpr->args = lappend(blexpr->args, rexpr); + return (Node *) blexpr; + } + } + return (Node *) makeBoolExpr(AND_EXPR, list_make2(lexpr, rexpr), location); +} + +static Node * +makeOrExpr(Node *lexpr, Node *rexpr, int location) +{ + /* Flatten "a OR b OR c ..." to a single BoolExpr on sight */ + if (IsA(lexpr, BoolExpr)) + { + BoolExpr *blexpr = (BoolExpr *) lexpr; + + if (blexpr->boolop == OR_EXPR) + { + blexpr->args = lappend(blexpr->args, rexpr); + return (Node *) blexpr; + } + } + return (Node *) makeBoolExpr(OR_EXPR, list_make2(lexpr, rexpr), location); +} + +static Node * +makeNotExpr(Node *expr, int location) +{ + return (Node *) makeBoolExpr(NOT_EXPR, list_make1(expr), location); +} + +static Node * +makeAArrayExpr(List *elements, int location) +{ + A_ArrayExpr *n = makeNode(A_ArrayExpr); + + n->elements = elements; + n->location = location; + return (Node *) n; +} + +static Node * +makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod, int location) +{ + SQLValueFunction *svf = makeNode(SQLValueFunction); + + svf->op = op; + /* svf->type will be filled during parse analysis */ + svf->typmod = typmod; + svf->location = location; + return (Node *) svf; +} + +static Node * +makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args, + int location) +{ + XmlExpr *x = makeNode(XmlExpr); + + x->op = op; + x->name = name; + /* + * named_args is a list of ResTarget; it'll be split apart into separate + * expression and name lists in transformXmlExpr(). + */ + x->named_args = named_args; + x->arg_names = NIL; + x->args = args; + /* xmloption, if relevant, must be filled in by caller */ + /* type and typmod will be filled in during parse analysis */ + x->type = InvalidOid; /* marks the node as not analyzed */ + x->location = location; + return (Node *) x; +} + +/* + * Merge the input and output parameters of a table function. + */ +static List * +mergeTableFuncParameters(List *func_args, List *columns) +{ + ListCell *lc; + + /* Explicit OUT and INOUT parameters shouldn't be used in this syntax */ + foreach(lc, func_args) + { + FunctionParameter *p = (FunctionParameter *) lfirst(lc); + + if (p->mode != FUNC_PARAM_DEFAULT && + p->mode != FUNC_PARAM_IN && + p->mode != FUNC_PARAM_VARIADIC) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("OUT and INOUT arguments aren't allowed in TABLE functions"))); + } + + return list_concat(func_args, columns); +} + +/* + * Determine return type of a TABLE function. A single result column + * returns setof that column's type; otherwise return setof record. + */ +static TypeName * +TableFuncTypeName(List *columns) +{ + TypeName *result; + + if (list_length(columns) == 1) + { + FunctionParameter *p = (FunctionParameter *) linitial(columns); + + result = copyObject(p->argType); + } + else + result = SystemTypeName("record"); + + result->setof = true; + + return result; +} + +/* + * Convert a list of (dotted) names to a RangeVar (like + * makeRangeVarFromNameList, but with position support). The + * "AnyName" refers to the any_name production in the grammar. + */ +static RangeVar * +makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner) +{ + RangeVar *r = makeNode(RangeVar); + + switch (list_length(names)) + { + case 1: + r->catalogname = NULL; + r->schemaname = NULL; + r->relname = strVal(linitial(names)); + break; + case 2: + r->catalogname = NULL; + r->schemaname = strVal(linitial(names)); + r->relname = strVal(lsecond(names)); + break; + case 3: + r->catalogname = strVal(linitial(names)); + r->schemaname = strVal(lsecond(names)); + r->relname = strVal(lthird(names)); + break; + default: + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("improper qualified name (too many dotted names): %s", + NameListToString(names)), + parser_errposition(position))); + break; + } + + r->relpersistence = RELPERSISTENCE_PERMANENT; + r->location = position; + + return r; +} + +/* Separate Constraint nodes from COLLATE clauses in a ColQualList */ +static void +SplitColQualList(List *qualList, + List **constraintList, CollateClause **collClause, + core_yyscan_t yyscanner) +{ + ListCell *cell; + + *collClause = NULL; + foreach(cell, qualList) + { + Node *n = (Node *) lfirst(cell); + + if (IsA(n, Constraint)) + { + /* keep it in list */ + continue; + } + if (IsA(n, CollateClause)) + { + CollateClause *c = (CollateClause *) n; + + if (*collClause) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("multiple COLLATE clauses not allowed"), + parser_errposition(c->location))); + *collClause = c; + } + else + elog(ERROR, "unexpected node type %d", (int) n->type); + /* remove non-Constraint nodes from qualList */ + qualList = foreach_delete_current(qualList, cell); + } + *constraintList = qualList; +} + +/* + * Process result of ConstraintAttributeSpec, and set appropriate bool flags + * in the output command node. Pass NULL for any flags the particular + * command doesn't support. + */ +static void +processCASbits(int cas_bits, int location, const char *constrType, + bool *deferrable, bool *initdeferred, bool *not_valid, + bool *no_inherit, core_yyscan_t yyscanner) +{ + /* defaults */ + if (deferrable) + *deferrable = false; + if (initdeferred) + *initdeferred = false; + if (not_valid) + *not_valid = false; + + if (cas_bits & (CAS_DEFERRABLE | CAS_INITIALLY_DEFERRED)) + { + if (deferrable) + *deferrable = true; + else + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + /* translator: %s is CHECK, UNIQUE, or similar */ + errmsg("%s constraints cannot be marked DEFERRABLE", + constrType), + parser_errposition(location))); + } + + if (cas_bits & CAS_INITIALLY_DEFERRED) + { + if (initdeferred) + *initdeferred = true; + else + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + /* translator: %s is CHECK, UNIQUE, or similar */ + errmsg("%s constraints cannot be marked DEFERRABLE", + constrType), + parser_errposition(location))); + } + + if (cas_bits & CAS_NOT_VALID) + { + if (not_valid) + *not_valid = true; + else + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + /* translator: %s is CHECK, UNIQUE, or similar */ + errmsg("%s constraints cannot be marked NOT VALID", + constrType), + parser_errposition(location))); + } + + if (cas_bits & CAS_NO_INHERIT) + { + if (no_inherit) + *no_inherit = true; + else + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + /* translator: %s is CHECK, UNIQUE, or similar */ + errmsg("%s constraints cannot be marked NO INHERIT", + constrType), + parser_errposition(location))); + } +} + +/*---------- + * Recursive view transformation + * + * Convert + * + * CREATE RECURSIVE VIEW relname (aliases) AS query + * + * to + * + * CREATE VIEW relname (aliases) AS + * WITH RECURSIVE relname (aliases) AS (query) + * SELECT aliases FROM relname + * + * Actually, just the WITH ... part, which is then inserted into the original + * view definition as the query. + * ---------- + */ +static Node * +makeRecursiveViewSelect(char *relname, List *aliases, Node *query) +{ + SelectStmt *s = makeNode(SelectStmt); + WithClause *w = makeNode(WithClause); + CommonTableExpr *cte = makeNode(CommonTableExpr); + List *tl = NIL; + ListCell *lc; + + /* create common table expression */ + cte->ctename = relname; + cte->aliascolnames = aliases; + cte->ctematerialized = CTEMaterializeDefault; + cte->ctequery = query; + cte->location = -1; + + /* create WITH clause and attach CTE */ + w->recursive = true; + w->ctes = list_make1(cte); + w->location = -1; + + /* create target list for the new SELECT from the alias list of the + * recursive view specification */ + foreach (lc, aliases) + { + ResTarget *rt = makeNode(ResTarget); + + rt->name = NULL; + rt->indirection = NIL; + rt->val = makeColumnRef(strVal(lfirst(lc)), NIL, -1, 0); + rt->location = -1; + + tl = lappend(tl, rt); + } + + /* create new SELECT combining WITH clause, target list, and fake FROM + * clause */ + s->withClause = w; + s->targetList = tl; + s->fromClause = list_make1(makeRangeVar(NULL, relname, -1)); + + return (Node *) s; +} + +/* parser_init() + * Initialize to parse one query string + */ +void +parser_init(base_yy_extra_type *yyext) +{ + yyext->parsetree = NIL; /* in case grammar forgets to set it */ +} |