use sqruff_lib_core::dialects::Dialect;
use sqruff_lib_core::dialects::init::DialectKind;
use sqruff_lib_core::dialects::syntax::SyntaxKind;
use sqruff_lib_core::helpers::{Config, ToMatchable};
use sqruff_lib_core::parser::grammar::anyof::{
    AnyNumberOf, any_set_of, one_of, optionally_bracketed,
};
use sqruff_lib_core::parser::grammar::conditional::Conditional;
use sqruff_lib_core::parser::grammar::delimited::Delimited;
use sqruff_lib_core::parser::grammar::sequence::{Bracketed, Sequence};
use sqruff_lib_core::parser::grammar::{Anything, Ref};
use sqruff_lib_core::parser::lexer::Matcher;
use sqruff_lib_core::parser::matchable::MatchableTrait;
use sqruff_lib_core::parser::node_matcher::NodeMatcher;
use sqruff_lib_core::parser::parsers::{MultiStringParser, RegexParser, StringParser, TypedParser};
use sqruff_lib_core::parser::segments::bracketed::BracketedSegmentMatcher;
use sqruff_lib_core::parser::segments::meta::MetaSegment;
use sqruff_lib_core::parser::types::ParseMode;

use super::sparksql_keywords::{RESERVED_KEYWORDS, UNRESERVED_KEYWORDS};
use crate::ansi;

