From 96924d1a58a7a570db4595128bc32b844c1777c9 Mon Sep 17 00:00:00 2001 From: "Sai Avishkar Sreerama (from Dev Box)" Date: Thu, 5 Mar 2026 13:28:44 -0600 Subject: [PATCH 1/5] JSON_OBJECTAGG support for OVER clause ## Description JSON_OBJECTAGG supports windowed aggregate usage via the OVER (PARTITION BY ...) clause in SQL Server, but ScriptDOM was missing support for parsing and generating it. This PR adds OVER clause support for JSON_OBJECTAGG in the TSql170 parser and script generator. SQL syntax now supported: `sql SELECT JSON_OBJECTAGG(CustomerName:OrderDate) OVER (PARTITION BY OrderDate) FROM Customers; SELECT JSON_OBJECTAGG(CustomerName:OrderDate ABSENT ON NULL) OVER (PARTITION BY OrderDate) FROM Customers; SELECT JSON_OBJECTAGG(CustomerName:OrderDate NULL ON NULL) OVER (PARTITION BY OrderDate) FROM Customers; SELECT JSON_OBJECTAGG(CustomerName:OrderDate NULL ON NULL RETURNING JSON) OVER (PARTITION BY OrderDate) FROM Customers; ` ## Changes - Updated JSON_OBJECTAGG production in TSql170.g to optionally parse an OVER clause - Enhanced SqlScriptGeneratorVisitor.FunctionCall.cs to generate the OVER clause in the output - Added new baseline and test scripts (JsonObjectAggOverClause170.sql in both Baselines170 and TestScripts) ## Additional Information No AST changes were needed -- FunctionCall.OverClause already exists in Ast.xml. The grammar uses overClauseNoOrderBy (not overClause) since, like regular aggregates (e.g., SUM() OVER (...)), the ORDER BY within the OVER clause is not applicable for JSON_OBJECTAGG as a windowed aggregate. All older parsers (80-160) produce 4 errors each (one per test statement) since the JSON_OBJECTAGG key:value syntax is not recognized pre-TSql170. --- SqlScriptDom/Parser/TSql/TSql170.g | 7 +++++++ .../SqlScriptGeneratorVisitor.FunctionCall.cs | 2 ++ .../Baselines170/JsonObjectAggOverClause170.sql | 11 +++++++++++ Test/SqlDom/Only170SyntaxTests.cs | 1 + .../SqlDom/TestScripts/JsonObjectAggOverClause170.sql | 11 +++++++++++ 5 files changed, 32 insertions(+) create mode 100644 Test/SqlDom/Baselines170/JsonObjectAggOverClause170.sql create mode 100644 Test/SqlDom/TestScripts/JsonObjectAggOverClause170.sql diff --git a/SqlScriptDom/Parser/TSql/TSql170.g b/SqlScriptDom/Parser/TSql/TSql170.g index 85ec9cf..d0769bf 100644 --- a/SqlScriptDom/Parser/TSql/TSql170.g +++ b/SqlScriptDom/Parser/TSql/TSql170.g @@ -33167,6 +33167,7 @@ jsonObjectBuiltInFunctionCall [FunctionCall vParent] jsonObjectAggBuiltInFunctionCall [FunctionCall vParent] { + OverClause vOverClause; } : ( jsonObjectAggExpressionList[vParent] @@ -33177,6 +33178,12 @@ jsonObjectAggBuiltInFunctionCall [FunctionCall vParent] { UpdateTokenInfo(vParent, tRParen); } + ( + vOverClause=overClauseNoOrderBy + { + vParent.OverClause = vOverClause; + } + )? ; jsonQueryBuiltInFunctionCall [FunctionCall vParent] diff --git a/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.FunctionCall.cs b/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.FunctionCall.cs index 70d7111..ffa5099 100644 --- a/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.FunctionCall.cs +++ b/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.FunctionCall.cs @@ -70,6 +70,8 @@ public override void ExplicitVisit(FunctionCall node) GenerateSpace(); GenerateReturnType(node?.ReturnType); GenerateSymbol(TSqlTokenType.RightParenthesis); + // Generate OVER clause for windowed json_objectagg + GenerateSpaceAndFragmentIfNotNull(node.OverClause); } else if (node.FunctionName.Value.ToUpper(CultureInfo.InvariantCulture) == CodeGenerationSupporter.JsonArray) { diff --git a/Test/SqlDom/Baselines170/JsonObjectAggOverClause170.sql b/Test/SqlDom/Baselines170/JsonObjectAggOverClause170.sql new file mode 100644 index 0000000..8aa7b2e --- /dev/null +++ b/Test/SqlDom/Baselines170/JsonObjectAggOverClause170.sql @@ -0,0 +1,11 @@ +SELECT JSON_OBJECTAGG(CustomerName:OrderDate) OVER (PARTITION BY OrderDate) +FROM Customers; + +SELECT JSON_OBJECTAGG(CustomerName:OrderDate ABSENT ON NULL) OVER (PARTITION BY OrderDate) +FROM Customers; + +SELECT JSON_OBJECTAGG(CustomerName:OrderDate NULL ON NULL) OVER (PARTITION BY OrderDate) +FROM Customers; + +SELECT JSON_OBJECTAGG(CustomerName:OrderDate NULL ON NULL RETURNING JSON) OVER (PARTITION BY OrderDate) +FROM Customers; \ No newline at end of file diff --git a/Test/SqlDom/Only170SyntaxTests.cs b/Test/SqlDom/Only170SyntaxTests.cs index a881c8e..f0b2ca8 100644 --- a/Test/SqlDom/Only170SyntaxTests.cs +++ b/Test/SqlDom/Only170SyntaxTests.cs @@ -21,6 +21,7 @@ public partial class SqlDomTests new ParserTest170("AiGenerateChunksTests170.sql", nErrors80: 19, nErrors90: 16, nErrors100: 15, nErrors110: 15, nErrors120: 15, nErrors130: 15, nErrors140: 15, nErrors150: 15, nErrors160: 15), new ParserTest170("JsonFunctionTests170.sql", nErrors80: 29, nErrors90: 8, nErrors100: 54, nErrors110: 54, nErrors120: 54, nErrors130: 54, nErrors140: 54, nErrors150: 54, nErrors160: 54), new ParserTest170("JsonArrayAggOrderBy170.sql", nErrors80: 10, nErrors90: 9, nErrors100: 9, nErrors110: 9, nErrors120: 9, nErrors130: 9, nErrors140: 9, nErrors150: 9, nErrors160: 9), + new ParserTest170("JsonObjectAggOverClause170.sql", nErrors80: 4, nErrors90: 4, nErrors100: 4, nErrors110: 4, nErrors120: 4, nErrors130: 4, nErrors140: 4, nErrors150: 4, nErrors160: 4), new ParserTest170("ComplexJsonObjectFunctionTests170.sql"), new ParserTest170("AiGenerateEmbeddingsTests170.sql", nErrors80: 14, nErrors90: 11, nErrors100: 11, nErrors110: 11, nErrors120: 11, nErrors130: 11, nErrors140: 11, nErrors150: 11, nErrors160: 11), new ParserTest170("CreateExternalModelStatementTests170.sql", nErrors80: 2, nErrors90: 2, nErrors100: 2, nErrors110: 2, nErrors120: 2, nErrors130: 4, nErrors140: 4, nErrors150: 4, nErrors160: 4), diff --git a/Test/SqlDom/TestScripts/JsonObjectAggOverClause170.sql b/Test/SqlDom/TestScripts/JsonObjectAggOverClause170.sql new file mode 100644 index 0000000..16edf83 --- /dev/null +++ b/Test/SqlDom/TestScripts/JsonObjectAggOverClause170.sql @@ -0,0 +1,11 @@ +-- JSON_OBJECTAGG with OVER clause (PARTITION BY) +SELECT JSON_OBJECTAGG(CustomerName:OrderDate) OVER (PARTITION BY OrderDate) FROM Customers; + +-- JSON_OBJECTAGG with ABSENT ON NULL and OVER clause +SELECT JSON_OBJECTAGG(CustomerName:OrderDate ABSENT ON NULL) OVER (PARTITION BY OrderDate) FROM Customers; + +-- JSON_OBJECTAGG with NULL ON NULL and OVER clause +SELECT JSON_OBJECTAGG(CustomerName:OrderDate NULL ON NULL) OVER (PARTITION BY OrderDate) FROM Customers; + +-- JSON_OBJECTAGG with NULL ON NULL, RETURNING JSON, and OVER clause +SELECT JSON_OBJECTAGG(CustomerName:OrderDate NULL ON NULL RETURNING JSON) OVER (PARTITION BY OrderDate) FROM Customers; \ No newline at end of file From af6556cb90fdc66fa6218b33676e083d5f1e3bd9 Mon Sep 17 00:00:00 2001 From: "Sai Avishkar Sreerama (from Dev Box)" Date: Thu, 5 Mar 2026 13:29:17 -0600 Subject: [PATCH 2/5] [SECURITY] Bump dotnet-sdk from 8.0.415 to 8.0.418 --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index b2e90b5..f64b7fb 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.415", + "version": "8.0.418", "rollForward": "latestMajor" }, "msbuild-sdks": { From 10d1394fcf872bbc796e5348f70955a9529742bb Mon Sep 17 00:00:00 2001 From: "Sai Avishkar Sreerama (from Dev Box)" Date: Thu, 5 Mar 2026 13:47:39 -0600 Subject: [PATCH 3/5] Fix: Make VECTOR_SEARCH TOP_N parameter optional and add WITH (FORCE_ANN_ONLY) support ## Summary Fixes ScriptDOM parser to correctly handle optional `TOP_N` parameter and `WITH (FORCE_ANN_ONLY)` query hint in `VECTOR_SEARCH` syntax, aligning with SQL Server 2025 behavior. ## Changes **Grammar (TSql170.g)** - Made `TOP_N` parameter optional - Added `WITH (FORCE_ANN_ONLY)` clause parsing **AST & Script Generator** - Added `ForceAnnOnly` boolean to `VectorSearchTableReference` - Updated script generator for nullable `TOP_N` and `WITH` clause output - Full round-trip fidelity maintained **Tests** - Added `VectorSearchOptionalTopNTests170.sql` (4 test scenarios) - Removed obsolete error test expecting mandatory `TOP_N` ## Example Syntax Now Supported ```sql -- Without TOP_N SELECT * FROM VECTOR_SEARCH(...) AS ann -- With query hint SELECT * FROM VECTOR_SEARCH(...) WITH (FORCE_ANN_ONLY) AS ann -- With TOP_N (backward compatible) SELECT * FROM VECTOR_SEARCH(..., TOP_N = 10) AS ann ``` Backward compatible - No breaking changes. --- .../testing.guidelines.instructions.md | 57 ++++++++++++++++++- .gitignore | 13 ++++- SqlScriptDom/Parser/TSql/Ast.xml | 1 + .../Parser/TSql/CodeGenerationSupporter.cs | 1 + SqlScriptDom/Parser/TSql/TSql170.g | 36 ++++++++---- ...ratorVisitor.VectorSearchTableReference.cs | 29 +++++++--- .../VectorSearchOptionalTopNTests170.sql | 33 +++++++++++ Test/SqlDom/Only170SyntaxTests.cs | 1 + Test/SqlDom/ParserErrorsTests.cs | 29 +++++++--- .../VectorSearchOptionalTopNTests170.sql | 33 +++++++++++ 10 files changed, 205 insertions(+), 28 deletions(-) create mode 100644 Test/SqlDom/Baselines170/VectorSearchOptionalTopNTests170.sql create mode 100644 Test/SqlDom/TestScripts/VectorSearchOptionalTopNTests170.sql diff --git a/.github/instructions/testing.guidelines.instructions.md b/.github/instructions/testing.guidelines.instructions.md index ec62811..5b1b477 100644 --- a/.github/instructions/testing.guidelines.instructions.md +++ b/.github/instructions/testing.guidelines.instructions.md @@ -321,6 +321,25 @@ Expected: 'SELECT JSON_ARRAY('value1', 'value2');' **Solution**: Copy the "Actual" output to your baseline file (note spacing differences). +**CRITICAL CHECK**: After copying baseline, compare it against your test script: +```sql +-- Input script (TestScripts/MyTest.sql) +SELECT * FROM FUNC() WITH (HINT); + +-- Generated baseline (Baselines170/MyTest.sql) +SELECT * +FROM FUNC(); +-- ⚠️ WHERE IS 'WITH (HINT)'? +``` + +**If baseline is missing syntax from input:** +1. **This is likely a BUG** - not just formatting difference +2. Check if AST has member to store the missing syntax +3. Verify grammar stores value: `vResult.PropertyName = vValue;` +4. Check script generator outputs the value: `GenerateFragmentIfNotNull(node.Property)` +5. If syntax should be preserved, add AST storage and script generation +6. Document in spec if intentional omission (e.g., query optimizer hints) + #### 2. Error Count Mismatch ``` TestYourFeature.sql: number of errors after parsing is different from expected. @@ -800,6 +819,38 @@ new ParserTest160("RegressionBugFix12345Tests160.sql", nErrors150: 1), // Bug e new ParserTest170("RegressionBugFix12345Tests170.sql", nErrors160: 1), // Bug existed in SQL 2022 ``` +## Round-Trip Fidelity Validation Checklist + +**CRITICAL: After generating baseline files, ALWAYS verify:** + +✅ **Input Preservation Check**: +1. Open test script side-by-side with baseline file +2. For each SQL statement, verify baseline preserves all syntax from input +3. Check optional clauses: `WITH`, `WHERE`, `HAVING`, `ORDER BY`, etc. +4. Check hints: Table hints, query hints, join hints +5. Check keywords: All keywords from input should appear in baseline (unless documented normalization) + +✅ **Missing Syntax Investigation**: +If baseline omits syntax from input: +- [ ] Is this intentional keyword normalization? (e.g., APPROX → APPROXIMATE) +- [ ] Is this a query optimizer hint that doesn't need preservation? +- [ ] Is this a BUG where AST doesn't store the value? + +✅ **Bug Indicators**: +- ❌ Input: `FUNCTION() WITH (HINT)` → Baseline: `FUNCTION()` = **LIKELY BUG** +- ❌ Input: `SELECT ... ORDER BY col` → Baseline: `SELECT ...` = **BUG** +- ✅ Input: `FETCH APPROX` → Baseline: `FETCH APPROXIMATE` = Acceptable normalization +- ✅ Input: `SELECT /*+ HINT */` → Baseline: `SELECT` = Query hint (document in spec) + +✅ **Resolution Steps**: +1. Check AST definition in `Ast.xml` for member to store value +2. Verify grammar assigns value: `vResult.Property = vValue;` +3. Check script generator outputs value: `if (node.Property != null) { ... }` +4. If missing: Add AST member, update grammar, update script generator, rebuild +5. Document decision in spec if intentional omission + +--- + ## Summary The SqlScriptDOM testing framework provides comprehensive validation of parser functionality through: @@ -808,7 +859,11 @@ The SqlScriptDOM testing framework provides comprehensive validation of parser f - **Cross-version validation** (Test syntax across SQL Server versions) - **Error condition testing** (Invalid syntax produces expected errors) - **Exact syntax verification** (Exact T-SQL from user requests is tested precisely) +- **Round-trip fidelity validation** (Baseline preserves all input syntax unless documented) Following these guidelines ensures robust test coverage for parser functionality and prevents regressions when adding new features or fixing bugs. -**Key Principle**: Always test the exact T-SQL syntax provided in user prompts or requests to verify that the specific syntax works as expected, rather than testing generalized or simplified versions of the syntax. \ No newline at end of file +**Key Principles**: +1. Always test the exact T-SQL syntax provided in user prompts or requests +2. Always verify baseline output preserves input syntax (missing syntax may indicate bugs) +3. Document any intentional omissions (normalization, query hints) in spec \ No newline at end of file diff --git a/.gitignore b/.gitignore index 997c172..adb0b54 100644 --- a/.gitignore +++ b/.gitignore @@ -359,4 +359,15 @@ out/ .packages/ # Temporary build artifacts -tmp/ \ No newline at end of file +tmp/ + +# Speckit files +speckit.files +.github/.specify/ +.github/agents/speckit.*.agent.md +.github/prompts/speckit.* + +# Specs directory - ignore all files except spec.md +specs/**/* +!specs/**/ +!specs/**/spec.md diff --git a/SqlScriptDom/Parser/TSql/Ast.xml b/SqlScriptDom/Parser/TSql/Ast.xml index d17d34e..1976cb1 100644 --- a/SqlScriptDom/Parser/TSql/Ast.xml +++ b/SqlScriptDom/Parser/TSql/Ast.xml @@ -4808,5 +4808,6 @@ + diff --git a/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs b/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs index 4f7e5be..4c8a97d 100644 --- a/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs +++ b/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs @@ -75,6 +75,7 @@ internal static class CodeGenerationSupporter internal const string AnsiWarnings = "ANSI_WARNINGS"; internal const string ForcePlan = "FORCEPLAN"; internal const string ForAppend = "FOR_APPEND"; + internal const string ForceAnnOnly = "FORCE_ANN_ONLY"; internal const string ShowPlanAll = "SHOWPLAN_ALL"; internal const string ShowPlanText = "SHOWPLAN_TEXT"; internal const string IO = "IO"; diff --git a/SqlScriptDom/Parser/TSql/TSql170.g b/SqlScriptDom/Parser/TSql/TSql170.g index d0769bf..a8a1b49 100644 --- a/SqlScriptDom/Parser/TSql/TSql170.g +++ b/SqlScriptDom/Parser/TSql/TSql170.g @@ -19321,19 +19321,35 @@ vectorSearchTableReference returns [VectorSearchTableReference vResult = Fragmen MatchString(vMetric, CodeGenerationSupporter.Cosine, CodeGenerationSupporter.Dot, CodeGenerationSupporter.Euclidean); vResult.Metric = vMetric; } - Comma tTopN:Identifier EqualsSign vTopN = signedIntegerOrVariableOrColumnReference - { - Match(tTopN, CodeGenerationSupporter.TopN); - - // Validate that TOP_N is not a negative number - if (vTopN is UnaryExpression unaryExpr && unaryExpr.UnaryExpressionType == UnaryExpressionType.Negative) + // TOP_N is optional per SQL Server 2025 (commit 12d3e8fc) + ( + Comma tTopN:Identifier EqualsSign vTopN = signedIntegerOrVariableOrColumnReference { - ThrowParseErrorException("SQL46010", unaryExpr, TSqlParserResource.SQL46010Message, "-"); + Match(tTopN, CodeGenerationSupporter.TopN); + + // Validate that TOP_N is not a negative number + if (vTopN is UnaryExpression unaryExpr && unaryExpr.UnaryExpressionType == UnaryExpressionType.Negative) + { + ThrowParseErrorException("SQL46010", unaryExpr, TSqlParserResource.SQL46010Message, "-"); + } + + vResult.TopN = vTopN; } - - vResult.TopN = vTopN; + )? + tRParen:RightParenthesis + { + UpdateTokenInfo(vResult, tRParen); } - RightParenthesis simpleTableReferenceAliasOpt[vResult] + // WITH clause per SQL Server 2025 (commit 12d3e8fc) + ( + With LeftParenthesis tForceAnnOnly:Identifier tRParen2:RightParenthesis + { + Match(tForceAnnOnly, CodeGenerationSupporter.ForceAnnOnly); + UpdateTokenInfo(vResult, tRParen2); + vResult.ForceAnnOnly = true; + } + )? + simpleTableReferenceAliasOpt[vResult] ; predictTableReference[SubDmlFlags subDmlFlags] returns [PredictTableReference vResult] diff --git a/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.VectorSearchTableReference.cs b/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.VectorSearchTableReference.cs index 55e4730..09b9aa4 100644 --- a/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.VectorSearchTableReference.cs +++ b/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.VectorSearchTableReference.cs @@ -15,9 +15,9 @@ partial class SqlScriptGeneratorVisitor /// TABLE = object[AS source_table_alias], /// COLUMN = vector_column, /// SIMILAR_TO = query_vector, - /// METRIC = { 'cosine' | 'dot' | 'euclidean' }, - /// TOP_N = k - /// ) [AS result_table_alias] + /// METRIC = { 'cosine' | 'dot' | 'euclidean' } + /// [, TOP_N = k] + /// ) [WITH (FORCE_ANN_ONLY)] [AS result_table_alias] /// public override void ExplicitVisit(VectorSearchTableReference node) { @@ -41,13 +41,28 @@ public override void ExplicitVisit(VectorSearchTableReference node) NewLineAndIndent(); GenerateNameEqualsValue(CodeGenerationSupporter.Metric, node.Metric); - GenerateSymbol(TSqlTokenType.Comma); - - NewLineAndIndent(); - GenerateNameEqualsValue(CodeGenerationSupporter.TopN, node.TopN); + + // TOP_N is optional per SQL Server 2025 (commit 12d3e8fc) + if (node.TopN != null) + { + GenerateSymbol(TSqlTokenType.Comma); + NewLineAndIndent(); + GenerateNameEqualsValue(CodeGenerationSupporter.TopN, node.TopN); + } NewLine(); GenerateSymbol(TSqlTokenType.RightParenthesis); + + // WITH (FORCE_ANN_ONLY) hint per SQL Server 2025 (commit 12d3e8fc) + if (node.ForceAnnOnly) + { + GenerateSpaceAndKeyword(TSqlTokenType.With); + GenerateSpace(); + GenerateSymbol(TSqlTokenType.LeftParenthesis); + GenerateIdentifier(CodeGenerationSupporter.ForceAnnOnly); + GenerateSymbol(TSqlTokenType.RightParenthesis); + } + GenerateSpaceAndAlias(node.Alias); PopAlignmentPoint(); diff --git a/Test/SqlDom/Baselines170/VectorSearchOptionalTopNTests170.sql b/Test/SqlDom/Baselines170/VectorSearchOptionalTopNTests170.sql new file mode 100644 index 0000000..6d31f39 --- /dev/null +++ b/Test/SqlDom/Baselines170/VectorSearchOptionalTopNTests170.sql @@ -0,0 +1,33 @@ +SELECT * +FROM VECTOR_SEARCH( + TABLE = graphnode, + COLUMN = embedding, + SIMILAR_TO = @qembedding, + METRIC = 'euclidean' + ); + +SELECT * +FROM VECTOR_SEARCH( + TABLE = graphnode, + COLUMN = embedding, + SIMILAR_TO = @qembedding, + METRIC = 'euclidean' + ) WITH (FORCE_ANN_ONLY); + +SELECT * +FROM VECTOR_SEARCH( + TABLE = graphnode, + COLUMN = embedding, + SIMILAR_TO = @qembedding, + METRIC = 'euclidean', + TOP_N = 20 + ); + +SELECT * +FROM VECTOR_SEARCH( + TABLE = graphnode, + COLUMN = embedding, + SIMILAR_TO = @qembedding, + METRIC = 'euclidean', + TOP_N = 20 + ) WITH (FORCE_ANN_ONLY); diff --git a/Test/SqlDom/Only170SyntaxTests.cs b/Test/SqlDom/Only170SyntaxTests.cs index f0b2ca8..12478fd 100644 --- a/Test/SqlDom/Only170SyntaxTests.cs +++ b/Test/SqlDom/Only170SyntaxTests.cs @@ -15,6 +15,7 @@ public partial class SqlDomTests new ParserTest170("RegexpTVFTests170.sql", nErrors80: 1, nErrors90: 1, nErrors100: 0, nErrors110: 0, nErrors120: 0, nErrors130: 0, nErrors140: 0, nErrors150: 0, nErrors160: 0), new ParserTest170("JsonIndexTests170.sql", nErrors80: 2, nErrors90: 10, nErrors100: 10, nErrors110: 10, nErrors120: 10, nErrors130: 10, nErrors140: 10, nErrors150: 10, nErrors160: 10), new ParserTest170("VectorIndexTests170.sql", nErrors80: 2, nErrors90: 12, nErrors100: 12, nErrors110: 12, nErrors120: 12, nErrors130: 12, nErrors140: 12, nErrors150: 12, nErrors160: 12), + new ParserTest170("VectorSearchOptionalTopNTests170.sql"), new ParserTest170("AlterDatabaseManualCutoverTests170.sql", nErrors80: 4, nErrors90: 4, nErrors100: 4, nErrors110: 4, nErrors120: 4, nErrors130: 4, nErrors140: 4, nErrors150: 4, nErrors160: 4), new ParserTest170("CreateColumnStoreIndexTests170.sql", nErrors80: 3, nErrors90: 3, nErrors100: 3, nErrors110: 3, nErrors120: 3, nErrors130: 0, nErrors140: 0, nErrors150: 0, nErrors160: 0), new ParserTest170("RegexpTests170.sql", nErrors80: 0, nErrors90: 0, nErrors100: 0, nErrors110: 0, nErrors120: 0, nErrors130: 0, nErrors140: 0, nErrors150: 0, nErrors160: 0), diff --git a/Test/SqlDom/ParserErrorsTests.cs b/Test/SqlDom/ParserErrorsTests.cs index 5a89afe..5e8b2c5 100644 --- a/Test/SqlDom/ParserErrorsTests.cs +++ b/Test/SqlDom/ParserErrorsTests.cs @@ -7059,6 +7059,7 @@ RETURNS NVARCHAR(101) RETURN @first + ' ' + @last END;"; ParserTestUtils.ErrorTestFabricDW(scalarFunctionSyntax2, new ParserErrorInfo(scalarFunctionSyntax2.IndexOf("INLINE"), "SQL46010", "INLINE")); + string scalarFunctionSyntax3 = @"CREATE OR ALTER FUNCTION dbo.CountProducts ( @ProductTable AS dbo.ProductType READONLY @@ -7097,11 +7098,22 @@ FROM @SalesData [SqlStudioTestCategory(Category.UnitTest)] public void IdentityColumnNegativeTestsFabricDW() { - string identityColumnSyntax = @"CREATE TABLE TestTable1 (ID INT IDENTITY(1,1), Name VARCHAR(50));"; - ParserTestUtils.ErrorTestFabricDW(identityColumnSyntax, new ParserErrorInfo(40, "SQL46010", "(")); - - string identityColumnSyntax2 = @"CREATE TABLE TestTable2 (RecordID BIGINT IDENTITY(100,5), Description NVARCHAR(200));"; - ParserTestUtils.ErrorTestFabricDW(identityColumnSyntax2, new ParserErrorInfo(49, "SQL46010", "(")); + string identityColumnSyntax = @"CREATE TABLE TestTable1 ( + ID INT IDENTITY(1,1), + Name VARCHAR(50) + ); + "; + string token = "IDENTITY"; + int errorOffSet = identityColumnSyntax.IndexOf(token) + token.Length; + ParserTestUtils.ErrorTestFabricDW(identityColumnSyntax, new ParserErrorInfo(errorOffSet, "SQL46010", "(")); + + string identityColumnSyntax2 = @"CREATE TABLE TestTable2 ( + RecordID BIGINT IDENTITY(100,5), + Description NVARCHAR(200) + ); + "; + errorOffSet = identityColumnSyntax2.IndexOf(token) + token.Length; + ParserTestUtils.ErrorTestFabricDW(identityColumnSyntax2, new ParserErrorInfo(errorOffSet, "SQL46010", "(")); } /// @@ -7445,10 +7457,9 @@ public void VectorSearchErrorTest170() "SELECT * FROM VECTOR_SEARCH(TABLE = tbl1, COLUMN = col1, SIMILAR_TO = query_vector)", new ParserErrorInfo(82, "SQL46010", ")")); - // Missing required parameters: TOP_N - ParserTestUtils.ErrorTest170( - "SELECT * FROM VECTOR_SEARCH(TABLE = tbl1, COLUMN = col1, SIMILAR_TO = query_vector, METRIC = 'dot')", - new ParserErrorInfo(98, "SQL46010", ")")); + // TOP_N is now OPTIONAL per SQL Server 2025 (commit 12d3e8fc) + // The following test case has been removed as it tested for TOP_N being mandatory + // which is no longer the case. VECTOR_SEARCH now accepts queries without TOP_N. // Invalid order: COLUMN before TABLE ParserTestUtils.ErrorTest170( diff --git a/Test/SqlDom/TestScripts/VectorSearchOptionalTopNTests170.sql b/Test/SqlDom/TestScripts/VectorSearchOptionalTopNTests170.sql new file mode 100644 index 0000000..e6ebb71 --- /dev/null +++ b/Test/SqlDom/TestScripts/VectorSearchOptionalTopNTests170.sql @@ -0,0 +1,33 @@ +-- Test 1: VECTOR_SEARCH without TOP_N (validates optional parameter) +SELECT * FROM VECTOR_SEARCH( + TABLE = graphnode, + COLUMN = embedding, + SIMILAR_TO = @qembedding, + METRIC = 'euclidean' +); + +-- Test 2: VECTOR_SEARCH without TOP_N + WITH (FORCE_ANN_ONLY) (validates both changes) +SELECT * FROM VECTOR_SEARCH( + TABLE = graphnode, + COLUMN = embedding, + SIMILAR_TO = @qembedding, + METRIC = 'euclidean' +) WITH (FORCE_ANN_ONLY); + +-- Test 3: VECTOR_SEARCH with TOP_N (validates backward compatibility) +SELECT * FROM VECTOR_SEARCH( + TABLE = graphnode, + COLUMN = embedding, + SIMILAR_TO = @qembedding, + METRIC = 'euclidean', + TOP_N = 20 +); + +-- Test 4: VECTOR_SEARCH with TOP_N + WITH (FORCE_ANN_ONLY) (validates both features together) +SELECT * FROM VECTOR_SEARCH( + TABLE = graphnode, + COLUMN = embedding, + SIMILAR_TO = @qembedding, + METRIC = 'euclidean', + TOP_N = 20 +) WITH (FORCE_ANN_ONLY); From 6768b6c2cd4f9309c218649547d1f3c8994a32ea Mon Sep 17 00:00:00 2001 From: "Sai Avishkar Sreerama (from Dev Box)" Date: Thu, 5 Mar 2026 13:48:08 -0600 Subject: [PATCH 4/5] Simplifies CREATE/ALTER EXTERNAL DATA SOURCE syntax for Fabric DW Simplifies CREATE/ALTER EXTERNAL DATA SOURCE syntax for Fabric DW to only support the LOCATION parameter, removing support for TYPE, PUSHDOWN, and other literal/ identifier options in the Fabric DW parser. Added positive and negative tests for this based on the specification. --- SqlScriptDom/Parser/TSql/TSqlFabricDW.g | 166 +----------------- ...teAlterExternalDataSourceTestsFabricDW.sql | 11 ++ Test/SqlDom/OnlyFabricDWSyntaxTests.cs | 3 +- Test/SqlDom/ParserErrorsTests.cs | 42 +++++ ...teAlterExternalDataSourceTestsFabricDW.sql | 11 ++ 5 files changed, 68 insertions(+), 165 deletions(-) create mode 100644 Test/SqlDom/BaselinesFabricDW/CreateAlterExternalDataSourceTestsFabricDW.sql create mode 100644 Test/SqlDom/TestScripts/CreateAlterExternalDataSourceTestsFabricDW.sql diff --git a/SqlScriptDom/Parser/TSql/TSqlFabricDW.g b/SqlScriptDom/Parser/TSql/TSqlFabricDW.g index 77019ed..21fd33d 100644 --- a/SqlScriptDom/Parser/TSql/TSqlFabricDW.g +++ b/SqlScriptDom/Parser/TSql/TSqlFabricDW.g @@ -23761,8 +23761,6 @@ dropSecurityPolicyStatement returns [DropSecurityPolicyStatement vResult = Fragm createExternalDataSourceStatement returns [CreateExternalDataSourceStatement vResult = FragmentFactory.CreateFragment()] { Identifier vName; - ExternalDataSourceOption vExternalDataSourceOption; - long encounteredOptions = 0; vResult.DataSourceType = ExternalDataSourceType.EXTERNAL_GENERICS; } : tData:Identifier tSource:Identifier vName = identifier @@ -23774,42 +23772,8 @@ createExternalDataSourceStatement returns [CreateExternalDataSourceStatement vRe } tWith:With LeftParenthesis - ( - {NextTokenMatches(CodeGenerationSupporter.Type)}? - externalDataSourceType[vResult] - | - {NextTokenMatches(CodeGenerationSupporter.Location)}? - externalDataSourceLocation[vResult] - | - {NextTokenMatches(CodeGenerationSupporter.PushdownOption)}? - externalDataSourcePushdownOption[vResult] - | - vExternalDataSourceOption = externalDataSourceLiteralOrIdentifierOption - { - CheckOptionDuplication(ref encounteredOptions, (int)vExternalDataSourceOption.OptionKind, vExternalDataSourceOption); - AddAndUpdateTokenInfo(vResult, vResult.ExternalDataSourceOptions, vExternalDataSourceOption); - } - ) - ( - tComma:Comma - ( - {NextTokenMatches(CodeGenerationSupporter.Type)}? - externalDataSourceType[vResult] - | - {NextTokenMatches(CodeGenerationSupporter.Location)}? - externalDataSourceLocation[vResult] - | - {NextTokenMatches(CodeGenerationSupporter.PushdownOption)}? - externalDataSourcePushdownOption[vResult] - | - vExternalDataSourceOption = externalDataSourceLiteralOrIdentifierOption - { - CheckOptionDuplication(ref encounteredOptions, (int)vExternalDataSourceOption.OptionKind, vExternalDataSourceOption); - AddAndUpdateTokenInfo(vResult, vResult.ExternalDataSourceOptions, vExternalDataSourceOption); - } - ) - )* + externalDataSourceLocation[vResult] tRParen:RightParenthesis { @@ -23817,45 +23781,6 @@ createExternalDataSourceStatement returns [CreateExternalDataSourceStatement vRe } ; -externalDataSourceType[CreateExternalDataSourceStatement vParent] - : - tDataSourceType:Identifier EqualsSign - { - Match(tDataSourceType, CodeGenerationSupporter.Type); - UpdateTokenInfo(vParent, tDataSourceType); - } - ( - tExternalDataSourceType:Identifier - { - if (TryMatch(tExternalDataSourceType, CodeGenerationSupporter.Hadoop)) - { - UpdateTokenInfo(vParent, tExternalDataSourceType); - vParent.DataSourceType = ExternalDataSourceType.HADOOP; - } - else if (TryMatch(tExternalDataSourceType, CodeGenerationSupporter.Rdbms)) - { - UpdateTokenInfo(vParent, tExternalDataSourceType); - vParent.DataSourceType = ExternalDataSourceType.RDBMS; - } - else if (TryMatch(tExternalDataSourceType, CodeGenerationSupporter.ShardMapManager)) - { - UpdateTokenInfo(vParent, tExternalDataSourceType); - vParent.DataSourceType = ExternalDataSourceType.SHARD_MAP_MANAGER; - } - else if (TryMatch(tExternalDataSourceType, CodeGenerationSupporter.BlobStorage)) - { - UpdateTokenInfo(vParent, tExternalDataSourceType); - vParent.DataSourceType = ExternalDataSourceType.BLOB_STORAGE; - } - else - { - UpdateTokenInfo(vParent, tExternalDataSourceType); - vParent.DataSourceType = ExternalDataSourceType.EXTERNAL_GENERICS; - } - } - ) - ; - externalDataSourceLocation[ExternalDataSourceStatement vParent] { Literal vLocation; @@ -23868,68 +23793,9 @@ externalDataSourceLocation[ExternalDataSourceStatement vParent] } ; -externalDataSourcePushdownOption[ExternalDataSourceStatement vParent] - : - tPushdownOption:Identifier - { - Match(tPushdownOption, CodeGenerationSupporter.PushdownOption); - UpdateTokenInfo(vParent, tPushdownOption); - } - EqualsSign - ( - tOn:On - { - vParent.PushdownOption = ExternalDataSourcePushdownOption.ON; - UpdateTokenInfo(vParent, tOn); - } - | tOff:Off - { - vParent.PushdownOption = ExternalDataSourcePushdownOption.OFF; - UpdateTokenInfo(vParent, tOff); - } - ) - ; - -externalDataSourceLiteralOrIdentifierOption returns [ExternalDataSourceLiteralOrIdentifierOption vResult = this.FragmentFactory.CreateFragment()] -{ - Literal vLiteral; - Identifier vIdentifier; -} - : - tOption:Identifier - { - vResult.OptionKind = ExternalDataSourceOptionHelper.Instance.ParseOption(tOption); - } - EqualsSign - ( - vIdentifier = identifier - { - if (vResult.OptionKind != ExternalDataSourceOptionKind.Credential) - { - throw GetUnexpectedTokenErrorException(tOption); - } - vResult.Value = IdentifierOrValueExpression(vIdentifier); - } - | - vLiteral = stringLiteral - { - if (vResult.OptionKind != ExternalDataSourceOptionKind.ResourceManagerLocation && - vResult.OptionKind != ExternalDataSourceOptionKind.DatabaseName && - vResult.OptionKind != ExternalDataSourceOptionKind.ShardMapName && - vResult.OptionKind != ExternalDataSourceOptionKind.ConnectionOptions) - { - throw GetUnexpectedTokenErrorException(tOption); - } - vResult.Value = IdentifierOrValueExpression(vLiteral); - } - ) - ; - alterExternalDataSourceStatement returns [AlterExternalDataSourceStatement vResult = FragmentFactory.CreateFragment()] { Identifier vName; - ExternalDataSourceOption vExternalDataSourceOption; - long encounteredOptions = 0; } : tData:Identifier tSource:Identifier vName = identifier { @@ -23940,36 +23806,8 @@ alterExternalDataSourceStatement returns [AlterExternalDataSourceStatement vResu } tSet:Set - ( - {NextTokenMatches(CodeGenerationSupporter.Location)}? - externalDataSourceLocation[vResult] - | - {NextTokenMatches(CodeGenerationSupporter.PushdownOption)}? - externalDataSourcePushdownOption[vResult] - | - vExternalDataSourceOption = externalDataSourceLiteralOrIdentifierOption - { - CheckOptionDuplication(ref encounteredOptions, (int)vExternalDataSourceOption.OptionKind, vExternalDataSourceOption); - AddAndUpdateTokenInfo(vResult, vResult.ExternalDataSourceOptions, vExternalDataSourceOption); - } - ) - ( - tComma:Comma - ( - {NextTokenMatches(CodeGenerationSupporter.Location)}? - externalDataSourceLocation[vResult] - | - {NextTokenMatches(CodeGenerationSupporter.PushdownOption)}? - externalDataSourcePushdownOption[vResult] - | - vExternalDataSourceOption = externalDataSourceLiteralOrIdentifierOption - { - CheckOptionDuplication(ref encounteredOptions, (int)vExternalDataSourceOption.OptionKind, vExternalDataSourceOption); - AddAndUpdateTokenInfo(vResult, vResult.ExternalDataSourceOptions, vExternalDataSourceOption); - } - ) - )* + externalDataSourceLocation[vResult] ; dropExternalDataSourceStatement returns [DropExternalDataSourceStatement vResult = FragmentFactory.CreateFragment()] diff --git a/Test/SqlDom/BaselinesFabricDW/CreateAlterExternalDataSourceTestsFabricDW.sql b/Test/SqlDom/BaselinesFabricDW/CreateAlterExternalDataSourceTestsFabricDW.sql new file mode 100644 index 0000000..fb3aef4 --- /dev/null +++ b/Test/SqlDom/BaselinesFabricDW/CreateAlterExternalDataSourceTestsFabricDW.sql @@ -0,0 +1,11 @@ +CREATE EXTERNAL DATA SOURCE MyDataSource + WITH ( + LOCATION = 'sqlserver://10.10.10.10:1433' + ); + +CREATE EXTERNAL DATA SOURCE BlobSource + WITH ( + LOCATION = 'wasbs://container@account.blob.core.windows.net' + ); + +ALTER EXTERNAL DATA SOURCE MyDataSource SET LOCATION = 'sqlserver://192.168.1.100:1433'; \ No newline at end of file diff --git a/Test/SqlDom/OnlyFabricDWSyntaxTests.cs b/Test/SqlDom/OnlyFabricDWSyntaxTests.cs index 5bee5e2..a2eaab8 100644 --- a/Test/SqlDom/OnlyFabricDWSyntaxTests.cs +++ b/Test/SqlDom/OnlyFabricDWSyntaxTests.cs @@ -10,6 +10,7 @@ public partial class SqlDomTests private static readonly ParserTest[] OnlyFabricDWTestInfos = { new ParserTestFabricDW("CloneTableTestsFabricDW.sql", nErrors80: 4, nErrors90: 4, nErrors100: 4, nErrors110: 4, nErrors120: 4, nErrors130: 4, nErrors140: 4, nErrors150: 4, nErrors160: 4, nErrors170: 4), + new ParserTestFabricDW("CreateAlterExternalDataSourceTestsFabricDW.sql", nErrors80: 2, nErrors90: 2, nErrors100: 2, nErrors110: 2, nErrors120: 2, nErrors130: 0, nErrors140: 0, nErrors150: 0, nErrors160: 0, nErrors170: 0), new ParserTestFabricDW("CreateAlterTableClusterByTestsFabricDW.sql", nErrors80: 6, nErrors90: 6, nErrors100: 6, nErrors110: 6, nErrors120: 6, nErrors130: 6, nErrors140: 6, nErrors150: 6, nErrors160: 6, nErrors170: 6), new ParserTestFabricDW("CreateExternalTableStatementTestsFabricDW.sql", nErrors80: 2, nErrors90: 2, nErrors100: 2, nErrors110: 2, nErrors120: 2, nErrors130: 2, nErrors140: 2, nErrors150: 2, nErrors160: 0, nErrors170: 0), new ParserTestFabricDW("CreateProcedureCloneTableTestsFabricDW.sql", nErrors80: 4, nErrors90: 4, nErrors100: 4, nErrors110: 4, nErrors120: 4, nErrors130: 4, nErrors140: 4, nErrors150: 4, nErrors160: 4, nErrors170: 4), @@ -23,7 +24,7 @@ public partial class SqlDomTests new ParserTestFabricDW("AiFixGrammarTestsFabricDW.sql", nErrors80: 2, nErrors90: 1, nErrors100: 2, nErrors110: 2, nErrors120: 2, nErrors130: 0, nErrors140: 0, nErrors150: 0, nErrors160: 0, nErrors170: 0), new ParserTestFabricDW("AiGenerateResponseTestsFabricDW.sql", nErrors80: 3, nErrors90: 1, nErrors100: 2, nErrors110: 2, nErrors120: 2, nErrors130: 0, nErrors140: 0, nErrors150: 0, nErrors160: 0, nErrors170: 0), new ParserTestFabricDW("AiSummarizeTestsFabricDW.sql", nErrors80: 2, nErrors90: 1, nErrors100: 2, nErrors110: 2, nErrors120: 2, nErrors130: 0, nErrors140: 0, nErrors150: 0, nErrors160: 0, nErrors170: 0), - new ParserTestFabricDW("AiTranslateTestsFabricDW.sql", nErrors80: 3, nErrors90: 1, nErrors100: 2, nErrors110: 2, nErrors120: 2, nErrors130: 0, nErrors140: 0, nErrors150: 0, nErrors160: 0, nErrors170: 0), + new ParserTestFabricDW("AiTranslateTestsFabricDW.sql", nErrors80: 3, nErrors90: 1, nErrors100: 2, nErrors110: 2, nErrors120: 2, nErrors130: 0, nErrors140: 0, nErrors150: 0, nErrors160: 0, nErrors170: 0) }; [TestMethod] diff --git a/Test/SqlDom/ParserErrorsTests.cs b/Test/SqlDom/ParserErrorsTests.cs index 5e8b2c5..34b0a92 100644 --- a/Test/SqlDom/ParserErrorsTests.cs +++ b/Test/SqlDom/ParserErrorsTests.cs @@ -5236,6 +5236,30 @@ public void CreateExternalDataSourceNegativeTest() ParserTestUtils.ErrorTest130("CREATE EXTERNAL DATA SOURCE eds1 WITH (TYPE = RDBMS, LOCATION = someServer, DATABASE_NAME = 'someDatabase', CREDENTIAL = someCred)", new ParserErrorInfo(64, "SQL46010", "someServer")); ParserTestUtils.ErrorTest130("CREATE EXTERNAL DATA SOURCE eds1 WITH (TYPE = RDBMS, LOCATION = 'someServer', DATABASE_NAME = someDatabase, CREDENTIAL = someCred)", new ParserErrorInfo(78, "SQL46010", "DATABASE_NAME")); ParserTestUtils.ErrorTest130("CREATE EXTERNAL DATA SOURCE eds1 WITH (TYPE = RDBMS, LOCATION = 'someServer', DATABASE_NAME = 'someDatabase', CREDENTIAL = 'someCred')", new ParserErrorInfo(110, "SQL46010", "CREDENTIAL")); + + // CREATE with TYPE parameter (no longer supported — parser expects LOCATION) + // + ParserTestUtils.ErrorTestFabricDW("CREATE EXTERNAL DATA SOURCE eds1 WITH (TYPE = HADOOP, LOCATION = 'protocol://ip_address:port')", new ParserErrorInfo(46, "SQL46010", "HADOOP")); + + // CREATE with extra CREDENTIAL option after LOCATION (only LOCATION allowed, no additional options) + // + ParserTestUtils.ErrorTestFabricDW("CREATE EXTERNAL DATA SOURCE eds1 WITH (LOCATION = 'protocol://ip_address:port', CREDENTIAL = cred1)", new ParserErrorInfo(78, "SQL46010", ",")); + + // CREATE with extra PUSHDOWN option after LOCATION + // + ParserTestUtils.ErrorTestFabricDW("CREATE EXTERNAL DATA SOURCE eds1 WITH (LOCATION = 'protocol://ip_address:port', PUSHDOWN = ON)", new ParserErrorInfo(78, "SQL46010", ",")); + + // CREATE with extra RESOURCE_MANAGER_LOCATION option after LOCATION + // + ParserTestUtils.ErrorTestFabricDW("CREATE EXTERNAL DATA SOURCE eds1 WITH (LOCATION = 'protocol://ip_address:port', RESOURCE_MANAGER_LOCATION = 'ip_address:port')", new ParserErrorInfo(78, "SQL46010", ",")); + + // CREATE with extra DATABASE_NAME option after LOCATION + // + ParserTestUtils.ErrorTestFabricDW("CREATE EXTERNAL DATA SOURCE eds1 WITH (LOCATION = 'someServer', DATABASE_NAME = 'someDatabase')", new ParserErrorInfo(62, "SQL46010", ",")); + + // CREATE with extra CONNECTION_OPTIONS after LOCATION + // + ParserTestUtils.ErrorTestFabricDW("CREATE EXTERNAL DATA SOURCE eds1 WITH (LOCATION = 'protocol://ip_address:port', CONNECTION_OPTIONS = 'some_options')", new ParserErrorInfo(78, "SQL46010", ",")); } @@ -5295,6 +5319,24 @@ public void AlterExternalDataSourceNegativeTest() ParserTestUtils.ErrorTest130("ALTER EXTERNAL DATA SOURCE eds1 SET LOCATION = 'someServer', DATABASE_NAME = someDatabase, SHARD_MAP_NAME = 'someShardMap', CREDENTIAL = someCred", new ParserErrorInfo(61, "SQL46010", "DATABASE_NAME")); ParserTestUtils.ErrorTest130("ALTER EXTERNAL DATA SOURCE eds1 SET LOCATION = 'someServer', DATABASE_NAME = 'someDatabase', SHARD_MAP_NAME = someShardMap, CREDENTIAL = someCred", new ParserErrorInfo(93, "SQL46010", "SHARD_MAP_NAME")); ParserTestUtils.ErrorTest130("ALTER EXTERNAL DATA SOURCE eds1 SET LOCATION = 'someServer', DATABASE_NAME = 'someDatabase', SHARD_MAP_NAME = 'someShardMap', CREDENTIAL = 'someCred'", new ParserErrorInfo(126, "SQL46010", "CREDENTIAL")); + + // ALTER with unsupported option instead of LOCATION + // + ParserTestUtils.ErrorTestFabricDW("ALTER EXTERNAL DATA SOURCE eds1 SET RESOURCE_MANAGER_LOCATION = 'ip_address:port'", new ParserErrorInfo(36, "SQL46005", "LOCATION", "RESOURCE_MANAGER_LOCATION")); + ParserTestUtils.ErrorTestFabricDW("ALTER EXTERNAL DATA SOURCE eds1 SET DATABASE_NAME = 'someDb'", new ParserErrorInfo(36, "SQL46005", "LOCATION", "DATABASE_NAME")); + ParserTestUtils.ErrorTestFabricDW("ALTER EXTERNAL DATA SOURCE eds1 SET CONNECTION_OPTIONS = 'some_options'", new ParserErrorInfo(36, "SQL46005", "LOCATION", "CONNECTION_OPTIONS")); + ParserTestUtils.ErrorTestFabricDW("ALTER EXTERNAL DATA SOURCE eds1 SET SHARD_MAP_NAME = 'someShardMap'", new ParserErrorInfo(36, "SQL46005", "LOCATION", "SHARD_MAP_NAME")); + + // ALTER with unsupported option where the value is not a string literal + // + ParserTestUtils.ErrorTestFabricDW("ALTER EXTERNAL DATA SOURCE eds1 SET PUSHDOWN = ON", new ParserErrorInfo(47, "SQL46010", "ON")); + ParserTestUtils.ErrorTestFabricDW("ALTER EXTERNAL DATA SOURCE eds1 SET CREDENTIAL = cred1", new ParserErrorInfo(49, "SQL46010", "cred1")); + + // ALTER with LOCATION followed by extra unsupported options (comma not expected after LOCATION value) + // + ParserTestUtils.ErrorTestFabricDW("ALTER EXTERNAL DATA SOURCE eds1 SET LOCATION = 'sqlserver://10.10.10.10:1433', PUSHDOWN = ON", new ParserErrorInfo(77, "SQL46010", ",")); + ParserTestUtils.ErrorTestFabricDW("ALTER EXTERNAL DATA SOURCE eds1 SET LOCATION = 'sqlserver://10.10.10.10:1433', CREDENTIAL = cred1", new ParserErrorInfo(77, "SQL46010", ",")); + ParserTestUtils.ErrorTestFabricDW("ALTER EXTERNAL DATA SOURCE eds1 SET LOCATION = 'sqlserver://10.10.10.10:1433', DATABASE_NAME = 'someDb'", new ParserErrorInfo(77, "SQL46010", ",")); } diff --git a/Test/SqlDom/TestScripts/CreateAlterExternalDataSourceTestsFabricDW.sql b/Test/SqlDom/TestScripts/CreateAlterExternalDataSourceTestsFabricDW.sql new file mode 100644 index 0000000..fb3aef4 --- /dev/null +++ b/Test/SqlDom/TestScripts/CreateAlterExternalDataSourceTestsFabricDW.sql @@ -0,0 +1,11 @@ +CREATE EXTERNAL DATA SOURCE MyDataSource + WITH ( + LOCATION = 'sqlserver://10.10.10.10:1433' + ); + +CREATE EXTERNAL DATA SOURCE BlobSource + WITH ( + LOCATION = 'wasbs://container@account.blob.core.windows.net' + ); + +ALTER EXTERNAL DATA SOURCE MyDataSource SET LOCATION = 'sqlserver://192.168.1.100:1433'; \ No newline at end of file From 6681bbf701b2da90e1b59ad8828c9fd29a731b85 Mon Sep 17 00:00:00 2001 From: "Sai Avishkar Sreerama (from Dev Box)" Date: Thu, 5 Mar 2026 13:49:56 -0600 Subject: [PATCH 5/5] Adding release notes for 170.179.0 --- release-notes/170/170.179.0.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 release-notes/170/170.179.0.md diff --git a/release-notes/170/170.179.0.md b/release-notes/170/170.179.0.md new file mode 100644 index 0000000..42788f0 --- /dev/null +++ b/release-notes/170/170.179.0.md @@ -0,0 +1,28 @@ +# Release Notes + +## Microsoft.SqlServer.TransactSql.ScriptDom 170.179.0 +This update brings the following changes over the previous release: + +### Target Platform Support + +* .NET Framework 4.7.2 (Windows x86, Windows x64) +* .NET 8 (Windows x86, Windows x64, Linux, macOS) +* .NET Standard 2.0+ (Windows x86, Windows x64, Linux, macOS) + +### Dependencies +* Updates .NET SDK to latest patch version 8.0.418 + +#### .NET Framework +#### .NET Core + +### New Features +* Adds JSON_OBJECTAGG support for OVER clause (windowed aggregate usage). +* Simplifies CREATE/ALTER EXTERNAL DATA SOURCE syntax for Fabric DW to only support the LOCATION parameter, removes support for TYPE, PUSHDOWN, and other literal/identifier options in the Fabric DW parser. + +### Fixed +* Fixes VECTOR_SEARCH TOP_N parameter to be optional. +* Fixes VECTOR_SEARCH to support WITH (FORCE_ANN_ONLY) query hint. + +### Changes + +### Known Issues