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