diff --git a/SqlScriptDom/Parser/TSql/Ast.xml b/SqlScriptDom/Parser/TSql/Ast.xml index 1976cb1..4c5a4f4 100644 --- a/SqlScriptDom/Parser/TSql/Ast.xml +++ b/SqlScriptDom/Parser/TSql/Ast.xml @@ -3630,10 +3630,12 @@ + + diff --git a/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs b/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs index 4c8a97d..d10f1b8 100644 --- a/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs +++ b/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs @@ -143,6 +143,8 @@ internal static class CodeGenerationSupporter internal const string Atomic = "ATOMIC"; internal const string Append = "APPEND"; internal const string AppendOnly = "APPEND_ONLY"; + internal const string Approximate = "APPROXIMATE"; + internal const string Approx = "APPROX"; internal const string Avg = "AVG"; internal const string Attested = "ATTESTED"; internal const string AuditGuid = "AUDIT_GUID"; diff --git a/SqlScriptDom/Parser/TSql/TSql170.g b/SqlScriptDom/Parser/TSql/TSql170.g index a8a1b49..88ce660 100644 --- a/SqlScriptDom/Parser/TSql/TSql170.g +++ b/SqlScriptDom/Parser/TSql/TSql170.g @@ -18389,33 +18389,99 @@ offsetClause returns [OffsetClause vResult = this.FragmentFactory.CreateFragment ScalarExpression vExpression; } : - tOffset:Identifier vExpression = expression - { - Match(tOffset, CodeGenerationSupporter.Offset); - UpdateTokenInfo(vResult,tOffset); - vResult.OffsetExpression = vExpression; - } - tOffsetRowOrRows:Identifier - { - Match(tOffsetRowOrRows, CodeGenerationSupporter.Row, CodeGenerationSupporter.Rows); - UpdateTokenInfo(vResult,tOffsetRowOrRows); - } ( - options {greedy=true;} : //Greedy due to conflict with FETCH statements - tFetch:Fetch tFirstOrNext:Identifier vExpression = expression + // Pattern 1: OFFSET with optional FETCH + tOffset:Identifier vExpression = expression + { + Match(tOffset, CodeGenerationSupporter.Offset); + UpdateTokenInfo(vResult,tOffset); + vResult.OffsetExpression = vExpression; + } + tOffsetRowOrRows:Identifier + { + Match(tOffsetRowOrRows, CodeGenerationSupporter.Row, CodeGenerationSupporter.Rows); + UpdateTokenInfo(vResult,tOffsetRowOrRows); + } + ( + options {greedy=true;} : //Greedy due to conflict with FETCH statements + tFetch:Fetch + tFirstOrNext:Identifier + { + // Check if this is APPROXIMATE/APPROX before FIRST/NEXT + if (TryMatch(tFirstOrNext, CodeGenerationSupporter.Approximate) || + TryMatch(tFirstOrNext, CodeGenerationSupporter.Approx)) + { + vResult.WithApproximate = true; + UpdateTokenInfo(vResult,tFirstOrNext); + + // Consume the actual FIRST/NEXT token + tFirstOrNext = LT(1); + consume(); + Match(tFirstOrNext, CodeGenerationSupporter.First, CodeGenerationSupporter.Next); + UpdateTokenInfo(vResult,tFirstOrNext); + } + else + { + // This is the FIRST/NEXT token directly + Match(tFirstOrNext, CodeGenerationSupporter.First, CodeGenerationSupporter.Next); + UpdateTokenInfo(vResult,tFirstOrNext); + } + } + vExpression = expression + { + vResult.FetchExpression = vExpression; + } + tFetchRowOrRows:Identifier tOnly:Identifier + { + Match(tFetchRowOrRows, CodeGenerationSupporter.Row, CodeGenerationSupporter.Rows); + Match(tOnly, CodeGenerationSupporter.Only); + UpdateTokenInfo(vResult, tOnly); + + // Validate: OFFSET cannot be used with FETCH APPROXIMATE + ValidateFetchApproximate(vResult); + } + )? + | + // Pattern 2: Standalone FETCH (APPROXIMATE only in TSql170+) + tFetch2:Fetch + { + UpdateTokenInfo(vResult,tFetch2); + } + tFirstOrNext2:Identifier + { + // Check if this is APPROXIMATE/APPROX before FIRST/NEXT + if (TryMatch(tFirstOrNext2, CodeGenerationSupporter.Approximate) || + TryMatch(tFirstOrNext2, CodeGenerationSupporter.Approx)) + { + vResult.WithApproximate = true; + UpdateTokenInfo(vResult,tFirstOrNext2); + + // Consume the actual FIRST/NEXT token + tFirstOrNext2 = LT(1); + consume(); + Match(tFirstOrNext2, CodeGenerationSupporter.First, CodeGenerationSupporter.Next); + UpdateTokenInfo(vResult,tFirstOrNext2); + } + else + { + // This is the FIRST/NEXT token directly + Match(tFirstOrNext2, CodeGenerationSupporter.First, CodeGenerationSupporter.Next); + UpdateTokenInfo(vResult,tFirstOrNext2); + } + } + vExpression = expression { - UpdateTokenInfo(vResult,tFetch); - Match(tFirstOrNext, CodeGenerationSupporter.First, CodeGenerationSupporter.Next); - UpdateTokenInfo(vResult,tFirstOrNext); vResult.FetchExpression = vExpression; } - tFetchRowOrRows:Identifier tOnly:Identifier + tFetchRowOrRows2:Identifier tOnly2:Identifier { - Match(tFetchRowOrRows, CodeGenerationSupporter.Row, CodeGenerationSupporter.Rows); - Match(tOnly, CodeGenerationSupporter.Only); - UpdateTokenInfo(vResult, tOnly); + Match(tFetchRowOrRows2, CodeGenerationSupporter.Row, CodeGenerationSupporter.Rows); + Match(tOnly2, CodeGenerationSupporter.Only); + UpdateTokenInfo(vResult, tOnly2); + + // No validation needed for standalone FETCH APPROXIMATE (no OFFSET present) } - )? + ) ; // This rule corresponds to topn in Sql Server grammar. @@ -18447,9 +18513,18 @@ topRowFilter returns [TopRowFilter vResult = this.FragmentFactory.CreateFragment ( With tId:Identifier { - Match(tId,CodeGenerationSupporter.Ties); - UpdateTokenInfo(vResult,tId); - vResult.WithTies = true; + if (TryMatch(tId, CodeGenerationSupporter.Approximate) || + TryMatch(tId, CodeGenerationSupporter.Approx)) + { + UpdateTokenInfo(vResult,tId); + vResult.WithApproximate = true; + } + else + { + Match(tId,CodeGenerationSupporter.Ties); + UpdateTokenInfo(vResult,tId); + vResult.WithTies = true; + } } )? ; diff --git a/SqlScriptDom/Parser/TSql/TSql170ParserBaseInternal.cs b/SqlScriptDom/Parser/TSql/TSql170ParserBaseInternal.cs index 9b5146b..be2cfa9 100644 --- a/SqlScriptDom/Parser/TSql/TSql170ParserBaseInternal.cs +++ b/SqlScriptDom/Parser/TSql/TSql170ParserBaseInternal.cs @@ -109,5 +109,21 @@ protected bool ContainsVectorInLookahead() return false; } + + /// + /// Validates that OFFSET is not used with FETCH APPROXIMATE. + /// SQL Server does not support combining OFFSET with FETCH APPROXIMATE. + /// + /// The offset clause to validate. + protected void ValidateFetchApproximate(OffsetClause offsetClause) + { + if (offsetClause != null && + offsetClause.WithApproximate && + offsetClause.OffsetExpression != null) + { + ThrowParseErrorException("SQL46145", offsetClause, + TSqlParserResource.SQL46145Message); + } + } } } diff --git a/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/ScriptWriter.cs b/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/ScriptWriter.cs index 85d2fac..e1ceb0e 100644 --- a/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/ScriptWriter.cs +++ b/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/ScriptWriter.cs @@ -321,7 +321,7 @@ private List Align() } // generate aligned token stream - List tokens = new List(); + List tokens = new List(_scriptWriterElements.Count); Int32 offset = 0; for (Int32 index = 0; index < _scriptWriterElements.Count; ++index) diff --git a/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.OffsetClause.cs b/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.OffsetClause.cs index 104be06..7c4a371 100644 --- a/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.OffsetClause.cs +++ b/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.OffsetClause.cs @@ -14,13 +14,27 @@ public override void ExplicitVisit(OffsetClause node) AlignmentPoint start = new AlignmentPoint(); MarkAndPushAlignmentPoint(start); - GenerateIdentifier(CodeGenerationSupporter.Offset); - GenerateSpaceAndFragmentIfNotNull(node.OffsetExpression); - GenerateSpaceAndIdentifier(CodeGenerationSupporter.Rows); + if (node.OffsetExpression != null) + { + GenerateIdentifier(CodeGenerationSupporter.Offset); + GenerateSpaceAndFragmentIfNotNull(node.OffsetExpression); + GenerateSpaceAndIdentifier(CodeGenerationSupporter.Rows); + } if (node.FetchExpression != null) { - GenerateSpaceAndKeyword(TSqlTokenType.Fetch); + if (node.OffsetExpression != null) + { + GenerateSpace(); + } + + GenerateKeyword(TSqlTokenType.Fetch); + + if (node.WithApproximate) + { + GenerateSpaceAndIdentifier(CodeGenerationSupporter.Approximate); + } + GenerateSpaceAndIdentifier(CodeGenerationSupporter.Next); GenerateSpaceAndFragmentIfNotNull(node.FetchExpression); GenerateSpaceAndIdentifier(CodeGenerationSupporter.Rows); diff --git a/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.TopRowFilter.cs b/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.TopRowFilter.cs index b8b9d8d..fcead26 100644 --- a/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.TopRowFilter.cs +++ b/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.TopRowFilter.cs @@ -19,10 +19,19 @@ public override void ExplicitVisit(TopRowFilter node) GenerateSpaceAndKeyword(TSqlTokenType.Percent); } - if (node.WithTies) + if (node.WithTies || node.WithApproximate) { GenerateSpaceAndKeyword(TSqlTokenType.With); - GenerateSpaceAndIdentifier(CodeGenerationSupporter.Ties); + + if (node.WithApproximate) + { + GenerateSpaceAndIdentifier(CodeGenerationSupporter.Approximate); + } + + if (node.WithTies) + { + GenerateSpaceAndIdentifier(CodeGenerationSupporter.Ties); + } } } } diff --git a/SqlScriptDom/TSqlParserResource.Designer.cs b/SqlScriptDom/TSqlParserResource.Designer.cs index 5cc8c0a..c189e19 100644 --- a/SqlScriptDom/TSqlParserResource.Designer.cs +++ b/SqlScriptDom/TSqlParserResource.Designer.cs @@ -1329,6 +1329,15 @@ internal static string SQL46144Message { } } + /// + /// Looks up a localized string similar to OFFSET clause cannot be used with FETCH APPROXIMATE. Use FETCH APPROXIMATE without OFFSET.. + /// + internal static string SQL46145Message { + get { + return ResourceManager.GetString("SQL46145Message", resourceCulture); + } + } + /// /// Looks up a localized string similar to User-defined. /// diff --git a/SqlScriptDom/TSqlParserResource.resx b/SqlScriptDom/TSqlParserResource.resx index c6d7213..49274a4 100644 --- a/SqlScriptDom/TSqlParserResource.resx +++ b/SqlScriptDom/TSqlParserResource.resx @@ -631,4 +631,7 @@ Required parameter {0} must be provided. + + OFFSET clause cannot be used with FETCH APPROXIMATE. Use FETCH APPROXIMATE without OFFSET. + \ No newline at end of file diff --git a/Test/SqlDom/Baselines170/FetchApproximateTests170.sql b/Test/SqlDom/Baselines170/FetchApproximateTests170.sql new file mode 100644 index 0000000..b29e66d --- /dev/null +++ b/Test/SqlDom/Baselines170/FetchApproximateTests170.sql @@ -0,0 +1,34 @@ +SELECT OrderID, + CustomerName +FROM Orders +ORDER BY OrderDate +FETCH APPROXIMATE NEXT 20 ROWS ONLY; + +SELECT OrderID, + CustomerName +FROM Orders +ORDER BY OrderDate +FETCH APPROXIMATE NEXT 20 ROWS ONLY; + +SELECT ProductID, + ProductName +FROM Products +ORDER BY Price +FETCH APPROXIMATE NEXT 15 ROWS ONLY; + +DECLARE @take AS INT = 50; + +SELECT OrderID, + CustomerName, + OrderDate +FROM Orders +ORDER BY OrderDate +FETCH APPROXIMATE NEXT @take ROWS ONLY; + +SELECT TOP 100 * +FROM (SELECT OrderID, + CustomerName + FROM Orders + ORDER BY OrderDate + FETCH APPROXIMATE NEXT 1000 ROWS ONLY) AS RecentOrders +ORDER BY OrderID; diff --git a/Test/SqlDom/Baselines170/TopWithApproximateTests170.sql b/Test/SqlDom/Baselines170/TopWithApproximateTests170.sql new file mode 100644 index 0000000..2c4d00e --- /dev/null +++ b/Test/SqlDom/Baselines170/TopWithApproximateTests170.sql @@ -0,0 +1,27 @@ +SELECT TOP 10 WITH APPROXIMATE * +FROM Orders +ORDER BY OrderDate; + +SELECT TOP 10 WITH APPROXIMATE * +FROM Orders +ORDER BY OrderDate; + +SELECT TOP (10) WITH APPROXIMATE * +FROM Orders +ORDER BY OrderDate; + +SELECT TOP (LEN(DB_NAME())) WITH APPROXIMATE * +FROM Orders +ORDER BY OrderDate; + +SELECT TOP 5 WITH APPROXIMATE OrderID, + CustomerName, + OrderDate +FROM Orders +ORDER BY OrderDate DESC; + +DECLARE @top AS INT = 20; + +SELECT TOP (@top) WITH APPROXIMATE * +FROM Products +ORDER BY Price; diff --git a/Test/SqlDom/Baselines170/VectorSearchApproximateTests170.sql b/Test/SqlDom/Baselines170/VectorSearchApproximateTests170.sql new file mode 100644 index 0000000..958e8a4 --- /dev/null +++ b/Test/SqlDom/Baselines170/VectorSearchApproximateTests170.sql @@ -0,0 +1,55 @@ +SELECT TOP 10 WITH APPROXIMATE 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 = 10 + ) AS ann +ORDER BY ann.distance; + + +GO +SELECT TOP 10 WITH APPROXIMATE 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 = 10 + ) AS ann +ORDER BY ann.distance; + + +GO +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 = 10 + ) AS ann +ORDER BY ann.distance +FETCH APPROXIMATE NEXT 10 ROWS ONLY; + + +GO +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 = 10 + ) AS ann +ORDER BY ann.distance +FETCH APPROXIMATE NEXT 10 ROWS ONLY; \ No newline at end of file diff --git a/Test/SqlDom/Only170SyntaxTests.cs b/Test/SqlDom/Only170SyntaxTests.cs index 12478fd..f987f4a 100644 --- a/Test/SqlDom/Only170SyntaxTests.cs +++ b/Test/SqlDom/Only170SyntaxTests.cs @@ -30,6 +30,9 @@ public partial class SqlDomTests new ParserTest170("DropExternalModelStatementTests170.sql", nErrors80: 1, nErrors90: 1, nErrors100: 1, nErrors110: 1, nErrors120: 1, nErrors130: 1, nErrors140: 1, nErrors150: 1, nErrors160: 1), new ParserTest170("VectorFunctionTests170.sql", nErrors80: 1, nErrors90: 1, nErrors100: 3, nErrors110: 3, nErrors120: 3, nErrors130: 3, nErrors140: 3, nErrors150: 3, nErrors160: 3), new ParserTest170("SecurityStatementExternalModelTests170.sql", nErrors80: 2, nErrors90: 17, nErrors100: 17, nErrors110: 17, nErrors120: 17, nErrors130: 17, nErrors140: 17, nErrors150: 17, nErrors160: 17), + new ParserTest170("TopWithApproximateTests170.sql", nErrors80: 6, nErrors90: 6, nErrors100: 6, nErrors110: 6, nErrors120: 6, nErrors130: 6, nErrors140: 6, nErrors150: 6, nErrors160: 6), + new ParserTest170("FetchApproximateTests170.sql"), + new ParserTest170("VectorSearchApproximateTests170.sql"), new ParserTest170("VectorTypeSecondParameterTests170.sql", nErrors80: 2, nErrors90: 2, nErrors100: 2, nErrors110: 2, nErrors120: 2, nErrors130: 2, nErrors140: 2, nErrors150: 2, nErrors160: 2), 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), diff --git a/Test/SqlDom/ParserErrorsTests.cs b/Test/SqlDom/ParserErrorsTests.cs index 34b0a92..636c526 100644 --- a/Test/SqlDom/ParserErrorsTests.cs +++ b/Test/SqlDom/ParserErrorsTests.cs @@ -7832,5 +7832,50 @@ public void AiTranslateNegativeTestsFabricDw() "SELECT AI_TRANSLATE('1', '2', '3')", new ParserErrorInfo(28, "SQL46010", ",")); } + + /// + /// Negative test for OFFSET with FETCH APPROXIMATE - SQL46145 error + /// OFFSET clause cannot be used with FETCH APPROXIMATE (introduced in SQL Server 2025) + /// + [TestMethod] + [Priority(0)] + [SqlStudioTestCategory(Category.UnitTest)] + public void OffsetWithFetchApproximateError() + { + // OFFSET + FETCH APPROXIMATE is invalid + ParserTestUtils.ErrorTest170( + "SELECT * FROM MyTable ORDER BY id OFFSET 5 ROWS FETCH APPROXIMATE NEXT 10 ROWS ONLY;", + new ParserErrorInfo(34, "SQL46145")); + } + + /// + /// Negative test for combining TIES with APPROXIMATE - syntax error + /// APPROXIMATE and TIES are mutually exclusive (introduced in SQL Server 2025) + /// + [TestMethod] + [Priority(0)] + [SqlStudioTestCategory(Category.UnitTest)] + public void TopWithTiesAndApproximateError() + { + // Cannot use both TIES and APPROXIMATE together + ParserTestUtils.ErrorTest170( + "SELECT TOP 10 WITH TIES WITH APPROXIMATE * FROM MyTable ORDER BY id;", + new ParserErrorInfo(24, "SQL46010", "WITH")); + } + + /// + /// Negative test for OFFSET with FETCH APPROX (abbreviated) - SQL46145 error + /// Verifies that abbreviated APPROX keyword also triggers validation + /// + [TestMethod] + [Priority(0)] + [SqlStudioTestCategory(Category.UnitTest)] + public void OffsetWithFetchApproxError() + { + // OFFSET + FETCH APPROX (abbreviated) is invalid + ParserTestUtils.ErrorTest170( + "SELECT * FROM MyTable ORDER BY id OFFSET 10 ROWS FETCH APPROX NEXT 5 ROWS ONLY;", + new ParserErrorInfo(34, "SQL46145")); + } } } diff --git a/Test/SqlDom/TestScripts/FetchApproximateTests170.sql b/Test/SqlDom/TestScripts/FetchApproximateTests170.sql new file mode 100644 index 0000000..e9b5405 --- /dev/null +++ b/Test/SqlDom/TestScripts/FetchApproximateTests170.sql @@ -0,0 +1,35 @@ +-- Test 1: FETCH APPROXIMATE NEXT (full keyword) - NO OFFSET +SELECT OrderID, CustomerName +FROM Orders +ORDER BY OrderDate +FETCH APPROXIMATE NEXT 20 ROWS ONLY; + +-- Test 2: FETCH APPROX NEXT (abbreviated keyword) - NO OFFSET +SELECT OrderID, CustomerName +FROM Orders +ORDER BY OrderDate +FETCH APPROX NEXT 20 ROWS ONLY; + +-- Test 3: FETCH APPROXIMATE FIRST (with FIRST instead of NEXT) - NO OFFSET +SELECT ProductID, ProductName +FROM Products +ORDER BY Price +FETCH APPROXIMATE FIRST 15 ROWS ONLY; + +-- Test 4: FETCH APPROXIMATE with parameter - NO OFFSET +DECLARE @take INT = 50; + +SELECT OrderID, CustomerName, OrderDate +FROM Orders +ORDER BY OrderDate +FETCH APPROXIMATE NEXT @take ROWS ONLY; + +-- Test 5: FETCH APPROXIMATE with complex expression - NO OFFSET +SELECT TOP 100 * +FROM ( + SELECT OrderID, CustomerName + FROM Orders + ORDER BY OrderDate + FETCH APPROXIMATE NEXT 1000 ROWS ONLY +) AS RecentOrders +ORDER BY OrderID; diff --git a/Test/SqlDom/TestScripts/TopWithApproximateTests170.sql b/Test/SqlDom/TestScripts/TopWithApproximateTests170.sql new file mode 100644 index 0000000..64bcd96 --- /dev/null +++ b/Test/SqlDom/TestScripts/TopWithApproximateTests170.sql @@ -0,0 +1,30 @@ +-- Test 1: TOP WITH APPROXIMATE (full keyword) +SELECT TOP 10 WITH APPROXIMATE * +FROM Orders +ORDER BY OrderDate; + +-- Test 2: TOP WITH APPROX (abbreviated form) +SELECT TOP 10 WITH APPROX * +FROM Orders +ORDER BY OrderDate; + +-- Test 3: TOP(expression) WITH APPROXIMATE +SELECT TOP(10) WITH APPROXIMATE * +FROM Orders +ORDER BY OrderDate; + +-- Test 4: TOP(function) WITH APPROX +SELECT TOP(LEN(DB_NAME())) WITH APPROX * +FROM Orders +ORDER BY OrderDate; + +-- Test 5: TOP WITH APPROXIMATE and column list +SELECT TOP 5 WITH APPROXIMATE OrderID, CustomerName, OrderDate +FROM Orders +ORDER BY OrderDate DESC; + +-- Test 6: TOP(variable) WITH APPROXIMATE +DECLARE @top INT = 20; +SELECT TOP(@top) WITH APPROXIMATE * +FROM Products +ORDER BY Price; diff --git a/Test/SqlDom/TestScripts/VectorSearchApproximateTests170.sql b/Test/SqlDom/TestScripts/VectorSearchApproximateTests170.sql new file mode 100644 index 0000000..1583f34 --- /dev/null +++ b/Test/SqlDom/TestScripts/VectorSearchApproximateTests170.sql @@ -0,0 +1,57 @@ +-- Tests for VECTOR_SEARCH with TOP WITH APPROXIMATE and FETCH APPROXIMATE +-- From ADO commit: 355ed5f69d5c8a589271a5a5d4df76665bb4e985 +-- Source: VectorSearch.xml tests top_with_approx, top_with_approximate, order_by_fetch_approx, order_by_fetch_approximate + +-- Test 1: TOP WITH APPROX (abbreviated) +SELECT TOP 10 WITH APPROX 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 = 10 + ) AS ann +ORDER BY ann.distance; +GO + +-- Test 2: TOP WITH APPROXIMATE (full keyword) +SELECT TOP 10 WITH APPROXIMATE 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 = 10 + ) AS ann +ORDER BY ann.distance; +GO + +-- Test 3: ORDER BY FETCH APPROX (abbreviated) +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 = 10 + ) AS ann +ORDER BY ann.distance +FETCH APPROX NEXT 10 ROWS ONLY; +GO + +-- Test 4: ORDER BY FETCH APPROXIMATE (full keyword) +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 = 10 + ) AS ann +ORDER BY ann.distance +FETCH APPROXIMATE NEXT 10 ROWS ONLY; +GO diff --git a/release-notes/170/170.191.0.md b/release-notes/170/170.191.0.md new file mode 100644 index 0000000..b023b3e --- /dev/null +++ b/release-notes/170/170.191.0.md @@ -0,0 +1,27 @@ +# Release Notes + +## Microsoft.SqlServer.TransactSql.ScriptDom 170.191.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 + +#### .NET Framework +#### .NET Core + +### New Features +* Adds TOP WITH APPROXIMATE and FETCH APPROXIMATE support. + +### Fixed +* Fixes allocation performance issue in ScriptWriter token list initialization. + +### Changes +* None + +### Known Issues +* None \ No newline at end of file