diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 9920c5e..afc4929 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -28,6 +28,7 @@ ScriptDom is a library for parsing and generating T-SQL scripts. It is primarily - `Test/SqlDom/` — unit tests, baselines and test scripts. See `Only170SyntaxTests.cs`, `TestScripts/`, and `Baselines170/`. - `.github/instructions/testing.guidelines.instructions.md` — comprehensive testing framework guide with patterns and best practices. - `.github/instructions/function.guidelines.instructions.md` — specialized guide for adding new T-SQL system functions. +- `.github/instructions/database_option.guidelines.instructions.md` — specialized guide for adding database options (ALTER/CREATE DATABASE SET options). ## Developer workflow & conventions (typical change cycle) 1. Add/modify grammar rule(s) in the correct `TSql*.g` (pick the _version_ the syntax belongs to). @@ -202,6 +203,7 @@ include: .github/instructions/parser.guidelines.instructions.md include: .github/instructions/function.guidelines.instructions.md include: .github/instructions/new_data_types.guidelines.instructions.md include: .github/instructions/new_index_types.guidelines.instructions.md +include: .github/instructions/database_option.guidelines.instructions.md include: .github/instructions/debugging_workflow.guidelines.instructions.md include: .github/instructions/grammer.guidelines.instructions.md include: .github/instructions/testing.guidelines.instructions.md diff --git a/.github/instructions/adding_new_parser.guidelines.instructions.md b/.github/instructions/adding_new_parser.guidelines.instructions.md new file mode 100644 index 0000000..ef2c541 --- /dev/null +++ b/.github/instructions/adding_new_parser.guidelines.instructions.md @@ -0,0 +1,797 @@ +# Guidelines for Adding New Parser Versions to SqlScriptDOM + +This guide provides step-by-step instructions for adding support for a new SQL Server version parser to the SqlScriptDOM library. This pattern was established from the TSql180 parser + +## When to Use This Guide + +Use this pattern when: +- ✅ Adding support for a **new SQL Server version** (e.g., SQL Server 2025, vnext releases) +- ✅ Creating a **new parser for a specific compatibility level** (e.g., 180, 190, etc.) +- ✅ Adding a **new SQL engine variant** (e.g., Azure SQL, Fabric DW) + +**Prerequisites:** +- Understand ANTLR v2 grammar syntax +- Familiarity with the ScriptDom parser architecture +- Knowledge of the new SQL Server version's feature set + +## Version Numbering Convention + +SqlScriptDOM uses a version numbering scheme that corresponds to SQL Server versions: +- TSql80 = SQL Server 2000 +- TSql90 = SQL Server 2005 +- TSql100 = SQL Server 2008 +- TSql110 = SQL Server 2012 +- TSql120 = SQL Server 2014 +- TSql130 = SQL Server 2016 +- TSql140 = SQL Server 2017 +- TSql150 = SQL Server 2019 +- TSql160 = SQL Server 2022 +- TSql170 = SQL Server 2025 + +**Naming Pattern**: `TSql{CompatibilityLevel}` where CompatibilityLevel is typically `(MajorVersion - 1900) * 10` + +## Step-by-Step Implementation Guide + +### Step 1: Add SqlVersion Enum Value + +Update the `SqlVersion` enumeration to include the new version. + +**File**: `SqlScriptDom/ScriptDom/SqlServer/SqlVersion.cs` + +```csharp +namespace Microsoft.SqlServer.TransactSql.ScriptDom +{ + public enum SqlVersion + { + // ... existing versions ... + + /// + /// Sql 18.0 mode + /// + Sql180 = 11, + } +} +``` + +**Key Points**: +- Use the next sequential enum value +- Add XML documentation comment describing the SQL Server version +- Maintain consistent naming: `Sql{CompatibilityLevel}` + +### Step 2: Create Grammar File + +Create a new ANTLR v2 grammar file that inherits from the previous version. + +**File**: `SqlScriptDom/Parser/TSql/TSql180.g` + +```antlr +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +options { + language = "CSharp"; + namespace = "Microsoft.SqlServer.TransactSql.ScriptDom"; +} + +{ + using System.Diagnostics; + using System.Globalization; + using System.Collections.Generic; +} + +class TSql180ParserInternal extends Parser("TSql180ParserBaseInternal"); +options { + k = 2; + defaultErrorHandler=false; + classHeaderPrefix = "internal partial"; + importVocab = TSql; +} + +{ + public TSql180ParserInternal(bool initialQuotedIdentifiersOn) + : base(initialQuotedIdentifiersOn) + { + initialize(); + } +} + +// Entry point rules (required for parser functionality) +entryPointChildObjectName returns [ChildObjectName vResult = null] + : + vResult=childObjectNameWithThreePrefixes + EOF + ; + +entryPointSchemaObjectName returns [SchemaObjectName vResult = null] + : + vResult=schemaObjectThreePartName + EOF + ; + +entryPointScalarDataType returns [DataTypeReference vResult = null] + : + vResult=scalarDataType + EOF + ; + +entryPointExpression returns [ScalarExpression vResult = null] + : + vResult=expression + EOF + ; + +entryPointBooleanExpression returns [BooleanExpression vResult = null] + : + vResult=booleanExpression + EOF + ; + +entryPointStatementList returns [StatementList vResult = null] + : + vResult=statementList + EOF + ; + +// Main script entry point +script returns [TSqlScript vResult = this.FragmentFactory.CreateFragment()] + : + vResult.Batches = batches + EOF + ; + +// Add new version-specific grammar rules below +// For initial version bump, typically inherit all rules from previous version +``` + +**Key Points**: +- Grammar file initially inherits all rules from the previous version via the base class +- Add version-specific rules only when new syntax is needed +- Entry point rules are required for various parsing scenarios +- The `importVocab = TSql` directive imports token definitions + +### Step 3: Add Grammar to Build Configuration + +Register the grammar file in the build system. + +**File**: `SqlScriptDom/GenerateFiles.props` + +```xml + + + + + + +``` + +**Key Points**: +- ANTLR will generate parser code during build from this grammar file +- Generated files go to `$(CsGenIntermediateOutputPath)` (under `obj/`) +- Never manually edit generated parser files + +### Step 4: Create Parser Base Internal Class + +Create the internal parser base class that inherits from the previous version. + +**File**: `SqlScriptDom/Parser/TSql/TSql180ParserBaseInternal.cs` + +```csharp +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; +using antlr; + +namespace Microsoft.SqlServer.TransactSql.ScriptDom +{ + internal abstract class TSql180ParserBaseInternal : TSql170ParserBaseInternal + { + #region Constructors + + // Required by ANTLR-generated code + protected TSql180ParserBaseInternal(TokenBuffer tokenBuf, int k) + : base(tokenBuf, k) + { + } + + protected TSql180ParserBaseInternal(ParserSharedInputState state, int k) + : base(state, k) + { + } + + protected TSql180ParserBaseInternal(TokenStream lexer, int k) + : base(lexer, k) + { + } + + /// + /// Primary constructor + /// + /// if set to true initial quoted identifiers will be set to on. + public TSql180ParserBaseInternal(bool initialQuotedIdentifiersOn) + : base(initialQuotedIdentifiersOn) + { + } + + #endregion + + /// + /// Gets the SQL version for this parser + /// + protected override SqlVersion SqlVersionCurrent + { + get { return SqlVersion.Sql180; } + } + + // Add version-specific helper methods and overrides here as needed + } +} +``` + +**Key Points**: +- Inherits from previous version's base internal class (e.g., `TSql170ParserBaseInternal`) +- Overrides `SqlVersionCurrent` property to return the new version +- Contains version-specific validation and helper methods +- The multiple constructors are required by ANTLR's generated code + +### Step 5: Create Public Parser Class + +Create the public parser class that users will instantiate. + +**File**: `SqlScriptDom/Parser/TSql/TSql180Parser.cs` + +```csharp +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using Microsoft.SqlServer.TransactSql.ScriptDom.Versioning; + +namespace Microsoft.SqlServer.TransactSql.ScriptDom +{ + /// + /// The TSql Parser for 18.0 + /// + [Serializable] + public class TSql180Parser : TSqlParser + { + /// + /// Parser flavor (standalone/azure/all) + /// + protected SqlEngineType engineType = SqlEngineType.All; + + /// + /// Initializes a new instance of the class. + /// + /// if set to true [initial quoted identifiers]. + public TSql180Parser(bool initialQuotedIdentifiers) + : base(initialQuotedIdentifiers) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// if set to true [initial quoted identifiers]. + /// Parser engine type + public TSql180Parser(bool initialQuotedIdentifiers, SqlEngineType engineType) + : base(initialQuotedIdentifiers) + { + this.engineType = engineType; + } + + internal override TSqlLexerBaseInternal GetNewInternalLexer() + { + return new TSql180LexerInternal(); + } + + #region Helper Methods + TSql180ParserInternal GetNewInternalParser() + { + return new TSql180ParserInternal(QuotedIdentifier); + } + + TSql180ParserInternal GetNewInternalParserForInput(TextReader input, out IList errors, + int startOffset, int startLine, int startColumn) + { + TSql180ParserInternal parser = GetNewInternalParser(); + InitializeInternalParserInput(parser, input, out errors, startOffset, startLine, startColumn); + return parser; + } + #endregion + + /// + /// The main parse method. + /// + /// The list of tokens to parse. + /// The parse errors. + /// The parsed TSqlFragment. + public override TSqlFragment Parse(IList tokens, out IList errors) + { + errors = new List(); + TSql180ParserInternal parser = GetNewInternalParser(); + parser.InitializeForNewInput(tokens, errors, false); + + TSqlFragment result = parser.ParseRuleWithStandardExceptionHandling(parser.script, ScriptEntryMethod); + + // Run versioning visitor for engine-specific validation + if (result != null) + { + VersioningVisitor versioningVisitor = new VersioningVisitor(engineType, SqlVersion.Sql180); + result.Accept(versioningVisitor); + + foreach (ParseError p in versioningVisitor.GetErrors()) + { + errors.Add(p); + } + } + + return result; + } + + /// + /// Parses an input string to get a ChildObjectName. + /// + public override ChildObjectName ParseChildObjectName(TextReader input, out IList errors, + int startOffset, int startLine, int startColumn) + { + TSql180ParserInternal parser = GetNewInternalParserForInput(input, out errors, startOffset, startLine, startColumn); + return parser.ParseRuleWithStandardExceptionHandling(parser.entryPointChildObjectName, "entryPointChildObjectName"); + } + + /// + /// Parses an input string to get a SchemaObjectName. + /// + public override SchemaObjectName ParseSchemaObjectName(TextReader input, out IList errors, + int startOffset, int startLine, int startColumn) + { + TSql180ParserInternal parser = GetNewInternalParserForInput(input, out errors, startOffset, startLine, startColumn); + return parser.ParseRuleWithStandardExceptionHandling(parser.entryPointSchemaObjectName, "entryPointSchemaObjectName"); + } + + /// + /// Parses an input string to get a data type. + /// + public override DataTypeReference ParseScalarDataType(TextReader input, out IList errors, + int startOffset, int startLine, int startColumn) + { + TSql180ParserInternal parser = GetNewInternalParserForInput(input, out errors, startOffset, startLine, startColumn); + return parser.ParseRuleWithStandardExceptionHandling(parser.entryPointScalarDataType, "entryPointScalarDataType"); + } + + /// + /// Parses an input string to get an expression. + /// + public override ScalarExpression ParseExpression(TextReader input, out IList errors, + int startOffset, int startLine, int startColumn) + { + TSql180ParserInternal parser = GetNewInternalParserForInput(input, out errors, startOffset, startLine, startColumn); + return parser.ParseRuleWithStandardExceptionHandling(parser.entryPointExpression, "entryPointExpression"); + } + + /// + /// Parses an input string to get a boolean expression. + /// + public override BooleanExpression ParseBooleanExpression(TextReader input, out IList errors, + int startOffset, int startLine, int startColumn) + { + TSql180ParserInternal parser = GetNewInternalParserForInput(input, out errors, startOffset, startLine, startColumn); + return parser.ParseRuleWithStandardExceptionHandling(parser.entryPointBooleanExpression, "entryPointBooleanExpression"); + } + + /// + /// Parses an input string to get a statement list. + /// + public override StatementList ParseStatementList(TextReader input, out IList errors, + int startOffset, int startLine, int startColumn) + { + TSql180ParserInternal parser = GetNewInternalParserForInput(input, out errors, startOffset, startLine, startColumn); + return parser.ParseRuleWithStandardExceptionHandling(parser.entryPointStatementList, "entryPointStatementList"); + } + + /// + /// Parses an input string to get a subquery expression with optional common table expressions. + /// + public override SelectStatement ParseSubQueryExpressionWithOptionalCTE(TextReader input, out IList errors, + int startOffset, int startLine, int startColumn) + { + TSql180ParserInternal parser = GetNewInternalParserForInput(input, out errors, startOffset, startLine, startColumn); + return parser.ParseRuleWithStandardExceptionHandling(parser.subQueryExpressionWithOptionalCTE, "subQueryExpressionWithOptionalCTE"); + } + } +} +``` + +**Key Points**: +- Public API that developers use to parse T-SQL +- Supports engine type filtering (Standalone, Azure, All) +- Implements all entry point methods for parsing specific constructs +- Runs versioning visitor for version-specific validation + +### Step 6: Create Script Generator + +Create the script generator class for generating T-SQL from the AST. + +**File**: `SqlScriptDom/ScriptDom/SqlServer/Sql180ScriptGenerator.cs` + +```csharp +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +using Microsoft.SqlServer.TransactSql.ScriptDom.ScriptGenerator; + +namespace Microsoft.SqlServer.TransactSql.ScriptDom +{ + /// + /// Script generator for T-SQL 180 + /// + public sealed class Sql180ScriptGenerator : SqlScriptGenerator + { + /// + /// Initializes a new instance of the class. + /// + public Sql180ScriptGenerator() + : this(new SqlScriptGeneratorOptions()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The options. + public Sql180ScriptGenerator(SqlScriptGeneratorOptions options) + : base(options) + { + options.SqlVersion = SqlVersion.Sql180; + } + + internal override SqlScriptGeneratorVisitor CreateSqlScriptGeneratorVisitor( + SqlScriptGeneratorOptions options, ScriptWriter scriptWriter) + { + ScriptGeneratorSupporter.CheckForNullReference(options, "options"); + ScriptGeneratorSupporter.CheckForNullReference(scriptWriter, "scriptWriter"); + + return new Sql180ScriptGeneratorVisitor(options, scriptWriter); + } + } +} +``` + +**Key Points**: +- Sealed class that cannot be inherited +- Sets `SqlVersion.Sql180` in options +- Creates version-specific visitor for AST traversal + +### Step 7: Create Script Generator Visitor + +Create the visitor class that generates T-SQL from the AST. + +**File**: `SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/Sql180ScriptGeneratorVisitor.cs` + +```csharp +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.SqlServer.TransactSql.ScriptDom.ScriptGenerator +{ + internal partial class Sql180ScriptGeneratorVisitor : Sql170ScriptGeneratorVisitor + { + public Sql180ScriptGeneratorVisitor(SqlScriptGeneratorOptions options, ScriptWriter writer) + : base(options, writer) + { + } + + // Add version-specific visitor overrides here as needed + // Override visit methods for AST nodes that have new syntax in this version + } +} +``` + +**Key Points**: +- Inherits from previous version's visitor (e.g., `Sql170ScriptGeneratorVisitor`) +- Override visit methods only for nodes with new version-specific syntax +- Uses visitor pattern to traverse and generate T-SQL from AST + +### Step 8: Update TSqlParser Factory + +Add the new version to the parser factory method. + +**File**: `SqlScriptDom/Parser/TSql/TSqlParser.cs` + +Find the `CreateParser` method and add a case: + +```csharp +public static TSqlParser CreateParser(SqlVersion tsqlParserVersion, bool initialQuotedIdentifiers = false) +{ + switch (tsqlParserVersion) + { + // ... existing cases ... + case SqlVersion.Sql170: + return new TSql170Parser(initialQuotedIdentifiers); + case SqlVersion.Sql180: + return new TSql180Parser(initialQuotedIdentifiers); + default: + throw new ArgumentException( + String.Format(CultureInfo.CurrentCulture, + SqlScriptGeneratorResource.UnknownEnumValue, + tsqlParserVersion, + "TSqlParserVersion"), + "tsqlParserVersion"); + } +} +``` + +### Step 9: Add SqlVersionFlags Support + +Add version flags for the new version if needed for validation logic. + +**File**: `SqlScriptDom/Parser/TSql/SqlVersionFlags.cs` + +```csharp +[Flags] +internal enum SqlVersionFlags +{ + // ... existing flags ... + TSql170AndAbove = TSql170 | TSql180, + TSql180 = 0x00000800, + TSql180AndAbove = TSql180, +} +``` + +**Key Points**: +- Add individual version flag as power of 2 +- Add `AndAbove` flag combining all versions >= this version +- Used for validation logic that checks syntax availability + +### Step 10: Update OptionsHelper (if needed) + +If the new version introduces new options or changes option availability, update the options helper. + +**File**: `SqlScriptDom/Parser/TSql/OptionsHelper.cs` + +Add version checks where options become available: + +```csharp +// Example: if a new option becomes available in 180 +internal static bool IsOptionValidForVersion(IndexOptionKind option, SqlVersion version) +{ + switch (option) + { + // ... existing options ... + case IndexOptionKind.NewOption: + return version >= SqlVersion.Sql180; + default: + return true; + } +} +``` + +### Step 11: Create Test Infrastructure + +Create test class for version-specific syntax tests. + +**File**: `Test/SqlDom/Only180SyntaxTests.cs` + +```csharp +using Microsoft.SqlServer.TransactSql.ScriptDom; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SqlStudio.Tests.AssemblyTools.TestCategory; +using System; +using System.Collections.Generic; +using System.IO; + +namespace SqlStudio.Tests.UTSqlScriptDom +{ + public partial class SqlDomTests + { + // Note: These filenames are case sensitive, match checked-in files exactly + private static readonly ParserTest[] Only180TestInfos = + { + // Add new 180-specific tests here as needed + // Example: + // new ParserTest180("NewFeatureTest180.sql"), + }; + + private static readonly ParserTest[] SqlAzure180_TestInfos = + { + // Add Azure-specific syntax tests here + }; + + /// + /// Test method for TSql180 version-specific syntax + /// + [TestMethod] + [Priority(0)] + [SqlStudioTestCategory(Category.UnitTest)] + public void TSql180SyntaxTests() + { + TSql180Parser parser = new TSql180Parser(true); + SqlScriptGenerator scriptGen = ParserTestUtils.CreateScriptGen(SqlVersion.Sql180); + + foreach (ParserTest t in Only180TestInfos) + { + ParserTest.ParseAndVerify(parser, scriptGen, t); + } + } + + /// + /// Test method for Azure-specific TSql180 syntax + /// + [TestMethod] + [Priority(0)] + [SqlStudioTestCategory(Category.UnitTest)] + public void TSql180AzureSyntaxTests() + { + TSql180Parser parser = new TSql180Parser(true, SqlEngineType.SqlAzure); + SqlScriptGenerator scriptGen = ParserTestUtils.CreateScriptGen(SqlVersion.Sql180); + + foreach (ParserTest t in SqlAzure180_TestInfos) + { + ParserTest.ParseAndVerify(parser, scriptGen, t); + } + } + } +} +``` + +### Step 12: Create Baselines Folder + +Create the baselines folder for test output files. + +**Directory**: `Test/SqlDom/Baselines180/` + +- Create this folder even if initially empty +- Test baseline files (expected T-SQL output) will be placed here +- Baselines are generated from test runs and verified manually + +### Step 13: Update Test Project Configuration + +Add the baselines folder to embedded resources. + +**File**: `Test/SqlDom/UTSqlScriptDom.csproj` + +```xml + + + + + + + + +``` + +### Step 14: Add Parser Test Helper Methods + +Add test utility methods for the new version. + +**File**: `Test/SqlDom/ParserTest.cs` + +Add constructor helper method: + +```csharp +/// +/// Creates a parser test for TSql180 with specified error expectations +/// +public static ParserTest ParserTest180( + string fileName, + int nErrors80 = 0, int nErrors90 = 0, int nErrors100 = 0, + int nErrors110 = 0, int nErrors120 = 0, int nErrors130 = 0, + int nErrors140 = 0, int nErrors150 = 0, int nErrors160 = 0, + int nErrors170 = 0, int nErrors180 = 0, int nErrorsFabricDW = int.MaxValue) +{ + return new ParserTest( + fileName, + new int[] + { + nErrors80, nErrors90, nErrors100, nErrors110, + nErrors120, nErrors130, nErrors140, nErrors150, + nErrors160, nErrors170, nErrors180, nErrorsFabricDW + }); +} +``` + +**File**: `Test/SqlDom/TestUtilities.cs` + +Update the error count array handling to support the new version index. + +### Step 15: Add Version Mapping Tests + +Add tests to verify version enum and parser mapping. + +**File**: `Test/SqlDom/TSqlParserTest.cs` + +```csharp +[TestMethod] +public void TSql180ParserVersionTest() +{ + TSqlParser parser = TSqlParser.CreateParser(SqlVersion.Sql180); + Assert.IsNotNull(parser); + Assert.IsInstanceOfType(parser, typeof(TSql180Parser)); +} +``` + +### Step 16: Build and Verify + +Build the project to generate parser code and verify everything compiles. + +```bash +# Build the ScriptDom project +dotnet build SqlScriptDom/Microsoft.SqlServer.TransactSql.ScriptDom.csproj -c Debug + +# Run tests to verify +dotnet test Test/SqlDom/UTSqlScriptDom.csproj -c Debug +``` + +**What Happens During Build**: +1. ANTLR generates lexer and parser C# files from `.g` grammar +2. Post-processing scripts clean up generated code +3. Generated files are compiled along with hand-written sources +4. Test baselines are embedded as resources + +## Summary Checklist + +When adding a new parser version, ensure you complete: + +- [ ] **Step 1**: Add `SqlVersion` enum value +- [ ] **Step 2**: Create `.g` grammar file +- [ ] **Step 3**: Add grammar to `GenerateFiles.props` +- [ ] **Step 4**: Create `TSqlXXXParserBaseInternal.cs` +- [ ] **Step 5**: Create `TSqlXXXParser.cs` +- [ ] **Step 6**: Create `SqlXXXScriptGenerator.cs` +- [ ] **Step 7**: Create `SqlXXXScriptGeneratorVisitor.cs` +- [ ] **Step 8**: Update `TSqlParser.cs` factory method +- [ ] **Step 9**: Add `SqlVersionFlags` support +- [ ] **Step 10**: Update `OptionsHelper.cs` if needed +- [ ] **Step 11**: Create `OnlyXXXSyntaxTests.cs` +- [ ] **Step 12**: Create `BaselinesXXX/` folder +- [ ] **Step 13**: Update `UTSqlScriptDom.csproj` +- [ ] **Step 14**: Add parser test helper methods +- [ ] **Step 15**: Add version mapping tests +- [ ] **Step 16**: Build and verify everything compiles + +## Common Pitfalls + +1. **Forgetting to add grammar to GenerateFiles.props**: Parser won't be generated +2. **Incorrect inheritance chain**: Must inherit from previous version's base class +3. **Missing entry point rules in grammar**: Certain parsing scenarios will fail +4. **Not creating Baselines folder**: Test project won't build due to missing embedded resources +5. **Wrong enum value**: Can conflict with existing versions or break version comparison logic +6. **Missing SqlVersionFlags**: Validation logic may not work correctly +7. **Not updating factory method**: Parser can't be instantiated via factory + +## Related Documentation + +- [Testing Guidelines](testing.guidelines.instructions.md) - How to add tests for new syntax +- [Bug Fixing Guidelines](bug_fixing.guidelines.instructions.md) - How to add new grammar rules +- [Grammar Guidelines](grammer.guidelines.instructions.md) - ANTLR v2 grammar patterns +- [Validation Fix Guidelines](Validation_fix.guidelines.instructions.md) - Version-gated validation + +## References + +- Example Implementation: TSql180 parser (commits 0b84f8f through f4f4c88) +- ANTLR v2 Documentation: See `tools/antlr-2.7.5` documentation +- Build System: See `SqlScriptDom/GenerateFiles.props` for generation targets diff --git a/.github/instructions/database_option.guidelines.instructions.md b/.github/instructions/database_option.guidelines.instructions.md new file mode 100644 index 0000000..825408d --- /dev/null +++ b/.github/instructions/database_option.guidelines.instructions.md @@ -0,0 +1,493 @@ +# Adding Database Options to ScriptDOM + +This guide covers how to add new database options to `ALTER DATABASE` and `CREATE DATABASE` statements in ScriptDOM. + +## Decision Tree: Choose Your Implementation Pattern + +``` +What values does your database option accept? +│ +├─ Simple ON/OFF only +│ └─ Pattern A: Use Generic OnOffDatabaseOption (easiest) +│ Examples: AutoClose, AutoShrink, DataRetention +│ +├─ ON/OFF with sub-options (e.g., CHANGE_TRACKING (AUTO_CLEANUP = ON)) +│ └─ Pattern B: Custom AST Class + Complex Sub-options +│ Examples: ChangeTrackingDatabaseOption, QueryStoreDatabaseOption +│ +├─ Specific enum values (e.g., FULL, BULK_LOGGED, SIMPLE) +│ └─ Pattern C: Custom AST Class + Enum Helper +│ Examples: RecoveryDatabaseOption, PageVerifyDatabaseOption +│ +└─ Special behavior (only output when ON, custom formatting) + └─ Pattern D: Custom AST Class + Custom Script Generator + Examples: LedgerOption, OptimizedLockingDatabaseOption +``` + +--- + +## Pattern A: Generic ON/OFF Database Option (Recommended for Simple Cases) + +Use this pattern when your option only accepts `ON` or `OFF` values. + +### Step 1: Add to DatabaseOptionKind Enum + +**File**: `SqlScriptDom/Parser/TSql/DatabaseOptionKind.cs` + +```csharp +public enum DatabaseOptionKind +{ + // ... existing options ... + OptimizedLocking = 71, + YourNewOption = 72 // ← Add your option +} +``` + +### Step 2: Add Keyword Constant + +**File**: `SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs` + +Add in alphabetical order within the Auto* section: + +```csharp +internal const string YourOption = "YOUR_OPTION"; +internal const string AutomaticTuning = "AUTOMATIC_TUNING"; +internal const string AutoShrink = "AUTO_SHRINK"; +``` + +### Step 3: Register in OnOffSimpleDbOptionsHelper + +**File**: `SqlScriptDom/Parser/TSql/OnOffSimpleDbOptionsHelper.cs` + +Add to the constructor under the appropriate version: + +```csharp +// 170 options +AddOptionMapping(DatabaseOptionKind.YourNewOption, + CodeGenerationSupporter.YourOption, + SqlVersionFlags.TSql170AndAbove); +``` + +### Step 4: Add to RequiresEqualsSign Method (If Needed) + +Some options require `=` between option name and value (e.g., `LEDGER = ON`). If your option needs this: + +**File**: `SqlScriptDom/Parser/TSql/OnOffSimpleDbOptionsHelper.cs` + +```csharp +internal bool RequiresEqualsSign(DatabaseOptionKind optionKind) +{ + switch (optionKind) + { + case DatabaseOptionKind.MemoryOptimizedElevateToSnapshot: + case DatabaseOptionKind.NestedTriggers: + case DatabaseOptionKind.TransformNoiseWords: + case DatabaseOptionKind.Ledger: + case DatabaseOptionKind.YourNewOption: // ← Add here if needed + return true; + default: + return false; + } +} +``` + +### Step 5: Create Test Files + +**Test Script**: `Test/SqlDom/TestScripts/AlterDatabaseYourOption170.sql` + +```sql +-- YourOption for VNext and Azure +ALTER DATABASE db + SET YOUR_OPTION = ON; + +ALTER DATABASE db + SET YOUR_OPTION = OFF; +``` + +**Baseline**: `Test/SqlDom/Baselines170/AlterDatabaseYourOption170.sql` + +```sql +ALTER DATABASE db + SET YOUR_OPTION = ON; + +ALTER DATABASE db + SET YOUR_OPTION = OFF; +``` + +**Test Configuration**: `Test/SqlDom/Only170SyntaxTests.cs` + +```csharp +new ParserTest170("AlterDatabaseYourOption170.sql", + nErrors80: 2, nErrors90: 2, nErrors100: 2, + nErrors110: 2, nErrors120: 2, nErrors130: 2, + nErrors140: 2, nErrors150: 2, nErrors160: 2), +``` + +### Pattern A Complete - No Ast.xml or Script Generator Changes Needed! + +--- + +## Pattern B: Custom AST Class with Sub-Options + +Use when your option has complex sub-options (e.g., `CHANGE_TRACKING (AUTO_CLEANUP = ON)`). + +### Example: ChangeTrackingDatabaseOption + +#### Step 1-3: Same as Pattern A + +Add to enum, CodeGenerationSupporter, and helpers as in Pattern A. + +#### Step 4: Define Custom AST Class + +**File**: `SqlScriptDom/Parser/TSql/Ast.xml` + +```xml + + + + + + + + + + + + + + + + + +``` + +#### Step 5: Create Custom Script Generator + +**File**: `SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.YourOptionDatabaseOption.cs` + +```csharp +namespace Microsoft.SqlServer.TransactSql.ScriptDom.ScriptGenerator +{ + partial class SqlScriptGeneratorVisitor + { + public override void ExplicitVisit(YourOptionDatabaseOption node) + { + GenerateIdentifier(CodeGenerationSupporter.YourOption); + GenerateSpaceAndKeyword(TSqlTokenType.EqualsSign); + GenerateSpace(); + GenerateOptionStateOnOff(node.OptionState); + + if (node.Details != null && node.Details.Count > 0) + { + GenerateSpace(); + GenerateParenthesisedCommaSeparatedList(node.Details); + } + } + + public override void ExplicitVisit(SubOption1Detail node) + { + GenerateNameEqualsValue( + CodeGenerationSupporter.SubOption1, + node.IsOn ? TSqlTokenType.On : TSqlTokenType.Off); + } + + public override void ExplicitVisit(SubOption2Detail node) + { + GenerateNameEqualsValue( + CodeGenerationSupporter.SubOption2, + node.Value); + } + } +} +``` + +#### Step 6: Update Grammar File + +**File**: `SqlScriptDom/Parser/TSql/TSql170.g` + +Add grammar rules to parse your option and sub-options. + +--- + +## Pattern C: Custom AST Class with Enum Values + +Use when your option accepts specific enum values (not just ON/OFF). + +### Example: RecoveryDatabaseOption + +#### Step 1-2: Add Enum and Keyword + +Same as Pattern A, plus define value enum: + +**File**: `SqlScriptDom/Parser/TSql/RecoveryDatabaseOptionKind.cs` + +```csharp +public enum RecoveryDatabaseOptionKind +{ + Full = 0, + BulkLogged = 1, + Simple = 2 +} +``` + +#### Step 3: Define Custom AST Class + +**File**: `SqlScriptDom/Parser/TSql/Ast.xml` + +```xml + + + + +``` + +#### Step 4: Create Helper for Values + +**File**: `SqlScriptDom/Parser/TSql/RecoveryDbOptionsHelper.cs` + +```csharp +internal class RecoveryDbOptionsHelper : OptionsHelper +{ + private RecoveryDbOptionsHelper() + { + AddOptionMapping(RecoveryDatabaseOptionKind.Full, + CodeGenerationSupporter.Full); + AddOptionMapping(RecoveryDatabaseOptionKind.BulkLogged, + CodeGenerationSupporter.BulkLogged); + AddOptionMapping(RecoveryDatabaseOptionKind.Simple, + CodeGenerationSupporter.Simple); + } + + internal static readonly RecoveryDbOptionsHelper Instance = + new RecoveryDbOptionsHelper(); +} +``` + +#### Step 5: Create Script Generator + +**File**: `SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.RecoveryDatabaseOption.cs` + +```csharp +namespace Microsoft.SqlServer.TransactSql.ScriptDom.ScriptGenerator +{ + partial class SqlScriptGeneratorVisitor + { + public override void ExplicitVisit(RecoveryDatabaseOption node) + { + GenerateIdentifier(CodeGenerationSupporter.Recovery); + GenerateSpace(); + RecoveryDbOptionsHelper.Instance.GenerateSourceForOption( + _writer, node.Value); + } + } +} +``` + +--- + +## Pattern D: Custom AST Class with Special Script Generation + +Use when the option has special formatting or conditional output. + +### Example: LedgerOption (Only outputs when ON) + +#### Step 1-2: Same as Pattern A + +#### Step 3: Define Custom AST Class + +**File**: `SqlScriptDom/Parser/TSql/Ast.xml` + +```xml + + + + +``` + +#### Step 4: Create Custom Script Generator + +**File**: `SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.LedgerOption.cs` + +```csharp +namespace Microsoft.SqlServer.TransactSql.ScriptDom.ScriptGenerator +{ + partial class SqlScriptGeneratorVisitor + { + public override void ExplicitVisit(LedgerOption node) + { + System.Diagnostics.Debug.Assert( + node.OptionKind == DatabaseOptionKind.Ledger); + + // Special behavior: Only output when ON + if (node.OptionState == OptionState.On) + { + GenerateNameEqualsValue( + CodeGenerationSupporter.Ledger, + node.OptionState.ToString()); + } + } + } +} +``` + +--- + +## Testing Checklist + +For all patterns: + +- [ ] Database option enum added to `DatabaseOptionKind.cs` +- [ ] Keyword constant added to `CodeGenerationSupporter.cs` +- [ ] Option registered in appropriate helper class +- [ ] Test script created in `TestScripts/` +- [ ] Baseline created in `Baselines/` +- [ ] Test configured in `OnlySyntaxTests.cs` +- [ ] Parser builds successfully +- [ ] Specific test passes +- [ ] **Full test suite passes** (593/593 tests) + +For Patterns B, C, D (Custom AST): + +- [ ] AST class defined in `Ast.xml` +- [ ] Custom script generator created (if needed) +- [ ] Helper class created (for enum values) +- [ ] Grammar rules updated (if new syntax) + +--- + +## Common Pitfalls + +### 1. Forgetting to Rebuild After Ast.xml Changes + +```bash +# Always rebuild after modifying Ast.xml +dotnet build SqlScriptDom/Microsoft.SqlServer.TransactSql.ScriptDom.csproj -c Debug +``` + +### 2. Incorrect Error Counts in Tests + +Error count = number of statements in test script that use the new option. + +```csharp +// WRONG: Count doesn't match statements +new ParserTest170("TwoStatements.sql", nErrors160: 1) // ❌ + +// RIGHT: Match statement count +new ParserTest170("TwoStatements.sql", nErrors160: 2) // ✓ +``` + +### 3. Missing RequiresEqualsSign Registration + +Some options need `=` between name and value: +- With `=`: `LEDGER = ON`, `AUTOMATIC_INDEX_COMPACTION = ON` +- Without `=`: `AUTO_CLOSE ON`, `AUTO_SHRINK ON` + +Check existing similar options to determine which pattern to follow. + +### 4. Not Running Full Test Suite + +Grammar changes can break unrelated tests. Always run: + +```bash +dotnet test Test/SqlDom/UTSqlScriptDom.csproj -c Debug +``` + +--- + +## Examples Reference + +### Pattern A Examples (Generic ON/OFF) +- `AutoClose` +- `AutoShrink` +- `DataRetention` + +**Files to check**: +- `DatabaseOptionKind.cs` (enum value) +- `OnOffSimpleDbOptionsHelper.cs` (registration) +- No Ast.xml changes needed +- No custom script generator needed + +### Pattern B Examples (Sub-Options) +- `ChangeTrackingDatabaseOption` +- `QueryStoreDatabaseOption` +- `AutomaticTuningDatabaseOption` + +**Files to check**: +- All Pattern A files, plus: +- `Ast.xml` (custom class with Details collection) +- Custom script generator + +### Pattern C Examples (Enum Values) +- `RecoveryDatabaseOption` +- `PageVerifyDatabaseOption` +- `CursorDefaultDatabaseOption` + +**Files to check**: +- All Pattern A files, plus: +- `Ast.xml` (custom class with Value member) +- Helper class for enum mapping +- Custom script generator + +### Pattern D Examples (Special Behavior) +- `LedgerOption` (only outputs when ON) +- `OptimizedLockingDatabaseOption` (custom formatting) + +**Files to check**: +- All Pattern A files, plus: +- `Ast.xml` (custom class) +- Custom script generator with special logic + +--- + +## Quick Reference: File Locations + +| Component | Path | +|-----------|------| +| Database option enum | `SqlScriptDom/Parser/TSql/DatabaseOptionKind.cs` | +| Keyword constants | `SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs` | +| ON/OFF helper | `SqlScriptDom/Parser/TSql/OnOffSimpleDbOptionsHelper.cs` | +| Other option helpers | `SqlScriptDom/Parser/TSql/DatabaseOptionKindHelper.cs` | +| AST definitions | `SqlScriptDom/Parser/TSql/Ast.xml` | +| Script generators | `SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/` | +| Test scripts | `Test/SqlDom/TestScripts/` | +| Baselines | `Test/SqlDom/Baselines/` | +| Test classes | `Test/SqlDom/OnlySyntaxTests.cs` | + +--- + +## Version Flags Reference + +```csharp +SqlVersionFlags.TSql80 // SQL Server 2000 +SqlVersionFlags.TSql90 // SQL Server 2005 +SqlVersionFlags.TSql100 // SQL Server 2008 +SqlVersionFlags.TSql110 // SQL Server 2012 +SqlVersionFlags.TSql120 // SQL Server 2014 +SqlVersionFlags.TSql130 // SQL Server 2016 +SqlVersionFlags.TSql140 // SQL Server 2017 +SqlVersionFlags.TSql150 // SQL Server 2019 +SqlVersionFlags.TSql160 // SQL Server 2022 +SqlVersionFlags.TSql170 // SQL Server 2025 +SqlVersionFlags.TSqlFabricDW // Fabric DW + +// Combined flags +SqlVersionFlags.TSql170AndAbove // SQL 2025+ +SqlVersionFlags.TSql160AndAbove // SQL 2022+ +``` + +For Azure SQL Database, Fabric, VNext: Use the latest version (currently `TSql170AndAbove`). diff --git a/.github/skills/add-feature/SKILL.md b/.github/skills/add-feature/SKILL.md new file mode 100644 index 0000000..ce63bec --- /dev/null +++ b/.github/skills/add-feature/SKILL.md @@ -0,0 +1,282 @@ +--- +name: add-feature +description: Add new T-SQL features to ScriptDOM parser. Asks questions about SQL Server version and feature type, then guides through grammar changes, AST updates, script generation, and testing using existing instruction files. Handles syntax additions, new functions, data types, index types, and validation rules. +argument-hint: "Feature description (e.g., 'Add VECTOR data type', 'Add JSON_OBJECT function')" +user-invocable: true +platforms: "SQL Server, Azure SQL DB, Fabric, Fabric DW, VNext" +--- + +## Overview + +This skill helps add new T-SQL features to the ScriptDOM parser by: +1. **Interviewing** you about the feature and target platform (SQL Server, Azure SQL DB, Fabric, Fabric DW, VNext) +2. **Determining the latest parser version** from `SqlVersionFlags.cs` (currently TSql170) +3. **Routing** you to the appropriate parser: + - SQL Server → version-specific parser (TSql120-170) + - Azure SQL DB/Fabric/VNext → latest parser version + - Fabric DW → separate TSqlFabricDW parser +4. **Classifying** the change type (grammar, validation, function, data type, etc.) +5. **Guiding** through implementation and testing without duplicating existing documentation + +--- + +## Step 1: Feature Discovery Interview + +Ask the user these questions to understand the feature: + +### Required Questions +1. **What T-SQL feature are you adding?** + - Example: "VECTOR data type", "JSON_OBJECT function", "RESUMABLE option for ALTER TABLE" + +2. **Which platform is this feature for?** + - **SQL Server** (on-premises / standalone) + - **Azure SQL Database** (uses latest parser version) + - **Fabric** (uses latest parser version) + - **Fabric DW** (uses separate TSqlFabricDW parser) + - **VNext** (future version, uses latest parser version) + +3. **If SQL Server, which version introduced this feature?** + - SQL Server 2014 (TSql120) + - SQL Server 2016 (TSql130) + - SQL Server 2017 (TSql140) + - SQL Server 2019 (TSql150) + - SQL Server 2022 (TSql160) + - SQL Server 2025 (TSql170) + - *(Skip if Azure SQL DB, Fabric, or VNext - these use latest version)* + +4. **Do you have example T-SQL syntax or Microsoft documentation links?** + - This helps verify the exact syntax requirements + +### Optional Context Questions +4. **Is this a new syntax element or enabling existing syntax in a new context?** + - New syntax: Requires grammar rules, AST nodes + - Validation fix: May only need version checks in validation code + +5. **Does similar syntax already work in other contexts?** + - Example: "RESUMABLE works for ALTER INDEX but not ALTER TABLE" + - This indicates a validation issue rather than missing grammar + +--- + +## Step 2: Determine Feature Type + +Based on the answers, classify the feature into one of these types: + +### Type A: Validation-Only Fix +**Indicators:** +- Similar syntax already works elsewhere in SQL +- Error message says "Option 'X' is not valid..." or "Feature 'Y' not supported..." +- The grammar may already parse it, but validation blocks it + +**→ Route to:** [grammar_validation.guidelines.instructions.md](../../instructions/grammar_validation.guidelines.instructions.md) + +### Type B: New Grammar/Syntax +**Indicators:** +- Completely new statement, clause, or operator +- Error says "Incorrect syntax near..." or "Unexpected token..." +- Parser doesn't recognize the syntax at all + +**→ Route to:** [bug_fixing.guidelines.instructions.md](../../instructions/bug_fixing.guidelines.instructions.md) + +### Type C: New System Function +**Indicators:** +- Adding a new built-in T-SQL function (e.g., JSON_OBJECT, STRING_AGG) +- May need special handling for RETURN statement contexts + +**→ Route to:** [function.guidelines.instructions.md](../../instructions/function.guidelines.instructions.md) + +### Type D: New Data Type +**Indicators:** +- Adding a completely new SQL Server data type (e.g., VECTOR, GEOGRAPHY) +- Requires custom parameter syntax (e.g., `VECTOR(1536, FLOAT32)`) + +**→ Route to:** [new_data_types.guidelines.instructions.md](../../instructions/new_data_types.guidelines.instructions.md) + +### Type E: New Index Type +**Indicators:** +- Adding a new CREATE INDEX variant (e.g., VECTOR INDEX, JSON INDEX) +- Requires specialized syntax different from standard indexes + +**→ Route to:** [new_index_types.guidelines.instructions.md](../../instructions/new_index_types.guidelines.instructions.md) + +### Type F: Parser Predicate Recognition (Parentheses) +**Indicators:** +- Identifier-based predicate works without parentheses but fails with them +- Error near closing parenthesis: `WHERE (REGEXP_LIKE(...))` fails + +**→ Route to:** [parser.guidelines.instructions.md](../../instructions/parser.guidelines.instructions.md) + +### Type G: Database Option (ALTER/CREATE DATABASE SET) +**Indicators:** +- Adding a database option for ALTER DATABASE or CREATE DATABASE statements +- Simple ON/OFF option (e.g., `SET AUTOMATIC_INDEX_COMPACTION = ON`) +- Complex option with sub-options (e.g., `SET CHANGE_TRACKING (AUTO_CLEANUP = ON)`) +- Enum-based option (e.g., `SET RECOVERY FULL`) + +**→ Route to:** [database_option.guidelines.instructions.md](../../instructions/database_option.guidelines.instructions.md) + +--- + +## Step 3: Grammar Rule Development + +For grammar changes (Types B, C, D, E, G), follow this workflow: + +### 3.1 Identify Target Grammar File + +**First, determine the latest parser version:** +Check `SqlScriptDom/Parser/TSql/SqlVersionFlags.cs` for the highest TSql version enum value (currently TSql170). + +**Then select the appropriate grammar file:** + +#### For SQL Server (on-premises): +- SQL 2014+: TSql120.g +- SQL 2016+: TSql130.g +- SQL 2017+: TSql140.g +- SQL 2019+: TSql150.g +- SQL 2022+: TSql160.g +- SQL 2025+: TSql170.g + +#### For Azure SQL Database, Fabric, VNext: +- Use the **latest parser version** (e.g., TSql170.g) +- Check `SqlVersionFlags.cs` to confirm the highest version + +#### For Fabric DW: +- Use **TSqlFabricDW.g** (separate parser with Fabric-specific syntax) + +### 3.2 Grammar Development Patterns +**Reference:** [grammar.guidelines.instructions.md](../../instructions/grammar.guidelines.instructions.md) + +Key patterns: +- Use `this.FragmentFactory.CreateFragment()` for AST nodes +- Use syntactic predicates `(LA(1) == Token)` for lookahead +- Avoid modifying shared grammar rules (create specialized versions instead) +- Include proper script generation in `ScriptGenerator` + +### 3.3 AST Updates +If adding new AST nodes: +1. Edit `SqlScriptDom/Parser/TSql/Ast.xml` +2. Rebuild to regenerate visitor classes +3. Implement script generation in `SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator` + +--- + +## Step 4: Testing + +**Reference:** [testing.guidelines.instructions.md](../../instructions/testing.guidelines.instructions.md) + +### 4.1 Create Test Files +1. **Input Script**: `Test/SqlDom/TestScripts/YourFeature.sql` + - Include all syntax variations + - Test edge cases, parameters, expressions + +2. **Baseline**: `Test/SqlDom/Baselines/YourFeature.sql` + - Expected pretty-printed output after parse → script gen → reparse + +### 4.2 Add Test Method + +#### For SQL Server versions (TSql120-170): +In `Test/SqlDom/OnlySyntaxTests.cs`: + +```csharp +[TestMethod] +public void YourFeatureTest() +{ + ParserTest("YourFeature.sql"); +} +``` + +#### For Fabric DW: +Add to the `OnlyFabricDWTestInfos` array in `Test/SqlDom/OnlyFabricDWSyntaxTests.cs`: + +```csharp +new ParserTestFabricDW("YourFeatureFabricDW.sql", + nErrors80: X, nErrors90: Y, nErrors100: Z, + nErrors110: A, nErrors120: B, nErrors130: C, + nErrors140: D, nErrors150: E, nErrors160: F, nErrors170: G), +``` +*(Set expected error counts for each parser version)* + +**Simplified patterns available** - see testing guidelines for: +- `ParserTestAllowingErrors` - for scripts with expected parse errors +- `ParserTestOutput` constructors - for custom output verification +- Error message verification - exact match or fragment-based + +### 4.3 Run Tests +```bash +# Build to regenerate parser +dotnet build SqlScriptDom/Microsoft.SqlServer.TransactSql.ScriptDom.csproj -c Debug + +# Run specific test +dotnet test --filter "FullyQualifiedName~YourFeatureTest" -c Debug + +# ALWAYS run full suite before committing +dotnet test Test/SqlDom/UTSqlScriptDom.csproj -c Debug +``` + +--- + +## Step 5: Debugging and Troubleshooting + +**Reference:** [debugging_workflow.guidelines.instructions.md](../../instructions/debugging_workflow.guidelines.instructions.md) + +Common issues: +- **Tests fail after grammar changes**: Likely modified a shared rule - create specialized version +- **Script generation mismatch**: Update `ScriptGenerator` for your AST nodes +- **Version errors**: Check validation code in `TSql80ParserBaseInternal.cs` + +--- + +## Step 6: Implementation Checklist + +Before marking the feature complete: + +- [ ] Grammar rules added/modified in correct `TSql.g` or `TSqlFabricDW.g` file +- [ ] AST nodes defined in `Ast.xml` (if new nodes needed) +- [ ] Script generation implemented for new AST nodes +- [ ] Validation rules updated (if version-gated) +- [ ] Test script created in `TestScripts/` (use appropriate suffix: `.sql` or `FabricDW.sql`) +- [ ] Baseline created in `Baselines/` or `BaselinesFabricDW/` +- [ ] Test method added to `OnlySyntaxTests.cs` or `OnlyFabricDWSyntaxTests.cs` +- [ ] All tests pass (including full test suite) +- [ ] No regressions in unrelated tests + +--- + +## Quick Reference: File Locations + +| Component | Path | +|-----------|------| +| Grammar files (SQL Server) | `SqlScriptDom/Parser/TSql/TSql*.g` | +| Grammar file (Fabric DW) | `SqlScriptDom/Parser/TSql/TSqlFabricDW.g` | +| Version detection | `SqlScriptDom/Parser/TSql/SqlVersionFlags.cs` | +| AST definition | `SqlScriptDom/Parser/TSql/Ast.xml` | +| Script generator | `SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator` | +| Validation code | `SqlScriptDom/Parser/TSql/TSql80ParserBaseInternal.cs` | +| Test scripts | `Test/SqlDom/TestScripts/` | +| Baselines | `Test/SqlDom/Baselines/` or `BaselinesFabricDW/` | +| Test classes | `Test/SqlDom/OnlySyntaxTests.cs` or `FabricDWSyntaxTests.cs` | + +--- + +## Agent Workflow + +When invoked, the agent should: + +1. **Determine the latest parser version** by reading `SqlScriptDom/Parser/TSql/SqlVersionFlags.cs` +2. **Interview the user** using the questions in Step 1 + - Ask about platform (SQL Server, Azure SQL DB, Fabric, Fabric DW, VNext) + - For Azure/Fabric/VNext → use latest parser version + - For Fabric DW → use TSqlFabricDW.g + - For SQL Server → ask for specific version +3. **Classify the feature type** using Step 2 criteria +4. **Load the appropriate instruction file** using `read_file` tool +5. **Guide through implementation** by referencing specific sections of the loaded instruction file +6. **Execute the development workflow**: + - Make grammar/validation changes + - Regenerate parser (build) + - Create test files + - Run tests + - Debug failures +7. **Verify completeness** using Step 6 checklist + +**Never duplicate content from instruction files** - always reference and quote specific sections. diff --git a/SqlScriptDom/GenerateFiles.props b/SqlScriptDom/GenerateFiles.props index b94a61f..ea6f573 100644 --- a/SqlScriptDom/GenerateFiles.props +++ b/SqlScriptDom/GenerateFiles.props @@ -13,6 +13,7 @@ + diff --git a/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs b/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs index d10f1b8..0ea6731 100644 --- a/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs +++ b/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs @@ -155,6 +155,7 @@ internal static class CodeGenerationSupporter internal const string AutoClose = "AUTO_CLOSE"; internal const string AutoCreateStatistics = "AUTO_CREATE_STATISTICS"; internal const string AutoDrop = "AUTO_DROP"; + internal const string AutomaticIndexCompaction = "AUTOMATIC_INDEX_COMPACTION"; internal const string AutogrowAllFiles = "AUTOGROW_ALL_FILES"; internal const string AutogrowSingleFile = "AUTOGROW_SINGLE_FILE"; internal const string Automatic = "AUTOMATIC"; diff --git a/SqlScriptDom/Parser/TSql/DatabaseOptionKind.cs b/SqlScriptDom/Parser/TSql/DatabaseOptionKind.cs index 84d3832..0786065 100644 --- a/SqlScriptDom/Parser/TSql/DatabaseOptionKind.cs +++ b/SqlScriptDom/Parser/TSql/DatabaseOptionKind.cs @@ -99,7 +99,12 @@ public enum DatabaseOptionKind ManualCutover = 69, PerformCutover = 70, - OptimizedLocking = 71 + OptimizedLocking = 71, + + // T-SQL 170 On/Off options + + // T-SQL 180 On/Off options + AutomaticIndexCompaction = 72 } #pragma warning restore 1591 diff --git a/SqlScriptDom/Parser/TSql/OnOffSimpleDbOptionsHelper.cs b/SqlScriptDom/Parser/TSql/OnOffSimpleDbOptionsHelper.cs index 696b6cf..b071c36 100644 --- a/SqlScriptDom/Parser/TSql/OnOffSimpleDbOptionsHelper.cs +++ b/SqlScriptDom/Parser/TSql/OnOffSimpleDbOptionsHelper.cs @@ -64,7 +64,9 @@ private OnOffSimpleDbOptionsHelper() AddOptionMapping(DatabaseOptionKind.Ledger, CodeGenerationSupporter.Ledger, SqlVersionFlags.TSql160AndAbove); // 170 options - // TODO: add any new 170 options here + + // 180 options + AddOptionMapping(DatabaseOptionKind.AutomaticIndexCompaction, CodeGenerationSupporter.AutomaticIndexCompaction, SqlVersionFlags.TSql180AndAbove); } @@ -83,6 +85,7 @@ internal bool RequiresEqualsSign(DatabaseOptionKind optionKind) case DatabaseOptionKind.NestedTriggers: case DatabaseOptionKind.TransformNoiseWords: case DatabaseOptionKind.Ledger: + case DatabaseOptionKind.AutomaticIndexCompaction: return true; default: return false; diff --git a/SqlScriptDom/Parser/TSql/OptionsHelper.cs b/SqlScriptDom/Parser/TSql/OptionsHelper.cs index 7262ab6..a14a9e5 100644 --- a/SqlScriptDom/Parser/TSql/OptionsHelper.cs +++ b/SqlScriptDom/Parser/TSql/OptionsHelper.cs @@ -128,6 +128,8 @@ internal SqlVersionFlags MapSqlVersionToSqlVersionFlags(SqlVersion sqlVersion) return SqlVersionFlags.TSql160; case SqlVersion.Sql170: return SqlVersionFlags.TSql170; + case SqlVersion.Sql180: + return SqlVersionFlags.TSql180; case SqlVersion.SqlFabricDW: return SqlVersionFlags.TSqlFabricDW; default: diff --git a/SqlScriptDom/Parser/TSql/SqlVersionFlags.cs b/SqlScriptDom/Parser/TSql/SqlVersionFlags.cs index 23bb379..60496e3 100644 --- a/SqlScriptDom/Parser/TSql/SqlVersionFlags.cs +++ b/SqlScriptDom/Parser/TSql/SqlVersionFlags.cs @@ -24,19 +24,21 @@ internal enum SqlVersionFlags TSql150 = 0x80, TSql160 = 0x100, TSql170 = 0x200, - TSqlFabricDW = 0x400, + TSqlFabricDW = 0x400, + TSql180 = 0x800, - TSqlAll = TSql80 | TSql90 | TSql100 | TSql110 | TSql120 | TSql130 | TSql140 | TSql150 | TSql160 | TSqlFabricDW | TSql170, - TSql90AndAbove = TSql90 | TSql100 | TSql110 | TSql120 | TSql130 | TSql140 | TSql150 | TSql160 | TSqlFabricDW | TSql170, - TSql100AndAbove = TSql100 | TSql110 | TSql120 | TSql130 | TSql140 | TSql150 | TSql160 | TSqlFabricDW | TSql170, - TSql110AndAbove = TSql110 | TSql120 | TSql130 | TSql140 | TSql150 | TSql160 | TSqlFabricDW | TSql170, - TSql120AndAbove = TSql120 | TSql130 | TSql140 | TSql150 | TSql160 | TSqlFabricDW | TSql170, - TSql130AndAbove = TSql130 | TSql140 | TSql150 | TSql160 | TSqlFabricDW | TSql170, - TSql140AndAbove = TSql140 | TSql150 | TSql160 | TSqlFabricDW | TSql170, - TSql150AndAbove = TSql150 | TSql160 | TSqlFabricDW | TSql170, - TSql160AndAbove = TSql160 | TSqlFabricDW | TSql170, - TSql170AndAbove = TSql170, - TSqlFabricDWAndAbove = TSql160 | TSqlFabricDW | TSql170, + TSqlAll = TSql80 | TSql90 | TSql100 | TSql110 | TSql120 | TSql130 | TSql140 | TSql150 | TSql160 | TSqlFabricDW | TSql170 | TSql180, + TSql90AndAbove = TSql90 | TSql100 | TSql110 | TSql120 | TSql130 | TSql140 | TSql150 | TSql160 | TSqlFabricDW | TSql170 | TSql180, + TSql100AndAbove = TSql100 | TSql110 | TSql120 | TSql130 | TSql140 | TSql150 | TSql160 | TSqlFabricDW | TSql170 | TSql180, + TSql110AndAbove = TSql110 | TSql120 | TSql130 | TSql140 | TSql150 | TSql160 | TSqlFabricDW | TSql170 | TSql180, + TSql120AndAbove = TSql120 | TSql130 | TSql140 | TSql150 | TSql160 | TSqlFabricDW | TSql170 | TSql180, + TSql130AndAbove = TSql130 | TSql140 | TSql150 | TSql160 | TSqlFabricDW | TSql170 | TSql180, + TSql140AndAbove = TSql140 | TSql150 | TSql160 | TSqlFabricDW | TSql170 | TSql180, + TSql150AndAbove = TSql150 | TSql160 | TSqlFabricDW | TSql170 | TSql180, + TSql160AndAbove = TSql160 | TSqlFabricDW | TSql170 | TSql180, + TSql170AndAbove = TSql170 | TSql180, + TSql180AndAbove = TSql180, + TSqlFabricDWAndAbove = TSql160 | TSqlFabricDW | TSql170 | TSql180, TSqlUnder110 = TSql80 | TSql90 | TSql100, TSqlUnder120 = TSql80 | TSql90 | TSql100 | TSql110, TSqlUnder130 = TSql80 | TSql90 | TSql100 | TSql110 | TSql120, @@ -44,5 +46,6 @@ internal enum SqlVersionFlags TSqlUnder150 = TSql80 | TSql90 | TSql100 | TSql110 | TSql120 | TSql130 | TSql140, TSqlUnder160 = TSql80 | TSql90 | TSql100 | TSql110 | TSql120 | TSql130 | TSql140 | TSql150, TSqlUnder170 = TSql80 | TSql90 | TSql100 | TSql110 | TSql120 | TSql130 | TSql140 | TSql150 | TSql160 | TSqlFabricDW, + TSqlUnder180 = TSql80 | TSql90 | TSql100 | TSql110 | TSql120 | TSql130 | TSql140 | TSql150 | TSql160 | TSql170 | TSqlFabricDW, } } \ No newline at end of file diff --git a/SqlScriptDom/Parser/TSql/TSql180.g b/SqlScriptDom/Parser/TSql/TSql180.g new file mode 100644 index 0000000..19e66ad --- /dev/null +++ b/SqlScriptDom/Parser/TSql/TSql180.g @@ -0,0 +1,35473 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +// sacaglar: Handling position information for ASTs +// The properties and AddX (X is the type of the parameter, e.g., AddStatement) +// update the position information of the AST. In the rare case when +// we are setting a bool, int, or string etc. we have to call UpdateTokenInfo +// with the token (because we are not passing the property a token, we are just +// passing a bool, int etc. +// Also for token that we don't track of like Comma, Semicolon etc. we have to +// call the same function. Alternatively the properties(StartOffset, FragmentLength) +// on Fragment.cs can be used for this purpose. + +options { + language = "CSharp"; + namespace = "Microsoft.SqlServer.TransactSql.ScriptDom"; +} + +{ + using System.Diagnostics; + using System.Globalization; + using System.Collections.Generic; +} + +class TSql180ParserInternal extends Parser("TSql180ParserBaseInternal"); +options { + k = 2; + defaultErrorHandler=false; + classHeaderPrefix = "internal partial"; + importVocab = TSql; +} + +{ + public TSql180ParserInternal(bool initialQuotedIdentifiersOn) + : base(initialQuotedIdentifiersOn) + { + initialize(); + } +} + +// Figure out a way to refactor exception handling +entryPointChildObjectName returns [ChildObjectName vResult = null] + : + vResult=childObjectNameWithThreePrefixes + EOF + ; + +entryPointSchemaObjectName returns [SchemaObjectName vResult = null] + : + vResult=schemaObjectFourPartName + EOF + ; + +entryPointScalarDataType returns [DataTypeReference vResult = null] + : + vResult = scalarDataType + EOF + ; + +entryPointExpression returns [ScalarExpression vResult = null] + : + vResult = expression + EOF + ; + +entryPointBooleanExpression returns [BooleanExpression vResult = null] + : + vResult = booleanExpression + EOF + ; + +entryPointStatementList returns [StatementList vResult = null] +{ + bool vParseErrorOccurred = false; +} + : + vResult = statementList[ref vParseErrorOccurred] + { + if (vParseErrorOccurred) + vResult = null; + } + EOF + ; + +entryPointSubqueryExpressionWithOptionalCTE returns [SelectStatement vResult = null] +{ + SelectFunctionReturnType vRetType; +} + : + vRetType = functionReturnClauseRelational + { + vResult = vRetType.SelectStatement; + } + EOF + ; + +entryPointIPv4Address returns [IPv4 vResult = null] + : + vResult = ipAddressV4 + EOF + ; + +entryPointConstantOrIdentifier returns [TSqlFragment vResult = null] + : + vResult = possibleNegativeConstantOrIdentifier + EOF + ; + +entryPointConstantOrIdentifierWithDefault returns [TSqlFragment vResult = null] + : + vResult = possibleNegativeConstantOrIdentifierWithDefault + EOF + ; + +script returns [TSqlScript vResult = this.FragmentFactory.CreateFragment()] +{ + TSqlBatch vCurrentBatch; + + // Script always includes all of the tokens... + if (vResult.ScriptTokenStream != null && vResult.ScriptTokenStream.Count > 0) + { + vResult.UpdateTokenInfo(0,vResult.ScriptTokenStream.Count-1); + } +} + : vCurrentBatch = batch + { + if (vCurrentBatch != null) + AddAndUpdateTokenInfo(vResult, vResult.Batches, vCurrentBatch); + } + ( + Go + { + ResetQuotedIdentifiersSettingToInitial(); + ThrowPartialAstIfPhaseOne(null); + } + vCurrentBatch = batch + { + if (vCurrentBatch != null) + AddAndUpdateTokenInfo(vResult, vResult.Batches, vCurrentBatch); + } + + )* + + tEof:EOF + { + UpdateTokenInfo(vResult,tEof); + } + ; + +// TODO, sacaglar: Tracking issue, bug# 584772 +batch returns [TSqlBatch vResult = null] +{ + TSqlStatement vStatement; +} + : (tSemi:Semicolon)* + ( + ( + Create (((Or Alter)? ( Proc | Procedure | Trigger | View | Function)) | ( Default | Rule | Schema | {NextTokenMatches(CodeGenerationSupporter.Federation)}? | {NextTokenMatches(CodeGenerationSupporter.Materialized)}? )) + | + Alter ( Proc | Procedure | Trigger | View | Function | {NextTokenMatches(CodeGenerationSupporter.Federation)}? | {NextTokenMatches(CodeGenerationSupporter.Materialized)}? ) + | + Use {NextTokenMatches(CodeGenerationSupporter.Federation) && LA(2) == Identifier}? + )=> + ( + vStatement=lastStatementOptSemi + { + if (vStatement != null) + { + if (vResult == null) + { + vResult = this.FragmentFactory.CreateFragment(); + } + AddAndUpdateTokenInfo(vResult, vResult.Statements, vStatement); + } + } + ) + | + (vStatement = optSimpleExecute + { + if (vStatement != null) // Can be empty + { + ThrowPartialAstIfPhaseOne(vStatement); + + if (vResult == null) + vResult = this.FragmentFactory.CreateFragment(); + + AddAndUpdateTokenInfo(vResult, vResult.Statements, vStatement); + } + } + (vStatement=statementOptSemi + { + if (vStatement != null) // statement can be null if there was a parse error. + { + if (vResult == null) + vResult = this.FragmentFactory.CreateFragment(); + + AddAndUpdateTokenInfo(vResult, vResult.Statements, vStatement); + } + } + )* + ) + ) + ; + exception + catch[TSqlParseErrorException exception] + { + if (!exception.DoNotLog) + { + AddParseError(exception.ParseError); + } + RecoverAtBatchLevel(); + } + catch[antlr.NoViableAltException exception] + { + ParseError error = GetFaultTolerantUnexpectedTokenError( + exception.token, exception, _tokenSource.LastToken.Offset); + AddParseError(error); + RecoverAtBatchLevel(); + } + catch[antlr.MismatchedTokenException exception] + { + ParseError error = GetFaultTolerantUnexpectedTokenError( + exception.token, exception, _tokenSource.LastToken.Offset); + AddParseError(error); + RecoverAtBatchLevel(); + } + catch[antlr.RecognitionException] + { + ParseError error = GetUnexpectedTokenError(); + AddParseError(error); + RecoverAtBatchLevel(); + } + catch[antlr.TokenStreamRecognitionException exception] + { + // This exception should be handled when we are creating TSqlTokenStream... + ParseError error = ProcessTokenStreamRecognitionException(exception, _tokenSource.LastToken.Offset); + AddParseError(error); + RecoverAtBatchLevel(); + } + catch[antlr.ANTLRException exception] + { + CreateInternalError("batch", exception); + } + +statementOptSemi returns [TSqlStatement vResult = null] + : vResult=statement optSemicolons[vResult] + ; + +lastStatementOptSemi returns [TSqlStatement vResult = null] + : vResult=lastStatement optSemicolons[vResult] + ; + +optSemicolons[TSqlStatement vParent] +{ + int nSemicolons = 0; +} + : ( + // Greedy behavior is good enough, we ignore the semicolons + options {greedy = true; } : + tSemi:Semicolon + { + ++nSemicolons; + if (vParent != null) // vResult can be null if there was a parse error. + UpdateTokenInfo(vParent,tSemi); + } + )* + ; + + +///////////////////////////////////////////////// +// Copy Command +///////////////////////////////////////////////// +copyStatement returns [CopyStatement vResult = FragmentFactory.CreateFragment()] +{ + SchemaObjectName vTo; + StringLiteral vFrom; +} + : + tCopy:Identifier + { + Match(tCopy, CodeGenerationSupporter.CopyCommand); + } + tInto: Into + vTo = schemaObjectThreePartName + { + UpdateTokenInfo(vResult,tCopy); + vResult.Into = vTo; + } + (copyColumnList[vResult])? + From vFrom = nonEmptyString /* No need to verify null as it's already nonempty */ + { + ExternalFileOption.CheckXMLValidity(vFrom, CodeGenerationSupporter.From); + AddAndUpdateTokenInfo(vResult, vResult.From, vFrom); + } + (Comma vFrom = nonEmptyString + { + ExternalFileOption.CheckXMLValidity(vFrom, CodeGenerationSupporter.From); + AddAndUpdateTokenInfo(vResult, vResult.From, vFrom); + } + )* + (copyWithClause[vResult])? + (optimizerHints[vResult, vResult.OptimizerHints])? +; + +copyColumnList [CopyStatement vParent] +{ + CopyOption copyOption = FragmentFactory.CreateFragment(); + ListTypeCopyOption vColumnOptions = FragmentFactory.CreateFragment();; + CopyColumnOption vColumnOption; + Int32 columnCount = 0; +} +: + tLParen: LeftParenthesis + { + copyOption.Kind = CopyOptionKind.ColumnOptions; + } + vColumnOption = copyColumnOption[ref columnCount, tLParen] + { + AddAndUpdateTokenInfo(vColumnOptions, vColumnOptions.Options, vColumnOption); + } + (Comma vColumnOption = copyColumnOption[ref columnCount, tLParen] + { + AddAndUpdateTokenInfo(vColumnOptions, vColumnOptions.Options, vColumnOption); + } + )* + tRParen:RightParenthesis + { + UpdateTokenInfo(vParent,tRParen); + copyOption.Value = vColumnOptions; + AddAndUpdateTokenInfo(vParent, vParent.Options, copyOption); + } +; + +copyColumnOption [ref Int32 columnCount, IToken tToken] returns [CopyColumnOption vResult = FragmentFactory.CreateFragment()] +{ + Identifier vColumnName; + ScalarExpression vDefaultColumnValue = null; + bool vDefaultSpecified = false; + IntegerLiteral vFieldNumber = null; +} + : + vColumnName = identifier + { + CheckAndIncrementColumnCount(ref columnCount, tToken); + } + (tDefault:Default vDefaultColumnValue = defaultValueLiteral + { + vDefaultSpecified = true; + })? + (vFieldNumber = integer)? + { + CopyListOptionsHelper.Instance.AssignCopyColumnOptions(vResult, vColumnName, vDefaultColumnValue, vDefaultSpecified, vFieldNumber, columnCount); + } +; + +copyWithClause [CopyStatement vParent] +{ + CopyOption vOption; + Int32 encountered = 0; +} + : With LeftParenthesis vOption = copyOption[ref encountered] + { + AddAndUpdateTokenInfo(vParent, vParent.Options, vOption); + } + (Comma vOption = copyOption[ref encountered] + { + AddAndUpdateTokenInfo(vParent, vParent.Options, vOption); + } + )* + tRParen:RightParenthesis + { + UpdateTokenInfo(vParent,tRParen); + } +; + +copyOption [ref Int32 encountered] returns [CopyOption vResult = FragmentFactory.CreateFragment()] +{ + CopyStatementOptionBase vValue = null; +} + : + ( + tOption:Identifier + { + vResult.Kind = CopyIdentifierOrValueOptionsHelper.Instance.ParseOption(tOption); + CheckCopyOptionDuplication(ref encountered, vResult.Kind, tOption); + } + | tIdentityInsert:IdentityInsert + { + vResult.Kind = CopyOptionKind.Identity_Insert; + CheckCopyOptionDuplication(ref encountered, vResult.Kind, tIdentityInsert); + } + | tCredential:Credential + { + vResult.Kind = CopyOptionKind.Credential; + CheckCopyOptionDuplication(ref encountered, vResult.Kind, tCredential); + } + ) + EqualsSign + ( + vValue = singleValueTypeCopyOption + { + CopyIdentifierOrValueOptionsHelper.Instance.AssignValueToCopyOption(vResult, (SingleValueTypeCopyOption) vValue); + } + | + vValue = copyCredentialOption + { + if ((vResult.Kind != CopyOptionKind.Credential || vResult.Kind != CopyOptionKind.ErrorFileCredential) && + CopyIdentifierOrValueOptionsHelper.Instance.ValidateCopyCredential((CopyCredentialOption)vValue)) + { + vResult.Value = (CopyCredentialOption) vValue; + } + else + { + ThrowIncorrectSyntaxErrorException(vValue); + } + } + ) +; + +singleValueTypeCopyOption returns [SingleValueTypeCopyOption vResult = FragmentFactory.CreateFragment()] +{ + Identifier vValue; + Literal vLiteral; +} + : + vValue=identifier + { + vResult.SingleValue = IdentifierOrValueExpression(vValue); + } + | vLiteral = integer + { + vResult.SingleValue = IdentifierOrValueExpression(vLiteral); + } + | vLiteral = stringLiteral + { + vResult.SingleValue = IdentifierOrValueExpression(vLiteral); + } +; + +copyCredentialOption returns [CopyCredentialOption vResult = FragmentFactory.CreateFragment()] +{ + StringLiteral vIdentityValue; + StringLiteral vSecretValue; +} + : + LeftParenthesis + Identity EqualsSign vIdentityValue = stringLiteral + { + vResult.Identity = vIdentityValue; + } + (Comma tSecret:Identifier EqualsSign vSecretValue = stringLiteral + { + Match(tSecret, CodeGenerationSupporter.Secret); + CopyIdentifierOrValueOptionsHelper.Instance.ValidateSecret(vSecretValue); + vResult.Secret = (StringLiteral) vSecretValue; + })? + RightParenthesis +; + + +///////////////////////////////////////////////// +// Rename table statement +///////////////////////////////////////////////// + +/* Syntax: +RENAME OBJECT [::] [Database].[Schema].OldTable TO NewTable +*/ + +renameEntityStatement returns [RenameEntityStatement vResult = this.FragmentFactory.CreateFragment()] +{ + SchemaObjectName vSchemaObjectName; + Identifier vRenamedEntityType; + Identifier vNewName; +} + :tRename:Identifier vRenamedEntityType = securityStatementPermission + { + Match(tRename, CodeGenerationSupporter.Rename); + vResult.RenameEntityType = ParseSecurityObjectKind(vRenamedEntityType); + if(!(vResult.RenameEntityType == SecurityObjectKind.Object)) + { + // RenameEntityStatement::Translate() throws an unexpected exception if the entity type is not + // "object". Since other object kinds are not supported in this statement, we throw + // a syntax error. + throw GetUnexpectedTokenErrorException(vRenamedEntityType); + } + } + + ( + DoubleColon + { + vResult.SeparatorType = SeparatorType.DoubleColon; + } + )? + + vSchemaObjectName = schemaObjectThreePartName + { + vResult.OldName = vSchemaObjectName; + } + To + ( + vNewName = identifier + { + vResult.NewName = vNewName; + } + ) + + { + UpdateTokenInfo(vResult, tRename); + } + ; + +///////////////////////////////////////////////// +// CREATE TABLE AS SELECT STATEMENT +///////////////////////////////////////////////// + +ctasCreateTableStatement [CreateTableStatement vParent] +{ + SelectStatement vSelectStatement; +} + : + (columnNameList[vParent, vParent.CtasColumns])? + ( + options {greedy = true; } : + withTableOptions[vParent] + ) + As + vSelectStatement = selectStatement[SubDmlFlags.None] + { + vParent.SelectStatement = vSelectStatement; + } + { + CheckCtasStatementHasDistributionOption(vParent); + } + ; + +///////////////////////////////////////////////// +// CREATE EXTERNAL TABLE AS SELECT STATEMENT +///////////////////////////////////////////////// +ctasCreateExternalTableStatement [CreateExternalTableStatement vParent] +{ + SelectStatement vSelectStatement; +} + : + ( + options { greedy = true; } : + withExternalTableOptions[vParent] + ) + As + vSelectStatement = selectStatement[SubDmlFlags.None] + { + vParent.SelectStatement = vSelectStatement; + } + { + CheckExternalTableCtasStatementHasNotRejectedRowLocationOption(vParent); + } + ; + +// This rule conflicts with identifierStatements (both can start with Identifier) +// We should update predicates here and in identifierStatements at the same time +optSimpleExecute returns [ExecuteStatement vResult = null] +{ + ExecutableProcedureReference vExecProc; + ExecuteSpecification vExecuteSpecification; +} + : {!NextTokenMatches(CodeGenerationSupporter.Disable) && !NextTokenMatches(CodeGenerationSupporter.Enable) && + !NextTokenMatches(CodeGenerationSupporter.Move) && !NextTokenMatches(CodeGenerationSupporter.Get) && + !NextTokenMatches(CodeGenerationSupporter.Receive) && !NextTokenMatches(CodeGenerationSupporter.Send) && + !NextTokenMatches(CodeGenerationSupporter.Throw) && !NextTokenMatches(CodeGenerationSupporter.Rename)}? + (vExecProc = execProc + { + vResult = FragmentFactory.CreateFragment(); + vExecuteSpecification = FragmentFactory.CreateFragment(); + vExecuteSpecification.ExecutableEntity = vExecProc; + vResult.ExecuteSpecification=vExecuteSpecification; + } + optSemicolons[vResult] + ) + | /* empty */ + ; + +statement returns [TSqlStatement vResult = null] +{ + // The next tokens offset is cached to help error + // recovery, so when error occurs if the next token is + // Create or Alter, and its offset is the same as + // vNextTokenOffset that means, this rule already + // tried to parsed and failed, so we should skip over. + // The case where it works is: + // select * from create table t1(c1 int) + int nextTokenLine = LT(1).getLine(); + int nextTokenColumn = LT(1).getColumn(); +} + : vResult=createTableStatement + | vResult=alterTableStatement + | vResult=createIndexStatement + | vResult=copyStatement + | vResult=declareStatements + | vResult=setStatements + | vResult=beginStatements + | vResult=breakStatement + | vResult=continueStatement + | vResult=ifStatement + | vResult=whileStatement + | vResult=labelStatement + | vResult=backupStatements + | vResult=restoreStatements + | vResult=gotoStatement + | vResult=saveTransactionStatement + | vResult=rollbackTransactionStatement + | vResult=commitTransactionStatement + | vResult=createStatisticsStatement + | vResult=updateStatisticsStatement + | vResult=alterDatabaseStatements + | vResult=executeStatement + | vResult=withCommonTableExpressionsAndXmlNamespacesStatements + | vResult=raiseErrorStatement + | vResult=alter2005Statements + | vResult=create2005Statements + | vResult=createDatabaseStatements + | vResult=addStatements + | vResult=identifierStatements + | vResult=printStatement + | vResult=waitForStatement + | vResult=readTextStatement + | vResult=updateTextStatement + | vResult=writeTextStatement + | vResult=lineNoStatement + | vResult=useStatement + | vResult=killStatements + | vResult=bulkInsertStatement + | vResult=insertBulkStatement + | vResult=checkpointStatement + | vResult=reconfigureStatement + | vResult=shutdownStatement + | vResult=setUserStatement + | vResult=truncateTableStatement + | vResult=grantStatement90 + | vResult=denyStatement90 + | vResult=revokeStatement90 + | vResult=returnStatement + | vResult=openStatements + | vResult=closeStatements + | vResult=deallocateCursorStatement + | vResult=fetchCursorStatement + | vResult=dropStatements + | vResult=dbccStatement + | vResult=revertStatement + | vResult=executeAsStatement + | vResult=endConversationStatement + ; + exception + catch[TSqlParseErrorException exception] + { + if (!exception.DoNotLog) + { + AddParseError(exception.ParseError); + } + RecoverAtStatementLevel(nextTokenLine, nextTokenColumn); + } + catch[antlr.NoViableAltException exception] + { + ParseError error = GetFaultTolerantUnexpectedTokenError( + exception.token, exception, _tokenSource.LastToken.Offset); + AddParseError(error); + RecoverAtStatementLevel(nextTokenLine, nextTokenColumn); + } + catch[antlr.MismatchedTokenException exception] + { + ParseError error = GetFaultTolerantUnexpectedTokenError( + exception.token, exception, _tokenSource.LastToken.Offset); + AddParseError(error); + RecoverAtStatementLevel(nextTokenLine, nextTokenColumn); + } + catch[antlr.RecognitionException] + { + ParseError error = GetUnexpectedTokenError(); + AddParseError(error); + RecoverAtStatementLevel(nextTokenLine, nextTokenColumn); + } + catch[antlr.TokenStreamRecognitionException exception] + { + // This exception should be handled when we are creating TSqlTokenStream... + ParseError error = ProcessTokenStreamRecognitionException(exception, _tokenSource.LastToken.Offset); + AddParseError(error); + RecoverAtStatementLevel(nextTokenLine, nextTokenColumn); + } + catch[antlr.ANTLRException exception] + { + CreateInternalError("statement", exception); + } + +withCommonTableExpressionsAndXmlNamespacesStatements returns [StatementWithCtesAndXmlNamespaces vResult = null] +{ + WithCtesAndXmlNamespaces vWithCommonTableExpressionsAndXmlNamespaces = null; +} + : + ( + vWithCommonTableExpressionsAndXmlNamespaces=withCommonTableExpressionsAndXmlNamespaces + )? + ( + vResult=select[SubDmlFlags.SelectNotForInsert] + { + // check for invalid combination of CHANGE_TRACKING_CONTEXT and Select statement + if ((vWithCommonTableExpressionsAndXmlNamespaces != null) && (vWithCommonTableExpressionsAndXmlNamespaces.ChangeTrackingContext != null)) + ThrowParseErrorException("SQL46072", vWithCommonTableExpressionsAndXmlNamespaces.ChangeTrackingContext, TSqlParserResource.SQL46072Message); + } + | + vResult=deleteStatement[SubDmlFlags.None] + | + vResult=insertStatement[SubDmlFlags.None] + | + vResult=updateStatement[SubDmlFlags.None] + | + vResult=mergeStatement[SubDmlFlags.None] + ) + { + vResult.WithCtesAndXmlNamespaces = vWithCommonTableExpressionsAndXmlNamespaces; + } + ; + +lastStatement returns [TSqlStatement vResult = null] + : vResult=createProcedureStatement + | vResult=alterProcedureStatement + | vResult=createTriggerStatement + | vResult=alterTriggerStatement + | vResult=createDefaultStatement + | vResult=createRuleStatement + | vResult=createViewStatement + | vResult=alterViewStatement + | vResult=createFunctionStatement + | vResult=alterFunctionStatement + | vResult=createSchemaStatement + | vResult=createIdentifierStatement + | vResult=alterIdentifierStatement + | vResult=useFederationStatement + | vResult=createOrAlterStatements + ; + +createIdentifierStatement returns [TSqlStatement vResult] + : tCreate:Create + ( + {NextTokenMatches(CodeGenerationSupporter.Materialized)}? + vResult=createMaterializedViewStatement + | + {NextTokenMatches(CodeGenerationSupporter.Federation)}? + vResult=createFederationStatement + ) + { + UpdateTokenInfo(vResult,tCreate); + } + ; + +alterIdentifierStatement returns [TSqlStatement vResult] + : tAlter:Alter + ( + {NextTokenMatches(CodeGenerationSupporter.Materialized)}? + vResult=alterMaterializedViewStatement + | + {NextTokenMatches(CodeGenerationSupporter.Federation)}? + vResult=alterFederationStatement + ) + { + UpdateTokenInfo(vResult,tAlter); + } + ; + +createOrAlterStatements returns [TSqlStatement vResult] + : tCreate:Create Or Alter + ( + vResult = createOrAlterFunctionStatement + | vResult = createOrAlterProcedureStatement + | vResult = createOrAlterTriggerStatement + | vResult = createOrAlterViewStatement + ) + { + UpdateTokenInfo(vResult,tCreate); + } + ; + +// This rule conflicts with optSimpleExecute (both can start with Identifier) +// We should update predicates here and in optSimpleExecute at the same time +identifierStatements returns [TSqlStatement vResult] + : {NextTokenMatches(CodeGenerationSupporter.Disable)}? + vResult=disableTriggerStatement + | {NextTokenMatches(CodeGenerationSupporter.Enable)}? + vResult=enableTriggerStatement + | {NextTokenMatches(CodeGenerationSupporter.Move)}? + vResult = moveConversationStatement + | {NextTokenMatches(CodeGenerationSupporter.Get)}? + vResult = getConversationGroupStatement + | {NextTokenMatches(CodeGenerationSupporter.Receive)}? + vResult = receiveStatement + | {NextTokenMatches(CodeGenerationSupporter.Send)}? + vResult = sendStatement + | {NextTokenMatches(CodeGenerationSupporter.Throw)}? + vResult = throwStatement + | {NextTokenMatches(CodeGenerationSupporter.Rename)}? + vResult = renameEntityStatement + ; + + +disableTriggerStatement returns [EnableDisableTriggerStatement vResult = this.FragmentFactory.CreateFragment()] + : + tDisable:Identifier + { + Match(tDisable, CodeGenerationSupporter.Disable); + UpdateTokenInfo(vResult,tDisable); + vResult.TriggerEnforcement = TriggerEnforcement.Disable; + } + enableDisableTriggerBody[vResult] + ; + +enableTriggerStatement returns [EnableDisableTriggerStatement vResult = this.FragmentFactory.CreateFragment()] + : + tEnable:Identifier + { + Match(tEnable, CodeGenerationSupporter.Enable); + UpdateTokenInfo(vResult,tEnable); + vResult.TriggerEnforcement = TriggerEnforcement.Enable; + } + enableDisableTriggerBody[vResult] + ; + +enableDisableTriggerBody[EnableDisableTriggerStatement vParent] +{ + SchemaObjectName vSchemaObjectName; + TriggerObject vTriggerObject; +} + : + Trigger + ( + vSchemaObjectName=schemaObjectThreePartName + { + AddAndUpdateTokenInfo(vParent, vParent.TriggerNames,vSchemaObjectName); + } + ( + Comma vSchemaObjectName=schemaObjectThreePartName + { + AddAndUpdateTokenInfo(vParent, vParent.TriggerNames,vSchemaObjectName); + } + )* + | + All + { + vParent.All = true; + } + ) + On vTriggerObject=triggerObject + { + vParent.TriggerObject = vTriggerObject; + } + ; + +create2005Statements returns [TSqlStatement vResult = null] + : tCreate:Create + ( + {NextTokenMatches(CodeGenerationSupporter.Aggregate)}? + vResult=createAggregateStatement + | + {NextTokenMatches(CodeGenerationSupporter.Application)}? + vResult=createApplicationRoleStatement + | + {NextTokenMatches(CodeGenerationSupporter.Assembly)}? + vResult=createAssemblyStatement + | + {NextTokenMatches(CodeGenerationSupporter.Asymmetric)}? + vResult=createAsymmetricKeyStatement + | + {NextTokenMatches(CodeGenerationSupporter.Availability)}? + vResult=createAvailabilityGroupStatement + | + {NextTokenMatches(CodeGenerationSupporter.Broker)}? + vResult=createBrokerPriorityStatement + | + {NextTokenMatches(CodeGenerationSupporter.Certificate)}? + vResult=createCertificateStatement + | + {NextTokenMatches(CodeGenerationSupporter.Column)}? + vResult=createColumnStatements + | + {NextTokenMatches(CodeGenerationSupporter.ColumnStore)}? + vResult=createColumnStoreIndexStatement[null, null] + | + {NextTokenMatches(CodeGenerationSupporter.Json)}? + vResult=createJsonIndexStatement[null, null] + | + {NextTokenMatches(CodeGenerationSupporter.Vector)}? + vResult=createVectorIndexStatement[null, null] + | + {NextTokenMatches(CodeGenerationSupporter.Contract)}? + vResult=createContractStatement + | + {NextTokenMatches(CodeGenerationSupporter.Credential)}? + vResult=createCredentialStatement + | + {NextTokenMatches(CodeGenerationSupporter.Cryptographic)}? + vResult=createCryptographicProviderStatement + | + {NextTokenMatches(CodeGenerationSupporter.Endpoint)}? + vResult=createEndpointStatement + | + {NextTokenMatches(CodeGenerationSupporter.Event)}? + vResult=createEventStatement // NOTIFICATION or SESSION + | + {NextTokenMatches(CodeGenerationSupporter.External)}? + vResult=createExternalStatements // EXTERNAL DATA SOURCE, FILE FORMAT, STREAM, TABLE, RESOURCE POOL, LIBRARY, LANGUAGE, MODEL + | + {NextTokenMatches(CodeGenerationSupporter.Fulltext)}? + vResult=createFulltextStatement // Index or CATALOG + | + vResult=createPrimaryXmlIndexStatement + | + {NextTokenMatches(CodeGenerationSupporter.Selective)}? + vResult=createSelectiveXmlIndexStatement + | + {NextTokenMatches(CodeGenerationSupporter.Xml)}? + vResult=createXmlStatements // Index or Schema + | + {NextTokenMatches(CodeGenerationSupporter.Login)}? + vResult=createLoginStatement + | + {NextTokenMatches(CodeGenerationSupporter.Message)}? + vResult=createMessageTypeStatement + | + {NextTokenMatches(CodeGenerationSupporter.Master)}? + vResult=createMasterKeyStatement + | + {NextTokenMatches(CodeGenerationSupporter.Partition)}? + vResult=createPartitionStatement // SCHEME or Function + | + {NextTokenMatches(CodeGenerationSupporter.Queue)}? + vResult=createQueueStatement + | + {NextTokenMatches(CodeGenerationSupporter.Remote)}? + vResult=createRemoteServiceBindingStatement + | + {NextTokenMatches(CodeGenerationSupporter.Resource)}? + vResult=createResourcePoolStatement + | + {NextTokenMatches(CodeGenerationSupporter.Role)}? + vResult=createRoleStatement + | + {NextTokenMatches(CodeGenerationSupporter.Route)}? + vResult=createRouteStatement + | + {NextTokenMatches(CodeGenerationSupporter.Search)}? + vResult=createSearchPropertyListStatement + | + {NextTokenMatches(CodeGenerationSupporter.Service)}? + vResult=createServiceStatement + | + {NextTokenMatches(CodeGenerationSupporter.Spatial)}? + vResult=createSpatialIndexStatement + | + {NextTokenMatches(CodeGenerationSupporter.Symmetric)}? + vResult=createSymmetricKeyStatement + | + {NextTokenMatches(CodeGenerationSupporter.Synonym)}? + vResult=createSynonymStatement + | + {NextTokenMatches(CodeGenerationSupporter.Type)}? + vResult=createTypeStatement + | + {NextTokenMatches(CodeGenerationSupporter.Server)}? + vResult=createServerStatements //AUDIT or ROLE + | + {NextTokenMatches(CodeGenerationSupporter.Workload)}? + vResult=createWorkloadStatements + | + {NextTokenMatches(CodeGenerationSupporter.Sequence)}? + vResult=createSequenceStatement + | + {NextTokenMatches(CodeGenerationSupporter.Security)}? + vResult=createSecurityPolicyStatement + | + vResult=createUserStatement + ) + { + UpdateTokenInfo(vResult,tCreate); + ThrowPartialAstIfPhaseOne(vResult); + } + ; + exception + catch[PhaseOnePartialAstException exception] + { + UpdateTokenInfo(exception.Statement, tCreate); + throw; + } + +createAggregateStatement returns [CreateAggregateStatement vResult = FragmentFactory.CreateFragment()] +{ + SchemaObjectName vSchemaObjectName; + ProcedureParameter vParameter; + AssemblyName vAssemblyName; + DataTypeReference vDataType; +} + : tAggregate:Identifier vSchemaObjectName=schemaObjectThreePartName + { + Match(tAggregate, CodeGenerationSupporter.Aggregate); + CheckTwoPartNameForSchemaObjectName(vSchemaObjectName, CodeGenerationSupporter.Aggregate); + vResult.Name = vSchemaObjectName; + ThrowPartialAstIfPhaseOne(vResult); + } + LeftParenthesis vParameter = aggregateParameter + { + AddAndUpdateTokenInfo(vResult, vResult.Parameters, vParameter); + } + (Comma vParameter = aggregateParameter + { + AddAndUpdateTokenInfo(vResult, vResult.Parameters, vParameter); + } + )* + RightParenthesis + tReturns:Identifier vDataType = scalarDataType + { + Match(tReturns,CodeGenerationSupporter.Returns); + vResult.ReturnType = vDataType; + } + External vAssemblyName = assemblyName + { + vResult.AssemblyName = vAssemblyName; + } + ; + +aggregateParameter returns [ProcedureParameter vResult = FragmentFactory.CreateFragment()] +{ + Identifier vParamName; + DataTypeReference vDataType; + NullableConstraintDefinition vNullableConstraintDefinition; +} + : vParamName = identifierVariable (As)? vDataType = scalarDataType + { + vResult.VariableName = vParamName; + vResult.DataType = vDataType; + } + ( + vNullableConstraintDefinition = nullableConstraint + { + vResult.Nullable=vNullableConstraintDefinition; + } + )? + ; + +createApplicationRoleStatement returns [CreateApplicationRoleStatement vResult = this.FragmentFactory.CreateFragment()] + : + applicationRoleStatement[vResult, true] + ; + +createAssemblyStatement returns [CreateAssemblyStatement vResult = this.FragmentFactory.CreateFragment()] +{ + Identifier vIdentifier; + AssemblyOption vOption; +} + : tAssembly:Identifier vIdentifier=identifier + { + Match(tAssembly, CodeGenerationSupporter.Assembly); + vResult.Name = vIdentifier; + ThrowPartialAstIfPhaseOne(vResult); + } + authorizationOpt[vResult] + From expressionList[vResult, vResult.Parameters] + ( + // Greedy due to conflict with withCommonTableExpressionsAndXmlNamespaces + options {greedy = true; } : + With tPermissionSet:Identifier EqualsSign + vOption=assemblyPermissionSetOption[tPermissionSet] + { + AddAndUpdateTokenInfo(vResult, vResult.Options, vOption); + } + )? + ; + +createExternalLibraryStatement returns [CreateExternalLibraryStatement vResult = this.FragmentFactory.CreateFragment()] +{ + Identifier vIdentifier; + ExternalLibraryFileOption vFileOption; + StringLiteral vLanguage; +} + : tLibrary:Identifier vIdentifier=identifier + { + Match(tLibrary, CodeGenerationSupporter.Library); + vResult.Name = vIdentifier; + ThrowPartialAstIfPhaseOne(vResult); + } + authorizationOpt[vResult] + From vFileOption=createExternalLibraryFileOption + { + vResult.ExternalLibraryFiles.Add(vFileOption); + } + (Comma vFileOption = createExternalLibraryFileOption + { + vResult.ExternalLibraryFiles.Add(vFileOption); + } + )* + With LeftParenthesis tLanguage:Identifier EqualsSign vLanguage=nonEmptyString + { + Match(tLanguage, CodeGenerationSupporter.Language); + vResult.Language = vLanguage; + } + RightParenthesis + ; + +alterExternalLibraryStatement returns [AlterExternalLibraryStatement vResult = this.FragmentFactory.CreateFragment()] +{ + Identifier vIdentifier; + ExternalLibraryFileOption vFileOption; + StringLiteral vLanguage; +} + : tLibrary:Identifier vIdentifier=identifier + { + Match(tLibrary, CodeGenerationSupporter.Library); + vResult.Name = vIdentifier; + ThrowPartialAstIfPhaseOne(vResult); + } + authorizationOpt[vResult] + Set vFileOption=alterExternalLibraryFileOption + { + vResult.ExternalLibraryFiles.Add(vFileOption); + } + With LeftParenthesis tLanguage:Identifier EqualsSign vLanguage=nonEmptyString + { + Match(tLanguage, CodeGenerationSupporter.Language); + vResult.Language = vLanguage; + } + RightParenthesis + ; + +dropExternalLibraryStatement returns [DropExternalLibraryStatement vResult = this.FragmentFactory.CreateFragment()] +{ + Identifier vIdentifier; +} + : tLibrary:Identifier vIdentifier=identifier + { + Match(tLibrary, CodeGenerationSupporter.Library); + vResult.Name = vIdentifier; + ThrowPartialAstIfPhaseOne(vResult); + } + authorizationOpt[vResult] + ; + +binaryOrString returns [ValueExpression vResult] + : + vResult=binary + | + vResult=stringLiteral + ; + +createExternalLibraryFileOption returns [ExternalLibraryFileOption vResult = FragmentFactory.CreateFragment()] +{ + ScalarExpression vContent; + Identifier vPlatform; +} + : LeftParenthesis tContent:Identifier EqualsSign vContent=binaryOrString + { + Match(tContent, CodeGenerationSupporter.Content); + vResult.Content = vContent; + } + (Comma tPlatform:Identifier EqualsSign vPlatform=identifier + { + Match(tPlatform, CodeGenerationSupporter.Platform); + vResult.Platform = vPlatform; + } + )* + RightParenthesis + ; + +alterExternalLibraryFileOption returns [ExternalLibraryFileOption vResult = FragmentFactory.CreateFragment()] +{ + ScalarExpression vContent; + Identifier vPlatform; +} + : LeftParenthesis tContent:Identifier EqualsSign vContent=binaryOrString + { + Match(tContent, CodeGenerationSupporter.Content); + vResult.Content = vContent; + } + (Comma tPlatform:Identifier EqualsSign vPlatform=identifier + { + Match(tPlatform, CodeGenerationSupporter.Platform); + vResult.Platform = vPlatform; + } + )* + RightParenthesis + ; + +createExternalLanguageStatement returns [CreateExternalLanguageStatement vResult = this.FragmentFactory.CreateFragment()] +{ + Identifier vIdentifier; + ExternalLanguageFileOption vFileOption; +} + : tLanguage:Identifier vIdentifier=identifier + { + Match(tLanguage, CodeGenerationSupporter.Language); + vResult.Name = vIdentifier; + ThrowPartialAstIfPhaseOne(vResult); + } + authorizationOpt[vResult] + From vFileOption=externalLanguageFileOption + { + vResult.ExternalLanguageFiles.Add(vFileOption); + } + (Comma vFileOption = externalLanguageFileOption + { + vResult.ExternalLanguageFiles.Add(vFileOption); + } + )* + ; + +externalLanguageFileOption returns [ExternalLanguageFileOption vResult = FragmentFactory.CreateFragment()] +{ + ScalarExpression vContent; + StringLiteral vFileName; + Identifier vPlatform; + StringLiteral vParameters; + StringLiteral vEnvironmentVariables; +} + : LeftParenthesis tContent:Identifier EqualsSign vContent=binaryOrString + { + Match(tContent, CodeGenerationSupporter.Content); + vResult.Content = vContent; + } + Comma tFileName:Identifier EqualsSign vFileName=nonEmptyString + { + Match(tFileName, CodeGenerationSupporter.File_Name); + vResult.FileName = vFileName; + } + ( + tComma:Comma + ( + {NextTokenMatches(CodeGenerationSupporter.Platform)}? + ( + tPlatform:Identifier EqualsSign vPlatform=identifier + { + Match(tPlatform, CodeGenerationSupporter.Platform); + vResult.Platform = vPlatform; + } + ) + | + {NextTokenMatches(CodeGenerationSupporter.Parameters)}? + ( + tParameters:Identifier EqualsSign vParameters=nonEmptyString + { + Match(tParameters, CodeGenerationSupporter.Parameters); + vResult.Parameters = vParameters; + } + ) + | + {NextTokenMatches(CodeGenerationSupporter.EnvironmentVariables)}? + ( + tEnvironmentVariables:Identifier EqualsSign vEnvironmentVariables=nonEmptyString + { + Match(tEnvironmentVariables, CodeGenerationSupporter.EnvironmentVariables); + vResult.EnvironmentVariables = vEnvironmentVariables; + } + ) + ) + )* + + RightParenthesis + ; + +alterExternalLanguageStatement returns [AlterExternalLanguageStatement vResult = this.FragmentFactory.CreateFragment()] +{ + Identifier vIdentifier; + ExternalLanguageFileOption vFileOption; + Identifier vPlatform; +} + : tLanguage:Identifier vIdentifier=identifier + { + Match(tLanguage, CodeGenerationSupporter.Language); + vResult.Name = vIdentifier; + vResult.Operation=new Identifier(); + ThrowPartialAstIfPhaseOne(vResult); + } + ( + authorizationOpt[vResult] + ( + {NextTokenMatches(CodeGenerationSupporter.Set)}? + ( + Set vFileOption=externalLanguageFileOption + { + vResult.ExternalLanguageFiles.Add(vFileOption); + vResult.Operation.Value = CodeGenerationSupporter.Set; + } + ) + | + {NextTokenMatches(CodeGenerationSupporter.Add)}? + ( + Add vFileOption=externalLanguageFileOption + { + vResult.ExternalLanguageFiles.Add(vFileOption); + vResult.Operation.Value = CodeGenerationSupporter.Add; + } + ) + | + {NextTokenMatches(CodeGenerationSupporter.Remove)}? + ( + tRemove:Identifier tPlatform:Identifier vPlatform=identifier + { + Match(tRemove, CodeGenerationSupporter.Remove); + vResult.Platform = vPlatform; + vResult.Operation.Value = CodeGenerationSupporter.Remove; + } + ) + ) + ) + + ; + +dropExternalLanguageStatement returns [DropExternalLanguageStatement vResult = this.FragmentFactory.CreateFragment()] +{ + Identifier vIdentifier; +} + : tLanguage:Identifier vIdentifier=identifier + { + Match(tLanguage, CodeGenerationSupporter.Language); + vResult.Name = vIdentifier; + ThrowPartialAstIfPhaseOne(vResult); + } + authorizationOpt[vResult] + ; + +createAsymmetricKeyStatement returns [CreateAsymmetricKeyStatement vResult = FragmentFactory.CreateFragment()] +{ + Identifier vIdentifier; + Literal vPassword; +} + : tAsymmetric:Identifier Key vIdentifier=identifier + { + Match(tAsymmetric, CodeGenerationSupporter.Asymmetric); + vResult.Name = vIdentifier; + ThrowPartialAstIfPhaseOne(vResult); + } + authorizationOpt[vResult] + createAsymmetricKeyParams[vResult] + ( + // Greedy due to linear approximation introduced after the rule securityStatementPermission + options {greedy = true; } : + vPassword = encryptClause + { + vResult.Password = vPassword; + } + )? + ; + +createAsymmetricKeyParams[CreateAsymmetricKeyStatement vParent] +{ + EncryptionSource vSource; +} + : From vSource = asymKeySource + { + vParent.KeySource=vSource; + } + | With asymKeySpec[vParent] + ; + +asymKeySource returns [EncryptionSource vResult] + : + vResult = fileEncryptionSource + | {NextTokenMatches(CodeGenerationSupporter.Assembly)}? + vResult = assemblyEncryptionSource + | vResult = providerEncryptionSource + ; + +assemblyEncryptionSource returns [AssemblyEncryptionSource vResult=FragmentFactory.CreateFragment()] +{ + Identifier vAssembly; +} + : tAssembly:Identifier vAssembly = identifier + { + Match(tAssembly, CodeGenerationSupporter.Assembly); + vResult.Assembly = vAssembly; + } + ; + +providerEncryptionSource returns [ProviderEncryptionSource vResult = FragmentFactory.CreateFragment()] +{ + Identifier vProviderName; +} + : tProvider:Identifier vProviderName = identifier + { + Match(tProvider, CodeGenerationSupporter.Provider); + vResult.Name = vProviderName; + } + providerKeySourceOptions[vResult.KeyOptions, vResult] + ; + +fileEncryptionSource returns [FileEncryptionSource vResult = FragmentFactory.CreateFragment()] +{ + Literal vFile; +} + : (tExecutable:Identifier + { + Match(tExecutable, CodeGenerationSupporter.Executable); + vResult.IsExecutable = true; + } + )? + File EqualsSign vFile = stringLiteral + { + vResult.File = vFile; + } + ; + +asymKeySpec [CreateAsymmetricKeyStatement vParent] + : tAlgorithm:Identifier EqualsSign tRealAlg:Identifier + { + Match(tAlgorithm,CodeGenerationSupporter.Algorithm); + vParent.EncryptionAlgorithm = EncryptionAlgorithmsHelper.Instance.ParseOption(tRealAlg); + UpdateTokenInfo(vParent,tRealAlg); + } + ; + +createCertificateStatement returns [CreateCertificateStatement vResult = FragmentFactory.CreateFragment()] +{ + Identifier vIdentifier; +} + : tCertificate:Identifier vIdentifier=identifier + { + Match(tCertificate, CodeGenerationSupporter.Certificate); + vResult.Name = vIdentifier; + ThrowPartialAstIfPhaseOne(vResult); + } + authorizationOpt[vResult] + createCertificateParams[vResult] + ( + // Greedy due to linear approximation introduced after the rule securityStatementPermission + options {greedy = true; } : + createCertificateActivityFlag[vResult] + )? + ; + +createCertificateParams [CreateCertificateStatement vParent] +{ + Literal vPassword; + CertificateOption vOption; + CertificateOptionKinds encounteredOptions = CertificateOptionKinds.None; +} + : From certificateSource[vParent] + | ( + (vPassword = encryptClause + { + vParent.EncryptionPassword = vPassword; + } + )? + With vOption = certificateOption[encounteredOptions] + { + encounteredOptions = encounteredOptions | vOption.Kind; + AddAndUpdateTokenInfo(vParent, vParent.CertificateOptions,vOption); + } + (Comma vOption = certificateOption[encounteredOptions] + { + encounteredOptions = encounteredOptions | vOption.Kind; + AddAndUpdateTokenInfo(vParent, vParent.CertificateOptions,vOption); + } + )* + ) + ; + +createCertificateActivityFlag [CertificateStatementBase vParent] +{ + OptionState vOptionState; +} + : tActive:Identifier For tBeginDialog:Identifier EqualsSign vOptionState = optionOnOff[vParent] + { + Match(tActive,CodeGenerationSupporter.Active); + Match(tBeginDialog,CodeGenerationSupporter.BeginDialog); + vParent.ActiveForBeginDialog = vOptionState; + } + ; + +certificateOption [CertificateOptionKinds encountered]returns [CertificateOption vResult = FragmentFactory.CreateFragment()] +{ + Literal vValue; +} + : tOption:Identifier EqualsSign vValue = stringLiteral + { + vResult.Kind = CertificateOptionKindsHelper.Instance.ParseOption(tOption); + vResult.Value = vValue; + CheckCertificateOptionDupication(encountered,vResult.Kind,tOption); + } + ; + +certificateSource [CreateCertificateStatement vParent] +{ + EncryptionSource vCertificateSource; +} + : + ( + vCertificateSource=fileEncryptionSource + ( + // Greedy due to conflict with withCommonTableExpressionsAndXmlNamespaces + options {greedy = true; } : + With privateKeySpec[vParent] + )? + | + vCertificateSource = assemblyEncryptionSource + ) + { + vParent.CertificateSource = vCertificateSource; + } + ; + +encryptClause returns [Literal vResult] + : tEncryption:Identifier By tPassword:Identifier EqualsSign vResult = stringLiteral + { + Match(tEncryption,CodeGenerationSupporter.Encryption); + Match(tPassword,CodeGenerationSupporter.Password); + } + ; + +privateKeySpec [CertificateStatementBase vParent] + : tPrivate:Identifier Key LeftParenthesis certificatePrivateKeySpec[vParent] (Comma certificatePrivateKeySpec[vParent])* tRParen:RightParenthesis + { + Match(tPrivate,CodeGenerationSupporter.Private); + UpdateTokenInfo(vParent,tRParen); + } + ; + +certificatePrivateKeySpec [CertificateStatementBase vParent] +{ + Literal vFilePath; +} + : passwordChangeOption[vParent] + | tFile:File EqualsSign vFilePath = stringLiteral + { + if (vParent.PrivateKeyPath != null) + throw GetUnexpectedTokenErrorException(tFile); + else + vParent.PrivateKeyPath = vFilePath; + } + ; + +passwordChangeOption [IPasswordChangeOption vParent] +{ + Literal vPassword; +} + : tEncryptionDecryption:Identifier By tPassword:Identifier EqualsSign vPassword = stringLiteral + { + if (TryMatch(tEncryptionDecryption,CodeGenerationSupporter.Encryption)) + { + if (vParent.EncryptionPassword != null) + throw GetUnexpectedTokenErrorException(tEncryptionDecryption); + else + vParent.EncryptionPassword = vPassword; + } + else + { + Match(tEncryptionDecryption,CodeGenerationSupporter.Decryption); + if (vParent.DecryptionPassword != null) + throw GetUnexpectedTokenErrorException(tEncryptionDecryption); + else + vParent.DecryptionPassword = vPassword; + } + } + ; + + +createContractStatement returns [CreateContractStatement vResult = FragmentFactory.CreateFragment()] +{ + Identifier vIdentifier; + ContractMessage vMessage; +} + : tContract:Identifier vIdentifier=identifier + { + Match(tContract, CodeGenerationSupporter.Contract); + vResult.Name = vIdentifier; + ThrowPartialAstIfPhaseOne(vResult); + } + authorizationOpt[vResult] + LeftParenthesis vMessage = contractMessage + { + AddAndUpdateTokenInfo(vResult, vResult.Messages,vMessage); + } + (Comma vMessage = contractMessage + { + AddAndUpdateTokenInfo(vResult, vResult.Messages,vMessage); + } + )* + tRParen:RightParenthesis + { + UpdateTokenInfo(vResult,tRParen); + } + ; + +contractMessage returns [ContractMessage vResult = FragmentFactory.CreateFragment()] +{ + Identifier vMessageName; +} + : vMessageName = identifier tSent:Identifier By + { + Match(tSent,CodeGenerationSupporter.Sent); + vResult.Name = vMessageName; + } + ( tAny:Any + { + vResult.SentBy = MessageSender.Any; + UpdateTokenInfo(vResult,tAny); + } + | tInitiatorTarget:Identifier + { + if (TryMatch(tInitiatorTarget,CodeGenerationSupporter.Initiator)) + vResult.SentBy = MessageSender.Initiator; + else + { + Match(tInitiatorTarget,CodeGenerationSupporter.Target); + vResult.SentBy = MessageSender.Target; + } + UpdateTokenInfo(vResult,tInitiatorTarget); + } + ) + ; + +createDatabaseScopedCredentialStatement returns [CreateCredentialStatement vResult = FragmentFactory.CreateFragment()] + : tScoped:Identifier + { + Match(tScoped, CodeGenerationSupporter.Scoped); + vResult.IsDatabaseScoped = true; + } + + credentialStatementBody[vResult] + + ; + +createCredentialStatement returns [CreateCredentialStatement vResult = FragmentFactory.CreateFragment()] +{ + Identifier vCryptographicProviderName; + vResult.IsDatabaseScoped = false; +} + : credentialStatementBody[vResult] + ( + For tCryptographic:Identifier tProvider:Identifier vCryptographicProviderName=identifier + { + Match(tCryptographic, CodeGenerationSupporter.Cryptographic); + Match(tProvider, CodeGenerationSupporter.Provider); + vResult.CryptographicProviderName = vCryptographicProviderName; + } + )? + ; + +credentialStatementBody [CredentialStatement vParent] +{ + Identifier vIdentifier; + Literal vLiteral; +} + : tCredential:Identifier vIdentifier=identifier + { + Match(tCredential, CodeGenerationSupporter.Credential); + vParent.Name = vIdentifier; + ThrowPartialAstIfPhaseOne(vParent); + } + With Identity EqualsSign vLiteral = stringLiteral + { + vParent.Identity = vLiteral; + } + (Comma tSecret:Identifier EqualsSign vLiteral = stringLiteral + { + Match(tSecret,CodeGenerationSupporter.Secret); + vParent.Secret = vLiteral; + } + )? + ; + +createServerStatements returns [TSqlStatement vResult] + : tServer:Identifier + { + Match(tServer, CodeGenerationSupporter.Server); + } + ( + {NextTokenMatches(CodeGenerationSupporter.Audit)}? + vResult=createServerAuditStatements + | + {NextTokenMatches(CodeGenerationSupporter.Role)}? + vResult=createServerRoleStatement + ) + ; + +createServerAuditStatements returns [TSqlStatement vResult] + : tAudit:Identifier + { + Match(tAudit, CodeGenerationSupporter.Audit); + } + ( + vResult = createServerAuditSpecificationStatement + | + vResult = createServerAuditStatement + ) + ; + +createServerAuditStatement returns [CreateServerAuditStatement vResult = FragmentFactory.CreateFragment()] +{ + Identifier vAuditName; + AuditTarget vTarget; + BooleanExpression vFilterPredicate; +} + : vAuditName = identifier + { + vResult.AuditName = vAuditName; + ThrowPartialAstIfPhaseOne(vResult); + } + vTarget = auditTargetClause[true] + { + vResult.AuditTarget = vTarget; + } + ( // Greedy due to conflict with withCommonTableExpressionsAndXmlNamespaces + options {greedy = true; } : + auditCreateWithClause[vResult] + )? + ( + Where vFilterPredicate=eventBooleanExpression + { + vResult.PredicateExpression = vFilterPredicate; + } + )? + ; + +auditTargetClause [bool pathRequired] returns [AuditTarget vResult = FragmentFactory.CreateFragment()] +{ + AuditTargetOption vOption; + bool pathOptionEncountered = false; +} + : tTo:To + { + UpdateTokenInfo(vResult,tTo); + } + ( + ( + tFile:File LeftParenthesis vOption = auditFileOption + { + vResult.TargetKind = AuditTargetKind.File; + AddAndUpdateTokenInfo(vResult, vResult.TargetOptions, vOption); + + pathOptionEncountered |= (vOption.OptionKind==AuditTargetOptionKind.FilePath); + } + ( + Comma vOption = auditFileOption + { + AddAndUpdateTokenInfo(vResult, vResult.TargetOptions, vOption); + + pathOptionEncountered |= vOption.OptionKind==AuditTargetOptionKind.FilePath; + } + )* + | + tUrl:Identifier LeftParenthesis vOption = auditUrlOption + { + Match(tUrl, CodeGenerationSupporter.Url); + vResult.TargetKind = AuditTargetKind.Url; + AddAndUpdateTokenInfo(vResult, vResult.TargetOptions, vOption); + + pathOptionEncountered |= (vOption.OptionKind==AuditTargetOptionKind.Path); + } + ( + Comma vOption = auditUrlOption + { + AddAndUpdateTokenInfo(vResult, vResult.TargetOptions, vOption); + + pathOptionEncountered |= vOption.OptionKind==AuditTargetOptionKind.Path; + } + )? + ) + tRParen:RightParenthesis + { + UpdateTokenInfo(vResult,tRParen); + if (pathRequired && !pathOptionEncountered) + { + if(tFile != null) + { + ThrowParseErrorException("SQL46056", tFile, TSqlParserResource.SQL46056Message); + } + else + { + ThrowParseErrorException("SQL46126", tUrl, TSqlParserResource.SQL46126Message); + } + } + } + | + tApplicationLogSecurityLogExternalMonitor:Identifier + { + if (TryMatch(tApplicationLogSecurityLogExternalMonitor, CodeGenerationSupporter.ApplicationLog)) + { + vResult.TargetKind = AuditTargetKind.ApplicationLog; + } + else if (TryMatch(tApplicationLogSecurityLogExternalMonitor, CodeGenerationSupporter.SecurityLog)) + { + vResult.TargetKind = AuditTargetKind.SecurityLog; + } + else + { + Match(tApplicationLogSecurityLogExternalMonitor, CodeGenerationSupporter.ExternalMonitor); + vResult.TargetKind = AuditTargetKind.ExternalMonitor; + } + UpdateTokenInfo(vResult,tApplicationLogSecurityLogExternalMonitor); + } + ) + ; + +// Corresponds to audit_file_option_element in SQL yacc grammar +auditFileOption returns [AuditTargetOption vResult = null] + : + {NextTokenMatches(CodeGenerationSupporter.MaxSize)}? + vResult = maxSizeAuditFileOption + | + {NextTokenMatches(CodeGenerationSupporter.MaxRolloverFiles)}? + vResult = maxRolloverFilesAuditFileOption + | + {NextTokenMatches(CodeGenerationSupporter.ReserveDiskSpace)}? + vResult = reserveDiskSpaceAuditFileOption + | + {NextTokenMatches(CodeGenerationSupporter.MaxFiles)}? + vResult = maxFilesAuditFileOption + | + vResult = pathAuditFileOption + ; + +auditUrlOption returns [AuditTargetOption vResult = null] + : + {NextTokenMatches(CodeGenerationSupporter.RetentionDays)}? + vResult = retentionDaysAuditUrlOption + | + vResult = pathAuditFileOption + ; + +maxSizeAuditFileOption returns [MaxSizeAuditTargetOption vResult = FragmentFactory.CreateFragment()] +{ + Literal vSize; +} + : tOption:Identifier EqualsSign + { + Match(tOption, CodeGenerationSupporter.MaxSize); + vResult.OptionKind=AuditTargetOptionKind.MaxSize; + UpdateTokenInfo(vResult, tOption); + } + ( + vSize = integer tUnit:Identifier + { + vResult.Size = vSize; + + if (TryMatch(tUnit, CodeGenerationSupporter.GB)) + { + vResult.Unit = MemoryUnit.GB; + ThrowIfTooLargeAuditFileSize(vSize, 10); + } + else if (TryMatch(tUnit, CodeGenerationSupporter.TB)) + { + vResult.Unit = MemoryUnit.TB; + ThrowIfTooLargeAuditFileSize(vSize, 20); + } + else + { + Match(tUnit, CodeGenerationSupporter.MB); + vResult.Unit = MemoryUnit.MB; + ThrowIfTooLargeAuditFileSize(vSize, 0); + } + + UpdateTokenInfo(vResult, tUnit); + } + | + tUnlimited:Identifier + { + Match(tUnlimited, CodeGenerationSupporter.Unlimited); + vResult.IsUnlimited = true; + vResult.Size = null; + vResult.Unit = MemoryUnit.Unspecified; + } + ) + ; + +retentionDaysAuditUrlOption returns [RetentionDaysAuditTargetOption vResult = FragmentFactory.CreateFragment()] +{ + Literal vDays; +} + : tRetentionDays:Identifier EqualsSign + { + Match(tRetentionDays, CodeGenerationSupporter.RetentionDays); + vResult.OptionKind=AuditTargetOptionKind.RetentionDays; + UpdateTokenInfo(vResult, tRetentionDays); + } + ( + vDays = integer + { + vResult.Days = vDays; + } + ) + ; + +maxRolloverFilesAuditFileOption returns [MaxRolloverFilesAuditTargetOption vResult = FragmentFactory.CreateFragment()] +{ + Literal vValue; +} + : tOption:Identifier EqualsSign + { + Match(tOption, CodeGenerationSupporter.MaxRolloverFiles); + vResult.OptionKind=AuditTargetOptionKind.MaxRolloverFiles; + UpdateTokenInfo(vResult, tOption); + } + ( + vValue = integer + { + vResult.Value = vValue; + } + | + tUnlimited:Identifier + { + Match(tUnlimited, CodeGenerationSupporter.Unlimited); + vResult.IsUnlimited = true; + UpdateTokenInfo(vResult, tUnlimited); + } + ) + ; + +maxFilesAuditFileOption returns [LiteralAuditTargetOption vResult = FragmentFactory.CreateFragment()] +{ + Literal vValue; +} + : tOption:Identifier EqualsSign vValue = integer + { + Match(tOption, CodeGenerationSupporter.MaxFiles); + vResult.OptionKind=AuditTargetOptionKind.MaxFiles; + UpdateTokenInfo(vResult, tOption); + vResult.Value = vValue; + } + ; + +reserveDiskSpaceAuditFileOption returns [OnOffAuditTargetOption vResult = FragmentFactory.CreateFragment()] +{ + OptionState vValue; +} + : tOption:Identifier EqualsSign vValue = optionOnOff[vResult] + { + Match(tOption, CodeGenerationSupporter.ReserveDiskSpace); + vResult.OptionKind=AuditTargetOptionKind.ReserveDiskSpace; + UpdateTokenInfo(vResult, tOption); + vResult.Value = vValue; + } + ; + +pathAuditFileOption returns [LiteralAuditTargetOption vResult = FragmentFactory.CreateFragment()] +{ + Literal vValue; +} + : tOption:Identifier EqualsSign vValue = stringLiteral + { + if(TryMatch(tOption, CodeGenerationSupporter.FilePath)) + { + vResult.OptionKind=AuditTargetOptionKind.FilePath; + } + else + { + Match(tOption, CodeGenerationSupporter.Path); + vResult.OptionKind=AuditTargetOptionKind.Path; + } + UpdateTokenInfo(vResult, tOption); + vResult.Value = vValue; + } + ; + +auditCreateWithClause [ServerAuditStatement vParent] +{ + AuditOption vOption; +} + : With LeftParenthesis vOption = auditCreateOption + { + AddAndUpdateTokenInfo(vParent, vParent.Options, vOption); + } + (Comma vOption = auditCreateOption + { + AddAndUpdateTokenInfo(vParent, vParent.Options, vOption); + } + )* + tRParen:RightParenthesis + { + UpdateTokenInfo(vParent, tRParen); + } + ; + +auditWithClause [ServerAuditStatement vParent] +{ + AuditOption vOption; +} + : With LeftParenthesis vOption = auditOption + { + AddAndUpdateTokenInfo(vParent, vParent.Options, vOption); + } + (Comma vOption = auditOption + { + AddAndUpdateTokenInfo(vParent, vParent.Options, vOption); + } + )* + tRParen:RightParenthesis + { + UpdateTokenInfo(vParent, tRParen); + } + ; + +// Corresponds to audit_create_option_element in SQL yacc +auditCreateOption returns [AuditOption vResult] + : + tOption:Identifier EqualsSign + ( + vResult = queueDelayAuditOption[tOption] + | + vResult = onFailureAuditOption[tOption] + | + vResult = auditGuidAuditOption[tOption] + | + vResult = operatorAuditOption[tOption] + ) + ; + +// Corresponds to audit_option_element in SQL yacc +auditOption returns [AuditOption vResult] + : + tOption:Identifier EqualsSign + ( + vResult = queueDelayAuditOption[tOption] + | + vResult = onFailureAuditOption[tOption] + | + {TryMatch(tOption, CodeGenerationSupporter.OperatorAudit)}? + vResult = operatorAuditOption[tOption] + | + {TryMatch(tOption, CodeGenerationSupporter.State)}? + vResult = stateAuditOption[tOption] + ) + ; + +queueDelayAuditOption [IToken tOption] returns [QueueDelayAuditOption vResult = FragmentFactory.CreateFragment()] +{ + Literal vValue; +} + : vValue = integer + { + Match(tOption, CodeGenerationSupporter.QueueDelay); + vResult.OptionKind=AuditOptionKind.QueueDelay; + UpdateTokenInfo(vResult,tOption); + vResult.Delay = vValue; + } + ; + +onFailureAuditOption [IToken tOption] returns [OnFailureAuditOption vResult = FragmentFactory.CreateFragment()] + : + { + Match(tOption, CodeGenerationSupporter.OnFailure); + UpdateTokenInfo(vResult,tOption); + vResult.OptionKind=AuditOptionKind.OnFailure; + } + ( + tContinue:Continue + { + UpdateTokenInfo(vResult,tContinue); + vResult.OnFailureAction = AuditFailureActionType.Continue; + } + | + tShutdown:Shutdown + { + UpdateTokenInfo(vResult,tShutdown); + vResult.OnFailureAction = AuditFailureActionType.Shutdown; + } + | + tIdentifier:Identifier + { + Match(tIdentifier, CodeGenerationSupporter.FailOperation); + UpdateTokenInfo(vResult, tIdentifier); + vResult.OnFailureAction = AuditFailureActionType.FailOperation; + } + ) + ; + +auditGuidAuditOption [IToken tOption] returns [AuditGuidAuditOption vResult = FragmentFactory.CreateFragment()] +{ + Literal vValue; +} + : vValue = stringLiteral + { + Match(tOption, CodeGenerationSupporter.AuditGuid); + ThrowIfWrongGuidFormat(vValue); + vResult.OptionKind=AuditOptionKind.AuditGuid; + UpdateTokenInfo(vResult,tOption); + vResult.Guid = vValue; + } + ; + +operatorAuditOption [IToken tOption] returns [OperatorAuditOption vResult = FragmentFactory.CreateFragment()] +{ + OptionState vValue; +} + : vValue = optionOnOff[vResult] + { + Match(tOption, CodeGenerationSupporter.OperatorAudit); + vResult.OptionKind=AuditOptionKind.OperatorAudit; + UpdateTokenInfo(vResult,tOption); + vResult.Value = vValue; + } + ; + +stateAuditOption [IToken tOption] returns [StateAuditOption vResult = FragmentFactory.CreateFragment()] +{ + OptionState vValue; +} + : vValue = optionOnOff[vResult] + { + Match(tOption, CodeGenerationSupporter.State); + vResult.OptionKind=AuditOptionKind.State; + UpdateTokenInfo(vResult,tOption); + vResult.Value = vValue; + } + ; + +createServerAuditSpecificationStatement returns [CreateServerAuditSpecificationStatement vResult = FragmentFactory.CreateFragment()] +{ + Identifier vAuditSpecName; + AuditSpecificationPart vPart; +} + : tSpecification:Identifier vAuditSpecName = identifier + { + Match(tSpecification, CodeGenerationSupporter.Specification); + vResult.SpecificationName = vAuditSpecName; + ThrowPartialAstIfPhaseOne(vResult); + } + auditSpecificationForClause[vResult] + ( // Conflicts with Add SIGNATURE (but it actually shouldn't, k=2 should be enough) + (Add LeftParenthesis) => + vPart = createAuditSpecificationDetail + { + AddAndUpdateTokenInfo(vResult, vResult.Parts, vPart); + } + (Comma vPart = createAuditSpecificationDetail + { + AddAndUpdateTokenInfo(vResult, vResult.Parts, vPart); + } + )* + )? + auditSpecificationStateOpt[vResult] + ; + +alterServerStatements returns [TSqlStatement vResult] + : + tServer:Identifier + { + Match(tServer, CodeGenerationSupporter.Server); + } + ( + {NextTokenMatches(CodeGenerationSupporter.Audit)}? + vResult = alterServerAuditStatements + | + {NextTokenMatches(CodeGenerationSupporter.Configuration)}? + vResult = alterServerConfigurationStatement + | + {NextTokenMatches(CodeGenerationSupporter.Role)}? + vResult = alterServerRoleStatement + ) + ; + +alterServerAuditStatements returns [TSqlStatement vResult] + : tAudit:Identifier + { + Match(tAudit, CodeGenerationSupporter.Audit); + } + ( + {NextTokenMatches(CodeGenerationSupporter.Specification)}? + vResult = alterServerAuditSpecificationStatement + | + vResult = alterServerAuditStatement + ) + ; + +alterServerAuditSpecificationStatement returns [AlterServerAuditSpecificationStatement vResult = FragmentFactory.CreateFragment()] +{ + Identifier vAuditSpecName; + AuditSpecificationPart vPart; +} + : tSpecification:Identifier vAuditSpecName = identifier + { + Match(tSpecification, CodeGenerationSupporter.Specification); + vResult.SpecificationName = vAuditSpecName; + ThrowPartialAstIfPhaseOne(vResult); + } + (auditSpecificationForClause[vResult])? + ( // Conflicts with Add SIGNATURE and Drop statements + ((Add|Drop) LeftParenthesis) => + vPart = auditSpecificationDetail + { + AddAndUpdateTokenInfo(vResult, vResult.Parts, vPart); + } + (Comma vPart = auditSpecificationDetail + { + AddAndUpdateTokenInfo(vResult, vResult.Parts, vPart); + } + )* + )? + auditSpecificationStateOpt[vResult] + ; + +alterServerAuditStatement returns [AlterServerAuditStatement vResult = FragmentFactory.CreateFragment()] +{ + Identifier vAuditName; + Identifier vNewName; + AuditTarget vTarget = null; + BooleanExpression vFilterPredicate = null; +} + : vAuditName = identifier + { + vResult.AuditName = vAuditName; + ThrowPartialAstIfPhaseOne(vResult); + } + ( + {NextTokenMatches(CodeGenerationSupporter.Modify)}? + tModify:Identifier tName:Identifier EqualsSign vNewName = identifier + { + Match(tModify, CodeGenerationSupporter.Modify); + Match(tName, CodeGenerationSupporter.Name); + vResult.NewName = vNewName; + } + | + ( + vTarget = auditTargetClause[false] + { + vResult.AuditTarget = vTarget; + } + )? + ( // Greedy due to conflict with withCommonTableExpressionsAndXmlNamespaces + options {greedy = true; } : + auditWithClause[vResult] + )? + ( + Where vFilterPredicate=eventBooleanExpression + { + vResult.PredicateExpression = vFilterPredicate; + } + )? + { + if(vTarget == null && (vResult.Options == null || vResult.Options.Count == 0) && vFilterPredicate == null) + { + ThrowIncorrectSyntaxErrorException(vAuditName); + } + } + | + tRemove:Identifier tWhere:Where + { + Match(tRemove, CodeGenerationSupporter.Remove); + UpdateTokenInfo(vResult, tWhere); + vResult.RemoveWhere=true; + } + ) + ; + +alterServerConfigurationStatement returns [TSqlStatement vResult] + : + tConfiguration:Identifier Set + { + Match(tConfiguration, CodeGenerationSupporter.Configuration); + } + ( + {NextTokenMatches(CodeGenerationSupporter.Process)}? + vResult = alterServerConfigurationSetProcessAffinityStatement + | + {NextTokenMatches(CodeGenerationSupporter.Buffer)}? + vResult = alterServerConfigurationSetBufferPoolExtensionStatement + | + {NextTokenMatches(CodeGenerationSupporter.Diagnostics)}? + vResult = alterServerConfigurationSetDiagnosticsLogStatement + | + {NextTokenMatches(CodeGenerationSupporter.Failover)}? + vResult = alterServerConfigurationSetFailoverClusterPropertyStatement + | + {NextTokenMatches(CodeGenerationSupporter.Hadr)}? + vResult = alterServerConfigurationSetHadrClusterStatement + | + {NextTokenMatches(CodeGenerationSupporter.SoftNuma)}? + vResult = alterServerConfigurationSetSoftNumaStatement + | + {NextTokenMatches(CodeGenerationSupporter.External)}? + vResult = alterServerConfigurationSetExternalAuthenticationStatement + ) + ; + + +alterServerConfigurationSetExternalAuthenticationStatement returns [AlterServerConfigurationSetExternalAuthenticationStatement vResult = FragmentFactory.CreateFragment()] +{ + AlterServerConfigurationExternalAuthenticationOption vOption; +} + : tExternal:External tAuthentication:Identifier + { + Match(tExternal, CodeGenerationSupporter.External); + Match(tAuthentication, CodeGenerationSupporter.Authentication); + } + vOption=alterServerConfigurationExternalAuthenticationContainerOption + { + AddAndUpdateTokenInfo(vResult, vResult.Options, vOption); + } + ; + +alterServerConfigurationExternalAuthenticationContainerOption returns [AlterServerConfigurationExternalAuthenticationContainerOption vResult = FragmentFactory.CreateFragment()] +{ + OnOffOptionValue vOptionValue; + AlterServerConfigurationExternalAuthenticationOption vAlterServerConfigurationExternalAuthenticationOption; +} + : vOptionValue=onOffOptionValue + { + vResult.OptionValue = vOptionValue; + vResult.OptionKind = AlterServerConfigurationExternalAuthenticationOptionKind.OnOff; + } + ( + { + // Additional options are only allowed when external authentication is set to ON + if (vOptionValue.OptionState != OptionState.On) + ThrowIncorrectSyntaxErrorException(vOptionValue); + } + LeftParenthesis + (vAlterServerConfigurationExternalAuthenticationOption=alterServerConfigurationExternalAuthenticationOption + { + AddAndUpdateTokenInfo(vResult, vResult.Suboptions, vAlterServerConfigurationExternalAuthenticationOption); + }) + RightParenthesis + | + { + // Empty rule: setting external authorization to OFF is the only allowed + if (vOptionValue.OptionState != OptionState.Off) + ThrowIncorrectSyntaxErrorException(vOptionValue); + } + ) + ; + +alterServerConfigurationExternalAuthenticationOption returns [AlterServerConfigurationExternalAuthenticationOption vResult] + : {NextTokenMatches(CodeGenerationSupporter.UseIdentity)}? + vResult = alterServerConfigurationExternalAuthenticationUseIdentityOption + | + {NextTokenMatches(CodeGenerationSupporter.CredentialName)}? + vResult = alterServerConfigurationExternalAuthenticationCredentialNameOption + ; + +alterServerConfigurationExternalAuthenticationCredentialNameOption returns [AlterServerConfigurationExternalAuthenticationOption vResult = FragmentFactory.CreateFragment()] +{ + LiteralOptionValue vCredentialName; +} + : tCredentialName:Identifier EqualsSign vCredentialName=stringLiteralOptionValue + { + Match(tCredentialName, CodeGenerationSupporter.CredentialName); + vResult.OptionKind = AlterServerConfigurationExternalAuthenticationOptionHelper.Instance.ParseOption(tCredentialName); + vResult.OptionValue = vCredentialName; + } + ; + +alterServerConfigurationExternalAuthenticationUseIdentityOption returns [AlterServerConfigurationExternalAuthenticationOption vResult = FragmentFactory.CreateFragment()] +{ +} + : tUseIdentity:Identifier + { + Match(tUseIdentity, CodeGenerationSupporter.UseIdentity); + vResult.OptionKind = AlterServerConfigurationExternalAuthenticationOptionHelper.Instance.ParseOption(tUseIdentity); + } + ; + +alterServerConfigurationSetSoftNumaStatement returns [AlterServerConfigurationSetSoftNumaStatement vResult = FragmentFactory.CreateFragment()] +{ + AlterServerConfigurationSoftNumaOption vOption; +} + : tSoftNuma:Identifier + { + Match(tSoftNuma, CodeGenerationSupporter.SoftNuma); + } + vOption=alterServerConfigurationSoftNumaOption + { + AddAndUpdateTokenInfo(vResult, vResult.Options, vOption); + } + ; + +alterServerConfigurationSoftNumaOption returns [AlterServerConfigurationSoftNumaOption vResult = FragmentFactory.CreateFragment()] +{ + OnOffOptionValue vOptionValue; +} + : vOptionValue=onOffOptionValue + { + vResult.OptionKind = AlterServerConfigurationSoftNumaOptionKind.OnOff; + vResult.OptionValue = vOptionValue; + } + + ; + +alterServerConfigurationSetBufferPoolExtensionStatement returns [AlterServerConfigurationSetBufferPoolExtensionStatement vResult = FragmentFactory.CreateFragment()] +{ + AlterServerConfigurationBufferPoolExtensionOption vOption; +} + : tBuffer:Identifier tPool:Identifier tExtension:Identifier + { + Match(tBuffer, CodeGenerationSupporter.Buffer); + Match(tPool, CodeGenerationSupporter.Pool); + Match(tExtension, CodeGenerationSupporter.Extension); + } + vOption=alterServerConfigurationBufferPoolExtensionContainerOption + { + AddAndUpdateTokenInfo(vResult, vResult.Options, vOption); + } + ; + +alterServerConfigurationBufferPoolExtensionContainerOption returns [AlterServerConfigurationBufferPoolExtensionContainerOption vResult = FragmentFactory.CreateFragment()] +{ + OnOffOptionValue vOptionValue; + AlterServerConfigurationBufferPoolExtensionOption vFileNameSuboption; + AlterServerConfigurationBufferPoolExtensionOption vSizeSuboption; +} + : vOptionValue=onOffOptionValue + { + vResult.OptionValue = vOptionValue; + vResult.OptionKind = AlterServerConfigurationBufferPoolExtensionOptionKind.OnOff; + } + ( + { + // Additional options are only allowed when buffer pool extension is set to ON + if (vOptionValue.OptionState != OptionState.On) + ThrowIncorrectSyntaxErrorException(vOptionValue); + } + tLParen:LeftParenthesis vFileNameSuboption=alterServerConfigurationBufferPoolExtensionFileNameOption + { + UpdateTokenInfo(vResult, tLParen); + AddAndUpdateTokenInfo(vResult, vResult.Suboptions, vFileNameSuboption); + } + tComma:Comma vSizeSuboption=alterServerConfigurationBufferPoolExtensionSizeOption tRParen:RightParenthesis + { + AddAndUpdateTokenInfo(vResult, vResult.Suboptions, vSizeSuboption); + UpdateTokenInfo(vResult, tRParen); + } + | + { + // Empty rule: setting buffer pool extension to OFF is the only allowed + if (vOptionValue.OptionState != OptionState.Off) + ThrowIncorrectSyntaxErrorException(vOptionValue); + } + ) + ; + +alterServerConfigurationBufferPoolExtensionFileNameOption returns [AlterServerConfigurationBufferPoolExtensionOption vResult = FragmentFactory.CreateFragment()] +{ + LiteralOptionValue vFileName; +} + : tFileName:Identifier EqualsSign vFileName=stringLiteralOptionValue + { + Match(tFileName, CodeGenerationSupporter.FileName); + vResult.OptionKind = AlterServerConfigurationBufferPoolExtensionOptionHelper.Instance.ParseOption(tFileName); + vResult.OptionValue = vFileName; + } + ; + +alterServerConfigurationBufferPoolExtensionSizeOption returns [AlterServerConfigurationBufferPoolExtensionSizeOption vResult = FragmentFactory.CreateFragment()] +{ + LiteralOptionValue vSize; + MemoryUnit vMemUnit; +} + : tSize:Identifier EqualsSign vSize=integerLiteralOptionValue vMemUnit=memUnit[vResult] + { + Match(tSize, CodeGenerationSupporter.Size); + + if (vMemUnit != MemoryUnit.KB && vMemUnit != MemoryUnit.MB && vMemUnit != MemoryUnit.GB) + ThrowIncorrectSyntaxErrorException(vSize); + + vResult.OptionKind = AlterServerConfigurationBufferPoolExtensionOptionHelper.Instance.ParseOption(tSize); + vResult.OptionValue = vSize; + vResult.SizeUnit = vMemUnit; + } + ; + +alterServerConfigurationSetDiagnosticsLogStatement returns [AlterServerConfigurationSetDiagnosticsLogStatement vResult = FragmentFactory.CreateFragment()] +{ + AlterServerConfigurationDiagnosticsLogOption vOption; +} + : tDiagnostics:Identifier tLog:Identifier + { + Match(tDiagnostics, CodeGenerationSupporter.Diagnostics); + Match(tLog, CodeGenerationSupporter.Log); + } + vOption=alterServerConfigurationDiagnosticsLogOption + { + AddAndUpdateTokenInfo(vResult, vResult.Options, vOption); + } + ; + +alterServerConfigurationDiagnosticsLogOption returns [AlterServerConfigurationDiagnosticsLogOption vResult = FragmentFactory.CreateFragment()] +{ + OptionValue vOptionValue; +} + : vOptionValue=onOffOptionValue + { + vResult.OptionKind = AlterServerConfigurationDiagnosticsLogOptionKind.OnOff; + vResult.OptionValue = vOptionValue; + } + | + {NextTokenMatches(CodeGenerationSupporter.MaxUnderscoreSize)}? + vResult=alterServerConfigurationDiagnosticsLogMaxSizeOption + | + tLogOption:Identifier EqualsSign + { + vResult.OptionKind = AlterServerConfigurationDiagnosticsLogOptionHelper.Instance.ParseOption(tLogOption); + } + ( + {vResult.OptionKind == AlterServerConfigurationDiagnosticsLogOptionKind.Path}? + vOptionValue=stringOrDefaultLiteralOptionValue + { + vResult.OptionValue = vOptionValue; + } + | + {vResult.OptionKind == AlterServerConfigurationDiagnosticsLogOptionKind.MaxFiles}? + vOptionValue=integerOrDefaultLiteralOptionValue + { + vResult.OptionValue = vOptionValue; + } + ) + ; + +alterServerConfigurationDiagnosticsLogMaxSizeOption returns [AlterServerConfigurationDiagnosticsLogMaxSizeOption vResult = FragmentFactory.CreateFragment()] +{ + OptionValue vOptionValue; +} + : tMaxSize:Identifier EqualsSign + { + vResult.OptionKind = AlterServerConfigurationDiagnosticsLogOptionHelper.Instance.ParseOption(tMaxSize); + if (vResult.OptionKind != AlterServerConfigurationDiagnosticsLogOptionKind.MaxSize) + ThrowIncorrectSyntaxErrorException(tMaxSize); + } + ( + vOptionValue=integerLiteralOptionValue tMB:Identifier + { + Match(tMB, CodeGenerationSupporter.MB); + vResult.OptionValue = vOptionValue; + vResult.SizeUnit = MemoryUnit.MB; + UpdateTokenInfo(vResult, tMB); + } + | + vOptionValue=defaultLiteralOptionValue + { + vResult.OptionValue = vOptionValue; + } + ) + ; + +alterServerConfigurationSetFailoverClusterPropertyStatement returns [AlterServerConfigurationSetFailoverClusterPropertyStatement vResult = FragmentFactory.CreateFragment()] +{ + AlterServerConfigurationFailoverClusterPropertyOption vOption; +} + : tFailover:Identifier tCluster:Identifier tProperty:Identifier + { + Match(tFailover, CodeGenerationSupporter.Failover); + Match(tCluster, CodeGenerationSupporter.Cluster); + Match(tProperty, CodeGenerationSupporter.Property); + } + vOption=alterServerConfigurationFailoverClusterPropertyOption + { + AddAndUpdateTokenInfo(vResult, vResult.Options, vOption); + } + ; + +alterServerConfigurationFailoverClusterPropertyOption returns [AlterServerConfigurationFailoverClusterPropertyOption vResult = FragmentFactory.CreateFragment()] +{ + OptionValue vOptionValue; +} + : tProperty:Identifier EqualsSign + { + vResult.OptionKind = AlterServerConfigurationFailoverClusterPropertyOptionHelper.Instance.ParseOption(tProperty); + } + ( + {vResult.OptionKind == AlterServerConfigurationFailoverClusterPropertyOptionKind.SqlDumperDumpFlags}? + vOptionValue=binaryOrDefaultLiteralOptionValue + { + vResult.OptionValue = vOptionValue; + } + | + {vResult.OptionKind == AlterServerConfigurationFailoverClusterPropertyOptionKind.SqlDumperDumpPath}? + vOptionValue=stringOrDefaultLiteralOptionValue + { + vResult.OptionValue = vOptionValue; + } + | + vOptionValue=integerOrDefaultLiteralOptionValue + { + vResult.OptionValue = vOptionValue; + } + ) + ; + +alterServerConfigurationSetHadrClusterStatement returns [AlterServerConfigurationSetHadrClusterStatement vResult = FragmentFactory.CreateFragment()] +{ + AlterServerConfigurationHadrClusterOption vOption; +} + : tHadr:Identifier tCluster:Identifier + { + Match(tHadr, CodeGenerationSupporter.Hadr); + Match(tCluster, CodeGenerationSupporter.Cluster); + } + vOption=alterServerConfigurationHadrClusterOption + { + AddAndUpdateTokenInfo(vResult, vResult.Options, vOption); + } + ; + +alterServerConfigurationHadrClusterOption returns [AlterServerConfigurationHadrClusterOption vResult = FragmentFactory.CreateFragment()] +{ + OptionValue vOptionValue; +} + : tOptionKind:Identifier EqualsSign + { + vResult.OptionKind = AlterServerConfigurationHadrClusterOptionHelper.Instance.ParseOption(tOptionKind); + } + ( + vOptionValue=stringLiteralOptionValue + { + vResult.OptionValue = vOptionValue; + } + | + tLocal:Identifier + { + Match(tLocal, CodeGenerationSupporter.Local); + vResult.IsLocal = true; + UpdateTokenInfo(vResult, tLocal); + } + ) + ; + +alterServerConfigurationSetProcessAffinityStatement returns [AlterServerConfigurationStatement vResult = FragmentFactory.CreateFragment()] + : tProcess:Identifier tAffinity:Identifier + { + Match(tProcess, CodeGenerationSupporter.Process); + Match(tAffinity, CodeGenerationSupporter.Affinity); + } + tCpuOrNumanode:Identifier EqualsSign + ( + affinityRangeList[vResult] + { + if (TryMatch(tCpuOrNumanode, CodeGenerationSupporter.Cpu)) + { + vResult.ProcessAffinity = ProcessAffinityType.Cpu; + } + else + { + Match(tCpuOrNumanode, CodeGenerationSupporter.NumaNode); + vResult.ProcessAffinity = ProcessAffinityType.NumaNode; + } + } + | + tAuto:Identifier + { + // AUTO implies CPU affinity + Match(tCpuOrNumanode, CodeGenerationSupporter.Cpu); + Match(tAuto, CodeGenerationSupporter.Auto); + + vResult.ProcessAffinity = ProcessAffinityType.CpuAuto; + + UpdateTokenInfo(vResult, tAuto); + } + ) + ; + +affinityRangeList [AlterServerConfigurationStatement vParent] +{ + ProcessAffinityRange vAffinityRange; +} + : vAffinityRange = affinityRange + { + AddAndUpdateTokenInfo(vParent, vParent.ProcessAffinityRanges, vAffinityRange); + } + (Comma vAffinityRange = affinityRange + { + AddAndUpdateTokenInfo(vParent, vParent.ProcessAffinityRanges, vAffinityRange); + } + )* + ; + +affinityRange returns [ProcessAffinityRange vResult = FragmentFactory.CreateFragment()] +{ + Literal vBoundary; +} + : vBoundary = integer + { + vResult.From = vBoundary; + } + (To vBoundary = integer + { + vResult.To = vBoundary; + } + )? + ; + +////////////////////////////////////////////////////////////////////// +// Alter Database +////////////////////////////////////////////////////////////////////// +alterDatabaseStatements returns [TSqlStatement vResult = null] + : tAlter:Alter Database + ( + // Conflicts with alterDatabase alternative below + {NextTokenMatches(CodeGenerationSupporter.Audit) && NextTokenMatches(CodeGenerationSupporter.Specification, 2)}? + vResult = alterDatabaseAuditSpecification[tAlter] + | + {NextTokenMatches(CodeGenerationSupporter.Scoped) && NextTokenMatches(CodeGenerationSupporter.Credential, 2)}? + vResult = alterDatabaseScopedCredentialStatement[tAlter] + | + {NextTokenMatches(CodeGenerationSupporter.Scoped) && NextTokenMatches(CodeGenerationSupporter.Configuration, 2)}? + vResult = alterDatabaseScopedConfigurationStatement[tAlter] + | + vResult = alterDatabase[tAlter] + | + vResult = alterDatabaseEncryptionKey[tAlter] + ) + ; + +alterDatabaseScopedConfigurationStatement[IToken tAlter] returns [AlterDatabaseScopedConfigurationStatement vResult] +{ + bool vSecondary = false; +} + : + tScoped:Identifier tConfiguration:Identifier + { + Match(tScoped, CodeGenerationSupporter.Scoped); + Match(tConfiguration, CodeGenerationSupporter.Configuration); + } + (For tSecondary:Identifier + { + vSecondary = true; + } + )? + ( + vResult = alterDatabaseScopedConfigSet[vSecondary] + | + vResult = alterDatabaseScopedConfigClear + ) + { + if (vSecondary) + { + vResult.Secondary = true; + UpdateTokenInfo(vResult,tSecondary); + } + UpdateTokenInfo(vResult,tAlter); + } + ; + +alterDatabaseScopedConfigClear returns [AlterDatabaseScopedConfigurationClearStatement vResult = FragmentFactory.CreateFragment()] +{ + DatabaseConfigurationClearOption vOption; +} + : + tClear:Identifier + { + Match(tClear, CodeGenerationSupporter.Clear); + UpdateTokenInfo(vResult, tClear); + } + vOption = databaseConfigurationClearOption + { + vResult.Option = vOption; + } + ; + +databaseConfigurationClearOption returns [DatabaseConfigurationClearOption vResult = FragmentFactory.CreateFragment()] +{ + BinaryLiteral vLiteral; +} + : + tOption:Identifier + { + vResult.OptionKind = DatabaseConfigClearOptionKindHelper.Instance.ParseOption(tOption, SqlVersionFlags.TSql170); + UpdateTokenInfo(vResult, tOption); + } + ( + vLiteral = binary + { + vResult.PlanHandle = vLiteral; + } + )? + ; + +alterDatabaseScopedConfigSet[bool forSecondary] returns [AlterDatabaseScopedConfigurationSetStatement vResult = FragmentFactory.CreateFragment()] +{ + DatabaseConfigurationSetOption vOption; +} + : + Set + ( + {NextTokenMatches(CodeGenerationSupporter.MaxDop)}? + vOption = alterDatabaseScopedMaxDopOption[forSecondary] + | + {NextTokenMatches(CodeGenerationSupporter.QueryOptimizerHotFixes) || NextTokenMatches(CodeGenerationSupporter.ParameterSniffing) || + NextTokenMatches(CodeGenerationSupporter.LegacyCardinalityEstimation)}? + vOption = alterDatabaseScopedOnOffPrimaryOption[forSecondary] + | + vOption = alterDatabaseScopedGenericOption[forSecondary] + ) + { + vResult.Option = vOption; + } + ; + +alterDatabaseScopedMaxDopOption[bool forSecondary] returns [MaxDopConfigurationOption vResult = FragmentFactory.CreateFragment()] +{ + Literal vValue; +} + : + tMaxDop:Identifier EqualsSign + { + vResult.OptionKind = DatabaseConfigSetOptionKindHelper.Instance.ParseOption(tMaxDop, SqlVersionFlags.TSql170); + UpdateTokenInfo(vResult, tMaxDop); + } + ( + vValue = integer + { + vResult.Value = vValue; + } + | + {NextTokenMatches(CodeGenerationSupporter.Primary)}? + tPrimary:Primary + { + if (!forSecondary) + { + ThrowParseErrorException("SQL46115", vResult, TSqlParserResource.SQL46115Message); + } + vResult.Primary = true; + UpdateTokenInfo(vResult, tPrimary); + } + ) + ; + +alterDatabaseScopedOnOffPrimaryOption[bool forSecondary] returns [OnOffPrimaryConfigurationOption vResult = FragmentFactory.CreateFragment()] +{ + DatabaseConfigurationOptionState vOptionState; +} + : + tOption:Identifier + { + vResult.OptionKind = DatabaseConfigSetOptionKindHelper.Instance.ParseOption(tOption, SqlVersionFlags.TSql170); + UpdateTokenInfo(vResult, tOption); + } + EqualsSign + ( + vOptionState = databaseConfigurationOptionOnOffPrimary[vResult] + { + if(!forSecondary && vOptionState == DatabaseConfigurationOptionState.Primary) + { + ThrowParseErrorException("SQL46115", vResult, TSqlParserResource.SQL46115Message); + } + } + ) + { + vResult.OptionState = vOptionState; + } + ; + +alterDatabaseScopedGenericOption[bool forSecondary] returns [GenericConfigurationOption vResult = FragmentFactory.CreateFragment()] +{ + Identifier vOptionName; + Identifier vValueOnOff; + IdentifierOrScalarExpression vValue; + IToken token = LT(1); +} + : + vOptionName = identifier + { + vResult.GenericOptionKind = vOptionName; + } + EqualsSign + ( + {NextTokenMatches(CodeGenerationSupporter.Primary)}? + tPrimary:Primary + { + vValue = CreateIdentifierOrScalarExpressionFromIdentifier(CreateIdentifierFromToken(tPrimary)); + UpdateTokenInfo(vValue, token); + if (!forSecondary) + { + ThrowParseErrorException("SQL46115", vValue, TSqlParserResource.SQL46115Message); + } + vResult.GenericOptionState = vValue; + } + | + vValue = stringOrSignedIntegerOrIdentifier + { + vResult.GenericOptionState = vValue; + } + | + vValueOnOff = onOff + { + vResult.GenericOptionState = CreateIdentifierOrScalarExpressionFromIdentifier(vValueOnOff);; + } + ) + ; + +alterDatabase [IToken tAlter] returns [AlterDatabaseStatement vResult = null] +{ + Identifier vIdentifier = null; + bool vUseCurrent = false; +} + : ( + vIdentifier=identifier + | + vIdentifier=sqlCommandIdentifier + | + tCurrent:Current + { + vUseCurrent=true; + } + ) + ( vResult = alterDbAdd + | {NextTokenMatches(CodeGenerationSupporter.Remove)}? + vResult = alterDbRemove + | {NextTokenMatches(CodeGenerationSupporter.Modify)}? + vResult = alterDbModify + | vResult = alterDbSet + | vResult = alterDbCollate + | vResult = alterDbRebuild // Undocumented - for PSS only + | vResult = alterDbPerformCutover + ) + { + if(vUseCurrent) + { + vResult.UseCurrent = true; + UpdateTokenInfo(vResult,tCurrent); + } + else + { + vResult.DatabaseName = vIdentifier; + } + UpdateTokenInfo(vResult,tAlter); + ThrowPartialAstIfPhaseOne(vResult); + } + ; + exception + catch[PhaseOnePartialAstException exception] + { + UpdateTokenInfo(exception.Statement,tAlter); + (exception.Statement as AlterDatabaseStatement).DatabaseName = vIdentifier; + throw; + } + +alterDbCollate returns [AlterDatabaseCollateStatement vResult = FragmentFactory.CreateFragment()] + : collation[vResult] + ; + +alterDbRebuild returns [AlterDatabaseRebuildLogStatement vResult = FragmentFactory.CreateFragment()] +{ + FileDeclaration vFileDeclaration; +} + : tRebuild:Identifier tLog:Identifier + { + Match(tRebuild, CodeGenerationSupporter.Rebuild); + Match(tLog, CodeGenerationSupporter.Log); + UpdateTokenInfo(vResult,tLog); + ThrowPartialAstIfPhaseOne(vResult); + } + (On vFileDeclaration = fileDecl[false] + { + vResult.FileDeclaration = vFileDeclaration; + } + )? + ; + +alterDbAdd returns [AlterDatabaseStatement vResult = null] + : Add + ( + vResult = alterDbAddFile + | + vResult = alterDbAddFilegroup + ) + ; + +// Add File / Add LOG File +alterDbAddFile returns [AlterDatabaseAddFileStatement vResult = FragmentFactory.CreateFragment()] +{ + Identifier vIdentifier; +} + : (tLog:Identifier + { + Match(tLog,CodeGenerationSupporter.Log); + vResult.IsLog = true; + } + )? + File + { + ThrowPartialAstIfPhaseOne(vResult); + } + fileDeclBodyList[vResult, vResult.FileDeclarations] + (vIdentifier = toFilegroup + { + vResult.FileGroup = vIdentifier; + } + )? + ; + +// Add FILEGROUP +alterDbAddFilegroup returns [AlterDatabaseAddFileGroupStatement vResult = FragmentFactory.CreateFragment()] +{ + Identifier vIdentifier; +} + : tFilegroup:Identifier vIdentifier=identifier + { + Match(tFilegroup, CodeGenerationSupporter.Filegroup); + vResult.FileGroup = vIdentifier; + } + (Contains tFileStreamOrMemoryOptimizedData:Identifier + { + if (TryMatch(tFileStreamOrMemoryOptimizedData, CodeGenerationSupporter.FileStream)) + { + vResult.ContainsFileStream = true;; + } + else + { + Match(tFileStreamOrMemoryOptimizedData, CodeGenerationSupporter.MemoryOptimizedData); + vResult.ContainsMemoryOptimizedData = true; + } + UpdateTokenInfo(vResult, tFileStreamOrMemoryOptimizedData); + } + )? + ; + +alterDbRemove returns [AlterDatabaseStatement vResult = null] +{ + Identifier vIdentifier; +} + : tRemove:Identifier + { + Match(tRemove,CodeGenerationSupporter.Remove); + } + (File vIdentifier = identifier + { + AlterDatabaseRemoveFileStatement removeFile = FragmentFactory.CreateFragment(); + removeFile.File = vIdentifier; + vResult = removeFile; + } + | + tFileGroup:Identifier vIdentifier = identifier + { + // REMOVE FILEGROUP + Match(tFileGroup,CodeGenerationSupporter.Filegroup); + AlterDatabaseRemoveFileGroupStatement vRemoveFilegroup = FragmentFactory.CreateFragment(); + vRemoveFilegroup.FileGroup = vIdentifier; + vResult = vRemoveFilegroup; + } + ) + ; + +alterDbModify returns [AlterDatabaseStatement vResult = null] +{ + Identifier vIdentifier; +} + : tModify:Identifier + { + Match(tModify,CodeGenerationSupporter.Modify); + } + ( + {NextTokenMatches(CodeGenerationSupporter.Name)}? + (tName:Identifier EqualsSign vIdentifier = identifier + { + // MODIFY NAME = + Match(tName,CodeGenerationSupporter.Name); + AlterDatabaseModifyNameStatement modifyDbName = FragmentFactory.CreateFragment(); + modifyDbName.NewDatabaseName = vIdentifier; + vResult = modifyDbName; + } + ) + | + (tFileGroup2:Identifier + { + Match(tFileGroup2,CodeGenerationSupporter.Filegroup); + } + vResult = alterDbModifyFilegroup + ) + | vResult = alterDbModifyFile + | vResult = alterDbModifyAzureOptions + ) + ; + +alterDbModifyAzureOptions returns [AlterDatabaseSetStatement vResult = FragmentFactory.CreateFragment()] + : + azureOptions[vResult, vResult.Options] + ( + With tManualCutover:Identifier + { + Match(tManualCutover, CodeGenerationSupporter.ManualCutover); + vResult.WithManualCutover = true; + UpdateTokenInfo(vResult, tManualCutover); + } + )? + ; + +// MODIFY File syntax +alterDbModifyFile returns [AlterDatabaseModifyFileStatement vResult = FragmentFactory.CreateFragment()] +{ + FileDeclaration vFileDecl; +} + : File + { + ThrowPartialAstIfPhaseOne(vResult); + } + vFileDecl = fileDecl[true] + { + vResult.FileDeclaration = vFileDecl; + } + ; + +alterDbModifyFilegroup returns [AlterDatabaseModifyFileGroupStatement vResult = FragmentFactory.CreateFragment()] +{ + Identifier vIdentifier, vIdentifier2; + AlterDatabaseTermination vTermination; +} + : vIdentifier = identifier + { + vResult.FileGroup = vIdentifier; + } + ( + (tName2:Identifier EqualsSign vIdentifier2 = identifier + { + // MODIFY FILEGROUP NAME = + Match(tName2,CodeGenerationSupporter.Name); + vResult.NewFileGroupName = vIdentifier2; + ThrowPartialAstIfPhaseOne(vResult); + } + ) + | tDefault:Default + { + // MODIFY FILEGROUP Default + vResult.MakeDefault = true; + UpdateTokenInfo(vResult,tDefault); + } + | + (tUpdatabilityOption:Identifier + { + // MODIFY FILEGROUP