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