From 9ad41ef3845243b121a8a957dbea036ed20f5787 Mon Sep 17 00:00:00 2001 From: Shiv Prashant Sood Date: Tue, 10 Feb 2026 19:51:13 +0000 Subject: [PATCH 1/5] Merged PR 1952906: JSON_ARRAYAGG support for OVER clause MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Pull Request Template for ScriptDom ## Description `JSON_ARRAYAGG` 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_ARRAYAGG` in the TSql170 parser and script generator. **SQL syntax now supported:** ```sql SELECT JSON_ARRAYAGG(name) OVER (PARTITION BY dept) FROM employees; SELECT JSON_ARRAYAGG(name ABSENT ON NULL) OVER (PARTITION BY dept) FROM employees; SELECT JSON_ARRAYAGG(name NULL ON NULL) OVER (PARTITION BY dept) FROM employees; SELECT JSON_ARRAYAGG(name ORDER BY name NULL ON NULL RETURNING JSON) OVER (PARTITION BY dept) FROM employees; ``` ## Code Change - [x] The [Common checklist](https://msdata.visualstudio.com/SQLToolsAndLibraries/_git/Common?path=/Templates/PR%20Checklist%20for%20SQLToolsAndLibraries.md&version=GBmain&_a=preview) has been reviewed and followed - [x] Code changes are accompanied by appropriate unit tests - [ ] Identified and included SMEs needed to review code changes - [X] Follow the [steps](https://msdata.visualstudio.com/SQLToolsAndLibraries/_wiki/wikis/SQLToolsAndLibraries.wiki/33838/Adding-or-Extending-TSql-support-in-Sql-Dom?anchor=make-the-changes-in) here to make changes in the code ## Testing - [X] Follow the [steps] All 22 TSql170Syntax tests pass across both net8.0 and net472 targets, covering all compat levels (80-170). ## Documentation - [ ] Update relevant documentation in the [wiki](https://dev.azure.com/msdata/SQLToolsAndLibraries/_wiki/wikis/SQLToolsAndLibraries.wiki) and the README.md file ## 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_ARRAYAGG as a windowed aggregate. TSql80 parser produces 10 errors (vs 9 for other old parsers) because TSql80 does not support the OVER keyword at all. Related work items: #5003355 --- SqlScriptDom/Parser/TSql/TSql170.g | 7 +++++++ .../SqlScriptGeneratorVisitor.FunctionCall.cs | 10 ++++++---- .../SqlDom/Baselines170/JsonArrayAggOrderBy170.sql | 14 +++++++++++++- Test/SqlDom/Only170SyntaxTests.cs | 2 +- Test/SqlDom/TestScripts/JsonArrayAggOrderBy170.sql | 14 +++++++++++++- 5 files changed, 40 insertions(+), 7 deletions(-) diff --git a/SqlScriptDom/Parser/TSql/TSql170.g b/SqlScriptDom/Parser/TSql/TSql170.g index b793bf5..f2c7dea 100644 --- a/SqlScriptDom/Parser/TSql/TSql170.g +++ b/SqlScriptDom/Parser/TSql/TSql170.g @@ -33104,6 +33104,7 @@ jsonArrayAggBuiltInFunctionCall [FunctionCall vParent] { ScalarExpression vExpression; OrderByClause vOrderByClause; + OverClause vOverClause; } : ( vExpression=expression @@ -33133,6 +33134,12 @@ jsonArrayAggBuiltInFunctionCall [FunctionCall vParent] { UpdateTokenInfo(vParent, tRParen); } + ( + vOverClause=overClauseNoOrderBy + { + vParent.OverClause = vOverClause; + } + )? ; jsonObjectBuiltInFunctionCall [FunctionCall vParent] diff --git a/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.FunctionCall.cs b/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.FunctionCall.cs index 20de09c..70d7111 100644 --- a/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.FunctionCall.cs +++ b/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.FunctionCall.cs @@ -94,6 +94,8 @@ public override void ExplicitVisit(FunctionCall node) GenerateSpace(); GenerateReturnType(node?.ReturnType); GenerateSymbol(TSqlTokenType.RightParenthesis); + // Generate OVER clause for windowed json_arrayagg + GenerateSpaceAndFragmentIfNotNull(node.OverClause); } else if (node.FunctionName.Value.ToUpper(CultureInfo.InvariantCulture) == CodeGenerationSupporter.JsonQuery) { @@ -115,10 +117,10 @@ public override void ExplicitVisit(FunctionCall node) else if (node.FunctionName.Value.ToUpper(CultureInfo.InvariantCulture) == CodeGenerationSupporter.JsonValue) { GenerateCommaSeparatedList(node.Parameters); - if (node.ReturnType?.Count > 0) //If there are return types then generate space and return type clause - { - GenerateSpace(); - GenerateReturnType(node?.ReturnType); + if (node.ReturnType?.Count > 0) //If there are return types then generate space and return type clause + { + GenerateSpace(); + GenerateReturnType(node?.ReturnType); } GenerateSymbol(TSqlTokenType.RightParenthesis); } diff --git a/Test/SqlDom/Baselines170/JsonArrayAggOrderBy170.sql b/Test/SqlDom/Baselines170/JsonArrayAggOrderBy170.sql index d2ab25f..8d8beef 100644 --- a/Test/SqlDom/Baselines170/JsonArrayAggOrderBy170.sql +++ b/Test/SqlDom/Baselines170/JsonArrayAggOrderBy170.sql @@ -16,4 +16,16 @@ FROM data; SELECT TOP (5) c.object_id, JSON_ARRAYAGG(c.name ORDER BY c.column_id) AS column_list FROM sys.columns AS c -GROUP BY c.object_id; \ No newline at end of file +GROUP BY c.object_id; + +SELECT JSON_ARRAYAGG(name) OVER (PARTITION BY dept) +FROM employees; + +SELECT JSON_ARRAYAGG(name ABSENT ON NULL) OVER (PARTITION BY dept) +FROM employees; + +SELECT JSON_ARRAYAGG(name NULL ON NULL) OVER (PARTITION BY dept) +FROM employees; + +SELECT JSON_ARRAYAGG(name ORDER BY name NULL ON NULL RETURNING JSON) OVER (PARTITION BY dept) +FROM employees; \ No newline at end of file diff --git a/Test/SqlDom/Only170SyntaxTests.cs b/Test/SqlDom/Only170SyntaxTests.cs index b4c1062..c0658a3 100644 --- a/Test/SqlDom/Only170SyntaxTests.cs +++ b/Test/SqlDom/Only170SyntaxTests.cs @@ -20,7 +20,7 @@ public partial class SqlDomTests new ParserTest170("RegexpTests170.sql", nErrors80: 0, nErrors90: 0, nErrors100: 0, nErrors110: 0, nErrors120: 0, nErrors130: 0, nErrors140: 0, nErrors150: 0, nErrors160: 0), 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: 6, nErrors90: 6, nErrors100: 6, nErrors110: 6, nErrors120: 6, nErrors130: 6, nErrors140: 6, nErrors150: 6, nErrors160: 6), + new ParserTest170("JsonArrayAggOrderBy170.sql", nErrors80: 10, nErrors90: 9, nErrors100: 9, nErrors110: 9, nErrors120: 9, nErrors130: 9, nErrors140: 9, nErrors150: 9, nErrors160: 9), 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/JsonArrayAggOrderBy170.sql b/Test/SqlDom/TestScripts/JsonArrayAggOrderBy170.sql index 094fb21..5062e67 100644 --- a/Test/SqlDom/TestScripts/JsonArrayAggOrderBy170.sql +++ b/Test/SqlDom/TestScripts/JsonArrayAggOrderBy170.sql @@ -18,4 +18,16 @@ SELECT JSON_ARRAYAGG(value ORDER BY value NULL ON NULL) FROM data; -- Real-world example with GROUP BY and system tables SELECT TOP(5) c.object_id, JSON_ARRAYAGG(c.name ORDER BY c.column_id) AS column_list FROM sys.columns AS c -GROUP BY c.object_id; \ No newline at end of file +GROUP BY c.object_id; + +-- JSON_ARRAYAGG with OVER clause (PARTITION BY) +SELECT JSON_ARRAYAGG(name) OVER (PARTITION BY dept) FROM employees; + +-- JSON_ARRAYAGG with ABSENT ON NULL and OVER clause +SELECT JSON_ARRAYAGG(name ABSENT ON NULL) OVER (PARTITION BY dept) FROM employees; + +-- JSON_ARRAYAGG with NULL ON NULL and OVER clause +SELECT JSON_ARRAYAGG(name NULL ON NULL) OVER (PARTITION BY dept) FROM employees; + +-- JSON_ARRAYAGG with ORDER BY, NULL ON NULL, RETURNING JSON, and OVER clause +SELECT JSON_ARRAYAGG(name ORDER BY name NULL ON NULL RETURNING JSON) OVER (PARTITION BY dept) FROM employees; \ No newline at end of file From 95ebf765af82aadd1e82a2b3934625755e6517cc Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Wed, 11 Feb 2026 22:55:31 +0000 Subject: [PATCH 2/5] Merged PR 1948594: Fix allocation issue in TSql80ParserBaseInternal.AddAndUpdateTokenInfo **🤖 AI-Generated Pull Request 🤖** This pull request was generated by the VS Perf Rel AI Agent. Please review this AI-generated PR with extra care! For more information, visit our [wiki](https://dev.azure.com/devdiv/DevDiv/_wiki/wikis/DevDiv.wiki/49206/). Please share feedback with [TIP Insights](mailto:tipinsights@microsoft.com) --- ## Problem High memory allocations were detected in the `TSql80ParserBaseInternal.AddAndUpdateTokenInfo` method, specifically in the overload that processes collections. The method uses a `foreach` loop to iterate over an `IList` collection, which causes the allocation of a boxed `List.Enumerator` object on every invocation. Since this is a hot path in the SQL parser (called frequently during parsing operations), these repeated allocations contribute significantly to GC pressure. Performance impact: This issue appears in 0.28% of high allocation traces and allocates 3.0 MB/sec at both the 50th and 90th percentiles. ## Solution Replaced the `foreach` loop with an index-based `for` loop in the `AddAndUpdateTokenInfo` method. The new implementation iterates using an integer index from 0 to `otherCollection.Count`, accessing each element via the indexer. This eliminates the enumerator allocation while preserving identical functionality and behavior. The change is made in `/SqlScriptDom/Parser/TSql/TSql80ParserBaseInternal.cs` at lines 292-299. **Before:** ```csharp foreach (TFragmentType item in otherCollection) { AddAndUpdateTokenInfo(node, collection, item); } ``` **After:** ```csharp for (int i = 0; i < otherCollection.Count; i++) { TFragmentType item = otherCollection[i]; AddAndUpdateTokenInfo(node, collection, item); } ``` ## Benefits - Eliminates boxing allocation of `List.Enumerator` on hot path - Reduces GC pressure and improves parser performance - Maintains identical behavior and semantics - Minimal code change with no API modifications [Best practices wiki](https://dev.azure.com/devdiv/DevDiv/_wiki/wikis/DevDiv.wiki/24181/Garbage-collection-(GC)) | [See related failure in PRISM](https://prism.vsdata.io/failure/?eventType=allocation&failureType=dualdirection&failureHash=df809ff3-2831-8a3a-b02e-ae2c306b6d5f) Fixes: [[PerfRelAgent][Allocation][{Environment}] Failure ID df809ff3-2831-8a3a-b02e-ae2c306b6d5f](https://msdata.visualstudio.com/web/wi.aspx?pcguid=8b119ea1-2e2a-4839-8db7-8c9e8d50f6fa&id=4996528)

