美文网首页
PostgreSQL查询SQL的语法分析(2)——语法分析

PostgreSQL查询SQL的语法分析(2)——语法分析

作者: hemny | 来源:发表于2018-08-30 18:45 被阅读167次

    一、背景

    postgreSQL命令的词法分析和语法分析是由Unix工具Yacc和Lex制作的。使用的是 Bison 和
    Flex。
    前面已经分析了 词法分析器代码
    这次介绍语法分析器代码

    二、代码分析

    由于词法分析器代码较多,文章只对其中部分做重点解说。

    • 语法分析器gram.y ——定义段
    /* src/backend/parser/gram.y */
    
    /* 定义段 */
    %{
    #include "postgres.h"
    
    #include <ctype.h>
    #include <limits.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 "parser/parse_expr.h"
    #include "storage/lmgr.h"
    #include "utils/date.h"
    #include "utils/datetime.h"
    #include "utils/numeric.h"
    #include "utils/xml.h"
    ...
    /* 定义 宏、函数*/
    ...
    
    %}
    ...
    
    /* 改变YYSTYPE的类型 */
    %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;
        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;
    }
    
    /* 用%type <node>方式声明非终结符的类型,绑定到 node */
    %type <node>    stmt schema_stmt
            AlterEventTrigStmt AlterCollationStmt
            AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
            AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
            AlterObjectDependsStmt AlterObjectSchemaStmt AlterOwnerStmt
            AlterOperatorStmt AlterSeqStmt AlterSystemStmt AlterTableStmt
            AlterTblSpcStmt AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt
            AlterCompositeTypeStmt AlterUserMappingStmt
            AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt
            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
            CreateAssertStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt
            CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt
            CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
            DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
            DropAssertStmt 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 RevokeStmt RevokeRoleStmt
            RuleActionStmt RuleActionStmtOrEmpty RuleStmt
            SecLabelStmt SelectStmt TransactionStmt 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
    
    %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 <ival>    vacuum_option_list vacuum_option_elem
                    analyze_option_list analyze_option_elem
    %type <boolean> opt_or_replace
                    opt_grant_grant_option opt_grant_admin_option
                    opt_nowait opt_if_exists opt_with_data
    %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 <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
                    database_name access_method_clause access_method attr_name
                    name cursor_name file_name
                    index_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 <list>方式声明非终结符的类型,绑定到 list*/
    %type <list>    stmtblock stmtmulti
                    OptTableElementList TableElementList OptInherit definition
                    OptTypedTableElementList TypedTableElementList
                    reloptions opt_reloptions
                    OptWith distinct_clause opt_all_clause 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 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
                    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
                    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 group_clause TriggerFuncArgs select_limit
                    opt_select_limit 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
                    publication_name_list
                    vacuum_relation_list opt_vacuum_relation_list
    
    %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 <value>   publication_name_item
    
    %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 <boolean> all_or_distinct
    
    %type <node>    join_outer 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 <node>    overlay_placing substr_from substr_for
    
    %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 opt_oids copy_delimiter
    
    %type <boolean> copy_from opt_program
    
    %type <ival>    opt_column event cursor_options opt_hold opt_set_data
    %type <objtype> drop_type_any_name drop_type_name drop_type_name_on_any_name
                    comment_type_any_name comment_type_name
                    security_label_type_any_name security_label_type_name
    
    %type <node>    fetch_args limit_clause 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
                    ExclusionWhereClause 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
    %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 <ival>    sub_type
    %type <value>   NumericOnly
    %type <list>    NumericOnly_list
    %type <alias>   alias_clause opt_alias_clause
    %type <list>    func_alias_clause
    %type <sortby>  sortby
    %type <ielem>   index_elem
    %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 <str>     explain_option_name
    %type <node>    explain_option_arg
    %type <defelt>  explain_option_elem
    %type <list>    explain_option_list
    
    %type <ival>    reindex_target_type reindex_target_multitable
    %type <ival>    reindex_option_list reindex_option_elem
    
    %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 var_name type_function_name param_name
    %type <str>     NonReservedWord NonReservedWord_or_Sconst
    %type <str>     createdb_opt_name
    %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 <node>    TableConstraint TableLikeClause
    %type <ival>    TableLikeOptionList TableLikeOption
    %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 <str>         part_strategy
    %type <partelem>    part_elem
    %type <list>        part_params
    %type <partboundspec> PartitionBoundSpec
    %type <node>        partbound_datum PartitionRangeDatum
    %type <list>        hash_partbound partbound_datum_list range_datum_list
    %type <defelt>      hash_partbound_elem
    
    /* 用%token<>方式声明yacc记号 */
    
    %token <str>    IDENT FCONST SCONST 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
        ASSERTION ASSIGNMENT ASYMMETRIC AT ATTACH ATTRIBUTE AUTHORIZATION
    
        BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
        BOOLEAN_P BOTH 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 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 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
        EXTENSION EXTERNAL EXTRACT
    
        FALSE_P FAMILY FETCH FILTER 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 NO NONE
        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 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 STRICT_P STRIP_P
        SUBSCRIPTION SUBSTRING 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
    
        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
    
    %token      NOT_LA NULLS_LA WITH_LA
    
    
    /* 预定义操作符的左右相关,并排序优先级 */
    %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 */
    %left       POSTFIXOP       /* dummy for postfix Op rules */
    /*
     * To support target_el without AS, we must give IDENT an explicit priority
     * between POSTFIXOP and Op.  We can safely assign the same priority to
     * various unreserved keywords as needed to resolve ambiguities (this can't
     * have any bad effects since obviously the keywords will still behave the
     * same as if they weren't keywords).  We need to do this:
     * for PARTITION, RANGE, ROWS, GROUPS to support opt_existing_window_name;
     * for RANGE, ROWS, GROUPS so that they can follow a_expr without creating
     * postfix-operator problems;
     * for GENERATED so that it can follow b_expr;
     * and for NULL so that it can follow b_expr in ColQualList without creating
     * postfix-operator problems.
     *
     * 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.
     *
     * 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.
     */
    %nonassoc   UNBOUNDED       /* ideally should have same precedence as IDENT */
    %nonassoc   IDENT GENERATED NULL_P 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
    /* kluge to keep xml_whitespace_option from causing shift/reduce conflicts */
    %right      PRESERVE STRIP_P
    
    %%
    
    • 语法分析器gram.y ——规则段
    /* 规则段 */
    /*
     *  整个字符串的parse.
     */
    stmtblock:  stmtmulti
                {
                    pg_yyget_extra(yyscanner)->parsetree = $1;
                }
            ;
    
    /*
     * 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.
     */
    /* 使用左递归形式,处理含有多个SQL */
    stmtmulti:  stmtmulti ';' 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;
                    }
                | stmt
                    {
                        if ($1 != NULL)
                            $$ = list_make1(makeRawStmt($1, 0));
                        else
                            $$ = NIL;
                    }
            ;
    
    /* 定义stmt的语法树,支持124中SQL的语法分析*/
    stmt :
                AlterEventTrigStmt
                | AlterCollationStmt
                | AlterDatabaseStmt
                | AlterDatabaseSetStmt
                | AlterDefaultPrivilegesStmt
                | AlterDomainStmt
                | AlterEnumStmt
                | AlterExtensionStmt
                | AlterExtensionContentsStmt
                | AlterFdwStmt
                | AlterForeignServerStmt
                | AlterForeignTableStmt
                | AlterFunctionStmt
                | AlterGroupStmt
                | AlterObjectDependsStmt
                | AlterObjectSchemaStmt
                | AlterOwnerStmt
                | AlterOperatorStmt
                | AlterPolicyStmt
                | AlterSeqStmt
                | AlterSystemStmt
                | AlterTableStmt
                | AlterTblSpcStmt
                | AlterCompositeTypeStmt
                | AlterPublicationStmt
                | AlterRoleSetStmt
                | AlterRoleStmt
                | AlterSubscriptionStmt
                | AlterTSConfigurationStmt
                | AlterTSDictionaryStmt
                | AlterUserMappingStmt
                | AnalyzeStmt
                | CallStmt
                | CheckPointStmt
                | ClosePortalStmt
                | ClusterStmt
                | CommentStmt
                | ConstraintsSetStmt
                | CopyStmt
                | CreateAmStmt
                | CreateAsStmt
                | CreateAssertStmt
                | 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 /*删除数据SQL*/
                | DiscardStmt
                | DoStmt
                | DropAssertStmt
                | DropCastStmt
                | DropOpClassStmt
                | DropOpFamilyStmt
                | DropOwnedStmt
                | DropPLangStmt
                | DropStmt
                | DropSubscriptionStmt
                | DropTableSpaceStmt
                | DropTransformStmt
                | DropRoleStmt
                | DropUserMappingStmt
                | DropdbStmt
                | ExecuteStmt
                | ExplainStmt
                | FetchStmt
                | GrantStmt
                | GrantRoleStmt
                | ImportForeignSchemaStmt
                | IndexStmt
                | InsertStmt /*插入数据SQL*/
                | ListenStmt
                | RefreshMatViewStmt
                | LoadStmt
                | LockStmt
                | NotifyStmt
                | PrepareStmt
                | ReassignOwnedStmt
                | ReindexStmt
                | RemoveAggrStmt
                | RemoveFuncStmt
                | RemoveOperStmt
                | RenameStmt
                | RevokeStmt
                | RevokeRoleStmt
                | RuleStmt
                | SecLabelStmt
                | SelectStmt /*查询数据SQL*/
                | TransactionStmt
                | TruncateStmt
                | UnlistenStmt
                | UpdateStmt /*修改数据SQL*/
                | VacuumStmt
                | VariableResetStmt
                | VariableSetStmt
                | VariableShowStmt
                | ViewStmt
                | /*EMPTY*/
                    { $$ = NULL; }
            ;
    ...
    
    /* 数据查询的SQL*/
    /*****************************************************************************
     *
     *      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.
     */
    /* 查询的SQL,分为非子查询和子查询两种,子查询的SQL,有() 括着*/
    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, NULL,
                                            yyscanner);
                        $$ = $1;
                    }
                | select_clause opt_sort_clause for_locking_clause opt_select_limit
                    {
                        insertSelectOptions((SelectStmt *) $1, $2, $3,
                                            list_nth($4, 0), list_nth($4, 1),
                                            NULL,
                                            yyscanner);
                        $$ = $1;
                    }
                | select_clause opt_sort_clause select_limit opt_for_locking_clause
                    {
                        insertSelectOptions((SelectStmt *) $1, $2, $4,
                                            list_nth($3, 0), list_nth($3, 1),
                                            NULL,
                                            yyscanner);
                        $$ = $1;
                    }
                | with_clause select_clause  /* 带有with的查询*/
                    {
                        insertSelectOptions((SelectStmt *) $2, NULL, NIL,
                                            NULL, NULL,
                                            $1,
                                            yyscanner);
                        $$ = $2;
                    }
                | with_clause select_clause sort_clause
                    {
                        insertSelectOptions((SelectStmt *) $2, $3, NIL,
                                            NULL, NULL,
                                            $1,
                                            yyscanner);
                        $$ = $2;
                    }
                | with_clause select_clause opt_sort_clause for_locking_clause opt_select_limit
                    {
                        insertSelectOptions((SelectStmt *) $2, $3, $4,
                                            list_nth($5, 0), list_nth($5, 1),
                                            $1,
                                            yyscanner);
                        $$ = $2;
                    }
                | with_clause select_clause opt_sort_clause select_limit opt_for_locking_clause
                    {
                        insertSelectOptions((SelectStmt *) $2, $3, $5,
                                            list_nth($4, 0), list_nth($4, 1),
                                            $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.
     *
     * 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
                    {
      /* 普通查询的查询语法(一):select [all] [col1...] [into ...] [from ...] [where ...] [group by ...] [ having ...] [window ...]*/
    
      /*创建一个SelectStmt节点,将子分析结果写入对应的属性中*/
                        SelectStmt *n = makeNode(SelectStmt);
                        n->targetList = $3;
                        n->intoClause = $4;
                        n->fromClause = $5;
                        n->whereClause = $6;
                        n->groupClause = $7;
                        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
                    {
      /* 普通查询的查询语法(二):select [DISTINCT] [col1...] [into ...] [from ...] [where ...] [group by ...] [ having ...] [window ...]*/
    
    /*创建一个SelectStmt节点,将子分析结果写入对应的属性中*/
                        SelectStmt *n = makeNode(SelectStmt);
                        n->distinctClause = $2;
                        n->targetList = $3;
                        n->intoClause = $4;
                        n->fromClause = $5;
                        n->whereClause = $6;
                        n->groupClause = $7;
                        n->havingClause = $8;
                        n->windowClause = $9;
                        $$ = (Node *)n;
                    }
                | values_clause                         { $$ = $1; }
                | TABLE relation_expr
                    {
      /* 普通查询的查询语法(三):SELECT * FROM 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 all_or_distinct select_clause
                    {
     /* 普通查询的查询语法(四):select_clause union [all/distinct] select_clause */
                        $$ = makeSetOp(SETOP_UNION, $3, $1, $4);
                    }
                | select_clause INTERSECT all_or_distinct select_clause
                    {
                        $$ = makeSetOp(SETOP_INTERSECT, $3, $1, $4);
                    }
                | select_clause EXCEPT all_or_distinct select_clause
                    {
                        $$ = makeSetOp(SETOP_EXCEPT, $3, $1, $4);
                    }
            ;
    
    /*
     * SQL standard WITH clause looks like:
     *
     * WITH [ RECURSIVE ] <query name> [ (<column>,...) ]
     *      AS (query) [ SEARCH or CYCLE clause ]
     *
     * We don't currently support the SEARCH or CYCLE clause.
     *
     * Recognizing WITH_LA here allows a CTE to be named TIME or ORDINALITY.
     */
     /* with 语句部分 */
    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 '(' PreparableStmt ')'
                {
                    CommonTableExpr *n = makeNode(CommonTableExpr);
                    n->ctename = $1;
                    n->aliascolnames = $2;
                    n->ctequery = $5;
                    n->location = @1;
                    $$ = (Node *) n;
                }
            ;
    
    opt_with_clause:
            with_clause                             { $$ = $1; }
            | /*EMPTY*/                             { $$ = NULL; }
            ;
    /* into 语句部分 */
    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.
     */
    /* into 语句部分的临时表 */
    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*/                             {}
            ;
    
    all_or_distinct:
                ALL                                     { $$ = true; }
                | DISTINCT                              { $$ = false; }
                | /*EMPTY*/                             { $$ = false; }
            ;
    
    /* We use (NIL) as a placeholder to indicate that all target expressions
     * should be placed in the DISTINCT list during parsetree analysis.
     */
    /* distinct语句部分 */
    distinct_clause:
                DISTINCT                                { $$ = list_make1(NIL); }
                | DISTINCT ON '(' expr_list ')'         { $$ = $4; }
            ;
    
    opt_all_clause:
                ALL                                     { $$ = NIL;}
                | /*EMPTY*/                             { $$ = NIL; }
            ;
    
    /* sort 语句部分 */
    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 */
                    }
            ;
    
    /* limit 语句部分 */
    select_limit:
                limit_clause offset_clause          { $$ = list_make2($2, $1); }
                | offset_clause limit_clause        { $$ = list_make2($1, $2); }
                | limit_clause                      { $$ = list_make2(NULL, $1); }
                | offset_clause                     { $$ = list_make2($1, NULL); }
            ;
    
    opt_select_limit:
                select_limit                        { $$ = $1; }
                | /* EMPTY */                       { $$ = list_make2(NULL,NULL); }
            ;
    
    limit_clause:
                LIMIT select_limit_value
                    { $$ = $2; }
                | 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
                    { $$ = $3; }
                | FETCH first_or_next row_or_rows ONLY
                    { $$ = makeIntConst(1, -1); }
            ;
    
    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 by 语句部分 */
    group_clause:
                GROUP_P BY group_by_list                { $$ = $3; }
                | /*EMPTY*/                             { $$ = NIL; }
            ;
    
    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 语句部分 */
    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 语句部分 */
    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.
     */
    /* from 语句部分 表对象部分,可以是子查询 */
    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.
     */
    /* join table 语句部分 */
    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->quals = NULL;
                        $$ = n;
                    }
                | table_ref join_type JOIN table_ref join_qual
                    {
                        JoinExpr *n = makeNode(JoinExpr);
                        n->jointype = $2;
                        n->isNatural = false;
                        n->larg = $1;
                        n->rarg = $4;
                        if ($5 != NULL && IsA($5, List))
                            n->usingClause = (List *) $5; /* USING clause */
                        else
                            n->quals = $5; /* ON clause */
                        $$ = n;
                    }
                | table_ref JOIN table_ref join_qual
                    {
                        /* letting join_type reduce to empty doesn't work */
                        JoinExpr *n = makeNode(JoinExpr);
                        n->jointype = JOIN_INNER;
                        n->isNatural = false;
                        n->larg = $1;
                        n->rarg = $3;
                        if ($4 != NULL && IsA($4, List))
                            n->usingClause = (List *) $4; /* USING clause */
                        else
                            n->quals = $4; /* ON clause */
                        $$ = n;
                    }
                | table_ref NATURAL join_type JOIN table_ref
                    {
                        JoinExpr *n = makeNode(JoinExpr);
                        n->jointype = $3;
                        n->isNatural = true;
                        n->larg = $1;
                        n->rarg = $5;
                        n->usingClause = NIL; /* figure out which columns later... */
                        n->quals = NULL; /* fill later */
                        $$ = n;
                    }
                | table_ref NATURAL JOIN table_ref
                    {
                        /* letting join_type reduce to empty doesn't work */
                        JoinExpr *n = makeNode(JoinExpr);
                        n->jointype = JOIN_INNER;
                        n->isNatural = true;
                        n->larg = $1;
                        n->rarg = $4;
                        n->usingClause = NIL; /* figure out which columns later... */
                        n->quals = NULL; /* fill later */
                        $$ = n;
                    }
            ;
    /* alias 语句部分 */
    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; }
            ;
    
    /*
     * 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 join_outer                         { $$ = JOIN_FULL; }
                | LEFT join_outer                       { $$ = JOIN_LEFT; }
                | RIGHT join_outer                      { $$ = JOIN_RIGHT; }
                | INNER_P                               { $$ = JOIN_INNER; }
            ;
    
    /* OUTER is just noise... */
    join_outer: OUTER_P                                 { $$ = NULL; }
                | /*EMPTY*/                             { $$ = NULL; }
            ;
    
    /* JOIN qualification clauses
     * Possibilities are:
     *  USING ( column list ) allows only unqualified column names,
     *                        which must match between tables.
     *  ON expr allows more general qualifications.
     *
     * We return USING as a List node, while an ON-expr will not be a List.
     */
    
    join_qual:  USING '(' name_list ')'                 { $$ = (Node *) $3; }
                | ON a_expr                             { $$ = $2; }
            ;
    
    
    relation_expr:
                qualified_name
                    {
                        /* 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 条件语句部分 */
    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->is_from_parent = 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;
                    }
            ;
    
    ...
    
    • 语法分析器gram.y ——用户子程序段(未完成)
    /* 用户子程序段 */
    

    总结

    本文居于查询SQL的语法结构,分析postgreSQL的语法分析器代码。
    如果你想修改postgresql的语法,你要关注下两个文件“gram.y”和“kwlist.h”。简要地说就是将新添加的关键字添加到kwlist.h中,同时修改gram.y文件中的语法规则,然后重新编译即可。

    相关文章

      网友评论

          本文标题:PostgreSQL查询SQL的语法分析(2)——语法分析

          本文链接:https://www.haomeiwen.com/subject/oqldwftx.html