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