AI-generated content may be incorrect

Co-authored-by: Naresh Joshi <nareshjo@microsoft.com> Related work items: #4996528 --- SqlScriptDom/Parser/TSql/TSql80ParserBaseInternal.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SqlScriptDom/Parser/TSql/TSql80ParserBaseInternal.cs b/SqlScriptDom/Parser/TSql/TSql80ParserBaseInternal.cs index a17fe59..4f47429 100644 --- a/SqlScriptDom/Parser/TSql/TSql80ParserBaseInternal.cs +++ b/SqlScriptDom/Parser/TSql/TSql80ParserBaseInternal.cs @@ -292,8 +292,9 @@ protected static void AddAndUpdateTokenInfo(TSqlFragment node, IL protected static void AddAndUpdateTokenInfo(TSqlFragment node, IList collection, IList otherCollection) where TFragmentType : TSqlFragment { - foreach (TFragmentType item in otherCollection) + for (int i = 0; i < otherCollection.Count; i++) { + TFragmentType item = otherCollection[i]; AddAndUpdateTokenInfo(node, collection, item); } } From f9ce42ffc44cd1c4da86cfd7fe2f4b3a2138da48 Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Thu, 12 Feb 2026 19:54:49 +0000 Subject: [PATCH 3/5] Merged PR 1881483: Add support for 3-part and 4-part identifiers in VECTOR_SEARCH TOP_N parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary This PR enables the VECTOR_SEARCH table-valued function to accept 3-part and 4-part column identifiers in the TOP_N parameter, allowing queries like: ```sql SELECT qt.qid, src.id, ann.distance FROM QueryTable qt CROSS APPLY VECTOR_SEARCH( TABLE = graphnode AS src, COLUMN = embedding, SIMILAR_TO = qt.qembedding, METRIC = 'euclidean', TOP_N = dbo.qt.top_n -- 3-part identifier now supported ) AS ann ``` Previously, the parser only accepted up to 2-part identifiers (e.g., `qt.top_n`) in the TOP_N parameter, which prevented queries using schema-qualified column references in CROSS APPLY scenarios. ## Changes Made ### Grammar Rule Update Modified the `vectorSearchColumnReferenceExpression` rule in `SqlScriptDom/Parser/TSql/TSql170.g` to allow up to 4-part identifiers instead of the previous 2-part limit. This change enables the TOP_N parameter to accept: - Simple identifiers: `@variable` or `columnName` - 2-part identifiers: `table.column` - 3-part identifiers: `schema.table.column` (the primary use case from the issue) - 4-part identifiers: `database.schema.table.column` The change was minimal—updating a single parameter from `multiPartIdentifier[2]` to `multiPartIdentifier[4]` in line 33971 of TSql170.g. This aligns with standard SQL Server column reference behavior in other contexts. ### Test Coverage Added comprehensive test coverage in `Test/SqlDom/TestScripts/VectorSearchCrossApplyTests170.sql` with a test case that validates the exact query pattern from the issue. The test includes: - A DECLARE statement setting up a VECTOR variable - A SELECT query with CROSS APPLY using VECTOR_SEARCH - The TOP_N parameter specified as a 3-part identifier (`dbo.qt.top_n`) The corresponding baseline file `Test/SqlDom/Baselines170/VectorSearchCrossApplyTests170.sql` captures the expected formatted output from the parser. Updated `Test/SqlDom/Only170SyntaxTests.cs` to register the new test case with appropriate error counts for older parser versions (TSql80 through TSql160), which still use the 2-part identifier limit and correctly report parse errors for this syntax. ## Impact This change only affects the TSql170 (SQL Server 2025) parser and is backward compatible. The AST definition already used `ScalarExpression` for the TopN member, so no AST changes were required. Older parser versions continue to enforce the 2-part identifier limit and generate expected parse errors for 3-part identifiers. The fix enables real-world scenarios where VECTOR_SEARCH is used in CROSS APPLY with outer references that require schema qualification, which is common in production databases with explicit schema naming conventions. Fixes: #4843961

