Skip to main content

sqruff_lib_dialects/
tsql.rs

1// T-SQL (Transact-SQL) dialect implementation for Microsoft SQL Server
2
3use itertools::Itertools;
4use sqruff_lib_core::dialects::Dialect;
5use sqruff_lib_core::dialects::init::DialectKind;
6use sqruff_lib_core::dialects::syntax::SyntaxKind;
7use sqruff_lib_core::helpers::{Config, ToMatchable};
8use sqruff_lib_core::parser::grammar::Ref;
9use sqruff_lib_core::parser::grammar::anyof::{AnyNumberOf, one_of, optionally_bracketed};
10use sqruff_lib_core::parser::grammar::conditional::Conditional;
11use sqruff_lib_core::parser::grammar::delimited::Delimited;
12use sqruff_lib_core::parser::grammar::sequence::{Bracketed, Sequence};
13use sqruff_lib_core::parser::lexer::Matcher;
14use sqruff_lib_core::parser::lookahead::LookaheadExclude;
15use sqruff_lib_core::parser::node_matcher::NodeMatcher;
16use sqruff_lib_core::parser::parsers::{RegexParser, StringParser, TypedParser};
17use sqruff_lib_core::parser::segments::generator::SegmentGenerator;
18use sqruff_lib_core::parser::segments::meta::MetaSegment;
19use sqruff_lib_core::parser::types::ParseMode;
20
21use crate::{ansi, tsql_keywords};
22use sqruff_lib_core::dialects::init::{DialectConfig, NullDialectConfig};
23use sqruff_lib_core::value::Value;
24
25/// Configuration for the T-SQL dialect.
26pub type TSQLDialectConfig = NullDialectConfig;
27
28pub fn dialect(config: Option<&Value>) -> Dialect {
29    // Parse and validate dialect configuration, falling back to defaults on failure
30    let _dialect_config: TSQLDialectConfig = config
31        .map(TSQLDialectConfig::from_value)
32        .unwrap_or_default();
33
34    raw_dialect().config(|dialect| dialect.expand())
35}
36
37pub fn raw_dialect() -> Dialect {
38    // Start with ANSI SQL as the base dialect and customize for T-SQL
39    let mut dialect = ansi::raw_dialect();
40    dialect.name = DialectKind::Tsql;
41
42    // Extend ANSI keywords with T-SQL specific keywords
43    // IMPORTANT: Don't clear ANSI keywords as they contain fundamental SQL keywords
44    dialect
45        .sets_mut("reserved_keywords")
46        .extend(tsql_keywords::tsql_additional_reserved_keywords());
47    dialect
48        .sets_mut("unreserved_keywords")
49        .extend(tsql_keywords::tsql_additional_unreserved_keywords());
50
51    // Add table hint keywords to unreserved keywords
52    dialect.sets_mut("unreserved_keywords").extend([
53        "NOLOCK",
54        "READUNCOMMITTED",
55        "READCOMMITTED",
56        "REPEATABLEREAD",
57        "SERIALIZABLE",
58        "READPAST",
59        "ROWLOCK",
60        "TABLOCK",
61        "TABLOCKX",
62        "UPDLOCK",
63        "XLOCK",
64        "NOEXPAND",
65        "INDEX",
66        "FORCESEEK",
67        "FORCESCAN",
68        "HOLDLOCK",
69        "SNAPSHOT",
70    ]);
71
72    // T-SQL specific operators
73    dialect.sets_mut("operator_symbols").extend([
74        "%=", "&=", "*=", "+=", "-=", "/=", "^=", "|=", // Compound assignment
75        "!<", "!>", // Special comparison operators
76    ]);
77
78    // T-SQL supports square brackets for identifiers and @ for variables
79    // Insert square bracket identifier before individual bracket matchers to ensure it's matched first
80    dialect.insert_lexer_matchers(
81        vec![
82            // Square brackets for identifiers: [Column Name]
83            Matcher::regex(
84                "tsql_square_bracket_identifier",
85                r"\[[^\]]*\]",
86                SyntaxKind::DoubleQuote,
87            ),
88        ],
89        "start_square_bracket",
90    );
91
92    // Insert other T-SQL specific matchers
93    dialect.insert_lexer_matchers(
94        vec![
95            // Variables: @MyVar (local) or @@ROWCOUNT (global/system)
96            Matcher::regex(
97                "tsql_variable",
98                r"@@?[a-zA-Z_][a-zA-Z0-9_]*",
99                SyntaxKind::TsqlVariable,
100            ),
101        ],
102        "equals",
103    );
104
105    // T-SQL specific lexer patches:
106    // 1. T-SQL only uses -- for inline comments, not # (which is used in temp table names)
107    // 2. Update word pattern to allow # at the beginning (temp tables) and end (SQL Server 2017+ syntax)
108    dialect.patch_lexer_matchers(vec![
109        Matcher::regex("inline_comment", r"--[^\n]*", SyntaxKind::InlineComment),
110        Matcher::regex(
111            "word",
112            r"##?[a-zA-Z0-9_]+|[0-9a-zA-Z_]+#?",
113            SyntaxKind::Word,
114        ),
115    ]);
116
117    // Since T-SQL uses square brackets as quoted identifiers and the lexer
118    // already maps them to SyntaxKind::DoubleQuote, the ANSI QuotedIdentifierSegment
119    // should handle them correctly. No additional parser configuration needed.
120
121    // Add T-SQL specific bare functions
122    dialect.sets_mut("bare_functions").extend([
123        "CURRENT_TIMESTAMP",
124        "CURRENT_USER",
125        "SESSION_USER",
126        "SYSTEM_USER",
127        "USER",
128    ]);
129
130    // Add aggregate and other functions
131    dialect
132        .sets_mut("aggregate_functions")
133        .extend(["STRING_AGG"]);
134
135    dialect
136        .sets_mut("special_functions")
137        .extend(["COALESCE", "NULLIF", "ISNULL"]);
138
139    // T-SQL datetime units
140    dialect.sets_mut("datetime_units").extend([
141        "YEAR",
142        "YY",
143        "YYYY",
144        "QUARTER",
145        "QQ",
146        "Q",
147        "MONTH",
148        "MM",
149        "M",
150        "DAYOFYEAR",
151        "DY",
152        "Y",
153        "DAY",
154        "DD",
155        "D",
156        "WEEK",
157        "WK",
158        "WW",
159        "WEEKDAY",
160        "DW",
161        "HOUR",
162        "HH",
163        "MINUTE",
164        "MI",
165        "N",
166        "SECOND",
167        "SS",
168        "S",
169        "MILLISECOND",
170        "MS",
171        "MICROSECOND",
172        "MCS",
173        "NANOSECOND",
174        "NS",
175    ]);
176
177    // Add T-SQL specific date functions
178    dialect.sets_mut("date_part_function_name").extend([
179        "DATEADD",
180        "DATEDIFF",
181        "DATENAME",
182        "DATEPART",
183        "DAY",
184        "MONTH",
185        "YEAR",
186        "GETDATE",
187        "GETUTCDATE",
188        "SYSDATETIME",
189        "SYSUTCDATETIME",
190        "SYSDATETIMEOFFSET",
191    ]);
192
193    // Add T-SQL string functions
194    dialect.sets_mut("scalar_functions").extend([
195        "SUBSTRING",
196        "CHARINDEX",
197        "LEN",
198        "LEFT",
199        "RIGHT",
200        "LTRIM",
201        "RTRIM",
202        "REPLACE",
203        "STUFF",
204        "PATINDEX",
205        "QUOTENAME",
206        "REPLICATE",
207        "REVERSE",
208        "SPACE",
209        "STR",
210        "UNICODE",
211    ]);
212
213    // T-SQL specific value table functions
214    dialect.sets_mut("value_table_functions").extend([
215        "OPENROWSET",
216        "OPENQUERY",
217        "OPENDATASOURCE",
218        "OPENXML",
219    ]);
220
221    // Add T-SQL specific grammar
222
223    // TOP clause support (e.g., SELECT TOP 10, TOP (10) PERCENT, TOP 5 WITH TIES)
224    // T-SQL allows DISTINCT/ALL followed by TOP
225    dialect.replace_grammar(
226        "SelectClauseModifierSegment",
227        AnyNumberOf::new(vec![
228            Ref::keyword("DISTINCT").to_matchable(),
229            Ref::keyword("ALL").to_matchable(),
230            // TOP alone
231            Sequence::new(vec![
232                // https://docs.microsoft.com/en-us/sql/t-sql/queries/top-transact-sql
233                Ref::keyword("TOP").to_matchable(),
234                optionally_bracketed(vec![Ref::new("ExpressionSegment").to_matchable()])
235                    .to_matchable(),
236                Ref::keyword("PERCENT").optional().to_matchable(),
237                Ref::keyword("WITH").optional().to_matchable(),
238                Ref::keyword("TIES").optional().to_matchable(),
239            ])
240            .to_matchable(),
241        ])
242        .to_matchable(),
243    );
244
245    // T-SQL supports CTEs with DML statements (INSERT, UPDATE, DELETE, MERGE)
246    // We add these to NonWithSelectableGrammar so WithCompoundStatementSegment can use them
247    dialect.add([(
248        "NonWithSelectableGrammar".into(),
249        one_of(vec![
250            Ref::new("SetExpressionSegment").to_matchable(),
251            optionally_bracketed(vec![Ref::new("SelectStatementSegment").to_matchable()])
252                .to_matchable(),
253            Ref::new("NonSetSelectableGrammar").to_matchable(),
254            Ref::new("UpdateStatementSegment").to_matchable(),
255            Ref::new("InsertStatementSegment").to_matchable(),
256            Ref::new("DeleteStatementSegment").to_matchable(),
257            Ref::new("MergeStatementSegment").to_matchable(),
258        ])
259        .to_matchable()
260        .into(),
261    )]);
262
263    // Add T-SQL assignment operator segment
264    dialect.add([(
265        "AssignmentOperatorSegment".into(),
266        NodeMatcher::new(SyntaxKind::AssignmentOperator, |_| {
267            Ref::new("RawEqualsSegment").to_matchable()
268        })
269        .to_matchable()
270        .into(),
271    )]);
272
273    // Override NakedIdentifierSegment to support T-SQL identifiers with # at the end
274    // T-SQL allows temporary table names like #temp or ##global
275    dialect.add([(
276        "NakedIdentifierSegment".into(),
277        SegmentGenerator::new(|dialect| {
278            // Generate the anti template from the set of reserved keywords
279            let reserved_keywords = dialect.sets("reserved_keywords");
280            let pattern = reserved_keywords.iter().join("|");
281            let anti_template = format!("^({pattern})$");
282
283            // T-SQL pattern: supports both temp tables (#temp, ##global) and identifiers ending with #
284            // Pattern explanation:
285            // - ##?[A-Za-z][A-Za-z0-9_]*    matches temp tables: #temp or ##global (case insensitive)
286            // - [A-Za-z0-9_]*[A-Za-z][A-Za-z0-9_]*#?   matches regular identifiers with optional # at end
287            RegexParser::new(
288                r"(##?[A-Za-z][A-Za-z0-9_]*|[A-Za-z0-9_]*[A-Za-z][A-Za-z0-9_]*#?)",
289                SyntaxKind::NakedIdentifier,
290            )
291            .anti_template(&anti_template)
292            .to_matchable()
293        })
294        .into(),
295    )]);
296
297    // DECLARE statement for variable declarations
298    // Syntax: DECLARE @var1 INT = 10, @var2 VARCHAR(50) = 'text'
299    dialect.add([
300        (
301            "DeclareStatementSegment".into(),
302            Ref::new("DeclareStatementGrammar").to_matchable().into(),
303        ),
304        (
305            "DeclareStatementGrammar".into(),
306            Sequence::new(vec![
307                Ref::keyword("DECLARE").to_matchable(),
308                // Multiple variables can be declared with comma separation
309                Delimited::new(vec![
310                    Sequence::new(vec![
311                        Ref::new("TsqlVariableSegment").to_matchable(),
312                        Sequence::new(vec![Ref::keyword("AS").to_matchable()])
313                            .config(|this| this.optional())
314                            .to_matchable(),
315                        one_of(vec![
316                            // Regular variable declaration
317                            Sequence::new(vec![
318                                Ref::new("DatatypeSegment").to_matchable(),
319                                Sequence::new(vec![
320                                    Ref::new("AssignmentOperatorSegment").to_matchable(),
321                                    Ref::new("ExpressionSegment").to_matchable(),
322                                ])
323                                .config(|this| this.optional())
324                                .to_matchable(),
325                            ])
326                            .to_matchable(),
327                            // Table variable declaration
328                            Sequence::new(vec![
329                                Ref::keyword("TABLE").to_matchable(),
330                                Bracketed::new(vec![
331                                    Delimited::new(vec![
332                                        one_of(vec![
333                                            Ref::new("TableConstraintSegment").to_matchable(),
334                                            Ref::new("ColumnDefinitionSegment").to_matchable(),
335                                        ])
336                                        .to_matchable(),
337                                    ])
338                                    .config(|this| this.allow_trailing())
339                                    .to_matchable(),
340                                ])
341                                .to_matchable(),
342                            ])
343                            .to_matchable(),
344                        ])
345                        .to_matchable(),
346                    ])
347                    .to_matchable(),
348                ])
349                .to_matchable(),
350            ])
351            .to_matchable()
352            .into(),
353        ),
354    ]);
355
356    // SET statement for variables
357    dialect.add([
358        (
359            "SetVariableStatementSegment".into(),
360            Ref::new("SetVariableStatementGrammar")
361                .to_matchable()
362                .into(),
363        ),
364        (
365            "SetVariableStatementGrammar".into(),
366            Sequence::new(vec![
367                Ref::keyword("SET").to_matchable(),
368                one_of(vec![
369                    // Variable assignment
370                    Sequence::new(vec![
371                        Ref::new("TsqlVariableSegment").to_matchable(),
372                        Ref::new("AssignmentOperatorSegment").to_matchable(),
373                        Ref::new("ExpressionSegment").to_matchable(),
374                    ])
375                    .to_matchable(),
376                    // SET DEADLOCK_PRIORITY
377                    Sequence::new(vec![
378                        Ref::keyword("DEADLOCK_PRIORITY").to_matchable(),
379                        one_of(vec![
380                            Ref::keyword("LOW").to_matchable(),
381                            Ref::keyword("NORMAL").to_matchable(),
382                            Ref::keyword("HIGH").to_matchable(),
383                            Ref::new("NumericLiteralSegment").to_matchable(), // Positive numbers
384                            Sequence::new(vec![
385                                // Negative numbers
386                                Ref::new("MinusSegment").to_matchable(),
387                                Ref::new("NumericLiteralSegment").to_matchable(),
388                            ])
389                            .to_matchable(),
390                            Ref::new("TsqlVariableSegment").to_matchable(),
391                        ])
392                        .to_matchable(),
393                    ])
394                    .to_matchable(),
395                    // SET options - supports both individual and shared ON/OFF
396                    one_of(vec![
397                        // Individual ON/OFF: SET NOCOUNT ON, XACT_ABORT OFF
398                        Delimited::new(vec![
399                            Sequence::new(vec![
400                                one_of(vec![
401                                    Ref::keyword("NOCOUNT").to_matchable(),
402                                    Ref::keyword("XACT_ABORT").to_matchable(),
403                                    Ref::keyword("QUOTED_IDENTIFIER").to_matchable(),
404                                    Ref::keyword("ANSI_NULLS").to_matchable(),
405                                    Ref::keyword("ANSI_PADDING").to_matchable(),
406                                    Ref::keyword("ANSI_WARNINGS").to_matchable(),
407                                    Ref::keyword("ARITHABORT").to_matchable(),
408                                    Ref::keyword("CONCAT_NULL_YIELDS_NULL").to_matchable(),
409                                    Ref::keyword("NUMERIC_ROUNDABORT").to_matchable(),
410                                ])
411                                .to_matchable(),
412                                one_of(vec![
413                                    Ref::keyword("ON").to_matchable(),
414                                    Ref::keyword("OFF").to_matchable(),
415                                ])
416                                .to_matchable(),
417                            ])
418                            .to_matchable(),
419                        ])
420                        .to_matchable(),
421                        // Shared ON/OFF: SET NOCOUNT, XACT_ABORT ON
422                        Sequence::new(vec![
423                            Delimited::new(vec![
424                                one_of(vec![
425                                    Ref::keyword("NOCOUNT").to_matchable(),
426                                    Ref::keyword("XACT_ABORT").to_matchable(),
427                                    Ref::keyword("QUOTED_IDENTIFIER").to_matchable(),
428                                    Ref::keyword("ANSI_NULLS").to_matchable(),
429                                    Ref::keyword("ANSI_PADDING").to_matchable(),
430                                    Ref::keyword("ANSI_WARNINGS").to_matchable(),
431                                    Ref::keyword("ARITHABORT").to_matchable(),
432                                    Ref::keyword("CONCAT_NULL_YIELDS_NULL").to_matchable(),
433                                    Ref::keyword("NUMERIC_ROUNDABORT").to_matchable(),
434                                ])
435                                .to_matchable(),
436                            ])
437                            .to_matchable(),
438                            one_of(vec![
439                                Ref::keyword("ON").to_matchable(),
440                                Ref::keyword("OFF").to_matchable(),
441                            ])
442                            .to_matchable(),
443                        ])
444                        .to_matchable(),
445                    ])
446                    .to_matchable(),
447                ])
448                .to_matchable(),
449            ])
450            .to_matchable()
451            .into(),
452        ),
453    ]);
454
455    // PRINT statement
456    dialect.add([
457        (
458            "PrintStatementSegment".into(),
459            Ref::new("PrintStatementGrammar").to_matchable().into(),
460        ),
461        (
462            "PrintStatementGrammar".into(),
463            Sequence::new(vec![
464                Ref::keyword("PRINT").to_matchable(),
465                Ref::new("ExpressionSegment").to_matchable(),
466            ])
467            .to_matchable()
468            .into(),
469        ),
470    ]);
471
472    // BEGIN...END blocks for grouping multiple statements
473    dialect.add([
474        (
475            "BeginEndBlockSegment".into(),
476            Sequence::new(vec![
477                Ref::keyword("BEGIN").to_matchable(),
478                MetaSegment::indent().to_matchable(),
479                AnyNumberOf::new(vec![
480                    Sequence::new(vec![
481                        one_of(vec![
482                            Ref::new("SelectableGrammar").to_matchable(),
483                            Ref::new("InsertStatementSegment").to_matchable(),
484                            Ref::new("UpdateStatementSegment").to_matchable(),
485                            Ref::new("DeleteStatementSegment").to_matchable(),
486                            Ref::new("CreateTableStatementSegment").to_matchable(),
487                            Ref::new("DropTableStatementSegment").to_matchable(),
488                            Ref::new("DeclareStatementSegment").to_matchable(),
489                            Ref::new("SetVariableStatementSegment").to_matchable(),
490                            Ref::new("PrintStatementSegment").to_matchable(),
491                            Ref::new("IfStatementSegment").to_matchable(),
492                            Ref::new("WhileStatementSegment").to_matchable(),
493                            Ref::new("TryBlockSegment").to_matchable(),
494                            Ref::new("GotoStatementSegment").to_matchable(),
495                            Ref::new("LabelSegment").to_matchable(),
496                            Ref::new("BeginEndBlockSegment").to_matchable(),
497                        ])
498                        .to_matchable(),
499                        Ref::new("DelimiterGrammar").optional().to_matchable(),
500                    ])
501                    .to_matchable(),
502                ])
503                .config(|this| {
504                    this.terminators = vec![
505                        // Terminate on END keyword
506                        Ref::keyword("END").to_matchable(),
507                        // Also terminate on statement keywords to help with boundary detection
508                        Ref::keyword("SELECT").to_matchable(),
509                        Ref::keyword("INSERT").to_matchable(),
510                        Ref::keyword("UPDATE").to_matchable(),
511                        Ref::keyword("DELETE").to_matchable(),
512                        Ref::keyword("CREATE").to_matchable(),
513                        Ref::keyword("DROP").to_matchable(),
514                        Ref::keyword("DECLARE").to_matchable(),
515                        Ref::keyword("SET").to_matchable(),
516                        Ref::keyword("PRINT").to_matchable(),
517                        Ref::keyword("IF").to_matchable(),
518                        Ref::keyword("WHILE").to_matchable(),
519                        Ref::keyword("BEGIN").to_matchable(),
520                        Ref::keyword("GOTO").to_matchable(),
521                    ];
522                })
523                .config(|this| this.min_times(0))
524                .to_matchable(),
525                MetaSegment::dedent().to_matchable(),
526                Ref::keyword("END").to_matchable(),
527            ])
528            .to_matchable()
529            .into(),
530        ),
531        (
532            "BeginEndBlockGrammar".into(),
533            Ref::new("BeginEndBlockSegment").to_matchable().into(),
534        ),
535    ]);
536
537    // TRY...CATCH blocks
538    dialect.add([(
539        "TryBlockSegment".into(),
540        Sequence::new(vec![
541            Ref::keyword("BEGIN").to_matchable(),
542            Ref::keyword("TRY").to_matchable(),
543            MetaSegment::indent().to_matchable(),
544            AnyNumberOf::new(vec![
545                Sequence::new(vec![
546                    Ref::new("StatementSegment").to_matchable(),
547                    Ref::new("DelimiterGrammar").optional().to_matchable(),
548                ])
549                .to_matchable(),
550            ])
551            .config(|this| {
552                this.terminators = vec![Ref::keyword("END").to_matchable()];
553            })
554            .to_matchable(),
555            MetaSegment::dedent().to_matchable(),
556            Ref::keyword("END").to_matchable(),
557            Ref::keyword("TRY").to_matchable(),
558            Ref::keyword("BEGIN").to_matchable(),
559            Ref::keyword("CATCH").to_matchable(),
560            MetaSegment::indent().to_matchable(),
561            AnyNumberOf::new(vec![
562                Sequence::new(vec![
563                    Ref::new("StatementSegment").to_matchable(),
564                    Ref::new("DelimiterGrammar").optional().to_matchable(),
565                ])
566                .to_matchable(),
567            ])
568            .config(|this| {
569                this.terminators = vec![Ref::keyword("END").to_matchable()];
570            })
571            .to_matchable(),
572            MetaSegment::dedent().to_matchable(),
573            Ref::keyword("END").to_matchable(),
574            Ref::keyword("CATCH").to_matchable(),
575        ])
576        .to_matchable()
577        .into(),
578    )]);
579
580    // GOTO statement and labels
581    dialect.add([
582        (
583            "GotoStatementSegment".into(),
584            Sequence::new(vec![
585                Ref::keyword("GOTO").to_matchable(),
586                Ref::new("NakedIdentifierSegment").to_matchable(), // Label name
587            ])
588            .to_matchable()
589            .into(),
590        ),
591        (
592            "LabelSegment".into(),
593            Sequence::new(vec![
594                Ref::new("NakedIdentifierSegment").to_matchable(), // Label name
595                Ref::new("ColonSegment").to_matchable(),
596            ])
597            .to_matchable()
598            .into(),
599        ),
600    ]);
601
602    // IF...ELSE statement
603    dialect.add([
604        (
605            "IfStatementSegment".into(),
606            Ref::new("IfStatementGrammar").to_matchable().into(),
607        ),
608        (
609            "IfStatementGrammar".into(),
610            Sequence::new(vec![
611                Ref::keyword("IF").to_matchable(),
612                Ref::new("ExpressionSegment").to_matchable(),
613                Ref::new("StatementSegment").to_matchable(),
614                Sequence::new(vec![
615                    Ref::keyword("ELSE").to_matchable(),
616                    Ref::new("StatementSegment").to_matchable(),
617                ])
618                .config(|this| this.optional())
619                .to_matchable(),
620            ])
621            .to_matchable()
622            .into(),
623        ),
624    ]);
625
626    // WHILE loop
627    dialect.add([
628        (
629            "WhileStatementSegment".into(),
630            Ref::new("WhileStatementGrammar").to_matchable().into(),
631        ),
632        (
633            "WhileStatementGrammar".into(),
634            Sequence::new(vec![
635                Ref::keyword("WHILE").to_matchable(),
636                Ref::new("ExpressionSegment").to_matchable(),
637                Ref::new("StatementSegment").to_matchable(),
638            ])
639            .to_matchable()
640            .into(),
641        ),
642    ]);
643
644    // PIVOT and UNPIVOT support
645    dialect.add([
646        (
647            "PivotUnpivotSegment".into(),
648            NodeMatcher::new(SyntaxKind::TableExpression, |_| {
649                Ref::new("PivotUnpivotGrammar").to_matchable()
650            })
651            .to_matchable()
652            .into(),
653        ),
654        (
655            "PivotUnpivotGrammar".into(),
656            one_of(vec![
657                // PIVOT (SUM(Amount) FOR Month IN ([Jan], [Feb], [Mar]))
658                Sequence::new(vec![
659                    Ref::keyword("PIVOT").to_matchable(),
660                    Bracketed::new(vec![
661                        Ref::new("FunctionSegment").to_matchable(),
662                        Ref::keyword("FOR").to_matchable(),
663                        Ref::new("ColumnReferenceSegment").to_matchable(),
664                        Ref::keyword("IN").to_matchable(),
665                        Bracketed::new(vec![
666                            Delimited::new(vec![Ref::new("LiteralGrammar").to_matchable()])
667                                .to_matchable(),
668                        ])
669                        .to_matchable(),
670                    ])
671                    .to_matchable(),
672                ])
673                .to_matchable(),
674                // UNPIVOT (Value FOR Month IN ([Jan], [Feb], [Mar]))
675                Sequence::new(vec![
676                    Ref::keyword("UNPIVOT").to_matchable(),
677                    Bracketed::new(vec![
678                        Ref::new("ColumnReferenceSegment").to_matchable(),
679                        Ref::keyword("FOR").to_matchable(),
680                        Ref::new("ColumnReferenceSegment").to_matchable(),
681                        Ref::keyword("IN").to_matchable(),
682                        Bracketed::new(vec![
683                            Delimited::new(vec![Ref::new("ColumnReferenceSegment").to_matchable()])
684                                .to_matchable(),
685                        ])
686                        .to_matchable(),
687                    ])
688                    .to_matchable(),
689                ])
690                .to_matchable(),
691            ])
692            .to_matchable()
693            .into(),
694        ),
695    ]);
696
697    // Override TransactionStatementSegment to require TRANSACTION/WORK after BEGIN
698    // This prevents BEGIN from being parsed as a transaction when it should be a BEGIN...END block
699    dialect.replace_grammar(
700        "TransactionStatementSegment",
701        NodeMatcher::new(SyntaxKind::TransactionStatement, |_| {
702            Sequence::new(vec![
703                one_of(vec![
704                    Ref::keyword("START").to_matchable(),
705                    Sequence::new(vec![
706                        Ref::keyword("BEGIN").to_matchable(),
707                        one_of(vec![
708                            Ref::keyword("TRANSACTION").to_matchable(),
709                            Ref::keyword("WORK").to_matchable(),
710                            Ref::keyword("TRAN").to_matchable(), // T-SQL also supports TRAN
711                        ])
712                        .to_matchable(),
713                    ])
714                    .to_matchable(),
715                    Ref::keyword("COMMIT").to_matchable(),
716                    Ref::keyword("ROLLBACK").to_matchable(),
717                    Ref::keyword("SAVE").to_matchable(), // T-SQL savepoints
718                ])
719                .to_matchable(),
720                one_of(vec![
721                    Ref::keyword("TRANSACTION").to_matchable(),
722                    Ref::keyword("WORK").to_matchable(),
723                    Ref::keyword("TRAN").to_matchable(), // T-SQL abbreviation
724                ])
725                .config(|this| this.optional())
726                .to_matchable(),
727                // Optional transaction/savepoint name
728                Ref::new("SingleIdentifierGrammar")
729                    .optional()
730                    .to_matchable(),
731            ])
732            .to_matchable()
733        })
734        .to_matchable(),
735    );
736
737    // GO batch separator - T-SQL uses GO to separate batches
738    dialect.add([
739        (
740            "BatchSeparatorSegment".into(),
741            Ref::new("BatchSeparatorGrammar").to_matchable().into(),
742        ),
743        (
744            "BatchSeparatorGrammar".into(),
745            Ref::keyword("GO").to_matchable().into(),
746        ),
747        (
748            "BatchDelimiterGrammar".into(),
749            Ref::new("BatchSeparatorGrammar").to_matchable().into(),
750        ),
751    ]);
752
753    // Override FileSegment to handle T-SQL batch separators (GO statements)
754    dialect.replace_grammar(
755        "FileSegment",
756        AnyNumberOf::new(vec![
757            one_of(vec![
758                Ref::new("StatementSegment").to_matchable(),
759                Ref::new("BatchDelimiterGrammar").to_matchable(),
760            ])
761            .to_matchable(),
762            Ref::new("DelimiterGrammar").optional().to_matchable(),
763        ])
764        .to_matchable(),
765    );
766
767    // Add T-SQL specific statement types to the statement segment
768    dialect.replace_grammar(
769        "StatementSegment",
770        one_of(vec![
771            // T-SQL specific statements (BEGIN...END blocks must come first to avoid transaction conflicts)
772            Ref::new("BeginEndBlockGrammar").to_matchable(),
773            Ref::new("TryBlockSegment").to_matchable(),
774            Ref::new("AtomicBlockSegment").to_matchable(),
775            Ref::new("DeclareStatementGrammar").to_matchable(),
776            Ref::new("SetVariableStatementGrammar").to_matchable(),
777            Ref::new("PrintStatementGrammar").to_matchable(),
778            Ref::new("IfStatementGrammar").to_matchable(),
779            Ref::new("WhileStatementGrammar").to_matchable(),
780            Ref::new("GotoStatementSegment").to_matchable(),
781            Ref::new("LabelSegment").to_matchable(),
782            Ref::new("BatchSeparatorGrammar").to_matchable(),
783            Ref::new("UseStatementGrammar").to_matchable(),
784            // Include all ANSI statement types
785            Ref::new("SelectableGrammar").to_matchable(),
786            Ref::new("MergeStatementSegment").to_matchable(),
787            Ref::new("InsertStatementSegment").to_matchable(),
788            Ref::new("TransactionStatementSegment").to_matchable(),
789            Ref::new("DropTableStatementSegment").to_matchable(),
790            Ref::new("DropViewStatementSegment").to_matchable(),
791            Ref::new("CreateUserStatementSegment").to_matchable(),
792            Ref::new("DropUserStatementSegment").to_matchable(),
793            Ref::new("TruncateStatementSegment").to_matchable(),
794            Ref::new("AccessStatementSegment").to_matchable(),
795            Ref::new("CreateTableStatementSegment").to_matchable(),
796            Ref::new("CreateRoleStatementSegment").to_matchable(),
797            Ref::new("DropRoleStatementSegment").to_matchable(),
798            Ref::new("AlterTableStatementSegment").to_matchable(),
799            Ref::new("CreateSchemaStatementSegment").to_matchable(),
800            Ref::new("SetSchemaStatementSegment").to_matchable(),
801            Ref::new("DropSchemaStatementSegment").to_matchable(),
802            Ref::new("DropTypeStatementSegment").to_matchable(),
803            Ref::new("CreateDatabaseStatementSegment").to_matchable(),
804            Ref::new("DropDatabaseStatementSegment").to_matchable(),
805            Ref::new("CreateIndexStatementSegment").to_matchable(),
806            Ref::new("DropIndexStatementSegment").to_matchable(),
807            Ref::new("CreateViewStatementSegment").to_matchable(),
808            Ref::new("DeleteStatementSegment").to_matchable(),
809            Ref::new("UpdateStatementSegment").to_matchable(),
810            Ref::new("CreateCastStatementSegment").to_matchable(),
811            Ref::new("DropCastStatementSegment").to_matchable(),
812            Ref::new("CreateFunctionStatementSegment").to_matchable(),
813            Ref::new("DropFunctionStatementSegment").to_matchable(),
814            Ref::new("CreateProcedureStatementSegment").to_matchable(),
815            Ref::new("DropProcedureStatementSegment").to_matchable(),
816            Ref::new("CreateModelStatementSegment").to_matchable(),
817            Ref::new("DropModelStatementSegment").to_matchable(),
818            Ref::new("DescribeStatementSegment").to_matchable(),
819            Ref::new("ExplainStatementSegment").to_matchable(),
820            Ref::new("CreateSequenceStatementSegment").to_matchable(),
821            Ref::new("AlterSequenceStatementSegment").to_matchable(),
822            Ref::new("DropSequenceStatementSegment").to_matchable(),
823            Ref::new("CreateTriggerStatementSegment").to_matchable(),
824            Ref::new("DropTriggerStatementSegment").to_matchable(),
825        ])
826        .config(|this| this.terminators = vec![Ref::new("DelimiterGrammar").to_matchable()])
827        .to_matchable(),
828    );
829
830    // USE statement for changing database context
831    dialect.add([
832        (
833            "UseStatementSegment".into(),
834            Ref::new("UseStatementGrammar").to_matchable().into(),
835        ),
836        (
837            "UseStatementGrammar".into(),
838            Sequence::new(vec![
839                Ref::keyword("USE").to_matchable(),
840                Ref::new("DatabaseReferenceSegment").to_matchable(),
841            ])
842            .to_matchable()
843            .into(),
844        ),
845    ]);
846
847    // Add variable reference support for T-SQL @ and @@ variables
848    dialect.add([
849        (
850            "TsqlVariableSegment".into(),
851            TypedParser::new(SyntaxKind::TsqlVariable, SyntaxKind::TsqlVariable)
852                .to_matchable()
853                .into(),
854        ),
855        (
856            "ParameterizedSegment".into(),
857            NodeMatcher::new(SyntaxKind::ParameterizedExpression, |_| {
858                Ref::new("TsqlVariableSegment").to_matchable()
859            })
860            .to_matchable()
861            .into(),
862        ),
863        (
864            "TsqlTableVariableSegment".into(),
865            NodeMatcher::new(SyntaxKind::TableReference, |_| {
866                Ref::new("TsqlVariableSegment").to_matchable()
867            })
868            .to_matchable()
869            .into(),
870        ),
871    ]);
872
873    // Update TableReferenceSegment to support T-SQL table variables
874    // Temp tables are now handled as regular ObjectReferenceSegment since they use word tokens
875    dialect.replace_grammar(
876        "TableReferenceSegment",
877        one_of(vec![
878            Ref::new("ObjectReferenceSegment").to_matchable(),
879            Ref::new("TsqlVariableSegment").to_matchable(),
880        ])
881        .to_matchable(),
882    );
883
884    // Update TableExpressionSegment to include PIVOT/UNPIVOT
885    dialect.replace_grammar(
886        "TableExpressionSegment",
887        one_of(vec![
888            Ref::new("ValuesClauseSegment").to_matchable(),
889            Ref::new("BareFunctionSegment").to_matchable(),
890            Ref::new("FunctionSegment").to_matchable(),
891            Ref::new("TableReferenceSegment").to_matchable(),
892            Bracketed::new(vec![Ref::new("SelectableGrammar").to_matchable()]).to_matchable(),
893            Sequence::new(vec![
894                Ref::new("TableReferenceSegment").to_matchable(),
895                Ref::new("PivotUnpivotGrammar").to_matchable(),
896            ])
897            .to_matchable(),
898        ])
899        .to_matchable(),
900    );
901
902    // Table hints support - Example: SELECT * FROM Users WITH (NOLOCK)
903    dialect.add([
904        (
905            "TableHintSegment".into(),
906            Sequence::new(vec![
907                Ref::keyword("WITH").to_matchable(),
908                Bracketed::new(vec![
909                    Delimited::new(vec![Ref::new("TableHintElement").to_matchable()])
910                        .to_matchable(),
911                ])
912                .config(|this| this.parse_mode = ParseMode::Greedy)
913                .to_matchable(),
914            ])
915            .to_matchable()
916            .into(),
917        ),
918        (
919            "TableHintElement".into(),
920            one_of(vec![
921                // Simple hints (just keywords)
922                Ref::keyword("NOLOCK").to_matchable(),
923                Ref::keyword("READUNCOMMITTED").to_matchable(),
924                Ref::keyword("READCOMMITTED").to_matchable(),
925                Ref::keyword("REPEATABLEREAD").to_matchable(),
926                Ref::keyword("SERIALIZABLE").to_matchable(),
927                Ref::keyword("READPAST").to_matchable(),
928                Ref::keyword("ROWLOCK").to_matchable(),
929                Ref::keyword("TABLOCK").to_matchable(),
930                Ref::keyword("TABLOCKX").to_matchable(),
931                Ref::keyword("UPDLOCK").to_matchable(),
932                Ref::keyword("XLOCK").to_matchable(),
933                Ref::keyword("NOEXPAND").to_matchable(),
934                Ref::keyword("FORCESEEK").to_matchable(),
935                Ref::keyword("FORCESCAN").to_matchable(),
936                Ref::keyword("HOLDLOCK").to_matchable(),
937                Ref::keyword("SNAPSHOT").to_matchable(),
938                // INDEX hint with parameter
939                Sequence::new(vec![
940                    Ref::keyword("INDEX").to_matchable(),
941                    Bracketed::new(vec![
942                        one_of(vec![
943                            Ref::new("NumericLiteralSegment").to_matchable(),
944                            Ref::new("NakedIdentifierSegment").to_matchable(),
945                        ])
946                        .to_matchable(),
947                    ])
948                    .to_matchable(),
949                ])
950                .to_matchable(),
951            ])
952            .to_matchable()
953            .into(),
954        ),
955    ]);
956
957    // Define PostTableExpressionGrammar to include T-SQL table hints
958    dialect.add([(
959        "PostTableExpressionGrammar".into(),
960        Ref::new("TableHintSegment")
961            .optional()
962            .to_matchable()
963            .into(),
964    )]);
965
966    // Override FromExpressionElementSegment to ensure table hints are parsed correctly
967    // The LookaheadExclude prevents WITH from being parsed as an alias when followed by (
968    dialect.replace_grammar(
969        "FromExpressionElementSegment",
970        Sequence::new(vec![
971            Ref::new("PreTableFunctionKeywordsGrammar")
972                .optional()
973                .to_matchable(),
974            optionally_bracketed(vec![Ref::new("TableExpressionSegment").to_matchable()])
975                .to_matchable(),
976            Ref::new("AliasExpressionSegment")
977                .exclude(one_of(vec![
978                    Ref::new("FromClauseTerminatorGrammar").to_matchable(),
979                    Ref::new("SamplingExpressionSegment").to_matchable(),
980                    Ref::new("JoinLikeClauseGrammar").to_matchable(),
981                    LookaheadExclude::new("WITH", "(").to_matchable(), // Prevents WITH from being parsed as alias when followed by (
982                ]))
983                .optional()
984                .to_matchable(),
985            Sequence::new(vec![
986                Ref::keyword("WITH").to_matchable(),
987                Ref::keyword("OFFSET").to_matchable(),
988                Ref::new("AliasExpressionSegment").to_matchable(),
989            ])
990            .config(|this| this.optional())
991            .to_matchable(),
992            Ref::new("SamplingExpressionSegment")
993                .optional()
994                .to_matchable(),
995            Ref::new("PostTableExpressionGrammar")
996                .optional()
997                .to_matchable(), // T-SQL table hints
998        ])
999        .to_matchable(),
1000    );
1001
1002    // Update JoinClauseSegment to handle APPLY syntax properly
1003    dialect.replace_grammar(
1004        "JoinClauseSegment",
1005        one_of(vec![
1006            // Standard JOIN syntax
1007            Sequence::new(vec![
1008                Ref::new("JoinTypeKeywordsGrammar")
1009                    .optional()
1010                    .to_matchable(),
1011                Ref::new("JoinKeywordsGrammar").to_matchable(),
1012                MetaSegment::indent().to_matchable(),
1013                Ref::new("FromExpressionElementSegment").to_matchable(),
1014                AnyNumberOf::new(vec![Ref::new("NestedJoinGrammar").to_matchable()]).to_matchable(),
1015                MetaSegment::dedent().to_matchable(),
1016                Sequence::new(vec![
1017                    Conditional::new(MetaSegment::indent())
1018                        .indented_using_on()
1019                        .to_matchable(),
1020                    one_of(vec![
1021                        Ref::new("JoinOnConditionSegment").to_matchable(),
1022                        Sequence::new(vec![
1023                            Ref::keyword("USING").to_matchable(),
1024                            MetaSegment::indent().to_matchable(),
1025                            Bracketed::new(vec![
1026                                Delimited::new(vec![
1027                                    Ref::new("SingleIdentifierGrammar").to_matchable(),
1028                                ])
1029                                .to_matchable(),
1030                            ])
1031                            .config(|this| this.parse_mode = ParseMode::Greedy)
1032                            .to_matchable(),
1033                            MetaSegment::dedent().to_matchable(),
1034                        ])
1035                        .to_matchable(),
1036                    ])
1037                    .to_matchable(),
1038                    Conditional::new(MetaSegment::dedent())
1039                        .indented_using_on()
1040                        .to_matchable(),
1041                ])
1042                .config(|this| this.optional())
1043                .to_matchable(),
1044            ])
1045            .to_matchable(),
1046            // NATURAL JOIN
1047            Sequence::new(vec![
1048                Ref::new("NaturalJoinKeywordsGrammar").to_matchable(),
1049                Ref::new("JoinKeywordsGrammar").to_matchable(),
1050                MetaSegment::indent().to_matchable(),
1051                Ref::new("FromExpressionElementSegment").to_matchable(),
1052                MetaSegment::dedent().to_matchable(),
1053            ])
1054            .to_matchable(),
1055            // T-SQL APPLY syntax
1056            Sequence::new(vec![
1057                one_of(vec![
1058                    Ref::keyword("CROSS").to_matchable(),
1059                    Ref::keyword("OUTER").to_matchable(),
1060                ])
1061                .to_matchable(),
1062                Ref::keyword("APPLY").to_matchable(),
1063                MetaSegment::indent().to_matchable(),
1064                Ref::new("FromExpressionElementSegment").to_matchable(),
1065                MetaSegment::dedent().to_matchable(),
1066            ])
1067            .to_matchable(),
1068        ])
1069        .to_matchable(),
1070    );
1071
1072    // T-SQL specific data type handling for MAX keyword and -1
1073    // Override BracketedArguments to accept MAX keyword and negative numbers
1074    dialect.replace_grammar(
1075        "BracketedArguments",
1076        Bracketed::new(vec![
1077            Delimited::new(vec![
1078                one_of(vec![
1079                    Ref::new("LiteralGrammar").to_matchable(),
1080                    Ref::keyword("MAX").to_matchable(),
1081                    // Support negative numbers like -1 for NVARCHAR(-1)
1082                    Sequence::new(vec![
1083                        Ref::new("SignedSegmentGrammar").to_matchable(),
1084                        Ref::new("NumericLiteralSegment").to_matchable(),
1085                    ])
1086                    .to_matchable(),
1087                ])
1088                .to_matchable(),
1089            ])
1090            .config(|this| {
1091                this.optional();
1092            })
1093            .to_matchable(),
1094        ])
1095        .to_matchable(),
1096    );
1097
1098    // APPLY clause support (CROSS APPLY and OUTER APPLY)
1099    // APPLY invokes a table-valued function for each row of the outer table
1100    // CROSS APPLY: Like INNER JOIN - returns only rows with results
1101    // OUTER APPLY: Like LEFT JOIN - returns all rows, NULLs when no results
1102    dialect.add([(
1103        "ApplyClauseSegment".into(),
1104        NodeMatcher::new(
1105            SyntaxKind::JoinClause,
1106            |_| // APPLY is classified as a join type
1107            Sequence::new(vec![
1108                one_of(vec![Ref::keyword("CROSS").to_matchable(), Ref::keyword("OUTER").to_matchable()]).to_matchable(),
1109                Ref::keyword("APPLY").to_matchable(),
1110                MetaSegment::indent().to_matchable(),
1111                Ref::new("FromExpressionElementSegment").to_matchable(), // The function or subquery
1112                MetaSegment::dedent().to_matchable()
1113            ])
1114            .to_matchable(),
1115        )
1116        .to_matchable()
1117        .into(),
1118    )]);
1119
1120    // Add JoinLikeClauseGrammar for T-SQL to include APPLY
1121    // This allows APPLY to be used wherever joins are allowed
1122    dialect.add([(
1123        "JoinLikeClauseGrammar".into(),
1124        Ref::new("ApplyClauseSegment").to_matchable().into(),
1125    )]);
1126
1127    // WITHIN GROUP support for ordered set aggregate functions
1128    dialect.add([(
1129        "WithinGroupClauseSegment".into(),
1130        NodeMatcher::new(SyntaxKind::WithingroupClause, |_| {
1131            Sequence::new(vec![
1132                Ref::keyword("WITHIN").to_matchable(),
1133                Ref::keyword("GROUP").to_matchable(),
1134                Bracketed::new(vec![
1135                    Ref::new("OrderByClauseSegment").optional().to_matchable(),
1136                ])
1137                .to_matchable(),
1138            ])
1139            .to_matchable()
1140        })
1141        .to_matchable()
1142        .into(),
1143    )]);
1144
1145    // Override PostFunctionGrammar to include WITHIN GROUP
1146    dialect.add([(
1147        "PostFunctionGrammar".into(),
1148        AnyNumberOf::new(vec![
1149            Ref::new("WithinGroupClauseSegment").to_matchable(),
1150            Ref::new("OverClauseSegment").to_matchable(),
1151            Ref::new("FilterClauseGrammar").to_matchable(),
1152        ])
1153        .to_matchable()
1154        .into(),
1155    )]);
1156
1157    // Add T-SQL IDENTITY constraint support
1158    dialect.add([(
1159        "IdentityConstraintGrammar".into(),
1160        Sequence::new(vec![
1161            Ref::keyword("IDENTITY").to_matchable(),
1162            Bracketed::new(vec![
1163                Ref::new("NumericLiteralSegment").to_matchable(), // seed
1164                Ref::new("CommaSegment").to_matchable(),
1165                Ref::new("NumericLiteralSegment").to_matchable(), // increment
1166            ])
1167            .config(|this| this.optional())
1168            .to_matchable(), // IDENTITY() can be empty
1169        ])
1170        .to_matchable()
1171        .into(),
1172    )]);
1173
1174    // Extend ColumnConstraintSegment to include T-SQL specific constraints
1175    dialect.add([(
1176        "ColumnConstraintSegment".into(),
1177        NodeMatcher::new(SyntaxKind::ColumnConstraintSegment, |_| {
1178            Sequence::new(vec![
1179                Sequence::new(vec![
1180                    Ref::keyword("CONSTRAINT").to_matchable(),
1181                    Ref::new("ObjectReferenceSegment").to_matchable(),
1182                ])
1183                .config(|this| this.optional())
1184                .to_matchable(),
1185                one_of(vec![
1186                    // NOT NULL / NULL
1187                    Sequence::new(vec![
1188                        Ref::keyword("NOT").optional().to_matchable(),
1189                        Ref::keyword("NULL").to_matchable(),
1190                    ])
1191                    .to_matchable(),
1192                    // CHECK constraint
1193                    Sequence::new(vec![
1194                        Ref::keyword("CHECK").to_matchable(),
1195                        Bracketed::new(vec![Ref::new("ExpressionSegment").to_matchable()])
1196                            .to_matchable(),
1197                    ])
1198                    .to_matchable(),
1199                    // DEFAULT constraint
1200                    Sequence::new(vec![
1201                        Ref::keyword("DEFAULT").to_matchable(),
1202                        Ref::new("ColumnConstraintDefaultGrammar").to_matchable(),
1203                    ])
1204                    .to_matchable(),
1205                    Ref::new("PrimaryKeyGrammar").to_matchable(),
1206                    Ref::new("UniqueKeyGrammar").to_matchable(),
1207                    Ref::new("IdentityConstraintGrammar").to_matchable(), // T-SQL IDENTITY
1208                    Ref::new("AutoIncrementGrammar").to_matchable(), // Keep ANSI AUTO_INCREMENT
1209                    Ref::new("ReferenceDefinitionGrammar").to_matchable(),
1210                    Ref::new("CommentClauseSegment").to_matchable(),
1211                    // COLLATE
1212                    Sequence::new(vec![
1213                        Ref::keyword("COLLATE").to_matchable(),
1214                        Ref::new("CollationReferenceSegment").to_matchable(),
1215                    ])
1216                    .to_matchable(),
1217                ])
1218                .to_matchable(),
1219            ])
1220            .to_matchable()
1221        })
1222        .to_matchable()
1223        .into(),
1224    )]);
1225
1226    // Add T-SQL variable support to LiteralGrammar
1227    dialect.add([(
1228        "LiteralGrammar".into(),
1229        one_of(vec![
1230            Ref::new("QuotedLiteralSegment").to_matchable(),
1231            Ref::new("NumericLiteralSegment").to_matchable(),
1232            Ref::new("BooleanLiteralGrammar").to_matchable(),
1233            Ref::new("QualifiedNumericLiteralSegment").to_matchable(),
1234            Ref::new("NullLiteralSegment").to_matchable(),
1235            Ref::new("DateTimeLiteralGrammar").to_matchable(),
1236            Ref::new("ArrayLiteralSegment").to_matchable(),
1237            Ref::new("TypedArrayLiteralSegment").to_matchable(),
1238            Ref::new("ObjectLiteralSegment").to_matchable(),
1239            Ref::new("ParameterizedSegment").to_matchable(), // Add T-SQL variables
1240        ])
1241        .to_matchable()
1242        .into(),
1243    )]);
1244
1245    // T-SQL CREATE PROCEDURE support
1246    dialect.add([
1247        (
1248            "CreateProcedureStatementSegment".into(),
1249            NodeMatcher::new(SyntaxKind::CreateProcedureStatement, |_| {
1250                Sequence::new(vec![
1251                    one_of(vec![
1252                        Ref::keyword("CREATE").to_matchable(),
1253                        Ref::keyword("ALTER").to_matchable(),
1254                        Sequence::new(vec![
1255                            Ref::keyword("CREATE").to_matchable(),
1256                            Ref::keyword("OR").to_matchable(),
1257                            Ref::keyword("ALTER").to_matchable(),
1258                        ])
1259                        .to_matchable(),
1260                    ])
1261                    .to_matchable(),
1262                    one_of(vec![
1263                        Ref::keyword("PROC").to_matchable(),
1264                        Ref::keyword("PROCEDURE").to_matchable(),
1265                    ])
1266                    .to_matchable(),
1267                    Ref::new("ObjectReferenceSegment").to_matchable(),
1268                    // Optional version number
1269                    Sequence::new(vec![
1270                        Ref::new("SemicolonSegment").to_matchable(),
1271                        Ref::new("NumericLiteralSegment").to_matchable(),
1272                    ])
1273                    .config(|this| this.optional())
1274                    .to_matchable(),
1275                    MetaSegment::indent().to_matchable(),
1276                    // Optional parameter list
1277                    Ref::new("ProcedureParameterListGrammar")
1278                        .optional()
1279                        .to_matchable(),
1280                    // Procedure options
1281                    Sequence::new(vec![
1282                        Ref::keyword("WITH").to_matchable(),
1283                        Delimited::new(vec![
1284                            Ref::keyword("ENCRYPTION").to_matchable(),
1285                            Ref::keyword("RECOMPILE").to_matchable(),
1286                            Ref::keyword("NATIVE_COMPILATION").to_matchable(),
1287                            Ref::keyword("SCHEMABINDING").to_matchable(),
1288                            Ref::new("ExecuteAsClauseGrammar").to_matchable(),
1289                        ])
1290                        .to_matchable(),
1291                    ])
1292                    .config(|this| this.optional())
1293                    .to_matchable(),
1294                    Sequence::new(vec![
1295                        Ref::keyword("FOR").to_matchable(),
1296                        Ref::keyword("REPLICATION").to_matchable(),
1297                    ])
1298                    .config(|this| this.optional())
1299                    .to_matchable(),
1300                    MetaSegment::dedent().to_matchable(),
1301                    Ref::keyword("AS").to_matchable(),
1302                    Ref::new("ProcedureDefinitionGrammar").to_matchable(),
1303                ])
1304                .to_matchable()
1305            })
1306            .to_matchable()
1307            .into(),
1308        ),
1309        (
1310            "DropProcedureStatementSegment".into(),
1311            NodeMatcher::new(SyntaxKind::DropProcedureStatement, |_| {
1312                Sequence::new(vec![
1313                    Ref::keyword("DROP").to_matchable(),
1314                    one_of(vec![
1315                        Ref::keyword("PROC").to_matchable(),
1316                        Ref::keyword("PROCEDURE").to_matchable(),
1317                    ])
1318                    .to_matchable(),
1319                    Ref::new("IfExistsGrammar").optional().to_matchable(),
1320                    Delimited::new(vec![Ref::new("ObjectReferenceSegment").to_matchable()])
1321                        .to_matchable(),
1322                ])
1323                .to_matchable()
1324            })
1325            .to_matchable()
1326            .into(),
1327        ),
1328        (
1329            "ProcedureParameterListGrammar".into(),
1330            one_of(vec![
1331                // Bracketed parameter list: (param1, param2, param3)
1332                Bracketed::new(vec![
1333                    Delimited::new(vec![Ref::new("ProcedureParameterGrammar").to_matchable()])
1334                        .config(|this| this.optional())
1335                        .to_matchable(),
1336                ])
1337                .to_matchable(),
1338                // Unbracketed parameter list: param1, param2, param3
1339                Delimited::new(vec![Ref::new("ProcedureParameterGrammar").to_matchable()])
1340                    .config(|this| this.optional())
1341                    .to_matchable(),
1342            ])
1343            .to_matchable()
1344            .into(),
1345        ),
1346        (
1347            "TsqlDatatypeSegment".into(),
1348            NodeMatcher::new(SyntaxKind::DataType, |_| {
1349                one_of(vec![
1350                    // Square bracket data type like [int], [varchar](100)
1351                    Sequence::new(vec![
1352                        TypedParser::new(SyntaxKind::DoubleQuote, SyntaxKind::DataTypeIdentifier)
1353                            .to_matchable(),
1354                        Ref::new("BracketedArguments").optional().to_matchable(),
1355                    ])
1356                    .to_matchable(),
1357                    // Regular data type (includes DatatypeIdentifierSegment for user-defined types)
1358                    Ref::new("DatatypeSegment").to_matchable(),
1359                ])
1360                .to_matchable()
1361            })
1362            .to_matchable()
1363            .into(),
1364        ),
1365        (
1366            "ProcedureParameterGrammar".into(),
1367            Sequence::new(vec![
1368                Ref::new("ParameterNameSegment").to_matchable(),
1369                Ref::new("TsqlDatatypeSegment").to_matchable(),
1370                // Optional VARYING keyword (for cursors and some special types)
1371                Ref::keyword("VARYING").optional().to_matchable(),
1372                // Optional NULL/NOT NULL
1373                Sequence::new(vec![
1374                    Ref::keyword("NOT").optional().to_matchable(),
1375                    Ref::keyword("NULL").to_matchable(),
1376                ])
1377                .config(|this| this.optional())
1378                .to_matchable(),
1379                // Optional default value
1380                Sequence::new(vec![
1381                    Ref::new("EqualsSegment").to_matchable(),
1382                    one_of(vec![
1383                        Ref::new("LiteralGrammar").to_matchable(),
1384                        Ref::keyword("NULL").to_matchable(),
1385                        // Function calls as defaults (e.g., NEWID())
1386                        Ref::new("FunctionSegment").to_matchable(),
1387                        // String literal with prefix (e.g., N'foo')
1388                        Sequence::new(vec![
1389                            Ref::new("NakedIdentifierSegment").to_matchable(), // N, B, X etc.
1390                            Ref::new("QuotedLiteralSegment").to_matchable(),
1391                        ])
1392                        .to_matchable(),
1393                    ])
1394                    .to_matchable(),
1395                ])
1396                .config(|this| this.optional())
1397                .to_matchable(),
1398                // Optional parameter modifiers (can appear in any order)
1399                AnyNumberOf::new(vec![
1400                    one_of(vec![
1401                        Ref::keyword("OUT").to_matchable(),
1402                        Ref::keyword("OUTPUT").to_matchable(),
1403                        Ref::keyword("READONLY").to_matchable(),
1404                    ])
1405                    .to_matchable(),
1406                ])
1407                .to_matchable(),
1408            ])
1409            .to_matchable()
1410            .into(),
1411        ),
1412        (
1413            "ParameterNameSegment".into(),
1414            Ref::new("TsqlVariableSegment").to_matchable().into(),
1415        ),
1416        (
1417            "ExecuteAsClauseGrammar".into(),
1418            Sequence::new(vec![
1419                Ref::keyword("EXECUTE").to_matchable(),
1420                Ref::keyword("AS").to_matchable(),
1421                one_of(vec![
1422                    Ref::keyword("CALLER").to_matchable(),
1423                    Ref::keyword("SELF").to_matchable(),
1424                    Ref::keyword("OWNER").to_matchable(),
1425                    Ref::new("QuotedLiteralSegment").to_matchable(), // user name
1426                ])
1427                .to_matchable(),
1428            ])
1429            .to_matchable()
1430            .into(),
1431        ),
1432        (
1433            "ProcedureDefinitionGrammar".into(),
1434            one_of(vec![
1435                // External CLR procedures (check this first as it's simpler)
1436                Sequence::new(vec![
1437                    Ref::keyword("EXTERNAL").to_matchable(),
1438                    Ref::keyword("NAME").to_matchable(),
1439                    Ref::new("ObjectReferenceSegment").to_matchable(),
1440                ])
1441                .to_matchable(),
1442                // Atomic blocks for natively compiled procedures
1443                Ref::new("AtomicBlockSegment").to_matchable(),
1444                // Single statement or block
1445                Ref::new("StatementSegment").to_matchable(),
1446                // Multiple statements for procedures without BEGIN...END
1447                AnyNumberOf::new(vec![
1448                    Sequence::new(vec![
1449                        Ref::new("StatementSegment").to_matchable(),
1450                        Ref::new("DelimiterGrammar").optional().to_matchable(),
1451                    ])
1452                    .to_matchable(),
1453                ])
1454                .config(|this| {
1455                    this.min_times(2); // At least 2 statements to use this branch
1456                    this.parse_mode = ParseMode::Greedy;
1457                    // Don't terminate on delimiters, keep consuming statements
1458                    this.terminators = vec![Ref::new("BatchSeparatorGrammar").to_matchable()];
1459                })
1460                .to_matchable(),
1461            ])
1462            .to_matchable()
1463            .into(),
1464        ),
1465        (
1466            "ProcedureStatementSegment".into(),
1467            // Just use StatementSegment for now - the ordering should handle precedence
1468            Ref::new("StatementSegment").to_matchable().into(),
1469        ),
1470        (
1471            "AtomicBlockSegment".into(),
1472            Sequence::new(vec![
1473                Ref::keyword("BEGIN").to_matchable(),
1474                Ref::keyword("ATOMIC").to_matchable(),
1475                Ref::keyword("WITH").to_matchable(),
1476                Bracketed::new(vec![
1477                    Delimited::new(vec![Ref::new("AtomicBlockOptionGrammar").to_matchable()])
1478                        .to_matchable(),
1479                ])
1480                .to_matchable(),
1481                MetaSegment::indent().to_matchable(),
1482                AnyNumberOf::new(vec![
1483                    Ref::new("StatementSegment").to_matchable(),
1484                    Ref::new("DelimiterGrammar").optional().to_matchable(),
1485                ])
1486                .to_matchable(),
1487                MetaSegment::dedent().to_matchable(),
1488                Ref::keyword("END").to_matchable(),
1489            ])
1490            .to_matchable()
1491            .into(),
1492        ),
1493        (
1494            "AtomicBlockOptionGrammar".into(),
1495            Sequence::new(vec![
1496                one_of(vec![
1497                    Ref::keyword("LANGUAGE").to_matchable(),
1498                    Ref::keyword("DATEFIRST").to_matchable(),
1499                    Ref::keyword("DATEFORMAT").to_matchable(),
1500                    Ref::keyword("DELAYED_DURABILITY").to_matchable(),
1501                    Sequence::new(vec![
1502                        Ref::keyword("TRANSACTION").to_matchable(),
1503                        Ref::keyword("ISOLATION").to_matchable(),
1504                        Ref::keyword("LEVEL").to_matchable(),
1505                    ])
1506                    .to_matchable(),
1507                ])
1508                .to_matchable(),
1509                Ref::new("EqualsSegment").to_matchable(),
1510                one_of(vec![
1511                    Ref::new("QuotedLiteralSegment").to_matchable(),
1512                    Ref::new("NumericLiteralSegment").to_matchable(),
1513                    Ref::new("NakedIdentifierSegment").to_matchable(),
1514                    // N'string' syntax for Unicode strings
1515                    Sequence::new(vec![
1516                        Ref::new("NakedIdentifierSegment").to_matchable(), // N prefix
1517                        Ref::new("QuotedLiteralSegment").to_matchable(),
1518                    ])
1519                    .to_matchable(),
1520                    // Special handling for multi-word isolation levels
1521                    Sequence::new(vec![
1522                        Ref::keyword("REPEATABLE").to_matchable(),
1523                        Ref::keyword("READ").to_matchable(),
1524                    ])
1525                    .to_matchable(),
1526                    Ref::keyword("SERIALIZABLE").to_matchable(),
1527                    Ref::keyword("SNAPSHOT").to_matchable(),
1528                    Ref::keyword("ON").to_matchable(),
1529                    Ref::keyword("OFF").to_matchable(),
1530                    // Date format values
1531                    Ref::keyword("MDY").to_matchable(),
1532                    Ref::keyword("DMY").to_matchable(),
1533                    Ref::keyword("YMD").to_matchable(),
1534                    Ref::keyword("YDM").to_matchable(),
1535                    Ref::keyword("MYD").to_matchable(),
1536                    Ref::keyword("DYM").to_matchable(),
1537                ])
1538                .to_matchable(),
1539            ])
1540            .to_matchable()
1541            .into(),
1542        ),
1543    ]);
1544
1545    // T-SQL supports alternative alias syntax: AliasName = Expression
1546    // The parser distinguishes between column references (table1.column1)
1547    // and alias assignments (AliasName = table1.column1)
1548    dialect.replace_grammar(
1549        "SelectClauseElementSegment",
1550        one_of(vec![
1551            // T-SQL alias equals pattern: AliasName = Expression
1552            Sequence::new(vec![
1553                Ref::new("NakedIdentifierSegment").to_matchable(),
1554                StringParser::new("=", SyntaxKind::RawComparisonOperator).to_matchable(),
1555                one_of(vec![
1556                    Ref::new("ColumnReferenceSegment").to_matchable(),
1557                    Ref::new("BaseExpressionElementGrammar").to_matchable(),
1558                ])
1559                .to_matchable(),
1560            ])
1561            .to_matchable(),
1562            // Wildcard expressions
1563            Ref::new("WildcardExpressionSegment").to_matchable(),
1564            // Everything else
1565            Sequence::new(vec![
1566                Ref::new("BaseExpressionElementGrammar").to_matchable(),
1567                Ref::new("AliasExpressionSegment").optional().to_matchable(),
1568            ])
1569            .to_matchable(),
1570        ])
1571        .to_matchable(),
1572    );
1573
1574    // T-SQL CREATE TABLE with Azure Synapse Analytics support
1575    dialect.replace_grammar(
1576        "CreateTableStatementSegment",
1577        NodeMatcher::new(SyntaxKind::CreateTableStatement, |_| {
1578            Sequence::new(vec![
1579                Ref::keyword("CREATE").to_matchable(),
1580                Ref::keyword("TABLE").to_matchable(),
1581                Ref::new("IfNotExistsGrammar").optional().to_matchable(),
1582                Ref::new("TableReferenceSegment").to_matchable(),
1583                one_of(vec![
1584                    // Regular CREATE TABLE with column definitions
1585                    Sequence::new(vec![
1586                        Bracketed::new(vec![
1587                            Delimited::new(vec![
1588                                one_of(vec![
1589                                    Ref::new("TableConstraintSegment").to_matchable(),
1590                                    Ref::new("ColumnDefinitionSegment").to_matchable(),
1591                                ])
1592                                .to_matchable(),
1593                            ])
1594                            .config(|this| this.allow_trailing())
1595                            .to_matchable(),
1596                        ])
1597                        .to_matchable(),
1598                        // Azure Synapse table options
1599                        Sequence::new(vec![
1600                            Ref::keyword("WITH").to_matchable(),
1601                            Bracketed::new(vec![
1602                                Delimited::new(vec![Ref::new("TableOptionGrammar").to_matchable()])
1603                                    .to_matchable(),
1604                            ])
1605                            .to_matchable(),
1606                        ])
1607                        .config(|this| this.optional())
1608                        .to_matchable(),
1609                    ])
1610                    .to_matchable(),
1611                    // CREATE TABLE AS SELECT with optional WITH clause before AS
1612                    Sequence::new(vec![
1613                        // Azure Synapse table options (required for CTAS)
1614                        Sequence::new(vec![
1615                            Ref::keyword("WITH").to_matchable(),
1616                            Bracketed::new(vec![
1617                                Delimited::new(vec![Ref::new("TableOptionGrammar").to_matchable()])
1618                                    .to_matchable(),
1619                            ])
1620                            .to_matchable(),
1621                        ])
1622                        .config(|this| this.optional())
1623                        .to_matchable(),
1624                        Ref::keyword("AS").to_matchable(),
1625                        optionally_bracketed(vec![Ref::new("SelectableGrammar").to_matchable()])
1626                            .to_matchable(),
1627                    ])
1628                    .to_matchable(),
1629                ])
1630                .to_matchable(),
1631            ])
1632            .to_matchable()
1633        })
1634        .to_matchable(),
1635    );
1636
1637    dialect.add([(
1638        "TableOptionGrammar".into(),
1639        one_of(vec![
1640            // Azure Synapse distribution options
1641            Sequence::new(vec![
1642                Ref::keyword("DISTRIBUTION").to_matchable(),
1643                Ref::new("EqualsSegment").to_matchable(),
1644                one_of(vec![
1645                    Ref::keyword("ROUND_ROBIN").to_matchable(),
1646                    Ref::keyword("REPLICATE").to_matchable(),
1647                    Sequence::new(vec![
1648                        Ref::keyword("HASH").to_matchable(),
1649                        Bracketed::new(vec![Ref::new("ColumnReferenceSegment").to_matchable()])
1650                            .to_matchable(),
1651                    ])
1652                    .to_matchable(),
1653                ])
1654                .to_matchable(),
1655            ])
1656            .to_matchable(),
1657            // Azure Synapse index options
1658            one_of(vec![
1659                Ref::keyword("HEAP").to_matchable(),
1660                Sequence::new(vec![
1661                    Ref::keyword("CLUSTERED").to_matchable(),
1662                    Ref::keyword("COLUMNSTORE").to_matchable(),
1663                    Ref::keyword("INDEX").to_matchable(),
1664                ])
1665                .to_matchable(),
1666                Sequence::new(vec![
1667                    Ref::keyword("CLUSTERED").to_matchable(),
1668                    Ref::keyword("INDEX").to_matchable(),
1669                    Bracketed::new(vec![
1670                        Delimited::new(vec![Ref::new("ColumnReferenceSegment").to_matchable()])
1671                            .to_matchable(),
1672                    ])
1673                    .to_matchable(),
1674                ])
1675                .to_matchable(),
1676            ])
1677            .to_matchable(),
1678            // Other table options
1679            Sequence::new(vec![
1680                Ref::keyword("PARTITION").to_matchable(),
1681                Bracketed::new(vec![
1682                    Ref::new("ColumnReferenceSegment").to_matchable(),
1683                    Ref::keyword("RANGE").to_matchable(),
1684                    one_of(vec![
1685                        Ref::keyword("LEFT").to_matchable(),
1686                        Ref::keyword("RIGHT").to_matchable(),
1687                    ])
1688                    .to_matchable(),
1689                    Ref::keyword("FOR").to_matchable(),
1690                    Ref::keyword("VALUES").to_matchable(),
1691                    Bracketed::new(vec![
1692                        Delimited::new(vec![Ref::new("ExpressionSegment").to_matchable()])
1693                            .to_matchable(),
1694                    ])
1695                    .to_matchable(),
1696                ])
1697                .to_matchable(),
1698            ])
1699            .to_matchable(),
1700        ])
1701        .to_matchable()
1702        .into(),
1703    )]);
1704
1705    // T-SQL uses + for both arithmetic and string concatenation
1706    dialect.add([(
1707        "StringBinaryOperatorGrammar".into(),
1708        one_of(vec![
1709            Ref::new("ConcatSegment").to_matchable(), // Standard || operator
1710            Ref::new("PlusSegment").to_matchable(),
1711        ])
1712        .to_matchable()
1713        .into(),
1714    )]);
1715
1716    // T-SQL specific data type identifier - allows case-insensitive user-defined types
1717    dialect.add([(
1718        "DatatypeIdentifierSegment".into(),
1719        SegmentGenerator::new(|_| {
1720            // Generate the anti template from the set of reserved keywords
1721            let anti_template = format!("^({})$", "NOT");
1722
1723            one_of(vec![
1724                // Case-insensitive pattern for T-SQL data type identifiers (including UDTs)
1725                RegexParser::new("[A-Za-z_][A-Za-z0-9_]*", SyntaxKind::DataTypeIdentifier)
1726                    .anti_template(&anti_template)
1727                    .to_matchable(),
1728                Ref::new("SingleIdentifierGrammar")
1729                    .exclude(Ref::new("NakedIdentifierSegment"))
1730                    .to_matchable(),
1731            ])
1732            .to_matchable()
1733        })
1734        .into(),
1735    )]);
1736
1737    // expand() must be called after all grammar modifications
1738
1739    dialect
1740}