pub fn raw_dialect() -> Dialect {
    let ansi_dialect = ansi::raw_dialect();
    let hive_dialect = super::hive::raw_dialect();
    let mut sparksql_dialect = ansi_dialect;
    sparksql_dialect.name = DialectKind::Sparksql;

    sparksql_dialect.patch_lexer_matchers(vec![
        Matcher::regex("inline_comment", r"(--)[^\n]*", SyntaxKind::InlineComment),
        Matcher::regex("equals", r"==|<=>|=", SyntaxKind::RawComparisonOperator),
        Matcher::regex("back_quote", r"`([^`]|``)*`", SyntaxKind::BackQuote),
        Matcher::legacy("numeric_literal", |s| s.starts_with(|ch: char| ch == '.' || ch == '-' || ch.is_ascii_alphanumeric()), r#"(?>(?>\d+\.\d+|\d+\.|\.\d+)([eE][+-]?\d+)?([dDfF]|BD|bd)?|\d+[eE][+-]?\d+([dDfF]|BD|bd)?|\d+([dDfFlLsSyY]|BD|bd)?)((?<=\.)|(?=\b))"#, SyntaxKind::NumericLiteral),
    ]);

    sparksql_dialect.insert_lexer_matchers(
        vec![
            Matcher::regex(
                "bytes_single_quote",
                r"X'([^'\\]|\\.)*'",
                SyntaxKind::BytesSingleQuote,
            ),
            Matcher::regex(
                "bytes_double_quote",
                r#"X"([^"\\]|\\.)*""#,
                SyntaxKind::BytesDoubleQuote,
            ),
        ],
        "single_quote",
    );

    sparksql_dialect.insert_lexer_matchers(
        vec![
            Matcher::regex(
                "bytes_single_quote",
                r"X'([^'\\]|\\.)*'",
                SyntaxKind::BytesSingleQuote,
            ),
            Matcher::regex(
                "bytes_double_quote",
                r#"X"([^"\\]|\\.)*""#,
                SyntaxKind::BytesDoubleQuote,
            ),
        ],
        "single_quote",
    );

    sparksql_dialect.insert_lexer_matchers(
        vec![Matcher::regex(
            "at_sign_literal",
            r"@\w*",
            SyntaxKind::AtSignLiteral,
        )],
        "word",
    );

    sparksql_dialect.insert_lexer_matchers(
        vec![
            Matcher::regex("file_literal", r#"[a-zA-Z0-9]*:?([a-zA-Z0-9\-_\.]*(/|\\)){2,}((([a-zA-Z0-9\-_\.]*(:|\?|=|&)[a-zA-Z0-9\-_\.]*)+)|([a-zA-Z0-9\-_\.]*\.[a-z]+))"#, SyntaxKind::FileLiteral),
        ],
        "newline",
    );

    sparksql_dialect.sets_mut("bare_functions").clear();
    sparksql_dialect.sets_mut("bare_functions").extend([
        "CURRENT_DATE",
        "CURRENT_TIMESTAMP",
        "CURRENT_USER",
    ]);

    sparksql_dialect.sets_mut("date_part_function_name").clear();
    sparksql_dialect
        .sets_mut("date_part_function_name")
        .extend([
            "DATE_ADD",
            "DATE_DIFF",
            "DATEADD",
            "DATEDIFF",
            "TIMESTAMPADD",
            "TIMESTAMPDIFF",
        ]);

    sparksql_dialect.sets_mut("datetime_units").clear();
    sparksql_dialect.sets_mut("datetime_units").extend([
        "YEAR",
        "YEARS",
        "YYYY",
        "YY",
        "QUARTER",
        "QUARTERS",
        "MONTH",
        "MONTHS",
        "MON",
        "MM",
        "WEEK",
        "WEEKS",
        "DAY",
        "DAYS",
        "DD",
        "HOUR",
        "HOURS",
        "MINUTE",
        "MINUTES",
        "SECOND",
        "SECONDS",
        "MILLISECOND",
        "MILLISECONDS",
        "MICROSECOND",
        "MICROSECONDS",
    ]);

    sparksql_dialect
        .sets_mut("unreserved_keywords")
        .extend(UNRESERVED_KEYWORDS);
    sparksql_dialect
        .sets_mut("reserved_keywords")
        .extend(RESERVED_KEYWORDS);

    sparksql_dialect.update_bracket_sets(
        "angle_bracket_pairs",
        vec![(
            "angle",
            "StartAngleBracketSegment",
            "EndAngleBracketSegment",
            false,
        )],
    );

    sparksql_dialect.add([
        (
            "SelectClauseTerminatorGrammar".into(),
            ansi::raw_dialect()
                .grammar("SelectClauseTerminatorGrammar")
                .copy(
                    Some(vec![
                        Sequence::new(vec![
                            Ref::keyword("CLUSTER").to_matchable(),
                            Ref::keyword("BY").to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("DISTRIBUTE").to_matchable(),
                            Ref::keyword("BY").to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("SORT").to_matchable(),
                            Ref::keyword("BY").to_matchable(),
                        ])
                        .to_matchable(),
                        Ref::keyword("QUALIFY").to_matchable(),
                    ]),
                    None,
                    None,
                    None,
                    Vec::new(),
                    false,
                )
                .into(),
        ),
        (
            "ComparisonOperatorGrammar".into(),
            one_of(vec![
                Ref::new("EqualsSegment").to_matchable(),
                Ref::new("EqualsSegment_a").to_matchable(),
                Ref::new("EqualsSegment_b").to_matchable(),
                Ref::new("GreaterThanSegment").to_matchable(),
                Ref::new("LessThanSegment").to_matchable(),
                Ref::new("GreaterThanOrEqualToSegment").to_matchable(),
                Ref::new("LessThanOrEqualToSegment").to_matchable(),
                Ref::new("NotEqualToSegment").to_matchable(),
                Ref::new("LikeOperatorSegment").to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("IS").to_matchable(),
                    Ref::keyword("DISTINCT").to_matchable(),
                    Ref::keyword("FROM").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("IS").to_matchable(),
                    Ref::keyword("NOT").to_matchable(),
                    Ref::keyword("DISTINCT").to_matchable(),
                    Ref::keyword("FROM").to_matchable(),
                ])
                .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "FromClauseTerminatorGrammar".into(),
            one_of(vec![
                Ref::keyword("WHERE").to_matchable(),
                Ref::keyword("LIMIT").to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("GROUP").to_matchable(),
                    Ref::keyword("BY").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("ORDER").to_matchable(),
                    Ref::keyword("BY").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("CLUSTER").to_matchable(),
                    Ref::keyword("BY").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("DISTRIBUTE").to_matchable(),
                    Ref::keyword("BY").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("SORT").to_matchable(),
                    Ref::keyword("BY").to_matchable(),
                ])
                .to_matchable(),
                Ref::keyword("HAVING").to_matchable(),
                Ref::keyword("QUALIFY").to_matchable(),
                Ref::new("SetOperatorSegment").to_matchable(),
                Ref::new("WithNoSchemaBindingClauseSegment").to_matchable(),
                Ref::new("WithDataClauseSegment").to_matchable(),
                Ref::keyword("KEYS").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "TemporaryGrammar".into(),
            Sequence::new(vec![
                Sequence::new(vec![Ref::keyword("GLOBAL").to_matchable()])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                one_of(vec![
                    Ref::keyword("TEMP").to_matchable(),
                    Ref::keyword("TEMPORARY").to_matchable(),
                ])
                .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "QuotedLiteralSegment".into(),
            one_of(vec![
                TypedParser::new(SyntaxKind::SingleQuote, SyntaxKind::QuotedLiteral).to_matchable(),
                TypedParser::new(SyntaxKind::DoubleQuote, SyntaxKind::QuotedLiteral).to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "LiteralGrammar".into(),
            sparksql_dialect
                .grammar("LiteralGrammar")
                .copy(
                    Some(vec![Ref::new("BytesQuotedLiteralSegment").to_matchable()]),
                    None,
                    None,
                    None,
                    Vec::new(),
                    false,
                )
                .into(),
        ),
        (
            "NaturalJoinKeywordsGrammar".into(),
            Sequence::new(vec![
                Ref::keyword("NATURAL").to_matchable(),
                Ref::new("JoinTypeKeywords").optional().to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "LikeGrammar".into(),
            one_of(vec![
                Sequence::new(vec![
                    one_of(vec![
                        Ref::keyword("LIKE").to_matchable(),
                        Ref::keyword("ILIKE").to_matchable(),
                    ])
                    .to_matchable(),
                    one_of(vec![
                        Ref::keyword("ALL").to_matchable(),
                        Ref::keyword("ANY").to_matchable(),
                        Ref::keyword("SOME").to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                ])
                .to_matchable(),
                Ref::keyword("RLIKE").to_matchable(),
                Ref::keyword("REGEXP").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "SingleIdentifierGrammar".into(),
            one_of(vec![
                Ref::new("NakedIdentifierSegment").to_matchable(),
                Ref::new("QuotedIdentifierSegment").to_matchable(),
                Ref::new("SingleQuotedIdentifierSegment").to_matchable(),
                Ref::new("BackQuotedIdentifierSegment").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "WhereClauseTerminatorGrammar".into(),
            one_of(vec![
                Ref::keyword("LIMIT").to_matchable(),
                Sequence::new(vec![
                    one_of(vec![
                        Ref::keyword("CLUSTER").to_matchable(),
                        Ref::keyword("DISTRIBUTE").to_matchable(),
                        Ref::keyword("GROUP").to_matchable(),
                        Ref::keyword("ORDER").to_matchable(),
                        Ref::keyword("SORT").to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::keyword("BY").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("ORDER").to_matchable(),
                    Ref::keyword("BY").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("DISTRIBUTE").to_matchable(),
                    Ref::keyword("BY").to_matchable(),
                ])
                .to_matchable(),
                Ref::keyword("HAVING").to_matchable(),
                Ref::keyword("QUALIFY").to_matchable(),
                Ref::keyword("WINDOW").to_matchable(),
                Ref::keyword("OVERLAPS").to_matchable(),
                Ref::keyword("APPLY").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "GroupByClauseTerminatorGrammar".into(),
            one_of(vec![
                Sequence::new(vec![
                    one_of(vec![
                        Ref::keyword("ORDER").to_matchable(),
                        Ref::keyword("DISTRIBUTE").to_matchable(),
                        Ref::keyword("CLUSTER").to_matchable(),
                        Ref::keyword("SORT").to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::keyword("BY").to_matchable(),
                ])
                .to_matchable(),
                Ref::keyword("LIMIT").to_matchable(),
                Ref::keyword("HAVING").to_matchable(),
                Ref::keyword("WINDOW").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "HavingClauseTerminatorGrammar".into(),
            one_of(vec![
                Sequence::new(vec![
                    one_of(vec![
                        Ref::keyword("ORDER").to_matchable(),
                        Ref::keyword("CLUSTER").to_matchable(),
                        Ref::keyword("DISTRIBUTE").to_matchable(),
                        Ref::keyword("SORT").to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::keyword("BY").to_matchable(),
                ])
                .to_matchable(),
                Ref::keyword("LIMIT").to_matchable(),
                Ref::keyword("QUALIFY").to_matchable(),
                Ref::keyword("WINDOW").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "ArithmeticBinaryOperatorGrammar".into(),
            one_of(vec![
                Ref::new("PlusSegment").to_matchable(),
                Ref::new("MinusSegment").to_matchable(),
                Ref::new("DivideSegment").to_matchable(),
                Ref::new("MultiplySegment").to_matchable(),
                Ref::new("ModuloSegment").to_matchable(),
                Ref::new("BitwiseAndSegment").to_matchable(),
                Ref::new("BitwiseOrSegment").to_matchable(),
                Ref::new("BitwiseXorSegment").to_matchable(),
                Ref::new("BitwiseLShiftSegment").to_matchable(),
                Ref::new("BitwiseRShiftSegment").to_matchable(),
                Ref::new("DivBinaryOperatorSegment").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "BinaryOperatorGrammar".into(),
            one_of(vec![
                Ref::new("ArithmeticBinaryOperatorGrammar").to_matchable(),
                Ref::new("StringBinaryOperatorGrammar").to_matchable(),
                Ref::new("BooleanBinaryOperatorGrammar").to_matchable(),
                Ref::new("ComparisonOperatorGrammar").to_matchable(),
                Ref::new("RightArrowOperator").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "AccessorGrammar".into(),
            AnyNumberOf::new(vec![
                Ref::new("ArrayAccessorSegment").to_matchable(),
                Ref::new("SemiStructuredAccessorSegment").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "ObjectReferenceTerminatorGrammar".into(),
            one_of(vec![
                Ref::keyword("ON").to_matchable(),
                Ref::keyword("AS").to_matchable(),
                Ref::keyword("USING").to_matchable(),
                Ref::new("CommaSegment").to_matchable(),
                Ref::new("CastOperatorSegment").to_matchable(),
                Ref::new("StartSquareBracketSegment").to_matchable(),
                Ref::new("StartBracketSegment").to_matchable(),
                Ref::new("BinaryOperatorGrammar").to_matchable(),
                Ref::new("DelimiterGrammar").to_matchable(),
                Ref::new("JoinLikeClauseGrammar").to_matchable(),
                BracketedSegmentMatcher::new().to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "FunctionContentsExpressionGrammar".into(),
            one_of(vec![
                Ref::new("ExpressionSegment").to_matchable(),
                Ref::new("StarSegment").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            // An `IDENTIFIER` clause segment.
            // https://docs.databricks.com/en/sql/language-manual/sql-ref-names-identifier-clause.html
            "IdentifierClauseSegment".into(),
            Sequence::new(vec![
                Ref::keyword("IDENTIFIER").to_matchable(),
                Bracketed::new(vec![Ref::new("ExpressionSegment").to_matchable()]).to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
    ]);

    sparksql_dialect.add([
        (
            "FileLiteralSegment".into(),
            TypedParser::new(SyntaxKind::FileLiteral, SyntaxKind::FileLiteral)
                .to_matchable()
                .into(),
        ),
        (
            "BackQuotedIdentifierSegment".into(),
            TypedParser::new(SyntaxKind::BackQuote, SyntaxKind::QuotedIdentifier)
                .to_matchable()
                .into(),
        ),
        (
            "NakedSemiStructuredElementSegment".into(),
            RegexParser::new("[A-Z0-9_]*", SyntaxKind::SemiStructuredElement)
                .to_matchable()
                .into(),
        ),
        (
            "QuotedSemiStructuredElementSegment".into(),
            TypedParser::new(SyntaxKind::SingleQuote, SyntaxKind::SemiStructuredElement)
                .to_matchable()
                .into(),
        ),
        (
            "RightArrowOperator".into(),
            StringParser::new("->", SyntaxKind::BinaryOperator)
                .to_matchable()
                .into(),
        ),
        (
            "BINARYFILE".into(),
            StringParser::new("BINARYFILE", SyntaxKind::FileFormat)
                .to_matchable()
                .into(),
        ),
        (
            "JSONFILE".into(),
            StringParser::new("JSONFILE", SyntaxKind::FileFormat)
                .to_matchable()
                .into(),
        ),
        (
            "RCFILE".into(),
            StringParser::new("RCFILE", SyntaxKind::FileFormat)
                .to_matchable()
                .into(),
        ),
        (
            "SEQUENCEFILE".into(),
            StringParser::new("SEQUENCEFILE", SyntaxKind::FileFormat)
                .to_matchable()
                .into(),
        ),
        (
            "TEXTFILE".into(),
            StringParser::new("TEXTFILE", SyntaxKind::FileFormat)
                .to_matchable()
                .into(),
        ),
        (
            "StartAngleBracketSegment".into(),
            StringParser::new("<", SyntaxKind::StartAngleBracket)
                .to_matchable()
                .into(),
        ),
        (
            "EndAngleBracketSegment".into(),
            StringParser::new(">", SyntaxKind::EndAngleBracket)
                .to_matchable()
                .into(),
        ),
        (
            "EqualsSegment_a".into(),
            StringParser::new("==", SyntaxKind::ComparisonOperator)
                .to_matchable()
                .into(),
        ),
        (
            "EqualsSegment_b".into(),
            StringParser::new("<=>", SyntaxKind::ComparisonOperator)
                .to_matchable()
                .into(),
        ),
        (
            "FILE".into(),
            MultiStringParser::new(vec!["FILE".into(), "FILES".into()], SyntaxKind::FileKeyword)
                .to_matchable()
                .into(),
        ),
        (
            "JAR".into(),
            MultiStringParser::new(vec!["JAR".into(), "JARS".into()], SyntaxKind::FileKeyword)
                .to_matchable()
                .into(),
        ),
        (
            "NOSCAN".into(),
            StringParser::new("NOSCAN", SyntaxKind::Keyword)
                .to_matchable()
                .into(),
        ),
        (
            "WHL".into(),
            StringParser::new("WHL", SyntaxKind::FileKeyword)
                .to_matchable()
                .into(),
        ),
        (
            "CommentGrammar".into(),
            hive_dialect.grammar("CommentGrammar").into(),
        ),
        (
            "LocationGrammar".into(),
            hive_dialect.grammar("LocationGrammar").into(),
        ),
        (
            "SerdePropertiesGrammar".into(),
            hive_dialect.grammar("SerdePropertiesGrammar").into(),
        ),
        (
            "StoredAsGrammar".into(),
            hive_dialect.grammar("StoredAsGrammar").into(),
        ),
        (
            "StoredByGrammar".into(),
            hive_dialect.grammar("StoredByGrammar").into(),
        ),
        (
            "StorageFormatGrammar".into(),
            hive_dialect.grammar("StorageFormatGrammar").into(),
        ),
        (
            "TerminatedByGrammar".into(),
            hive_dialect.grammar("TerminatedByGrammar").into(),
        ),
        (
            "PropertyGrammar".into(),
            Sequence::new(vec![
                Ref::new("PropertyNameSegment").to_matchable(),
                Ref::new("EqualsSegment").optional().to_matchable(),
                one_of(vec![
                    Ref::new("LiteralGrammar").to_matchable(),
                    // when property value is Java Class Name
                    Delimited::new(vec![
                        Ref::new("PropertiesNakedIdentifierSegment").to_matchable(),
                    ])
                    .config(|config| {
                        config.delimiter(Ref::new("DotSegment"));
                    })
                    .to_matchable(),
                ])
                .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "PropertyNameListGrammar".into(),
            Delimited::new(vec![Ref::new("PropertyNameSegment").to_matchable()])
                .to_matchable()
                .into(),
        ),
        (
            "BracketedPropertyNameListGrammar".into(),
            Bracketed::new(vec![Ref::new("PropertyNameListGrammar").to_matchable()])
                .to_matchable()
                .into(),
        ),
        (
            "PropertyListGrammar".into(),
            Delimited::new(vec![Ref::new("PropertyGrammar").to_matchable()])
                .to_matchable()
                .into(),
        ),
        (
            "BracketedPropertyListGrammar".into(),
            Bracketed::new(vec![Ref::new("PropertyListGrammar").to_matchable()])
                .to_matchable()
                .into(),
        ),
        (
            "OptionsGrammar".into(),
            Sequence::new(vec![
                Ref::keyword("OPTIONS").to_matchable(),
                Ref::new("BracketedPropertyListGrammar").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "BucketSpecGrammar".into(),
            Sequence::new(vec![
                Ref::new("ClusteredBySpecGrammar").to_matchable(),
                Ref::new("SortedBySpecGrammar").optional().to_matchable(),
                Ref::keyword("INTO").to_matchable(),
                Ref::new("NumericLiteralSegment").to_matchable(),
                Ref::keyword("BUCKETS").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "ClusteredBySpecGrammar".into(),
            Sequence::new(vec![
                Ref::keyword("CLUSTERED").to_matchable(),
                Ref::keyword("BY").to_matchable(),
                Ref::new("BracketedColumnReferenceListGrammar").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "DatabasePropertiesGrammar".into(),
            Sequence::new(vec![
                Ref::keyword("DBPROPERTIES").to_matchable(),
                Ref::new("BracketedPropertyListGrammar").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "DataSourcesV2FileTypeGrammar".into(),
            one_of(vec![
                Ref::keyword("AVRO").to_matchable(),
                Ref::keyword("CSV").to_matchable(),
                Ref::keyword("JSON").to_matchable(),
                Ref::keyword("PARQUET").to_matchable(),
                Ref::keyword("ORC").to_matchable(),
                Ref::keyword("DELTA").to_matchable(),
                Ref::keyword("CSV").to_matchable(),
                Ref::keyword("ICEBERG").to_matchable(),
                Ref::keyword("TEXT").to_matchable(),
                Ref::keyword("BINARYFILE").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "FileFormatGrammar".into(),
            one_of(vec![
                Ref::new("DataSourcesV2FileTypeGrammar").to_matchable(),
                Ref::keyword("SEQUENCEFILE").to_matchable(),
                Ref::keyword("TEXTFILE").to_matchable(),
                Ref::keyword("RCFILE").to_matchable(),
                Ref::keyword("JSONFILE").to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("INPUTFORMAT").to_matchable(),
                    Ref::new("QuotedLiteralSegment").to_matchable(),
                    Ref::keyword("OUTPUTFORMAT").to_matchable(),
                    Ref::new("QuotedLiteralSegment").to_matchable(),
                ])
                .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "TimestampAsOfGrammar".into(),
            Sequence::new(vec![
                Ref::keyword("TIMESTAMP").to_matchable(),
                Ref::keyword("AS").to_matchable(),
                Ref::keyword("OF").to_matchable(),
                one_of(vec![
                    Ref::new("QuotedLiteralSegment").to_matchable(),
                    Ref::new("BareFunctionSegment").to_matchable(),
                    Ref::new("FunctionSegment").to_matchable(),
                ])
                .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "VersionAsOfGrammar".into(),
            Sequence::new(vec![
                Ref::keyword("VERSION").to_matchable(),
                Ref::keyword("AS").to_matchable(),
                Ref::keyword("OF").to_matchable(),
                Ref::new("NumericLiteralSegment").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "StartHintSegment".into(),
            StringParser::new("/*+", SyntaxKind::StartHint)
                .to_matchable()
                .into(),
        ),
        (
            "EndHintSegment".into(),
            StringParser::new("*/", SyntaxKind::EndHint)
                .to_matchable()
                .into(),
        ),
        (
            "PartitionSpecGrammar".into(),
            Sequence::new(vec![
                one_of(vec![
                    Ref::keyword("PARTITION").to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("PARTITIONED").to_matchable(),
                        Ref::keyword("BY").to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable(),
                Bracketed::new(vec![
                    Delimited::new(vec![
                        one_of(vec![
                            Ref::new("ColumnDefinitionSegment").to_matchable(),
                            Sequence::new(vec![
                                Ref::new("ColumnReferenceSegment").to_matchable(),
                                Ref::new("EqualsSegment").optional().to_matchable(),
                                Ref::new("LiteralGrammar").optional().to_matchable(),
                                Ref::new("CommentGrammar").optional().to_matchable(),
                            ])
                            .to_matchable(),
                            Ref::new("IcebergTransformationSegment")
                                .optional()
                                .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "PartitionFieldGrammar".into(),
            Sequence::new(vec![
                Ref::keyword("PARTITION").to_matchable(),
                Ref::keyword("FIELD").to_matchable(),
                Delimited::new(vec![
                    one_of(vec![
                        Ref::new("ColumnDefinitionSegment").to_matchable(),
                        Sequence::new(vec![
                            Ref::new("ColumnReferenceSegment").to_matchable(),
                            Ref::new("EqualsSegment").optional().to_matchable(),
                            Ref::new("LiteralGrammar").optional().to_matchable(),
                            Ref::new("CommentGrammar").optional().to_matchable(),
                        ])
                        .to_matchable(),
                        Ref::new("IcebergTransformationSegment")
                            .optional()
                            .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("WITH").optional().to_matchable(),
                    Delimited::new(vec![
                        one_of(vec![
                            Ref::new("ColumnDefinitionSegment").to_matchable(),
                            Sequence::new(vec![
                                Ref::new("ColumnReferenceSegment").to_matchable(),
                                Ref::new("EqualsSegment").optional().to_matchable(),
                                Ref::new("LiteralGrammar").optional().to_matchable(),
                                Ref::new("CommentGrammar").optional().to_matchable(),
                            ])
                            .to_matchable(),
                            Ref::new("IcebergTransformationSegment")
                                .optional()
                                .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .config(|config| {
                    config.optional();
                })
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("AS").to_matchable(),
                    Ref::new("NakedIdentifierSegment").to_matchable(),
                ])
                .config(|config| {
                    config.optional();
                })
                .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "PropertiesNakedIdentifierSegment".into(),
            RegexParser::new(
                "[A-Z0-9]*[A-Z][A-Z0-9]*",
                SyntaxKind::PropertiesNakedIdentifier,
            )
            .to_matchable()
            .into(),
        ),
        (
            "ResourceFileGrammar".into(),
            one_of(vec![
                Ref::new("JAR").to_matchable(),
                Ref::new("WHL").to_matchable(),
                Ref::new("FILE").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "ResourceLocationGrammar".into(),
            Sequence::new(vec![
                Ref::keyword("USING").to_matchable(),
                Ref::new("ResourceFileGrammar").to_matchable(),
                Ref::new("QuotedLiteralSegment").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "SortedBySpecGrammar".into(),
            Sequence::new(vec![
                Ref::keyword("SORTED").to_matchable(),
                Ref::keyword("BY").to_matchable(),
                Bracketed::new(vec![
                    Delimited::new(vec![
                        Sequence::new(vec![
                            Ref::new("ColumnReferenceSegment").to_matchable(),
                            one_of(vec![
                                Ref::keyword("ASC").to_matchable(),
                                Ref::keyword("DESC").to_matchable(),
                            ])
                            .config(|config| {
                                config.optional();
                            })
                            .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable(),
            ])
            .config(|config| {
                config.optional();
            })
            .to_matchable()
            .into(),
        ),
        (
            "UnsetTablePropertiesGrammar".into(),
            Sequence::new(vec![
                Ref::keyword("UNSET").to_matchable(),
                Ref::keyword("TBLPROPERTIES").to_matchable(),
                Ref::new("IfExistsGrammar").optional().to_matchable(),
                Ref::new("BracketedPropertyNameListGrammar").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "TablePropertiesGrammar".into(),
            Sequence::new(vec![
                Ref::keyword("TBLPROPERTIES").to_matchable(),
                Ref::new("BracketedPropertyListGrammar").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "BytesQuotedLiteralSegment".into(),
            one_of(vec![
                TypedParser::new(SyntaxKind::BytesSingleQuote, SyntaxKind::BytesQuotedLiteral)
                    .to_matchable(),
                TypedParser::new(SyntaxKind::BytesDoubleQuote, SyntaxKind::BytesQuotedLiteral)
                    .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "JoinTypeKeywords".into(),
            one_of(vec![
                Ref::keyword("CROSS").to_matchable(),
                Ref::keyword("INNER").to_matchable(),
                Sequence::new(vec![
                    one_of(vec![
                        Ref::keyword("FULL").to_matchable(),
                        Ref::keyword("LEFT").to_matchable(),
                        Ref::keyword("RIGHT").to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::keyword("OUTER").optional().to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("LEFT").optional().to_matchable(),
                    Ref::keyword("SEMI").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("LEFT").optional().to_matchable(),
                    Ref::keyword("ANTI").to_matchable(),
                ])
                .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "AtSignLiteralSegment".into(),
            TypedParser::new(SyntaxKind::AtSignLiteral, SyntaxKind::AtSignLiteral)
                .to_matchable()
                .into(),
        ),
        (
            "SignedQuotedLiteralSegment".into(),
            one_of(vec![
                TypedParser::new(SyntaxKind::SingleQuote, SyntaxKind::SignedQuotedLiteral)
                    .to_matchable(),
                TypedParser::new(SyntaxKind::DoubleQuote, SyntaxKind::SignedQuotedLiteral)
                    .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "OrRefreshGrammar".into(),
            Sequence::new(vec![
                Ref::keyword("OR").to_matchable(),
                Ref::keyword("REFRESH").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "WidgetNameIdentifierSegment".into(),
            RegexParser::new("[A-Z][A-Z0-9_]*", SyntaxKind::WidgetNameIdentifier)
                .to_matchable()
                .into(),
        ),
        (
            "WidgetDefaultGrammar".into(),
            Sequence::new(vec![
                Ref::keyword("DEFAULT").to_matchable(),
                Ref::new("QuotedLiteralSegment").to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "TableDefinitionSegment".into(),
            Sequence::new(vec![
                one_of(vec![
                    Ref::new("OrReplaceGrammar").to_matchable(),
                    Ref::new("OrRefreshGrammar").to_matchable(),
                ])
                .config(|config| {
                    config.optional();
                })
                .to_matchable(),
                Ref::new("TemporaryGrammar").optional().to_matchable(),
                Ref::keyword("EXTERNAL").optional().to_matchable(),
                Ref::keyword("STREAMING").optional().to_matchable(),
                Ref::keyword("LIVE").optional().to_matchable(),
                Ref::keyword("TABLE").to_matchable(),
                Ref::new("IfNotExistsGrammar").optional().to_matchable(),
                one_of(vec![
                    Ref::new("FileReferenceSegment").to_matchable(),
                    Ref::new("TableReferenceSegment").to_matchable(),
                ])
                .to_matchable(),
                one_of(vec![
                    Bracketed::new(vec![
                        Delimited::new(vec![
                            Sequence::new(vec![
                                one_of(vec![
                                    Ref::new("ColumnDefinitionSegment").to_matchable(),
                                    Ref::new("GeneratedColumnDefinitionSegment").to_matchable(),
                                ])
                                .to_matchable(),
                                Ref::new("CommentGrammar").optional().to_matchable(),
                            ])
                            .to_matchable(),
                            Ref::new("ConstraintStatementSegment")
                                .optional()
                                .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("LIKE").to_matchable(),
                        one_of(vec![
                            Ref::new("FileReferenceSegment").to_matchable(),
                            Ref::new("TableReferenceSegment").to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .config(|config| {
                    config.optional();
                })
                .to_matchable(),
                Ref::new("UsingClauseSegment").optional().to_matchable(),
                any_set_of(vec![
                    Ref::new("RowFormatClauseSegment").to_matchable(),
                    Ref::new("StoredAsGrammar").to_matchable(),
                    Ref::new("CommentGrammar").to_matchable(),
                    Ref::new("OptionsGrammar").to_matchable(),
                    Ref::new("PartitionSpecGrammar").to_matchable(),
                    Ref::new("BucketSpecGrammar").to_matchable(),
                    Ref::new("LocationGrammar").to_matchable(),
                    Ref::new("CommentGrammar").to_matchable(),
                    Ref::new("TablePropertiesGrammar").to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("CLUSTER").to_matchable(),
                        Ref::keyword("BY").to_matchable(),
                        Ref::new("BracketedColumnReferenceListGrammar").to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .config(|config| {
                    config.optional();
                })
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("AS").optional().to_matchable(),
                    optionally_bracketed(vec![Ref::new("SelectableGrammar").to_matchable()])
                        .to_matchable(),
                ])
                .config(|config| {
                    config.optional();
                })
                .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
    ]);

    sparksql_dialect.insert_lexer_matchers(
        vec![Matcher::legacy(
            "start_hint",
            |s| s.starts_with("/*+"),
            r"\/\*\+",
            SyntaxKind::StartHint,
        )],
        "block_comment",
    );

    sparksql_dialect.insert_lexer_matchers(
        vec![Matcher::regex("end_hint", r"\*\/", SyntaxKind::EndHint)],
        "single_quote",
    );

    sparksql_dialect.insert_lexer_matchers(
        vec![Matcher::string("end_hint", r"->", SyntaxKind::RightArrow)],
        "like_operator",
    );

    sparksql_dialect.add([
        (
            "SQLConfPropertiesSegment".into(),
            NodeMatcher::new(SyntaxKind::SqlConfOption, |_| {
                Sequence::new(vec![
                    StringParser::new("-", SyntaxKind::Dash).to_matchable(),
                    StringParser::new("v", SyntaxKind::SqlConfOption).to_matchable(),
                ])
                .config(|config| {
                    config.disallow_gaps();
                })
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "DivBinaryOperatorSegment".into(),
            NodeMatcher::new(SyntaxKind::BinaryOperator, |_| {
                Ref::keyword("DIV").to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "QualifyClauseSegment".into(),
            NodeMatcher::new(SyntaxKind::QualifyClause, |_| {
                Sequence::new(vec![
                    Ref::keyword("QUALIFY").to_matchable(),
                    MetaSegment::indent().to_matchable(),
                    optionally_bracketed(vec![Ref::new("ExpressionSegment").to_matchable()])
                        .to_matchable(),
                    MetaSegment::dedent().to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
    ]);

    sparksql_dialect.add([
        (
            "PrimitiveTypeSegment".into(),
            NodeMatcher::new(SyntaxKind::PrimitiveType, |_| {
                one_of(vec![
                    Ref::keyword("BOOLEAN").to_matchable(),
                    Ref::keyword("TINYINT").to_matchable(),
                    Ref::keyword("LONG").to_matchable(),
                    Ref::keyword("SMALLINT").to_matchable(),
                    Ref::keyword("INT").to_matchable(),
                    Ref::keyword("INTEGER").to_matchable(),
                    Ref::keyword("BIGINT").to_matchable(),
                    Ref::keyword("FLOAT").to_matchable(),
                    Ref::keyword("REAL").to_matchable(),
                    Ref::keyword("DOUBLE").to_matchable(),
                    Ref::keyword("DATE").to_matchable(),
                    Ref::keyword("TIMESTAMP").to_matchable(),
                    Ref::keyword("TIMESTAMP_LTZ").to_matchable(),
                    Ref::keyword("TIMESTAMP_NTZ").to_matchable(),
                    Ref::keyword("STRING").to_matchable(),
                    Sequence::new(vec![
                        one_of(vec![
                            Ref::keyword("CHAR").to_matchable(),
                            Ref::keyword("CHARACTER").to_matchable(),
                            Ref::keyword("VARCHAR").to_matchable(),
                            Ref::keyword("DECIMAL").to_matchable(),
                            Ref::keyword("DEC").to_matchable(),
                            Ref::keyword("NUMERIC").to_matchable(),
                        ])
                        .to_matchable(),
                        Ref::new("BracketedArguments").optional().to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::keyword("BINARY").to_matchable(),
                    Ref::keyword("INTERVAL").to_matchable(),
                    Ref::keyword("VARIANT").to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "ArrayTypeSegment".into(),
            hive_dialect.grammar("ArrayTypeSegment").into(),
        ),
        (
            "StructTypeSegment".into(),
            hive_dialect.grammar("StructTypeSegment").into(),
        ),
        (
            "StructTypeSchemaSegment".into(),
            hive_dialect.grammar("StructTypeSchemaSegment").into(),
        ),
    ]);

    sparksql_dialect.add([
        (
            "SemiStructuredAccessorSegment".into(),
            NodeMatcher::new(SyntaxKind::SemiStructuredExpression, |_| {
                Sequence::new(vec![
                    Ref::new("ColonSegment").to_matchable(),
                    one_of(vec![
                        Ref::new("NakedSemiStructuredElementSegment").to_matchable(),
                        Bracketed::new(vec![
                            Ref::new("QuotedSemiStructuredElementSegment").to_matchable(),
                        ])
                        .config(|config| {
                            config.bracket_type = "square";
                        })
                        .to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::new("ArrayAccessorSegment").optional().to_matchable(),
                    AnyNumberOf::new(vec![
                        Sequence::new(vec![
                            one_of(vec![
                                Ref::new("DotSegment").to_matchable(),
                                Ref::new("ColonSegment").to_matchable(),
                            ])
                            .to_matchable(),
                            one_of(vec![
                                Ref::new("NakedSemiStructuredElementSegment").to_matchable(),
                                Bracketed::new(vec![
                                    Ref::new("QuotedSemiStructuredElementSegment").to_matchable(),
                                ])
                                .config(|config| {
                                    config.bracket_type = "square";
                                })
                                .to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                        Ref::new("ArrayAccessorSegment").optional().to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "DatatypeSegment".into(),
            NodeMatcher::new(SyntaxKind::DataType, |_| {
                one_of(vec![
                    Ref::new("PrimitiveTypeSegment").to_matchable(),
                    Ref::new("ArrayTypeSegment").to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("MAP").to_matchable(),
                        Bracketed::new(vec![
                            Sequence::new(vec![
                                Ref::new("DatatypeSegment").to_matchable(),
                                Ref::new("CommaSegment").to_matchable(),
                                Ref::new("DatatypeSegment").to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .config(|config| {
                            config.bracket_pairs_set = "angle_bracket_pairs";
                            config.bracket_type = "angle";
                        })
                        .to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::new("StructTypeSegment").to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            // An `ALTER DATABASE/SCHEMA` statement.
            // http://spark.apache.org/docs/latest/sql-ref-syntax-ddl-alter-database.html
            "AlterDatabaseStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::AlterDatabaseStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("ALTER").to_matchable(),
                    one_of(vec![
                        Ref::keyword("DATABASE").to_matchable(),
                        Ref::keyword("SCHEMA").to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::new("DatabaseReferenceSegment").to_matchable(),
                    Ref::keyword("SET").to_matchable(),
                    one_of(vec![
                        Ref::new("DatabasePropertiesGrammar").to_matchable(),
                        Ref::new("LocationGrammar").to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            // A `SET VARIABLE` statement used to set session variables.
            // https://spark.apache.org/docs/4.0.0-preview2/sql-ref-syntax-aux-set-var.html
            "SetVariableStatementSegment".into(),
            Sequence::new(vec![
                Ref::keyword("SET").to_matchable(),
                one_of(vec![
                    Ref::keyword("VAR").to_matchable(),
                    Ref::keyword("VARIABLE").to_matchable(),
                ])
                .to_matchable(),
                one_of(vec![
                    Bracketed::new(vec![
                        Delimited::new(vec![Ref::new("SingleIdentifierGrammar").to_matchable()])
                            .to_matchable(),
                    ])
                    .to_matchable(),
                    Delimited::new(vec![Ref::new("SingleIdentifierGrammar").to_matchable()])
                        .to_matchable(),
                ])
                .to_matchable(),
                Ref::new("EqualsSegment").to_matchable(),
                one_of(vec![
                    Ref::keyword("DEFAULT").to_matchable(),
                    Ref::new("ExpressionSegment").to_matchable(),
                    Bracketed::new(vec![Ref::new("ExpressionSegment").to_matchable()])
                        .to_matchable(),
                ])
                .to_matchable(),
            ])
            .allow_gaps(true)
            .to_matchable()
            .into(),
        ),
    ]);

    sparksql_dialect.replace_grammar(
        "AlterTableStatementSegment",
        Sequence::new(vec![
            Ref::keyword("ALTER").to_matchable(),
            Ref::keyword("TABLE").to_matchable(),
            Ref::new("TableReferenceSegment").to_matchable(),
            MetaSegment::indent().to_matchable(),
            one_of(vec![
                Sequence::new(vec![
                    Ref::keyword("RENAME").to_matchable(),
                    Ref::keyword("TO").to_matchable(),
                    Ref::new("TableReferenceSegment").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::new("PartitionSpecGrammar").to_matchable(),
                    Ref::keyword("RENAME").to_matchable(),
                    Ref::keyword("TO").to_matchable(),
                    Ref::new("PartitionSpecGrammar").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("RENAME").to_matchable(),
                    Ref::keyword("COLUMN").to_matchable(),
                    Ref::new("ColumnReferenceSegment").to_matchable(),
                    Ref::keyword("TO").to_matchable(),
                    Ref::new("ColumnReferenceSegment").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("ADD").to_matchable(),
                    one_of(vec![
                        Ref::keyword("COLUMNS").to_matchable(),
                        Ref::keyword("COLUMN").to_matchable(),
                    ])
                    .to_matchable(),
                    MetaSegment::indent().to_matchable(),
                    optionally_bracketed(vec![
                        Delimited::new(vec![
                            Sequence::new(vec![
                                Ref::new("ColumnFieldDefinitionSegment").to_matchable(),
                                one_of(vec![
                                    Ref::keyword("FIRST").to_matchable(),
                                    Sequence::new(vec![
                                        Ref::keyword("AFTER").to_matchable(),
                                        Ref::new("ColumnReferenceSegment").to_matchable(),
                                    ])
                                    .to_matchable(),
                                ])
                                .config(|config| {
                                    config.optional();
                                })
                                .to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                    MetaSegment::dedent().to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    one_of(vec![
                        Ref::keyword("ALTER").to_matchable(),
                        Ref::keyword("CHANGE").to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::keyword("COLUMN").optional().to_matchable(),
                    MetaSegment::indent().to_matchable(),
                    AnyNumberOf::new(vec![
                        Ref::new("ColumnReferenceSegment")
                            .exclude(one_of(vec![
                                Ref::keyword("COMMENT").to_matchable(),
                                Ref::keyword("TYPE").to_matchable(),
                                Ref::new("DatatypeSegment").to_matchable(),
                                Ref::keyword("FIRST").to_matchable(),
                                Ref::keyword("AFTER").to_matchable(),
                                Ref::keyword("SET").to_matchable(),
                                Ref::keyword("DROP").to_matchable(),
                            ]))
                            .config(|config| {
                                config.exclude = one_of(vec![
                                    Ref::keyword("COMMENT").to_matchable(),
                                    Ref::keyword("TYPE").to_matchable(),
                                    Ref::new("DatatypeSegment").to_matchable(),
                                    Ref::keyword("FIRST").to_matchable(),
                                    Ref::keyword("AFTER").to_matchable(),
                                    Ref::keyword("SET").to_matchable(),
                                    Ref::keyword("DROP").to_matchable(),
                                ])
                                .to_matchable()
                                .into();
                            })
                            .to_matchable(),
                    ])
                    .config(|config| {
                        config.max_times = Some(2);
                    })
                    .to_matchable(),
                    Ref::keyword("TYPE").optional().to_matchable(),
                    Ref::new("DatatypeSegment").optional().to_matchable(),
                    Ref::new("CommentGrammar").optional().to_matchable(),
                    one_of(vec![
                        Ref::keyword("FIRST").to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("AFTER").to_matchable(),
                            Ref::new("ColumnReferenceSegment").to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                    Sequence::new(vec![
                        one_of(vec![
                            Ref::keyword("SET").to_matchable(),
                            Ref::keyword("DROP").to_matchable(),
                        ])
                        .to_matchable(),
                        Ref::keyword("NOT").to_matchable(),
                        Ref::keyword("NULL").to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                    MetaSegment::dedent().to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("REPLACE").to_matchable(),
                    Ref::keyword("COLUMNS").to_matchable(),
                    Bracketed::new(vec![
                        Delimited::new(vec![
                            Sequence::new(vec![
                                Ref::new("ColumnDefinitionSegment").to_matchable(),
                                Ref::new("CommentGrammar").optional().to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("DROP").to_matchable(),
                    one_of(vec![
                        Sequence::new(vec![
                            Ref::keyword("COLUMN").to_matchable(),
                            Ref::new("ColumnReferenceSegment").to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("COLUMNS").to_matchable(),
                            Bracketed::new(vec![
                                Delimited::new(vec![
                                    AnyNumberOf::new(vec![
                                        Ref::new("ColumnReferenceSegment").to_matchable(),
                                    ])
                                    .to_matchable(),
                                ])
                                .to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("ADD").to_matchable(),
                    Ref::new("IfNotExistsGrammar").optional().to_matchable(),
                    AnyNumberOf::new(vec![
                        Ref::new("PartitionSpecGrammar").to_matchable(),
                        Ref::new("PartitionFieldGrammar").to_matchable(),
                    ])
                    .config(|config| {
                        config.min_times = 1;
                    })
                    .to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("DROP").to_matchable(),
                    Ref::new("IfExistsGrammar").optional().to_matchable(),
                    one_of(vec![
                        Ref::new("PartitionSpecGrammar").to_matchable(),
                        Ref::new("PartitionFieldGrammar").to_matchable(),
                    ])
                    .to_matchable(),
                    Sequence::new(vec![Ref::keyword("PURGE").to_matchable()])
                        .config(|config| {
                            config.optional();
                        })
                        .to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("REPLACE").to_matchable(),
                    Ref::new("PartitionFieldGrammar").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("RECOVER").to_matchable(),
                    Ref::keyword("PARTITIONS").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("SET").to_matchable(),
                    Ref::new("TablePropertiesGrammar").to_matchable(),
                ])
                .to_matchable(),
                Ref::new("UnsetTablePropertiesGrammar").to_matchable(),
                Sequence::new(vec![
                    Ref::new("PartitionSpecGrammar").optional().to_matchable(),
                    Ref::keyword("SET").to_matchable(),
                    one_of(vec![
                        Sequence::new(vec![
                            Ref::keyword("SERDEPROPERTIES").to_matchable(),
                            Ref::new("BracketedPropertyListGrammar").to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("SERDE").to_matchable(),
                            Ref::new("QuotedLiteralSegment").to_matchable(),
                            Ref::new("SerdePropertiesGrammar").optional().to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::new("PartitionSpecGrammar").optional().to_matchable(),
                    Ref::keyword("SET").to_matchable(),
                    Ref::keyword("FILEFORMAT").to_matchable(),
                    Ref::new("DataSourceFormatSegment").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::new("PartitionSpecGrammar").optional().to_matchable(),
                    Ref::keyword("SET").to_matchable(),
                    Ref::new("LocationGrammar").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    MetaSegment::indent().to_matchable(),
                    one_of(vec![
                        Ref::keyword("ADD").to_matchable(),
                        Ref::keyword("DROP").to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::keyword("CONSTRAINT").to_matchable(),
                    Ref::new("ColumnReferenceSegment")
                        .exclude(Ref::keyword("CHECK"))
                        .config(|config| {
                            config.exclude = Ref::keyword("CHECK").to_matchable().into();
                        })
                        .to_matchable(),
                    Ref::keyword("CHECK").optional().to_matchable(),
                    Bracketed::new(vec![Ref::new("ExpressionSegment").to_matchable()])
                        .config(|config| {
                            config.optional();
                        })
                        .to_matchable(),
                    MetaSegment::dedent().to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("WRITE").to_matchable(),
                    AnyNumberOf::new(vec![
                        Sequence::new(vec![
                            Ref::keyword("DISTRIBUTED").to_matchable(),
                            Ref::keyword("BY").to_matchable(),
                            Ref::keyword("PARTITION").to_matchable(),
                        ])
                        .config(|config| {
                            config.optional();
                        })
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("LOCALLY").optional().to_matchable(),
                            Ref::keyword("ORDERED").to_matchable(),
                            Ref::keyword("BY").to_matchable(),
                            MetaSegment::indent().to_matchable(),
                            Delimited::new(vec![
                                Sequence::new(vec![
                                    Ref::new("ColumnReferenceSegment").to_matchable(),
                                    one_of(vec![
                                        Ref::keyword("ASC").to_matchable(),
                                        Ref::keyword("DESC").to_matchable(),
                                    ])
                                    .config(|config| {
                                        config.optional();
                                    })
                                    .to_matchable(),
                                    Sequence::new(vec![
                                        Ref::keyword("NULLS").to_matchable(),
                                        one_of(vec![
                                            Ref::keyword("FIRST").to_matchable(),
                                            Ref::keyword("LAST").to_matchable(),
                                        ])
                                        .to_matchable(),
                                    ])
                                    .config(|config| {
                                        config.optional();
                                    })
                                    .to_matchable(),
                                ])
                                .to_matchable(),
                            ])
                            .config(|config| {
                                config.optional();
                            })
                            .to_matchable(),
                            MetaSegment::dedent().to_matchable(),
                        ])
                        .config(|config| {
                            config.optional();
                        })
                        .to_matchable(),
                    ])
                    .config(|config| {
                        config.min_times = 1;
                        config.max_times_per_element = Some(1);
                    })
                    .to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("SET").to_matchable(),
                    Ref::keyword("IDENTIFIER").to_matchable(),
                    Ref::keyword("FIELDS").to_matchable(),
                    MetaSegment::indent().to_matchable(),
                    Delimited::new(vec![
                        Sequence::new(vec![Ref::new("ColumnReferenceSegment").to_matchable()])
                            .to_matchable(),
                    ])
                    .to_matchable(),
                    MetaSegment::dedent().to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("DROP").to_matchable(),
                    Ref::keyword("IDENTIFIER").to_matchable(),
                    Ref::keyword("FIELDS").to_matchable(),
                    MetaSegment::indent().to_matchable(),
                    Delimited::new(vec![
                        Sequence::new(vec![Ref::new("ColumnReferenceSegment").to_matchable()])
                            .to_matchable(),
                    ])
                    .to_matchable(),
                    MetaSegment::dedent().to_matchable(),
                ])
                .to_matchable(),
            ])
            .to_matchable(),
            MetaSegment::dedent().to_matchable(),
        ])
        .to_matchable(),
    );

    sparksql_dialect.add([(
        "ColumnFieldDefinitionSegment".into(),
        NodeMatcher::new(SyntaxKind::ColumnDefinition, |_| {
            Sequence::new(vec![
                Ref::new("ColumnReferenceSegment").to_matchable(),
                Ref::new("DatatypeSegment").to_matchable(),
                Bracketed::new(vec![Anything::new().to_matchable()])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                AnyNumberOf::new(vec![
                    Ref::new("ColumnConstraintSegment")
                        .optional()
                        .to_matchable(),
                ])
                .to_matchable(),
            ])
            .to_matchable()
        })
        .to_matchable()
        .into(),
    )]);

    sparksql_dialect.add([(
        "AlterViewStatementSegment".into(),
        NodeMatcher::new(SyntaxKind::AlterViewStatement, |_| {
            Sequence::new(vec![
                Ref::keyword("ALTER").to_matchable(),
                Ref::keyword("VIEW").to_matchable(),
                Ref::new("TableReferenceSegment").to_matchable(),
                one_of(vec![
                    Sequence::new(vec![
                        Ref::keyword("RENAME").to_matchable(),
                        Ref::keyword("TO").to_matchable(),
                        Ref::new("TableReferenceSegment").to_matchable(),
                    ])
                    .to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("SET").to_matchable(),
                        Ref::new("TablePropertiesGrammar").to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::new("UnsetTablePropertiesGrammar").to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("AS").to_matchable(),
                        optionally_bracketed(vec![
                            Ref::new("SelectStatementSegment").to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable(),
            ])
            .to_matchable()
        })
        .to_matchable()
        .into(),
    )]);

    sparksql_dialect.add([(
        "JoinLikeClauseGrammar".into(),
        Sequence::new(vec![
            one_of(vec![
                Ref::new("PivotClauseSegment").to_matchable(),
                Ref::new("UnpivotClauseSegment").to_matchable(),
                Ref::new("LateralViewClauseSegment").to_matchable(),
            ])
            .to_matchable(),
            Ref::new("AliasExpressionSegment").optional().to_matchable(),
        ])
        .to_matchable()
        .into(),
    )]);

    sparksql_dialect.add([
        // An Unpivot segment.
        // https://spark.apache.org/docs/latest/sql-ref-syntax-qry-select-unpivot.html
        (
            "UnpivotClauseSegment".into(),
            NodeMatcher::new(SyntaxKind::UnpivotClause, |_| {
                Sequence::new(vec![
                    Ref::keyword("UNPIVOT").to_matchable(),
                    Sequence::new(vec![
                        one_of(vec![
                            Ref::keyword("INCLUDE").to_matchable(),
                            Ref::keyword("EXCLUDE").to_matchable(),
                        ])
                        .to_matchable(),
                        Ref::keyword("NULLS").to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                    MetaSegment::indent().to_matchable(),
                    Bracketed::new(vec![
                        one_of(vec![
                            Ref::new("SingleValueColumnUnpivotSegment").to_matchable(),
                            Ref::new("MultiValueColumnUnpivotSegment").to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                    MetaSegment::dedent().to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "SingleValueColumnUnpivotSegment".into(),
            Sequence::new(vec![
                Ref::new("SingleIdentifierGrammar").to_matchable(),
                Ref::keyword("FOR").to_matchable(),
                Ref::new("SingleIdentifierGrammar").to_matchable(),
                Ref::keyword("IN").to_matchable(),
                Bracketed::new(vec![
                    MetaSegment::indent().to_matchable(),
                    Delimited::new(vec![
                        Sequence::new(vec![
                            Ref::new("ColumnReferenceSegment").to_matchable(),
                            Ref::new("AliasExpressionSegment").optional().to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                    MetaSegment::dedent().to_matchable(),
                ])
                .config(|config| {
                    config.parse_mode = ParseMode::Greedy;
                })
                .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "MultiValueColumnUnpivotSegment".into(),
            Sequence::new(vec![
                Bracketed::new(vec![
                    Delimited::new(vec![Ref::new("SingleIdentifierGrammar").to_matchable()])
                        .to_matchable(),
                ])
                .to_matchable(),
                MetaSegment::indent().to_matchable(),
                Ref::keyword("FOR").to_matchable(),
                Ref::new("SingleIdentifierGrammar").to_matchable(),
                Ref::keyword("IN").to_matchable(),
                Bracketed::new(vec![
                    MetaSegment::indent().to_matchable(),
                    Delimited::new(vec![
                        Sequence::new(vec![
                            Bracketed::new(vec![
                                MetaSegment::indent().to_matchable(),
                                Delimited::new(vec![
                                    Ref::new("ColumnReferenceSegment").to_matchable(),
                                ])
                                .to_matchable(),
                            ])
                            .to_matchable(),
                            Ref::new("AliasExpressionSegment").optional().to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .config(|config| {
                    config.parse_mode = ParseMode::Greedy;
                })
                .to_matchable(),
                MetaSegment::dedent().to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
    ]);

    sparksql_dialect.replace_grammar(
        "CreateDatabaseStatementSegment",
        Sequence::new(vec![
            Ref::keyword("CREATE").to_matchable(),
            one_of(vec![
                Ref::keyword("DATABASE").to_matchable(),
                Ref::keyword("SCHEMA").to_matchable(),
            ])
            .to_matchable(),
            Ref::new("IfNotExistsGrammar").optional().to_matchable(),
            Ref::new("DatabaseReferenceSegment").to_matchable(),
            Ref::new("CommentGrammar").optional().to_matchable(),
            Ref::new("LocationGrammar").optional().to_matchable(),
            Sequence::new(vec![
                Ref::keyword("WITH").to_matchable(),
                Ref::keyword("DBPROPERTIES").to_matchable(),
                Ref::new("BracketedPropertyListGrammar").to_matchable(),
            ])
            .config(|config| {
                config.optional();
            })
            .to_matchable(),
        ])
        .to_matchable(),
    );

    sparksql_dialect.replace_grammar(
        "CreateFunctionStatementSegment",
        Sequence::new(vec![
            Ref::keyword("CREATE").to_matchable(),
            Sequence::new(vec![
                Ref::keyword("OR").to_matchable(),
                Ref::keyword("REPLACE").to_matchable(),
            ])
            .config(|config| {
                config.optional();
            })
            .to_matchable(),
            Ref::new("TemporaryGrammar").optional().to_matchable(),
            Ref::keyword("FUNCTION").to_matchable(),
            Ref::new("IfNotExistsGrammar").optional().to_matchable(),
            Ref::new("FunctionNameIdentifierSegment").to_matchable(),
            Ref::keyword("AS").to_matchable(),
            Ref::new("QuotedLiteralSegment").to_matchable(),
            Ref::new("ResourceLocationGrammar")
                .optional()
                .to_matchable(),
        ])
        .to_matchable(),
    );

    sparksql_dialect.replace_grammar(
        "CreateTableStatementSegment",
        Sequence::new(vec![
            Ref::keyword("CREATE").to_matchable(),
            Ref::new("TableDefinitionSegment").to_matchable(),
        ])
        .to_matchable(),
    );

    sparksql_dialect.add([(
        "CreateHiveFormatTableStatementSegment".into(),
        hive_dialect.grammar("CreateTableStatementSegment").into(),
    )]);

    // SparkSQL supports CTEs with DML statements (INSERT, UPDATE, DELETE, etc.)
    // We add these to NonWithSelectableGrammar so WithCompoundStatementSegment can use them
    sparksql_dialect.add([(
        "NonWithSelectableGrammar".into(),
        one_of(vec![
            Ref::new("SetExpressionSegment").to_matchable(),
            optionally_bracketed(vec![Ref::new("SelectStatementSegment").to_matchable()])
                .to_matchable(),
            Ref::new("NonSetSelectableGrammar").to_matchable(),
            Ref::new("UpdateStatementSegment").to_matchable(),
            Ref::new("InsertStatementSegment").to_matchable(),
            Ref::new("DeleteStatementSegment").to_matchable(),
            Ref::new("MergeStatementSegment").to_matchable(),
            Ref::new("InsertOverwriteDirectorySegment").to_matchable(),
        ])
        .to_matchable()
        .into(),
    )]);

    sparksql_dialect.replace_grammar(
        "CreateViewStatementSegment",
        Sequence::new(vec![
            Ref::keyword("CREATE").to_matchable(),
            one_of(vec![
                Ref::new("OrReplaceGrammar").to_matchable(),
                Ref::new("OrRefreshGrammar").to_matchable(),
            ])
            .config(|config| {
                config.optional();
            })
            .to_matchable(),
            Ref::new("TemporaryGrammar").optional().to_matchable(),
            Ref::keyword("STREAMING").optional().to_matchable(),
            Ref::keyword("LIVE").optional().to_matchable(),
            Ref::keyword("MATERIALIZED").optional().to_matchable(),
            Ref::keyword("VIEW").to_matchable(),
            Ref::new("IfNotExistsGrammar").optional().to_matchable(),
            Ref::new("TableReferenceSegment").to_matchable(),
            Sequence::new(vec![
                Bracketed::new(vec![
                    Delimited::new(vec![
                        Sequence::new(vec![
                            Ref::new("ColumnReferenceSegment").to_matchable(),
                            Ref::new("CommentGrammar").optional().to_matchable(),
                        ])
                        .to_matchable(),
                        Ref::new("ConstraintStatementSegment")
                            .optional()
                            .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable(),
            ])
            .config(|config| {
                config.optional();
            })
            .to_matchable(),
            Sequence::new(vec![
                Ref::keyword("USING").to_matchable(),
                Ref::new("DataSourceFormatSegment").to_matchable(),
            ])
            .config(|config| {
                config.optional();
            })
            .to_matchable(),
            Ref::new("OptionsGrammar").optional().to_matchable(),
            Ref::new("CommentGrammar").optional().to_matchable(),
            Ref::new("TablePropertiesGrammar").optional().to_matchable(),
            Sequence::new(vec![
                Ref::keyword("AS").to_matchable(),
                optionally_bracketed(vec![Ref::new("SelectableGrammar").to_matchable()])
                    .to_matchable(),
            ])
            .config(|config| {
                config.optional();
            })
            .to_matchable(),
            Ref::new("WithNoSchemaBindingClauseSegment")
                .optional()
                .to_matchable(),
        ])
        .to_matchable(),
    );
    sparksql_dialect.add([
        (
            "CreateWidgetStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::CreateWidgetStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("CREATE").to_matchable(),
                    Ref::keyword("WIDGET").to_matchable(),
                    one_of(vec![
                        Sequence::new(vec![
                            Ref::keyword("DROPDOWN").to_matchable(),
                            Ref::new("WidgetNameIdentifierSegment").to_matchable(),
                            Ref::new("WidgetDefaultGrammar").to_matchable(),
                            Sequence::new(vec![
                                Ref::keyword("CHOICES").to_matchable(),
                                Ref::new("SelectStatementSegment").to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("TEXT").to_matchable(),
                            Ref::new("WidgetNameIdentifierSegment").to_matchable(),
                            Ref::new("WidgetDefaultGrammar").to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "ReplaceTableStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::ReplaceTableStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("REPLACE").to_matchable(),
                    Ref::new("TableDefinitionSegment").to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "RemoveWidgetStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::RemoveWidgetStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("REMOVE").to_matchable(),
                    Ref::keyword("WIDGET").to_matchable(),
                    Ref::new("WidgetNameIdentifierSegment").to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
    ]);

    sparksql_dialect.replace_grammar(
        "DropDatabaseStatementSegment",
        Sequence::new(vec![
            Ref::keyword("DROP").to_matchable(),
            one_of(vec![
                Ref::keyword("DATABASE").to_matchable(),
                Ref::keyword("SCHEMA").to_matchable(),
            ])
            .to_matchable(),
            Ref::new("IfExistsGrammar").optional().to_matchable(),
            Ref::new("DatabaseReferenceSegment").to_matchable(),
            Ref::new("DropBehaviorGrammar").optional().to_matchable(),
        ])
        .to_matchable(),
    );
    sparksql_dialect.add([(
        "DropFunctionStatementSegment".into(),
        NodeMatcher::new(SyntaxKind::DropFunctionStatement, |_| {
            Sequence::new(vec![
                Ref::keyword("DROP").to_matchable(),
                Ref::new("TemporaryGrammar").optional().to_matchable(),
                Ref::keyword("FUNCTION").to_matchable(),
                Ref::new("IfExistsGrammar").optional().to_matchable(),
                Ref::new("FunctionNameSegment").to_matchable(),
            ])
            .to_matchable()
        })
        .to_matchable()
        .into(),
    )]);

    sparksql_dialect.add([(
        "MsckRepairTableStatementSegment".into(),
        hive_dialect
            .grammar("MsckRepairTableStatementSegment")
            .into(),
    )]);

    sparksql_dialect.replace_grammar(
        "TruncateStatementSegment",
        Sequence::new(vec![
            Ref::keyword("TRUNCATE").to_matchable(),
            Ref::keyword("TABLE").to_matchable(),
            Ref::new("TableReferenceSegment").to_matchable(),
            Ref::new("PartitionSpecGrammar").optional().to_matchable(),
        ])
        .to_matchable(),
    );
    sparksql_dialect.add([
        (
            "UseDatabaseStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::UseDatabaseStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("USE").to_matchable(),
                    Ref::new("DatabaseReferenceSegment").to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "InsertBracketedColumnReferenceListGrammar".into(),
            Ref::new("BracketedColumnReferenceListGrammar")
                .to_matchable()
                .into(),
        ),
        (
            "InsertStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::InsertStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("INSERT").to_matchable(),
                    one_of(vec![
                        Ref::keyword("INTO").to_matchable(),
                        Ref::keyword("OVERWRITE").to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::keyword("TABLE").optional().to_matchable(),
                    Ref::new("TableReferenceSegment").to_matchable(),
                    Ref::new("PartitionSpecGrammar").optional().to_matchable(),
                    Ref::new("InsertBracketedColumnReferenceListGrammar")
                        .optional()
                        .to_matchable(),
                    one_of(vec![
                        AnyNumberOf::new(vec![Ref::new("ValuesClauseSegment").to_matchable()])
                            .config(|config| {
                                config.min_times = 1;
                            })
                            .to_matchable(),
                        Ref::new("SelectableGrammar").to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("TABLE").optional().to_matchable(),
                            Ref::new("TableReferenceSegment").to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("FROM").to_matchable(),
                            Ref::new("TableReferenceSegment").to_matchable(),
                            Ref::keyword("SELECT").to_matchable(),
                            Delimited::new(vec![Ref::new("ColumnReferenceSegment").to_matchable()])
                                .to_matchable(),
                            Ref::new("WhereClauseSegment").optional().to_matchable(),
                            Ref::new("GroupByClauseSegment").optional().to_matchable(),
                            Ref::new("OrderByClauseSegment").optional().to_matchable(),
                            Ref::new("LimitClauseSegment").optional().to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "InsertOverwriteDirectorySegment".into(),
            NodeMatcher::new(SyntaxKind::InsertOverwriteDirectoryStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("INSERT").to_matchable(),
                    Ref::keyword("OVERWRITE").to_matchable(),
                    Ref::keyword("LOCAL").optional().to_matchable(),
                    Ref::keyword("DIRECTORY").to_matchable(),
                    Ref::new("QuotedLiteralSegment").optional().to_matchable(),
                    Ref::keyword("USING").to_matchable(),
                    Ref::new("DataSourceFormatSegment").to_matchable(),
                    Ref::new("OptionsGrammar").optional().to_matchable(),
                    one_of(vec![
                        AnyNumberOf::new(vec![Ref::new("ValuesClauseSegment").to_matchable()])
                            .config(|config| {
                                config.min_times = 1;
                            })
                            .to_matchable(),
                        Ref::new("SelectableGrammar").to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "InsertOverwriteDirectoryHiveFmtSegment".into(),
            NodeMatcher::new(SyntaxKind::InsertOverwriteDirectoryHiveFmtStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("INSERT").to_matchable(),
                    Ref::keyword("OVERWRITE").to_matchable(),
                    Ref::keyword("LOCAL").optional().to_matchable(),
                    Ref::keyword("DIRECTORY").to_matchable(),
                    Ref::new("QuotedLiteralSegment").to_matchable(),
                    Ref::new("RowFormatClauseSegment").optional().to_matchable(),
                    Ref::new("StoredAsGrammar").optional().to_matchable(),
                    one_of(vec![
                        AnyNumberOf::new(vec![Ref::new("ValuesClauseSegment").to_matchable()])
                            .config(|config| {
                                config.min_times = 1;
                            })
                            .to_matchable(),
                        Ref::new("SelectableGrammar").to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "LoadDataSegment".into(),
            NodeMatcher::new(SyntaxKind::LoadDataStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("LOAD").to_matchable(),
                    Ref::keyword("DATA").to_matchable(),
                    Ref::keyword("LOCAL").optional().to_matchable(),
                    Ref::keyword("INPATH").to_matchable(),
                    Ref::new("QuotedLiteralSegment").to_matchable(),
                    Ref::keyword("OVERWRITE").optional().to_matchable(),
                    Ref::keyword("INTO").to_matchable(),
                    Ref::keyword("TABLE").to_matchable(),
                    Ref::new("TableReferenceSegment").to_matchable(),
                    Ref::new("PartitionSpecGrammar").optional().to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "ClusterByClauseSegment".into(),
            NodeMatcher::new(SyntaxKind::ClusterByClause, |_| {
                Sequence::new(vec![
                    Ref::keyword("CLUSTER").to_matchable(),
                    Ref::keyword("BY").to_matchable(),
                    MetaSegment::indent().to_matchable(),
                    Delimited::new(vec![
                        Sequence::new(vec![
                            one_of(vec![
                                Ref::new("ColumnReferenceSegment").to_matchable(),
                                Ref::new("NumericLiteralSegment").to_matchable(),
                                Ref::new("ExpressionSegment").to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .config(|config| {
                        config.terminators = vec![
                            Ref::keyword("LIMIT").to_matchable(),
                            Ref::keyword("HAVING").to_matchable(),
                            Ref::keyword("WINDOW").to_matchable(),
                            Ref::new("FrameClauseUnitGrammar").to_matchable(),
                            Ref::keyword("SEPARATOR").to_matchable(),
                        ];
                    })
                    .to_matchable(),
                    MetaSegment::dedent().to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "DistributeByClauseSegment".into(),
            NodeMatcher::new(SyntaxKind::DistributeByClause, |_| {
                Sequence::new(vec![
                    Ref::keyword("DISTRIBUTE").to_matchable(),
                    Ref::keyword("BY").to_matchable(),
                    MetaSegment::indent().to_matchable(),
                    Delimited::new(vec![
                        Sequence::new(vec![
                            one_of(vec![
                                Ref::new("ColumnReferenceSegment").to_matchable(),
                                Ref::new("NumericLiteralSegment").to_matchable(),
                                Ref::new("ExpressionSegment").to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .config(|config| {
                        config.terminators = vec![
                            Ref::keyword("SORT").to_matchable(),
                            Ref::keyword("LIMIT").to_matchable(),
                            Ref::keyword("HAVING").to_matchable(),
                            Ref::keyword("WINDOW").to_matchable(),
                            Ref::new("FrameClauseUnitGrammar").to_matchable(),
                            Ref::keyword("SEPARATOR").to_matchable(),
                        ];
                    })
                    .to_matchable(),
                    MetaSegment::dedent().to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "HintFunctionSegment".into(),
            NodeMatcher::new(SyntaxKind::HintFunction, |_| {
                Sequence::new(vec![
                    Ref::new("FunctionNameSegment").to_matchable(),
                    Bracketed::new(vec![
                        Delimited::new(vec![
                            AnyNumberOf::new(vec![
                                Ref::new("SingleIdentifierGrammar").to_matchable(),
                                Ref::new("NumericLiteralSegment").to_matchable(),
                                Ref::new("TableReferenceSegment").to_matchable(),
                                Ref::new("ColumnReferenceSegment").to_matchable(),
                            ])
                            .config(|config| {
                                config.min_times = 1;
                            })
                            .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "SelectHintSegment".into(),
            NodeMatcher::new(SyntaxKind::SelectHint, |_| {
                Sequence::new(vec![
                    Sequence::new(vec![
                        Ref::new("StartHintSegment").to_matchable(),
                        Delimited::new(vec![
                            AnyNumberOf::new(vec![Ref::new("HintFunctionSegment").to_matchable()])
                                .config(|config| {
                                    config.min_times = 1;
                                })
                                .to_matchable(),
                        ])
                        .config(|config| {
                            config.terminators = vec![Ref::new("EndHintSegment").to_matchable()];
                        })
                        .to_matchable(),
                        Ref::new("EndHintSegment").to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
    ]);

    sparksql_dialect.replace_grammar(
        "LimitClauseSegment",
        Sequence::new(vec![
            Ref::keyword("LIMIT").to_matchable(),
            MetaSegment::indent().to_matchable(),
            one_of(vec![
                Ref::new("NumericLiteralSegment").to_matchable(),
                Ref::keyword("ALL").to_matchable(),
                Ref::new("FunctionSegment").to_matchable(),
            ])
            .to_matchable(),
            MetaSegment::dedent().to_matchable(),
        ])
        .to_matchable(),
    );

    sparksql_dialect.replace_grammar(
        "SetOperatorSegment",
        one_of(vec![
            Sequence::new(vec![
                one_of(vec![
                    Ref::keyword("EXCEPT").to_matchable(),
                    Ref::keyword("MINUS").to_matchable(),
                ])
                .to_matchable(),
                Ref::keyword("ALL").optional().to_matchable(),
            ])
            .to_matchable(),
            Sequence::new(vec![
                one_of(vec![
                    Ref::keyword("UNION").to_matchable(),
                    Ref::keyword("INTERSECT").to_matchable(),
                ])
                .to_matchable(),
                one_of(vec![
                    Ref::keyword("DISTINCT").to_matchable(),
                    Ref::keyword("ALL").to_matchable(),
                ])
                .config(|config| {
                    config.optional();
                })
                .to_matchable(),
            ])
            .to_matchable(),
        ])
        .config(|config| {
            config.exclude = Some(
                Sequence::new(vec![
                    Ref::keyword("EXCEPT").to_matchable(),
                    Bracketed::new(vec![Anything::new().to_matchable()]).to_matchable(),
                ])
                .to_matchable(),
            )
        })
        .to_matchable(),
    );

    sparksql_dialect.replace_grammar(
        "SelectClauseModifierSegment",
        Sequence::new(vec![
            Ref::new("SelectHintSegment").optional().to_matchable(),
            one_of(vec![
                Ref::keyword("DISTINCT").to_matchable(),
                Ref::keyword("ALL").to_matchable(),
            ])
            .config(|config| {
                config.optional();
            })
            .to_matchable(),
        ])
        .to_matchable(),
    );

    sparksql_dialect.replace_grammar(
        "UnorderedSelectStatementSegment",
        ansi::get_unordered_select_statement_segment_grammar().copy(
            Some(vec![
                Ref::new("QualifyClauseSegment").optional().to_matchable(),
                Ref::new("ClusterByClauseSegment").optional().to_matchable(),
                Ref::new("DistributeByClauseSegment")
                    .optional()
                    .to_matchable(),
                Ref::new("SortByClauseSegment").optional().to_matchable(),
            ]),
            None,
            None,
            Some(vec![
                Ref::new("OverlapsClauseSegment").optional().to_matchable(),
            ]),
            Vec::new(),
            false,
        ),
    );

    sparksql_dialect.replace_grammar(
        "SelectStatementSegment",
        ansi::select_statement()
            .copy(
                Some(vec![
                    Ref::new("ClusterByClauseSegment").optional().to_matchable(),
                    Ref::new("DistributeByClauseSegment")
                        .optional()
                        .to_matchable(),
                    Ref::new("SortByClauseSegment").optional().to_matchable(),
                ]),
                None,
                Some(Ref::new("LimitClauseSegment").optional().to_matchable()),
                None,
                Vec::new(),
                false,
            )
            .copy(
                Some(vec![
                    Ref::new("QualifyClauseSegment").optional().to_matchable(),
                ]),
                None,
                Some(Ref::new("OrderByClauseSegment").optional().to_matchable()),
                None,
                Vec::new(),
                false,
            ),
    );

    sparksql_dialect.replace_grammar(
        // Enhance `GROUP BY` clause like in `SELECT` for 'CUBE' and 'ROLLUP`.
        // https://spark.apache.org/docs/latest/sql-ref-syntax-qry-select-groupby.html
        "GroupByClauseSegment",
        Sequence::new(vec![
            Ref::keyword("GROUP").to_matchable(),
            Ref::keyword("BY").to_matchable(),
            MetaSegment::indent().to_matchable(),
            one_of(vec![
                Delimited::new(vec![
                    Ref::new("CubeRollupClauseSegment").to_matchable(),
                    Ref::new("GroupingSetsClauseSegment").to_matchable(),
                    Ref::new("ColumnReferenceSegment").to_matchable(),
                    Ref::new("NumericLiteralSegment").to_matchable(),
                    Ref::new("ExpressionSegment").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Delimited::new(vec![
                        Ref::new("ColumnReferenceSegment").to_matchable(),
                        Ref::new("NumericLiteralSegment").to_matchable(),
                        Ref::new("ExpressionSegment").to_matchable(),
                    ])
                    .to_matchable(),
                    one_of(vec![
                        Ref::new("WithCubeRollupClauseSegment").to_matchable(),
                        Ref::new("GroupingSetsClauseSegment").to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable(),
            ])
            .to_matchable(),
            MetaSegment::dedent().to_matchable(),
        ])
        .to_matchable(),
    );
    sparksql_dialect.add([
        (
            "WithCubeRollupClauseSegment".into(),
            NodeMatcher::new(SyntaxKind::WithCubeRollupClause, |_| {
                Sequence::new(vec![
                    Ref::keyword("WITH").to_matchable(),
                    one_of(vec![
                        Ref::keyword("CUBE").to_matchable(),
                        Ref::keyword("ROLLUP").to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "SortByClauseSegment".into(),
            NodeMatcher::new(SyntaxKind::SortByClause, |_| {
                Sequence::new(vec![
                    Ref::keyword("SORT").to_matchable(),
                    Ref::keyword("BY").to_matchable(),
                    MetaSegment::indent().to_matchable(),
                    Delimited::new(vec![
                        Sequence::new(vec![
                            one_of(vec![
                                Ref::new("ColumnReferenceSegment").to_matchable(),
                                Ref::new("NumericLiteralSegment").to_matchable(),
                                Ref::new("ExpressionSegment").to_matchable(),
                            ])
                            .to_matchable(),
                            one_of(vec![
                                Ref::keyword("ASC").to_matchable(),
                                Ref::keyword("DESC").to_matchable(),
                            ])
                            .config(|config| {
                                config.optional();
                            })
                            .to_matchable(),
                            Sequence::new(vec![
                                Ref::keyword("NULLS").to_matchable(),
                                one_of(vec![
                                    Ref::keyword("FIRST").to_matchable(),
                                    Ref::keyword("LAST").to_matchable(),
                                ])
                                .to_matchable(),
                            ])
                            .config(|config| {
                                config.optional();
                            })
                            .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .config(|config| {
                        config.terminators = vec![
                            Ref::keyword("LIMIT").to_matchable(),
                            Ref::keyword("HAVING").to_matchable(),
                            Ref::keyword("QUALIFY").to_matchable(),
                            Ref::keyword("WINDOW").to_matchable(),
                            Ref::new("FrameClauseUnitGrammar").to_matchable(),
                            Ref::keyword("SEPARATOR").to_matchable(),
                        ];
                    })
                    .to_matchable(),
                    MetaSegment::dedent().to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
    ]);

    // A `TABLESAMPLE` clause following a table identifier.
    // https://spark.apache.org/docs/latest/sql-ref-syntax-qry-select-sampling.html
    sparksql_dialect.replace_grammar(
        "SamplingExpressionSegment",
        Sequence::new(vec![
            Ref::keyword("TABLESAMPLE").to_matchable(),
            one_of(vec![
                Bracketed::new(vec![
                    Ref::new("NumericLiteralSegment").to_matchable(),
                    one_of(vec![
                        Ref::keyword("PERCENT").to_matchable(),
                        Ref::keyword("ROWS").to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable(),
                Bracketed::new(vec![
                    Ref::keyword("BUCKET").to_matchable(),
                    Ref::new("NumericLiteralSegment").to_matchable(),
                    Ref::keyword("OUT").to_matchable(),
                    Ref::keyword("OF").to_matchable(),
                    Ref::new("NumericLiteralSegment").to_matchable(),
                ])
                .to_matchable(),
            ])
            .to_matchable(),
        ])
        .to_matchable(),
    );

    sparksql_dialect.add([
        (
            "LateralViewClauseSegment".into(),
            NodeMatcher::new(SyntaxKind::LateralViewClause, |_| {
                Sequence::new(vec![
                    MetaSegment::indent().to_matchable(),
                    Ref::keyword("LATERAL").to_matchable(),
                    Ref::keyword("VIEW").to_matchable(),
                    Ref::keyword("OUTER").optional().to_matchable(),
                    Ref::new("FunctionSegment").to_matchable(),
                    one_of(vec![
                        Sequence::new(vec![
                            Ref::new("SingleIdentifierGrammar").to_matchable(),
                            Sequence::new(vec![
                                Ref::keyword("AS").optional().to_matchable(),
                                Delimited::new(vec![
                                    Ref::new("SingleIdentifierGrammar").to_matchable(),
                                ])
                                .to_matchable(),
                            ])
                            .config(|config| {
                                config.optional();
                            })
                            .to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("AS").optional().to_matchable(),
                            Delimited::new(vec![
                                Ref::new("SingleIdentifierGrammar").to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                    MetaSegment::dedent().to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "PivotClauseSegment".into(),
            NodeMatcher::new(SyntaxKind::PivotClause, |_| {
                Sequence::new(vec![
                    MetaSegment::indent().to_matchable(),
                    Ref::keyword("PIVOT").to_matchable(),
                    Bracketed::new(vec![
                        MetaSegment::indent().to_matchable(),
                        Delimited::new(vec![
                            Sequence::new(vec![
                                Ref::new("BaseExpressionElementGrammar").to_matchable(),
                                Ref::new("AliasExpressionSegment").optional().to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                        Ref::keyword("FOR").to_matchable(),
                        optionally_bracketed(vec![
                            one_of(vec![
                                Ref::new("SingleIdentifierGrammar").to_matchable(),
                                Delimited::new(vec![
                                    Ref::new("SingleIdentifierGrammar").to_matchable(),
                                ])
                                .to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                        Ref::keyword("IN").to_matchable(),
                        Bracketed::new(vec![
                            Delimited::new(vec![
                                Sequence::new(vec![
                                    one_of(vec![
                                        Bracketed::new(vec![
                                            Delimited::new(vec![
                                                Ref::new("ExpressionSegment").to_matchable(),
                                            ])
                                            .to_matchable(),
                                        ])
                                        .config(|config| {
                                            config.parse_mode(ParseMode::Greedy);
                                        })
                                        .to_matchable(),
                                        Delimited::new(vec![
                                            Ref::new("ExpressionSegment").to_matchable(),
                                        ])
                                        .to_matchable(),
                                    ])
                                    .to_matchable(),
                                    Ref::new("AliasExpressionSegment").optional().to_matchable(),
                                ])
                                .to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                        MetaSegment::dedent().to_matchable(),
                    ])
                    .to_matchable(),
                    MetaSegment::dedent().to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "TransformClauseSegment".into(),
            NodeMatcher::new(SyntaxKind::TransformClause, |_| {
                Sequence::new(vec![
                    Ref::keyword("TRANSFORM").to_matchable(),
                    Bracketed::new(vec![
                        Delimited::new(vec![Ref::new("SingleIdentifierGrammar").to_matchable()])
                            .to_matchable(),
                    ])
                    .config(|config| {
                        config.parse_mode(ParseMode::Greedy);
                    })
                    .to_matchable(),
                    MetaSegment::indent().to_matchable(),
                    Ref::new("RowFormatClauseSegment").optional().to_matchable(),
                    Ref::keyword("USING").to_matchable(),
                    Ref::new("QuotedLiteralSegment").to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("AS").to_matchable(),
                        Bracketed::new(vec![
                            Delimited::new(vec![
                                AnyNumberOf::new(vec![
                                    Ref::new("SingleIdentifierGrammar").to_matchable(),
                                    Ref::new("DatatypeSegment").to_matchable(),
                                ])
                                .to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                    Ref::new("RowFormatClauseSegment").optional().to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "RowFormatClauseSegment".into(),
            hive_dialect.grammar("RowFormatClauseSegment").into(),
        ),
        (
            "SkewedByClauseSegment".into(),
            hive_dialect.grammar("SkewedByClauseSegment").into(),
        ),
    ]);

    sparksql_dialect.replace_grammar(
        "ExplainStatementSegment",
        Sequence::new(vec![
            Ref::keyword("EXPLAIN").to_matchable(),
            one_of(vec![
                Ref::keyword("EXTENDED").to_matchable(),
                Ref::keyword("CODEGEN").to_matchable(),
                Ref::keyword("COST").to_matchable(),
                Ref::keyword("FORMATTED").to_matchable(),
            ])
            .config(|config| {
                config.optional();
            })
            .to_matchable(),
            Ref::new("StatementSegment").to_matchable(),
        ])
        .to_matchable(),
    );

    sparksql_dialect.add([
        (
            "AddFileSegment".into(),
            NodeMatcher::new(SyntaxKind::AddFileStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("ADD").to_matchable(),
                    Ref::keyword("FILE").to_matchable(),
                    AnyNumberOf::new(vec![Ref::new("QuotedLiteralSegment").to_matchable()])
                        .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "AddJarSegment".into(),
            NodeMatcher::new(SyntaxKind::AddJarStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("ADD").to_matchable(),
                    Ref::keyword("JAR").to_matchable(),
                    AnyNumberOf::new(vec![
                        Ref::new("QuotedLiteralSegment").to_matchable(),
                        Ref::new("FileLiteralSegment").to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "AnalyzeTableSegment".into(),
            NodeMatcher::new(SyntaxKind::AnalyzeTableStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("ANALYZE").to_matchable(),
                    one_of(vec![
                        Sequence::new(vec![
                            Ref::keyword("TABLE").to_matchable(),
                            Ref::new("TableReferenceSegment").to_matchable(),
                            Ref::new("PartitionSpecGrammar").optional().to_matchable(),
                            Ref::keyword("COMPUTE").to_matchable(),
                            Ref::keyword("STATISTICS").to_matchable(),
                            one_of(vec![
                                Ref::keyword("NOSCAN").to_matchable(),
                                Sequence::new(vec![
                                    Ref::keyword("FOR").to_matchable(),
                                    Ref::keyword("COLUMNS").to_matchable(),
                                    optionally_bracketed(vec![
                                        Delimited::new(vec![
                                            Ref::new("ColumnReferenceSegment").to_matchable(),
                                        ])
                                        .to_matchable(),
                                    ])
                                    .to_matchable(),
                                ])
                                .to_matchable(),
                            ])
                            .config(|config| {
                                config.optional();
                            })
                            .to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("TABLES").to_matchable(),
                            Sequence::new(vec![
                                one_of(vec![
                                    Ref::keyword("FROM").to_matchable(),
                                    Ref::keyword("IN").to_matchable(),
                                ])
                                .to_matchable(),
                                Ref::new("DatabaseReferenceSegment").to_matchable(),
                            ])
                            .config(|config| {
                                config.optional();
                            })
                            .to_matchable(),
                            Ref::keyword("COMPUTE").to_matchable(),
                            Ref::keyword("STATISTICS").to_matchable(),
                            Ref::keyword("NOSCAN").optional().to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "CacheTableSegment".into(),
            NodeMatcher::new(SyntaxKind::CacheTable, |_| {
                Sequence::new(vec![
                    Ref::keyword("CACHE").to_matchable(),
                    Ref::keyword("LAZY").optional().to_matchable(),
                    Ref::keyword("TABLE").to_matchable(),
                    Ref::new("TableReferenceSegment").to_matchable(),
                    Ref::new("OptionsGrammar").optional().to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("AS").optional().to_matchable(),
                        Ref::new("SelectableGrammar").to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "ClearCacheSegment".into(),
            NodeMatcher::new(SyntaxKind::ClearCache, |_| {
                Sequence::new(vec![
                    Ref::keyword("CLEAR").to_matchable(),
                    Ref::keyword("CACHE").to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "DescribeObjectGrammar".into(),
            one_of(vec![
                Sequence::new(vec![
                    one_of(vec![
                        Ref::keyword("DATABASE").to_matchable(),
                        Ref::keyword("SCHEMA").to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::keyword("EXTENDED").optional().to_matchable(),
                    Ref::new("DatabaseReferenceSegment").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("FUNCTION").to_matchable(),
                    Ref::keyword("EXTENDED").optional().to_matchable(),
                    Ref::new("FunctionNameSegment").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("TABLE").optional().to_matchable(),
                    Ref::keyword("EXTENDED").optional().to_matchable(),
                    Ref::new("TableReferenceSegment").to_matchable(),
                    Ref::new("PartitionSpecGrammar").optional().to_matchable(),
                    Sequence::new(vec![
                        Ref::new("SingleIdentifierGrammar").to_matchable(),
                        AnyNumberOf::new(vec![
                            Sequence::new(vec![
                                Ref::new("DotSegment").to_matchable(),
                                Ref::new("SingleIdentifierGrammar").to_matchable(),
                            ])
                            .config(|config| {
                                config.disallow_gaps();
                            })
                            .to_matchable(),
                        ])
                        .config(|config| {
                            config.max_times = Some(2);
                            config.disallow_gaps();
                        })
                        .to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                        config.disallow_gaps();
                    })
                    .to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("QUERY").optional().to_matchable(),
                    one_of(vec![
                        Sequence::new(vec![
                            Ref::keyword("TABLE").to_matchable(),
                            Ref::new("TableReferenceSegment").to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("FROM").to_matchable(),
                            Ref::new("TableReferenceSegment").to_matchable(),
                            Ref::keyword("SELECT").to_matchable(),
                            Delimited::new(vec![Ref::new("ColumnReferenceSegment").to_matchable()])
                                .to_matchable(),
                            Ref::new("WhereClauseSegment").optional().to_matchable(),
                            Ref::new("GroupByClauseSegment").optional().to_matchable(),
                            Ref::new("OrderByClauseSegment").optional().to_matchable(),
                            Ref::new("LimitClauseSegment").optional().to_matchable(),
                        ])
                        .to_matchable(),
                        Ref::new("StatementSegment").to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable(),
            ])
            .config(|config| {
                config.exclude = one_of(vec![
                    Ref::keyword("HISTORY").to_matchable(),
                    Ref::keyword("DETAIL").to_matchable(),
                ])
                .to_matchable()
                .into();
            })
            .to_matchable()
            .into(),
        ),
        // A `DESCRIBE` statement.
        // This class provides coverage for databases, tables, functions, and queries.

        // NB: These are similar enough that it makes sense to include them in a
        // common class, especially since there wouldn't be any specific rules that
        // would apply to one describe vs another, but they could be broken out to
        // one class per describe statement type.

        // https://spark.apache.org/docs/latest/sql-ref-syntax-aux-describe-database.html
        // https://spark.apache.org/docs/latest/sql-ref-syntax-aux-describe-function.html
        // https://spark.apache.org/docs/latest/sql-ref-syntax-aux-describe-query.html
        // https://spark.apache.org/docs/latest/sql-ref-syntax-aux-describe-table.html
        (
            "DescribeStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::DescribeStatement, |_| {
                Sequence::new(vec![
                    one_of(vec![
                        Ref::keyword("DESCRIBE").to_matchable(),
                        Ref::keyword("DESC").to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::new("DescribeObjectGrammar").to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "ListFileSegment".into(),
            NodeMatcher::new(SyntaxKind::ListFileStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("LIST").to_matchable(),
                    Ref::keyword("FILE").to_matchable(),
                    AnyNumberOf::new(vec![Ref::new("QuotedLiteralSegment").to_matchable()])
                        .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "ListJarSegment".into(),
            NodeMatcher::new(SyntaxKind::ListJarStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("LIST").to_matchable(),
                    Ref::keyword("JAR").to_matchable(),
                    AnyNumberOf::new(vec![Ref::new("QuotedLiteralSegment").to_matchable()])
                        .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "RefreshStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::RefreshStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("REFRESH").to_matchable(),
                    one_of(vec![
                        Ref::new("QuotedLiteralSegment").to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("TABLE").optional().to_matchable(),
                            Ref::new("TableReferenceSegment").to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("FUNCTION").to_matchable(),
                            Ref::new("FunctionNameSegment").to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "ResetStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::ResetStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("RESET").to_matchable(),
                    Delimited::new(vec![Ref::new("SingleIdentifierGrammar").to_matchable()])
                        .config(|config| {
                            config.delimiter(Ref::new("DotSegment"));
                            config.optional();
                        })
                        .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "ShowViewsGrammar".into(),
            Sequence::new(vec![
                Ref::keyword("VIEWS").to_matchable(),
                Sequence::new(vec![
                    one_of(vec![
                        Ref::keyword("FROM").to_matchable(),
                        Ref::keyword("IN").to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::new("DatabaseReferenceSegment").to_matchable(),
                ])
                .config(|config| {
                    config.optional();
                })
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("LIKE").to_matchable(),
                    Ref::new("QuotedLiteralSegment").to_matchable(),
                ])
                .config(|config| {
                    config.optional();
                })
                .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "SetStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::SetStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("SET").to_matchable(),
                    Ref::new("SQLConfPropertiesSegment")
                        .optional()
                        .to_matchable(),
                    one_of(vec![
                        Ref::new("PropertyListGrammar").to_matchable(),
                        Ref::new("PropertyNameSegment").to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            // Common class for `SHOW` statements.
            //
            // NB: These are similar enough that it makes sense to include them in a
            // common class, especially since there wouldn't be any specific rules that
            // would apply to one show vs another, but they could be broken out to
            // one class per show statement type.
            //
            // https://spark.apache.org/docs/latest/sql-ref-syntax-aux-show-columns.html
            // https://spark.apache.org/docs/latest/sql-ref-syntax-aux-show-create-table.html
            // https://spark.apache.org/docs/latest/sql-ref-syntax-aux-show-databases.html
            // https://spark.apache.org/docs/latest/sql-ref-syntax-aux-show-functions.html
            // https://spark.apache.org/docs/latest/sql-ref-syntax-aux-show-partitions.html
            // https://spark.apache.org/docs/latest/sql-ref-syntax-aux-show-table.html
            // https://spark.apache.org/docs/latest/sql-ref-syntax-aux-show-tables.html
            // https://spark.apache.org/docs/latest/sql-ref-syntax-aux-show-tblproperties.html
            // https://spark.apache.org/docs/latest/sql-ref-syntax-aux-show-views.html
            "ShowStatement".into(),
            NodeMatcher::new(SyntaxKind::ShowStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("SHOW").to_matchable(),
                    Ref::new("ShowObjectGrammar").to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "UncacheTableSegment".into(),
            NodeMatcher::new(SyntaxKind::UncacheTable, |_| {
                Sequence::new(vec![
                    Ref::keyword("UNCACHE").to_matchable(),
                    Ref::keyword("TABLE").to_matchable(),
                    Ref::new("IfExistsGrammar").optional().to_matchable(),
                    Ref::new("TableReferenceSegment").to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
    ]);

    sparksql_dialect.replace_grammar(
        "StatementSegment",
        ansi::statement_segment().copy(
            Some(vec![
                Ref::new("AlterDatabaseStatementSegment").to_matchable(),
                Ref::new("AlterTableStatementSegment").to_matchable(),
                Ref::new("AlterViewStatementSegment").to_matchable(),
                Ref::new("CreateHiveFormatTableStatementSegment").to_matchable(),
                Ref::new("MsckRepairTableStatementSegment").to_matchable(),
                Ref::new("UseDatabaseStatementSegment").to_matchable(),
                Ref::new("AddFileSegment").to_matchable(),
                Ref::new("AddJarSegment").to_matchable(),
                Ref::new("AnalyzeTableSegment").to_matchable(),
                Ref::new("CacheTableSegment").to_matchable(),
                Ref::new("ClearCacheSegment").to_matchable(),
                Ref::new("ListFileSegment").to_matchable(),
                Ref::new("ListJarSegment").to_matchable(),
                Ref::new("RefreshStatementSegment").to_matchable(),
                Ref::new("ResetStatementSegment").to_matchable(),
                Ref::new("SetStatementSegment").to_matchable(),
                Ref::new("ShowStatement").to_matchable(),
                Ref::new("UncacheTableSegment").to_matchable(),
                Ref::new("InsertOverwriteDirectorySegment").to_matchable(),
                Ref::new("InsertOverwriteDirectoryHiveFmtSegment").to_matchable(),
                Ref::new("LoadDataSegment").to_matchable(),
                Ref::new("ClusterByClauseSegment").to_matchable(),
                Ref::new("DistributeByClauseSegment").to_matchable(),
                Ref::new("VacuumStatementSegment").to_matchable(),
                Ref::new("DescribeHistoryStatementSegment").to_matchable(),
                Ref::new("DescribeDetailStatementSegment").to_matchable(),
                Ref::new("GenerateManifestFileStatementSegment").to_matchable(),
                Ref::new("ConvertToDeltaStatementSegment").to_matchable(),
                Ref::new("RestoreTableStatementSegment").to_matchable(),
                Ref::new("ConstraintStatementSegment").to_matchable(),
                Ref::new("ApplyChangesIntoStatementSegment").to_matchable(),
                Ref::new("CreateWidgetStatementSegment").to_matchable(),
                Ref::new("RemoveWidgetStatementSegment").to_matchable(),
                Ref::new("ReplaceTableStatementSegment").to_matchable(),
                Ref::new("SetVariableStatementSegment").to_matchable(),
            ]),
            None,
            None,
            Some(vec![
                Ref::new("TransactionStatementSegment").to_matchable(),
                Ref::new("CreateSchemaStatementSegment").to_matchable(),
                Ref::new("SetSchemaStatementSegment").to_matchable(),
                Ref::new("CreateModelStatementSegment").to_matchable(),
                Ref::new("DropModelStatementSegment").to_matchable(),
            ]),
            Vec::new(),
            false,
        ),
    );

    sparksql_dialect.replace_grammar(
        "JoinClauseSegment",
        one_of(vec![
            Sequence::new(vec![
                Ref::new("JoinTypeKeywords").optional().to_matchable(),
                Ref::new("JoinKeywordsGrammar").to_matchable(),
                MetaSegment::indent().to_matchable(),
                Ref::new("FromExpressionElementSegment").to_matchable(),
                MetaSegment::dedent().to_matchable(),
                Conditional::new(MetaSegment::indent())
                    .indented_using_on()
                    .to_matchable(),
                one_of(vec![
                    Ref::new("JoinOnConditionSegment").to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("USING").to_matchable(),
                        Conditional::new(MetaSegment::indent()).to_matchable(),
                        Bracketed::new(vec![
                            Delimited::new(vec![
                                Ref::new("SingleIdentifierGrammar").to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .config(|config| {
                            config.parse_mode(ParseMode::Greedy);
                        })
                        .to_matchable(),
                        Conditional::new(MetaSegment::dedent()).to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .config(|config| {
                    config.optional();
                })
                .to_matchable(),
                Conditional::new(MetaSegment::dedent())
                    .indented_using_on()
                    .to_matchable(),
            ])
            .to_matchable(),
            Sequence::new(vec![
                Ref::new("NaturalJoinKeywordsGrammar").to_matchable(),
                Ref::new("JoinKeywordsGrammar").to_matchable(),
                MetaSegment::indent().to_matchable(),
                Ref::new("FromExpressionElementSegment").to_matchable(),
                MetaSegment::dedent().to_matchable(),
            ])
            .to_matchable(),
        ])
        .to_matchable(),
    );

    sparksql_dialect.replace_grammar(
        "AliasExpressionSegment",
        Sequence::new(vec![
            Ref::keyword("AS").optional().to_matchable(),
            one_of(vec![
                Sequence::new(vec![
                    Ref::new("SingleIdentifierGrammar")
                        .optional()
                        .to_matchable(),
                    Bracketed::new(vec![Ref::new("SingleIdentifierListSegment").to_matchable()])
                        .to_matchable(),
                ])
                .to_matchable(),
                Ref::new("SingleIdentifierGrammar").to_matchable(),
            ])
            .config(|config| {
                config.exclude = one_of(vec![
                    Ref::keyword("LATERAL").to_matchable(),
                    Ref::new("JoinTypeKeywords").to_matchable(),
                    Ref::keyword("WINDOW").to_matchable(),
                    Ref::keyword("PIVOT").to_matchable(),
                    Ref::keyword("KEYS").to_matchable(),
                    Ref::keyword("FROM").to_matchable(),
                ])
                .to_matchable()
                .into();
            })
            .to_matchable(),
        ])
        .to_matchable(),
    );

    sparksql_dialect.replace_grammar(
        "ValuesClauseSegment",
        Sequence::new(vec![
            Ref::keyword("VALUES").to_matchable(),
            Delimited::new(vec![
                one_of(vec![
                    Bracketed::new(vec![
                        Delimited::new(vec![
                            Ref::keyword("NULL").to_matchable(),
                            Ref::new("ExpressionSegment").to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .config(|config| {
                        config.parse_mode(ParseMode::Greedy);
                    })
                    .to_matchable(),
                    Ref::keyword("NULL").to_matchable(),
                    Ref::new("ExpressionSegment").to_matchable(),
                ])
                .config(|config| {
                    config.exclude = one_of(vec![Ref::keyword("VALUES").to_matchable()])
                        .to_matchable()
                        .into();
                })
                .to_matchable(),
            ])
            .to_matchable(),
            Ref::new("AliasExpressionSegment")
                .exclude(one_of(vec![
                    Ref::keyword("LIMIT").to_matchable(),
                    Ref::keyword("ORDER").to_matchable(),
                ]))
                .optional()
                .config(|config| {
                    config.exclude = one_of(vec![
                        Ref::keyword("LIMIT").to_matchable(),
                        Ref::keyword("ORDER").to_matchable(),
                    ])
                    .to_matchable()
                    .into();
                })
                .to_matchable(),
            Ref::new("OrderByClauseSegment").optional().to_matchable(),
            Ref::new("LimitClauseSegment").optional().to_matchable(),
        ])
        .to_matchable(),
    );

    sparksql_dialect.replace_grammar(
        "TableExpressionSegment",
        one_of(vec![
            Ref::new("ValuesClauseSegment").to_matchable(),
            Ref::new("BareFunctionSegment").to_matchable(),
            Ref::new("FunctionSegment").to_matchable(),
            Sequence::new(vec![
                one_of(vec![
                    Ref::new("FileReferenceSegment").to_matchable(),
                    Ref::new("TableReferenceSegment").to_matchable(),
                ])
                .to_matchable(),
                one_of(vec![
                    Ref::new("AtSignLiteralSegment").to_matchable(),
                    Sequence::new(vec![
                        MetaSegment::indent().to_matchable(),
                        one_of(vec![
                            Ref::new("TimestampAsOfGrammar").to_matchable(),
                            Ref::new("VersionAsOfGrammar").to_matchable(),
                        ])
                        .to_matchable(),
                        MetaSegment::dedent().to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .config(|config| {
                    config.optional();
                })
                .to_matchable(),
            ])
            .to_matchable(),
            Bracketed::new(vec![Ref::new("SelectableGrammar").to_matchable()]).to_matchable(),
        ])
        .to_matchable(),
    );
    sparksql_dialect.add([(
        "FileReferenceSegment".into(),
        NodeMatcher::new(SyntaxKind::FileReference, |_| {
            Sequence::new(vec![
                Ref::new("DataSourcesV2FileTypeGrammar").to_matchable(),
                Ref::new("DotSegment").to_matchable(),
                Ref::new("BackQuotedIdentifierSegment").to_matchable(),
            ])
            .to_matchable()
        })
        .to_matchable()
        .into(),
    )]);

    sparksql_dialect.replace_grammar(
        "FromExpressionElementSegment",
        Sequence::new(vec![
            Ref::new("PreTableFunctionKeywordsGrammar")
                .optional()
                .to_matchable(),
            optionally_bracketed(vec![Ref::new("TableExpressionSegment").to_matchable()])
                .to_matchable(),
            Ref::new("SamplingExpressionSegment")
                .optional()
                .to_matchable(),
            Ref::new("AliasExpressionSegment")
                .exclude(one_of(vec![
                    Ref::new("FromClauseTerminatorGrammar").to_matchable(),
                    Ref::new("JoinLikeClauseGrammar").to_matchable(),
                ]))
                .optional()
                .to_matchable(),
            Ref::new("SamplingExpressionSegment")
                .optional()
                .to_matchable(),
            AnyNumberOf::new(vec![Ref::new("LateralViewClauseSegment").to_matchable()])
                .to_matchable(),
            Ref::new("NamedWindowSegment").optional().to_matchable(),
            Ref::new("PostTableExpressionGrammar")
                .optional()
                .to_matchable(),
        ])
        .to_matchable(),
    );
    sparksql_dialect.add([
        (
            "PropertyNameSegment".into(),
            NodeMatcher::new(SyntaxKind::PropertyNameIdentifier, |_| {
                Sequence::new(vec![
                    one_of(vec![
                        Delimited::new(vec![
                            Ref::new("PropertiesNakedIdentifierSegment").to_matchable(),
                        ])
                        .config(|config| {
                            config.delimiter(Ref::new("DotSegment"));
                            config.disallow_gaps();
                        })
                        .to_matchable(),
                        Ref::new("SingleIdentifierGrammar").to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "GeneratedColumnDefinitionSegment".into(),
            NodeMatcher::new(SyntaxKind::GeneratedColumnDefinition, |_| {
                Sequence::new(vec![
                    Ref::new("SingleIdentifierGrammar").to_matchable(),
                    Ref::new("DatatypeSegment").to_matchable(),
                    Bracketed::new(vec![Anything::new().to_matchable()])
                        .config(|config| {
                            config.optional();
                        })
                        .to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("GENERATED").to_matchable(),
                        Ref::keyword("ALWAYS").to_matchable(),
                        Ref::keyword("AS").to_matchable(),
                        Bracketed::new(vec![
                            one_of(vec![
                                Ref::new("FunctionSegment").to_matchable(),
                                Ref::new("BareFunctionSegment").to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                    AnyNumberOf::new(vec![
                        Ref::new("ColumnConstraintSegment")
                            .optional()
                            .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
    ]);

    sparksql_dialect.replace_grammar(
        "MergeUpdateClauseSegment",
        Sequence::new(vec![
            Ref::keyword("UPDATE").to_matchable(),
            one_of(vec![
                Sequence::new(vec![
                    Ref::keyword("SET").to_matchable(),
                    Ref::new("WildcardIdentifierSegment").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    MetaSegment::indent().to_matchable(),
                    Ref::new("SetClauseListSegment").to_matchable(),
                    MetaSegment::dedent().to_matchable(),
                ])
                .to_matchable(),
            ])
            .to_matchable(),
        ])
        .to_matchable(),
    );

    sparksql_dialect.replace_grammar(
        "MergeInsertClauseSegment",
        Sequence::new(vec![
            Ref::keyword("INSERT").to_matchable(),
            one_of(vec![
                Ref::new("WildcardIdentifierSegment").to_matchable(),
                Sequence::new(vec![
                    MetaSegment::indent().to_matchable(),
                    Ref::new("BracketedColumnReferenceListGrammar").to_matchable(),
                    MetaSegment::dedent().to_matchable(),
                    Ref::new("ValuesClauseSegment").to_matchable(),
                ])
                .to_matchable(),
            ])
            .to_matchable(),
        ])
        .to_matchable(),
    );

    sparksql_dialect.replace_grammar(
        "UpdateStatementSegment",
        Sequence::new(vec![
            Ref::keyword("UPDATE").to_matchable(),
            one_of(vec![
                Ref::new("FileReferenceSegment").to_matchable(),
                Ref::new("TableReferenceSegment").to_matchable(),
            ])
            .to_matchable(),
            Ref::new("AliasExpressionSegment")
                .exclude(Ref::keyword("SET"))
                .optional()
                .config(|config| {
                    config.exclude = Ref::keyword("SET").to_matchable().into();
                })
                .to_matchable(),
            Ref::new("SetClauseListSegment").to_matchable(),
            Ref::new("WhereClauseSegment").to_matchable(),
        ])
        .to_matchable(),
    );
    sparksql_dialect.add([(
        "IntervalLiteralSegment".into(),
        NodeMatcher::new(SyntaxKind::IntervalLiteral, |_| {
            Sequence::new(vec![
                Ref::new("SignedSegmentGrammar").optional().to_matchable(),
                one_of(vec![
                    Ref::new("NumericLiteralSegment").to_matchable(),
                    Ref::new("SignedQuotedLiteralSegment").to_matchable(),
                ])
                .to_matchable(),
                Ref::new("DatetimeUnitSegment").to_matchable(),
                Ref::keyword("TO").optional().to_matchable(),
                Ref::new("DatetimeUnitSegment").optional().to_matchable(),
            ])
            .to_matchable()
        })
        .to_matchable()
        .into(),
    )]);

    sparksql_dialect.replace_grammar(
        "IntervalExpressionSegment",
        Sequence::new(vec![
            Ref::keyword("INTERVAL").to_matchable(),
            one_of(vec![
                AnyNumberOf::new(vec![Ref::new("IntervalLiteralSegment").to_matchable()])
                    .to_matchable(),
                Ref::new("QuotedLiteralSegment").to_matchable(),
            ])
            .to_matchable(),
        ])
        .to_matchable(),
    );
    sparksql_dialect.add([
        (
            "VacuumStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::VacuumStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("VACUUM").to_matchable(),
                    one_of(vec![
                        Ref::new("QuotedLiteralSegment").to_matchable(),
                        Ref::new("FileReferenceSegment").to_matchable(),
                        Ref::new("TableReferenceSegment").to_matchable(),
                    ])
                    .to_matchable(),
                    one_of(vec![
                        Sequence::new(vec![
                            Ref::keyword("RETAIN").to_matchable(),
                            Ref::new("NumericLiteralSegment").to_matchable(),
                            Ref::new("DatetimeUnitSegment").to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("DRY").to_matchable(),
                            Ref::keyword("RUN").to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "DescribeHistoryStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::DescribeHistoryStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("DESCRIBE").to_matchable(),
                    Ref::keyword("HISTORY").to_matchable(),
                    one_of(vec![
                        Ref::new("QuotedLiteralSegment").to_matchable(),
                        Ref::new("FileReferenceSegment").to_matchable(),
                        Ref::new("TableReferenceSegment").to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::new("LimitClauseSegment").optional().to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "DescribeDetailStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::DescribeDetailStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("DESCRIBE").to_matchable(),
                    Ref::keyword("DETAIL").to_matchable(),
                    one_of(vec![
                        Ref::new("QuotedLiteralSegment").to_matchable(),
                        Ref::new("FileReferenceSegment").to_matchable(),
                        Ref::new("TableReferenceSegment").to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "GenerateManifestFileStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::GenerateManifestFileStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("GENERATE").to_matchable(),
                    StringParser::new("symlink_format_manifest", SyntaxKind::SymlinkFormatManifest)
                        .to_matchable(),
                    Ref::keyword("FOR").to_matchable(),
                    Ref::keyword("TABLE").to_matchable(),
                    one_of(vec![
                        Ref::new("QuotedLiteralSegment").to_matchable(),
                        Ref::new("FileReferenceSegment").to_matchable(),
                        Ref::new("TableReferenceSegment").to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "ConvertToDeltaStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::ConvertToDeltaStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("CONVERT").to_matchable(),
                    Ref::keyword("TO").to_matchable(),
                    Ref::keyword("DELTA").to_matchable(),
                    Ref::new("FileReferenceSegment").to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("NO").to_matchable(),
                        Ref::keyword("STATISTICS").to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                    Ref::new("PartitionSpecGrammar").optional().to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "RestoreTableStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::RestoreTableStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("RESTORE").to_matchable(),
                    Ref::keyword("TABLE").to_matchable(),
                    one_of(vec![
                        Ref::new("QuotedLiteralSegment").to_matchable(),
                        Ref::new("FileReferenceSegment").to_matchable(),
                        Ref::new("TableReferenceSegment").to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::keyword("TO").to_matchable(),
                    one_of(vec![
                        Ref::new("TimestampAsOfGrammar").to_matchable(),
                        Ref::new("VersionAsOfGrammar").to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "ConstraintStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::ConstraintStatement, |_| {
                Sequence::new(vec![
                    Ref::keyword("CONSTRAINT").to_matchable(),
                    Ref::new("ObjectReferenceSegment").to_matchable(),
                    Ref::keyword("EXPECT").to_matchable(),
                    Bracketed::new(vec![Ref::new("ExpressionSegment").to_matchable()])
                        .to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("ON").to_matchable(),
                        Ref::keyword("VIOLATION").to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                    one_of(vec![
                        Sequence::new(vec![
                            Ref::keyword("FAIL").to_matchable(),
                            Ref::keyword("UPDATE").to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("DROP").to_matchable(),
                            Ref::keyword("ROW").to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            // A statement ingest CDC data a target table.
            // https://docs.databricks.com/workflows/delta-live-tables/delta-live-tables-cdc.html#sql
            "ApplyChangesIntoStatementSegment".into(),
            NodeMatcher::new(SyntaxKind::ApplyChangesIntoStatement, |_| {
                Sequence::new(vec![
                    Sequence::new(vec![
                        Ref::keyword("APPLY").to_matchable(),
                        Ref::keyword("CHANGES").to_matchable(),
                        Ref::keyword("INTO").to_matchable(),
                    ])
                    .to_matchable(),
                    MetaSegment::indent().to_matchable(),
                    Ref::new("TableExpressionSegment").to_matchable(),
                    MetaSegment::dedent().to_matchable(),
                    Ref::new("FromClauseSegment").to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("KEYS").to_matchable(),
                        MetaSegment::indent().to_matchable(),
                        Ref::new("BracketedColumnReferenceListGrammar").to_matchable(),
                        MetaSegment::dedent().to_matchable(),
                    ])
                    .to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("IGNORE").to_matchable(),
                        Ref::keyword("NULL").to_matchable(),
                        Ref::keyword("UPDATES").to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                    Ref::new("WhereClauseSegment").optional().to_matchable(),
                    AnyNumberOf::new(vec![
                        Sequence::new(vec![
                            Ref::keyword("APPLY").to_matchable(),
                            Ref::keyword("AS").to_matchable(),
                            one_of(vec![
                                Ref::keyword("DELETE").to_matchable(),
                                Ref::keyword("TRUNCATE").to_matchable(),
                            ])
                            .to_matchable(),
                            Ref::keyword("WHEN").to_matchable(),
                            Ref::new("ColumnReferenceSegment").to_matchable(),
                            Ref::new("EqualsSegment").to_matchable(),
                            Ref::new("QuotedLiteralSegment").to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .config(|config| {
                        config.max_times = Some(2);
                    })
                    .to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("SEQUENCE").to_matchable(),
                        Ref::keyword("BY").to_matchable(),
                        Ref::new("ColumnReferenceSegment").to_matchable(),
                    ])
                    .to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("COLUMNS").to_matchable(),
                        one_of(vec![
                            Delimited::new(vec![Ref::new("ColumnReferenceSegment").to_matchable()])
                                .to_matchable(),
                            Sequence::new(vec![
                                Ref::new("StarSegment").to_matchable(),
                                Ref::keyword("EXCEPT").to_matchable(),
                                Ref::new("BracketedColumnReferenceListGrammar").to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("STORED").to_matchable(),
                        Ref::keyword("AS").to_matchable(),
                        Ref::keyword("SCD").to_matchable(),
                        Ref::keyword("TYPE").to_matchable(),
                        Ref::new("NumericLiteralSegment").to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("TRACK").to_matchable(),
                        Ref::keyword("HISTORY").to_matchable(),
                        Ref::keyword("ON").to_matchable(),
                        one_of(vec![
                            Delimited::new(vec![Ref::new("ColumnReferenceSegment").to_matchable()])
                                .to_matchable(),
                            Sequence::new(vec![
                                Ref::new("StarSegment").to_matchable(),
                                Ref::keyword("EXCEPT").to_matchable(),
                                Ref::new("BracketedColumnReferenceListGrammar").to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
    ]);

    sparksql_dialect.replace_grammar(
        "WildcardExpressionSegment",
        ansi::wildcard_expression_segment().copy(
            Some(vec![
                Ref::new("ExceptClauseSegment").optional().to_matchable(),
            ]),
            None,
            None,
            None,
            Vec::new(),
            false,
        ),
    );

    // A reference to an object.
    // allow whitespace
    sparksql_dialect.replace_grammar(
        "ObjectReferenceSegment",
        Delimited::new(vec![
            one_of(vec![
                Ref::new("SingleIdentifierGrammar").to_matchable(),
                Ref::new("IdentifierClauseSegment").to_matchable(),
            ])
            .to_matchable(),
        ])
        .config(|config| {
            config.delimiter(Ref::new("ObjectReferenceDelimiterGrammar"));
            config.terminators = vec![Ref::new("ObjectReferenceTerminatorGrammar").to_matchable()];
            config.disallow_gaps();
        })
        .to_matchable(),
    );

    sparksql_dialect.add([
        (
            "ExceptClauseSegment".into(),
            NodeMatcher::new(SyntaxKind::SelectExceptClause, |_| {
                Sequence::new(vec![
                    Ref::keyword("EXCEPT").to_matchable(),
                    Bracketed::new(vec![
                        Delimited::new(vec![Ref::new("ColumnReferenceSegment").to_matchable()])
                            .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "SelectClauseSegment".into(),
            NodeMatcher::new(SyntaxKind::SelectClause, |_| {
                Sequence::new(vec![
                    Ref::keyword("SELECT").to_matchable(),
                    one_of(vec![
                        Ref::new("TransformClauseSegment").to_matchable(),
                        Sequence::new(vec![
                            Ref::new("SelectClauseModifierSegment")
                                .optional()
                                .to_matchable(),
                            MetaSegment::indent().to_matchable(),
                            Delimited::new(vec![
                                Ref::new("SelectClauseElementSegment").to_matchable(),
                            ])
                            .config(|config| {
                                config.allow_trailing = true;
                            })
                            .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .config(|config| {
                    config.terminators =
                        vec![Ref::new("SelectClauseTerminatorGrammar").to_matchable()];
                    config.parse_mode(ParseMode::GreedyOnceStarted);
                })
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "UsingClauseSegment".into(),
            NodeMatcher::new(SyntaxKind::UsingClause, |_| {
                Sequence::new(vec![
                    Ref::keyword("USING").to_matchable(),
                    Ref::new("DataSourceFormatSegment").to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "DataSourceFormatSegment".into(),
            NodeMatcher::new(SyntaxKind::DataSourceFormat, |_| {
                one_of(vec![
                    Ref::new("FileFormatGrammar").to_matchable(),
                    Ref::keyword("JDBC").to_matchable(),
                    Ref::new("ObjectReferenceSegment").to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
        (
            "IcebergTransformationSegment".into(),
            NodeMatcher::new(SyntaxKind::IcebergTransformation, |_| {
                one_of(vec![
                    Sequence::new(vec![
                        one_of(vec![
                            Ref::keyword("YEARS").to_matchable(),
                            Ref::keyword("MONTHS").to_matchable(),
                            Ref::keyword("DAYS").to_matchable(),
                            Ref::keyword("DATE").to_matchable(),
                            Ref::keyword("HOURS").to_matchable(),
                            Ref::keyword("DATE_HOUR").to_matchable(),
                        ])
                        .to_matchable(),
                        Bracketed::new(vec![Ref::new("ColumnReferenceSegment").to_matchable()])
                            .to_matchable(),
                    ])
                    .to_matchable(),
                    Sequence::new(vec![
                        one_of(vec![
                            Ref::keyword("BUCKET").to_matchable(),
                            Ref::keyword("TRUNCATE").to_matchable(),
                        ])
                        .to_matchable(),
                        Bracketed::new(vec![
                            Sequence::new(vec![
                                Ref::new("NumericLiteralSegment").to_matchable(),
                                Ref::new("CommaSegment").to_matchable(),
                                Ref::new("ColumnReferenceSegment").to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable()
            })
            .to_matchable()
            .into(),
        ),
    ]);

    sparksql_dialect.add([
        (
            // Show Functions
            "ShowFunctionsGrammar".into(),
            Sequence::new(vec![
                one_of(vec![
                    Ref::keyword("USER").to_matchable(),
                    Ref::keyword("SYSTEM").to_matchable(),
                    Ref::keyword("ALL").to_matchable(),
                ])
                .config(|config| {
                    config.optional();
                })
                .to_matchable(),
                Ref::keyword("FUNCTIONS").to_matchable(),
                one_of(vec![
                    // qualified function from a database
                    Sequence::new(vec![
                        Ref::new("DatabaseReferenceSegment").to_matchable(),
                        Ref::new("DotSegment").to_matchable(),
                        Ref::new("FunctionNameSegment").to_matchable(),
                    ])
                    .config(|config| {
                        config.disallow_gaps();
                        config.optional();
                    })
                    .to_matchable(),
                    // non-qualified function
                    Ref::new("FunctionNameSegment").optional().to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("LIKE").to_matchable(),
                        Ref::new("QuotedLiteralSegment").to_matchable(),
                    ])
                    .config(|config| {
                        config.optional();
                    })
                    .to_matchable(),
                ])
                .config(|config| {
                    config.optional();
                })
                .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "ShowTablesGrammar".into(),
            Sequence::new(vec![
                Ref::keyword("TABLES").to_matchable(),
                Sequence::new(vec![
                    one_of(vec![
                        Ref::keyword("FROM").to_matchable(),
                        Ref::keyword("IN").to_matchable(),
                    ])
                    .to_matchable(),
                    Ref::new("DatabaseReferenceSegment").to_matchable(),
                ])
                .config(|config| {
                    config.optional();
                })
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("LIKE").to_matchable(),
                    Ref::new("QuotedLiteralSegment").to_matchable(),
                ])
                .config(|config| {
                    config.optional();
                })
                .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "ShowDatabasesSchemasGrammar".into(),
            Sequence::new(vec![
                one_of(vec![
                    Ref::keyword("DATABASES").to_matchable(),
                    Ref::keyword("SCHEMAS").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    Ref::keyword("LIKE").to_matchable(),
                    Ref::new("QuotedLiteralSegment").to_matchable(),
                ])
                .config(|config| {
                    config.optional();
                })
                .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "ShowObjectGrammar".into(),
            one_of(vec![
                Sequence::new(vec![
                    one_of(vec![
                        Ref::new("ShowFunctionsGrammar").to_matchable(),
                        Ref::new("ShowViewsGrammar").to_matchable(),
                        Ref::new("ShowTablesGrammar").to_matchable(),
                        Ref::new("ShowDatabasesSchemasGrammar").to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("CREATE").to_matchable(),
                            Ref::keyword("TABLE").to_matchable(),
                            Ref::new("TableExpressionSegment").to_matchable(),
                            Sequence::new(vec![
                                Ref::keyword("AS").to_matchable(),
                                Ref::keyword("SERDE").to_matchable(),
                            ])
                            .config(|config| {
                                config.optional();
                            })
                            .to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("COLUMNS").to_matchable(),
                            Ref::keyword("IN").to_matchable(),
                            Ref::new("TableExpressionSegment").to_matchable(),
                            Sequence::new(vec![
                                Ref::keyword("IN").to_matchable(),
                                Ref::new("DatabaseReferenceSegment").to_matchable(),
                            ])
                            .config(|config| {
                                config.optional();
                            })
                            .to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            one_of(vec![
                                Ref::keyword("USER").to_matchable(),
                                Ref::keyword("SYSTEM").to_matchable(),
                                Ref::keyword("ALL").to_matchable(),
                            ])
                            .config(|config| {
                                config.optional();
                            })
                            .to_matchable(),
                            Ref::keyword("FUNCTIONS").to_matchable(),
                            one_of(vec![
                                Sequence::new(vec![
                                    Ref::new("DatabaseReferenceSegment").to_matchable(),
                                    Ref::new("DotSegment").to_matchable(),
                                    Ref::new("FunctionNameSegment").to_matchable(),
                                ])
                                .config(|config| {
                                    config.disallow_gaps();
                                    config.optional();
                                })
                                .to_matchable(),
                                Ref::new("FunctionNameSegment").optional().to_matchable(),
                                Sequence::new(vec![
                                    Ref::keyword("LIKE").to_matchable(),
                                    Ref::new("QuotedLiteralSegment").to_matchable(),
                                ])
                                .config(|config| {
                                    config.optional();
                                })
                                .to_matchable(),
                            ])
                            .to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("PARTITIONS").to_matchable(),
                            Ref::new("TableReferenceSegment").to_matchable(),
                            Ref::new("PartitionSpecGrammar").optional().to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("TABLE").to_matchable(),
                            Ref::keyword("EXTENDED").to_matchable(),
                            Sequence::new(vec![
                                one_of(vec![
                                    Ref::keyword("IN").to_matchable(),
                                    Ref::keyword("FROM").to_matchable(),
                                ])
                                .to_matchable(),
                                Ref::new("DatabaseReferenceSegment").to_matchable(),
                            ])
                            .config(|config| {
                                config.optional();
                            })
                            .to_matchable(),
                            Ref::keyword("LIKE").to_matchable(),
                            Ref::new("QuotedLiteralSegment").to_matchable(),
                            Ref::new("PartitionSpecGrammar").optional().to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("TBLPROPERTIES").to_matchable(),
                            Ref::new("TableReferenceSegment").to_matchable(),
                            Ref::new("BracketedPropertyNameListGrammar")
                                .optional()
                                .to_matchable(),
                        ])
                        .to_matchable(),
                        Sequence::new(vec![
                            Ref::keyword("VIEWS").to_matchable(),
                            Sequence::new(vec![
                                one_of(vec![
                                    Ref::keyword("FROM").to_matchable(),
                                    Ref::keyword("IN").to_matchable(),
                                ])
                                .to_matchable(),
                                Ref::new("DatabaseReferenceSegment").to_matchable(),
                            ])
                            .config(|config| {
                                config.optional();
                            })
                            .to_matchable(),
                            Sequence::new(vec![
                                Ref::keyword("LIKE").to_matchable(),
                                Ref::new("QuotedLiteralSegment").to_matchable(),
                            ])
                            .config(|config| {
                                config.optional();
                            })
                            .to_matchable(),
                        ])
                        .to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable(),
            ])
            .to_matchable()
            .into(),
        ),
    ]);

    sparksql_dialect.replace_grammar(
        "FrameClauseSegment",
        {
            let frame_extent = one_of(vec![
                Sequence::new(vec![
                    Ref::keyword("CURRENT").to_matchable(),
                    Ref::keyword("ROW").to_matchable(),
                ])
                .to_matchable(),
                Sequence::new(vec![
                    one_of(vec![
                        Ref::new("NumericLiteralSegment").to_matchable(),
                        Ref::keyword("UNBOUNDED").to_matchable(),
                        Ref::new("IntervalExpressionSegment").to_matchable(),
                    ])
                    .to_matchable(),
                    one_of(vec![
                        Ref::keyword("PRECEDING").to_matchable(),
                        Ref::keyword("FOLLOWING").to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable(),
            ]);

            Sequence::new(vec![
                Ref::new("FrameClauseUnitGrammar").to_matchable(),
                one_of(vec![
                    frame_extent.clone().to_matchable(),
                    Sequence::new(vec![
                        Ref::keyword("BETWEEN").to_matchable(),
                        frame_extent.clone().to_matchable(),
                        Ref::keyword("AND").to_matchable(),
                        frame_extent.to_matchable(),
                    ])
                    .to_matchable(),
                ])
                .to_matchable(),
            ])
        }
        .to_matchable(),
    );
    sparksql_dialect
}

use sqruff_lib_core::dialects::init::DialectConfig;
use sqruff_lib_core::value::Value;

sqruff_lib_core::dialect_config!(SparkSQLDialectConfig {});

pub fn dialect(config: Option<&Value>) -> Dialect {
    // Parse and validate dialect configuration, falling back to defaults on failure
    let _dialect_config: SparkSQLDialectConfig = config
        .map(SparkSQLDialectConfig::from_value)
        .unwrap_or_default();

    raw_dialect().config(|c| c.expand())
}