AI-generated content may be incorrect

reference: https://msdata.visualstudio.com/Database%20Systems/_git/DsMainDev/commit/15b0ead69fc5a8ba9eb1d4d84735c4e9d6ab5718?_a=co... --- SqlScriptDom/Parser/TSql/TSql170.g | 2 +- Test/SqlDom/Baselines170/VectorFunctionTests170.sql | 2 +- .../Baselines170/VectorSearchCrossApplyTests170.sql | 12 ++++++++++++ Test/SqlDom/Only170SyntaxTests.cs | 1 + Test/SqlDom/TestScripts/VectorFunctionTests170.sql | 2 +- .../TestScripts/VectorSearchCrossApplyTests170.sql | 12 ++++++++++++ 6 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 Test/SqlDom/Baselines170/VectorSearchCrossApplyTests170.sql create mode 100644 Test/SqlDom/TestScripts/VectorSearchCrossApplyTests170.sql diff --git a/SqlScriptDom/Parser/TSql/TSql170.g b/SqlScriptDom/Parser/TSql/TSql170.g index f2c7dea..043108a 100644 --- a/SqlScriptDom/Parser/TSql/TSql170.g +++ b/SqlScriptDom/Parser/TSql/TSql170.g @@ -33987,7 +33987,7 @@ vectorSearchColumnReferenceExpression returns [ColumnReferenceExpression vResult MultiPartIdentifier vMultiPartIdentifier; } : - vMultiPartIdentifier=multiPartIdentifier[2] + vMultiPartIdentifier=multiPartIdentifier[4] { vResult.ColumnType = ColumnType.Regular; vResult.MultiPartIdentifier = vMultiPartIdentifier; diff --git a/Test/SqlDom/Baselines170/VectorFunctionTests170.sql b/Test/SqlDom/Baselines170/VectorFunctionTests170.sql index 9ab5f85..95e076d 100644 --- a/Test/SqlDom/Baselines170/VectorFunctionTests170.sql +++ b/Test/SqlDom/Baselines170/VectorFunctionTests170.sql @@ -45,4 +45,4 @@ WHERE outerref.id IN (SELECT src.id SIMILAR_TO = @qv, METRIC = 'cosine', TOP_N = outerref.max_results - ) AS ann); \ No newline at end of file + ) AS ann); diff --git a/Test/SqlDom/Baselines170/VectorSearchCrossApplyTests170.sql b/Test/SqlDom/Baselines170/VectorSearchCrossApplyTests170.sql new file mode 100644 index 0000000..3e715b6 --- /dev/null +++ b/Test/SqlDom/Baselines170/VectorSearchCrossApplyTests170.sql @@ -0,0 +1,12 @@ +DECLARE @qv AS VECTOR(1536); + +SELECT qt.qid, + src.id, + ann.distance +FROM QueryTable AS qt CROSS APPLY VECTOR_SEARCH( + TABLE = graphnode AS src, + COLUMN = embedding, + SIMILAR_TO = qt.qembedding, + METRIC = 'euclidean', + TOP_N = dbo.qt.top_n + ) AS ann; diff --git a/Test/SqlDom/Only170SyntaxTests.cs b/Test/SqlDom/Only170SyntaxTests.cs index c0658a3..a881c8e 100644 --- a/Test/SqlDom/Only170SyntaxTests.cs +++ b/Test/SqlDom/Only170SyntaxTests.cs @@ -32,6 +32,7 @@ public partial class SqlDomTests new ParserTest170("RegexpLikeTests170.sql", nErrors80: 15, nErrors90: 15, nErrors100: 15, nErrors110: 18, nErrors120: 18, nErrors130: 18, nErrors140: 18, nErrors150: 18, nErrors160: 18), new ParserTest170("OptimizedLockingTests170.sql", nErrors80: 2, nErrors90: 2, nErrors100: 2, nErrors110: 2, nErrors120: 2, nErrors130: 2, nErrors140: 2, nErrors150: 2, nErrors160: 2), new ParserTest170("CreateEventSessionNotLikePredicate.sql", nErrors80: 2, nErrors90: 1, nErrors100: 1, nErrors110: 1, nErrors120: 1, nErrors130: 0, nErrors140: 0, nErrors150: 0, nErrors160: 0), + new ParserTest170("VectorSearchCrossApplyTests170.sql", nErrors80: 1, nErrors90: 1, nErrors100: 1, nErrors110: 1, nErrors120: 1, nErrors130: 1, nErrors140: 1, nErrors150: 1, nErrors160: 1), // Complex query with VECTOR types - parses syntactically in all versions (optimization fix), but VECTOR type only valid in TSql170 new ParserTest170("ComplexQueryTests170.sql"), // Comment preservation tests - basic SQL syntax works in all versions diff --git a/Test/SqlDom/TestScripts/VectorFunctionTests170.sql b/Test/SqlDom/TestScripts/VectorFunctionTests170.sql index a06f083..76795ae 100644 --- a/Test/SqlDom/TestScripts/VectorFunctionTests170.sql +++ b/Test/SqlDom/TestScripts/VectorFunctionTests170.sql @@ -52,4 +52,4 @@ WHERE outerref.id IN ( METRIC = 'cosine', TOP_N = outerref.max_results ) AS ann -) \ No newline at end of file +) diff --git a/Test/SqlDom/TestScripts/VectorSearchCrossApplyTests170.sql b/Test/SqlDom/TestScripts/VectorSearchCrossApplyTests170.sql new file mode 100644 index 0000000..71dfcc5 --- /dev/null +++ b/Test/SqlDom/TestScripts/VectorSearchCrossApplyTests170.sql @@ -0,0 +1,12 @@ +DECLARE @qv VECTOR(1536); + +SELECT qt.qid, src.id, ann.distance +FROM QueryTable qt +CROSS APPLY + VECTOR_SEARCH( + TABLE = graphnode AS src, + COLUMN = embedding, + SIMILAR_TO = qt.qembedding, + METRIC = 'euclidean', + TOP_N = dbo.qt.top_n + ) AS ann From b727d43f545af0f8572360c55a547dad4cb11f89 Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Thu, 12 Feb 2026 21:26:01 +0000 Subject: [PATCH 4/5] Merged PR 1881565: ## Fix VECTOR_SEARCH SIMILAR_TO Parameter to Reject Subqueries ### Summary The VECTOR_SEARCH function in the TSql170 parser was incorrectly allowing subqueries in the SIMILAR_TO parameter. This fix adds validation to properly reject subqueries and throw an appropriate parse error, aligning with SQL Server 2025 implementation requirements. ### Problem The SIMILAR_TO parameter was accepting any scalar expression, including subqueries wrapped in parentheses. This allowed invalid syntax like: ```sql SELECT * FROM VECTOR_SEARCH( TABLE = graphnode, COLUMN = embedding, SIMILAR_TO = (SELECT TOP 1 embedding FROM GTQuery), -- Should error but didn't METRIC = 'euclidean', TOP_N = 20 ) AS ann ``` This syntax should be rejected because subqueries are not supported in the SIMILAR_TO parameter, similar to how they are restricted in other contexts. ### Solution Added validation logic in the `vectorSearchTableReference` grammar rule (`SqlScriptDom/Parser/TSql/TSql170.g`) that checks if the SIMILAR_TO expression is a `ScalarSubquery` type. When detected, the parser now throws a SQL46098 error with the message "Subqueries are not allowed in this context. Only scalar expressions are allowed." The validation follows the same pattern used elsewhere in the grammar for similar restrictions and is placed immediately after matching the SIMILAR_TO identifier, before assigning the expression to the result object. ### Testing Added a comprehensive error test case in `Test/SqlDom/ParserErrorsTests.cs` within the existing `VectorSearchErrorTest170` method. The test verifies that: - Subqueries in the SIMILAR_TO parameter are properly rejected - The correct error code (SQL46098) is generated - The error position is accurately reported Valid syntax continues to work correctly, including: - Variables: `SIMILAR_TO = @qv` - Column references: `SIMILAR_TO = outerref.vector_col` - Other scalar expressions that are not subqueries ### Impact - **All 558 existing tests pass** with no regressions - **Minimal change**: Only 12 lines added across 2 files - **Consistent behavior**: Uses the same error code and message pattern as other subquery restrictions in the parser - **Backward compatible**: Only rejects previously invalid syntax that should not have been allowed Fixes: #4844065

