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