AI-generated content may be incorrect

Co-authored-by: Leila Lali <llali@microsoft.com> Related work items: #4844065 --- SqlScriptDom/Parser/TSql/TSql170.g | 7 +++++++ Test/SqlDom/ParserErrorsTests.cs | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/SqlScriptDom/Parser/TSql/TSql170.g b/SqlScriptDom/Parser/TSql/TSql170.g index 043108a..85ec9cf 100644 --- a/SqlScriptDom/Parser/TSql/TSql170.g +++ b/SqlScriptDom/Parser/TSql/TSql170.g @@ -19306,6 +19306,13 @@ vectorSearchTableReference returns [VectorSearchTableReference vResult = Fragmen Comma tSimilarTo:Identifier EqualsSign vSimilarTo = expression { Match(tSimilarTo, CodeGenerationSupporter.SimilarTo); + + // Validate that SIMILAR_TO does not contain a subquery + if (vSimilarTo is ScalarSubquery) + { + ThrowParseErrorException("SQL46098", vSimilarTo, TSqlParserResource.SQL46098Message); + } + vResult.SimilarTo = vSimilarTo; } Comma tMetric:Identifier EqualsSign vMetric = stringLiteral diff --git a/Test/SqlDom/ParserErrorsTests.cs b/Test/SqlDom/ParserErrorsTests.cs index 2204c30..5a89afe 100644 --- a/Test/SqlDom/ParserErrorsTests.cs +++ b/Test/SqlDom/ParserErrorsTests.cs @@ -7544,6 +7544,11 @@ public void VectorSearchErrorTest170() ParserTestUtils.ErrorTest170( "SELECT * FROM VECTOR_SEARCH('tbl1', 'col1', 'query_vector', 'dot', 5)", new ParserErrorInfo(28, "SQL46010", "'tbl1'")); + + // Subquery not allowed in SIMILAR_TO parameter + ParserTestUtils.ErrorTest170( + "SELECT * FROM VECTOR_SEARCH(TABLE = graphnode, COLUMN = embedding, SIMILAR_TO = (SELECT TOP 1 embedding FROM GTQuery), METRIC = 'euclidean', TOP_N = 20) AS ann", + new ParserErrorInfo(80, "SQL46098")); } /// From 451d553147c4433aba807379b8f96e60ec29387c Mon Sep 17 00:00:00 2001 From: Leila Lali Date: Fri, 13 Feb 2026 18:27:16 +0000 Subject: [PATCH 5/5] Merged PR 1959169: Adding release notes for 170.168.0 # Pull Request Template for ScriptDom ## Description Please provide a detailed description, include the link to the design specification or SQL feature document for the new TSQL syntaxes. Make sure to add links to the Github or DevDiv issue Before submitting your pull request, please ensure you have completed the following: ## Code Change - [ ] The [Common checklist](https://msdata.visualstudio.com/SQLToolsAndLibraries/_git/Common?path=/Templates/PR%20Checklist%20for%20SQLToolsAndLibraries.md&version=GBmain&_a=preview) has been reviewed and followed - [ ] Code changes are accompanied by appropriate unit tests - [ ] Identified and included SMEs needed to review code changes - [ ] Follow the [steps](https://msdata.visualstudio.com/SQLToolsAndLibraries/_wiki/wikis/SQLToolsAndLibraries.wiki/33838/Adding-or-Extending-TSql-support-in-Sql-Dom?anchor=make-the-changes-in) here to make changes in the code ## Testing - [ ] Follow the [steps](https://msdata.visualstudio.com/SQLToolsAndLibraries/_wiki/wikis/SQLToolsAndLibraries.wiki/33838/Adding-or-Extending-TSql-support-in-Sql-Dom?anchor=to-extend-the-tests-do-the-following%3A) here to add new tests for your feature ## Documentation - [ ] Update relevant documentation in the [wiki](https://dev.azure.com/msdata/SQLToolsAndLibraries/_wiki/wikis/SQLToolsAndLibraries.wiki) and the README.md file ## Additional Information Please provide any additional information that might be helpful for the reviewers Adding release notes for 170.168.0 --- release-notes/170/170.168.0.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 release-notes/170/170.168.0.md diff --git a/release-notes/170/170.168.0.md b/release-notes/170/170.168.0.md new file mode 100644 index 0000000..1f4253b --- /dev/null +++ b/release-notes/170/170.168.0.md @@ -0,0 +1,26 @@ +# Release Notes + +## Microsoft.SqlServer.TransactSql.ScriptDom 170.168.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.415 + +#### .NET Framework +#### .NET Core + +### New Features +* Adds formatter option for preserving comments + +### Fixed +* Fixes parsing VECTOR_SEARCH SIMILAR_TO parameter to reject subquery. +* Fixes parsing VECTOR_SEARCH TOP_N parameter to support multi-part indentifier. +### Changes + +### Known Issues