diff --git a/SqlScriptDom/Parser/TSql/Ast.xml b/SqlScriptDom/Parser/TSql/Ast.xml
index 9a1d8b5..482f047 100644
--- a/SqlScriptDom/Parser/TSql/Ast.xml
+++ b/SqlScriptDom/Parser/TSql/Ast.xml
@@ -649,6 +649,7 @@
+
diff --git a/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs b/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs
index acb3acb..228acf2 100644
--- a/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs
+++ b/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs
@@ -524,6 +524,9 @@ internal static class CodeGenerationSupporter
internal const string JsonObject = "JSON_OBJECT";
internal const string JsonObjectAgg = "JSON_OBJECTAGG";
internal const string JsonArrayAgg = "JSON_ARRAYAGG";
+ internal const string JsonQuery = "JSON_QUERY";
+ internal const string Array = "ARRAY";
+ internal const string Wrapper = "WRAPPER";
internal const string Keep = "KEEP";
internal const string KeepDefaults = "KEEPDEFAULTS";
internal const string KeepFixed = "KEEPFIXED";
diff --git a/SqlScriptDom/Parser/TSql/TSql170.g b/SqlScriptDom/Parser/TSql/TSql170.g
index cb1a22d..b04a570 100644
--- a/SqlScriptDom/Parser/TSql/TSql170.g
+++ b/SqlScriptDom/Parser/TSql/TSql170.g
@@ -32836,6 +32836,9 @@ builtInFunctionCall returns [FunctionCall vResult = FragmentFactory.CreateFragme
|
{(vResult.FunctionName != null && vResult.FunctionName.Value.ToUpper(CultureInfo.InvariantCulture) == CodeGenerationSupporter.JsonArrayAgg)}?
jsonArrayAggBuiltInFunctionCall[vResult]
+ |
+ {(vResult.FunctionName != null && vResult.FunctionName.Value.ToUpper(CultureInfo.InvariantCulture) == CodeGenerationSupporter.JsonQuery)}?
+ jsonQueryBuiltInFunctionCall[vResult]
|
{(vResult.FunctionName != null && vResult.FunctionName.Value.ToUpper(CultureInfo.InvariantCulture) == CodeGenerationSupporter.Trim) &&
(NextTokenMatches(CodeGenerationSupporter.Leading) | NextTokenMatches(CodeGenerationSupporter.Trailing) | NextTokenMatches(CodeGenerationSupporter.Both))}?
@@ -32936,6 +32939,41 @@ jsonObjectAggBuiltInFunctionCall [FunctionCall vParent]
}
;
+jsonQueryBuiltInFunctionCall [FunctionCall vParent]
+{
+ ScalarExpression vExpression;
+ ScalarExpression vPath;
+}
+ : vExpression=expression
+ {
+ AddAndUpdateTokenInfo(vParent, vParent.Parameters, vExpression);
+ }
+ (
+ Comma vPath=expression
+ {
+ AddAndUpdateTokenInfo(vParent, vParent.Parameters, vPath);
+ }
+ )?
+ tRParen:RightParenthesis
+ {
+ UpdateTokenInfo(vParent, tRParen);
+ }
+ (
+ With tArray:Identifier tWrapper:Identifier
+ {
+ if (!tArray.getText().Equals(CodeGenerationSupporter.Array, StringComparison.OrdinalIgnoreCase))
+ {
+ throw GetUnexpectedTokenErrorException(tArray);
+ }
+ if (!tWrapper.getText().Equals(CodeGenerationSupporter.Wrapper, StringComparison.OrdinalIgnoreCase))
+ {
+ throw GetUnexpectedTokenErrorException(tWrapper);
+ }
+ vParent.WithArrayWrapper = true;
+ }
+ )?
+ ;
+
regularBuiltInFunctionCall [FunctionCall vParent]
{
ColumnReferenceExpression vColumn;
diff --git a/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.FunctionCall.cs b/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.FunctionCall.cs
index 0daaff0..25c8bd0 100644
--- a/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.FunctionCall.cs
+++ b/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.FunctionCall.cs
@@ -93,6 +93,22 @@ public override void ExplicitVisit(FunctionCall node)
GenerateReturnType(node?.ReturnType);
GenerateSymbol(TSqlTokenType.RightParenthesis);
}
+ else if (node.FunctionName.Value.ToUpper(CultureInfo.InvariantCulture) == CodeGenerationSupporter.JsonQuery)
+ {
+ GenerateCommaSeparatedList(node.Parameters);
+ GenerateSymbol(TSqlTokenType.RightParenthesis);
+
+ // Handle WITH ARRAY WRAPPER clause
+ if (node.WithArrayWrapper)
+ {
+ GenerateSpace();
+ GenerateKeyword(TSqlTokenType.With);
+ GenerateSpace();
+ GenerateIdentifier(CodeGenerationSupporter.Array);
+ GenerateSpace();
+ GenerateIdentifier(CodeGenerationSupporter.Wrapper);
+ }
+ }
else
{
GenerateUniqueRowFilter(node.UniqueRowFilter, false);
diff --git a/Test/SqlDom/Baselines170/JsonFunctionTests170.sql b/Test/SqlDom/Baselines170/JsonFunctionTests170.sql
index 0b3f776..c8042bc 100644
--- a/Test/SqlDom/Baselines170/JsonFunctionTests170.sql
+++ b/Test/SqlDom/Baselines170/JsonFunctionTests170.sql
@@ -134,6 +134,22 @@ AS
SELECT JSON_OBJECTAGG(c1:c2) AS jsoncontents
FROM (VALUES ('key1', 'c'), ('key2', 'b'), ('key3', 'a')) AS t(c1, c2);
+
+GO
+SELECT JSON_QUERY('{ "a": 1 }');
+
+SELECT JSON_QUERY('{ "a": 1 }', '$.a');
+
+SELECT JSON_QUERY('{ "a": [1,2,3] }', '$.a') WITH ARRAY WRAPPER;
+
+
+GO
+CREATE VIEW dbo.jsonfunctest
+AS
+SELECT JSON_OBJECTAGG(c1:c2) AS jsoncontents
+FROM (VALUES ('key1', 'c'), ('key2', 'b'), ('key3', 'a')) AS t(c1, c2);
+
+
GO
SELECT TOP (5) c.object_id,
JSON_OBJECTAGG(c.name:c.column_id) AS columns
diff --git a/Test/SqlDom/Only170SyntaxTests.cs b/Test/SqlDom/Only170SyntaxTests.cs
index 488f624..523cca0 100644
--- a/Test/SqlDom/Only170SyntaxTests.cs
+++ b/Test/SqlDom/Only170SyntaxTests.cs
@@ -19,7 +19,7 @@ public partial class SqlDomTests
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),
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: 11, nErrors90: 8, nErrors100: 36, nErrors110: 36, nErrors120: 36, nErrors130: 36, nErrors140: 36, nErrors150: 36, nErrors160: 36),
+ new ParserTest170("JsonFunctionTests170.sql", nErrors80: 13, nErrors90: 8, nErrors100: 38, nErrors110: 38, nErrors120: 38, nErrors130: 38, nErrors140: 38, nErrors150: 38, nErrors160: 38),
new ParserTest170("AiGenerateEmbeddingsTests170.sql", nErrors80: 12, nErrors90: 9, nErrors100: 9, nErrors110: 9, nErrors120: 9, nErrors130: 9, nErrors140: 9, nErrors150: 9, nErrors160: 9),
new ParserTest170("CreateExternalModelStatementTests170.sql", nErrors80: 2, nErrors90: 2, nErrors100: 2, nErrors110: 2, nErrors120: 2, nErrors130: 4, nErrors140: 4, nErrors150: 4, nErrors160: 4),
new ParserTest170("AlterExternalModelStatementTests170.sql", nErrors80: 2, nErrors90: 2, nErrors100: 2, nErrors110: 2, nErrors120: 2, nErrors130: 5, nErrors140: 5, nErrors150: 5, nErrors160: 5),
diff --git a/Test/SqlDom/ParserErrorsTests.cs b/Test/SqlDom/ParserErrorsTests.cs
index 26b5e09..b793a14 100644
--- a/Test/SqlDom/ParserErrorsTests.cs
+++ b/Test/SqlDom/ParserErrorsTests.cs
@@ -577,6 +577,43 @@ public void JsonArrayAggSyntaxNegativeTest()
new ParserErrorInfo(34, "SQL46010", "NULL"));
}
+ ///
+ /// Negative tests for JSON_QUERY syntax in functions
+ ///
+ [TestMethod]
+ [Priority(0)]
+ [SqlStudioTestCategory(Category.UnitTest)]
+ public void JsonQuerySyntaxNegativeTest()
+ {
+ // Cannot use WITH without ARRAY WRAPPER (incomplete syntax)
+ ParserTestUtils.ErrorTest170("SELECT JSON_QUERY('{ \"a\": 1 }') WITH",
+ new ParserErrorInfo(32, "SQL46010", "WITH"));
+
+ // Cannot use WITH ARRAY without WRAPPER (unexpected end of file)
+ ParserTestUtils.ErrorTest170("SELECT JSON_QUERY('{ \"a\": 1 }') WITH ARRAY",
+ new ParserErrorInfo(42, "SQL46029", ""));
+
+ // Cannot use WITH WRAPPER without ARRAY (unexpected end of file)
+ ParserTestUtils.ErrorTest170("SELECT JSON_QUERY('{ \"a\": 1 }') WITH WRAPPER",
+ new ParserErrorInfo(44, "SQL46029", ""));
+
+ // Cannot use incorrect keyword instead of ARRAY
+ ParserTestUtils.ErrorTest170("SELECT JSON_QUERY('{ \"a\": 1 }') WITH OBJECT WRAPPER",
+ new ParserErrorInfo(37, "SQL46010", "OBJECT"));
+
+ // Cannot use incorrect keyword instead of WRAPPER
+ ParserTestUtils.ErrorTest170("SELECT JSON_QUERY('{ \"a\": 1 }') WITH ARRAY OBJECT",
+ new ParserErrorInfo(43, "SQL46010", "OBJECT"));
+
+ // Cannot use JSON_QUERY with colon syntax (key:value pairs like JSON_OBJECT)
+ ParserTestUtils.ErrorTest170("SELECT JSON_QUERY('name':'value')",
+ new ParserErrorInfo(24, "SQL46010", ":"));
+
+ // WITH ARRAY WRAPPER must come after closing parenthesis, not before
+ ParserTestUtils.ErrorTest170("SELECT JSON_QUERY('{ \"a\": 1 }' WITH ARRAY WRAPPER)",
+ new ParserErrorInfo(31, "SQL46010", "WITH"));
+ }
+
///
/// Negative tests for Data Masking Alter Column syntax.
///
diff --git a/Test/SqlDom/TestScripts/JsonFunctionTests170.sql b/Test/SqlDom/TestScripts/JsonFunctionTests170.sql
index f5847a0..89564cf 100644
--- a/Test/SqlDom/TestScripts/JsonFunctionTests170.sql
+++ b/Test/SqlDom/TestScripts/JsonFunctionTests170.sql
@@ -90,46 +90,59 @@ SELECT JSON_OBJECTAGG();
SELECT JSON_OBJECTAGG('name':1);
-SELECT JSON_OBJECTAGG('name':JSON_ARRAY(1, 2));
+SELECT JSON_OBJECTAGG('name':JSON_ARRAY(1, 2));
SELECT JSON_OBJECTAGG('name':'b' NULL ON NULL RETURNING JSON);
SELECT JSON_OBJECTAGG('name':'b' ABSENT ON NULL RETURNING JSON);
SELECT JSON_OBJECTAGG('name':'b' RETURNING JSON);
-
+
SELECT JSON_ARRAYAGG('name');
-SELECT JSON_ARRAYAGG('a');
-SELECT JSON_OBJECTAGG( c1:c2 )
-SELECT JSON_OBJECTAGG( c1:'c2' )
-
-SELECT JSON_ARRAYAGG('a' NULL ON NULL);
-
-SELECT JSON_ARRAYAGG('a' NULL ON NULL RETURNING JSON);
-
+SELECT JSON_ARRAYAGG('a');
+SELECT JSON_OBJECTAGG( c1:c2 )
+SELECT JSON_OBJECTAGG( c1:'c2' )
+
+SELECT JSON_ARRAYAGG('a' NULL ON NULL);
+
+SELECT JSON_ARRAYAGG('a' NULL ON NULL RETURNING JSON);
+
SELECT s.session_id,
JSON_ARRAYAGG(s.host_name)
FROM sys.dm_exec_sessions AS s
-WHERE s.is_user_process = 1;
-
+WHERE s.is_user_process = 1;
+
SELECT s.session_id,
JSON_ARRAYAGG(s.host_name NULL ON NULL)
FROM sys.dm_exec_sessions AS s
-WHERE s.is_user_process = 1;
-
+WHERE s.is_user_process = 1;
+
SELECT s.session_id,
JSON_ARRAYAGG(s.host_name NULL ON NULL RETURNING JSON)
FROM sys.dm_exec_sessions AS s
-WHERE s.is_user_process = 1;
-
-GO
-CREATE VIEW dbo.jsonfunctest AS
- SELECT JSON_OBJECTAGG( c1:c2 ) as jsoncontents
- FROM (
- VALUES('key1', 'c'), ('key2', 'b'), ('key3','a')
- ) AS t(c1, c2);
-
-GO
-SELECT TOP(5) c.object_id, JSON_OBJECTAGG(c.name:c.column_id) AS columns
- FROM sys.columns AS c
- GROUP BY c.object_id;
\ No newline at end of file
+WHERE s.is_user_process = 1;
+
+GO
+CREATE VIEW dbo.jsonfunctest AS
+ SELECT JSON_OBJECTAGG( c1:c2 ) as jsoncontents
+ FROM (
+ VALUES('key1', 'c'), ('key2', 'b'), ('key3','a')
+ ) AS t(c1, c2);
+
+GO
+
+SELECT JSON_QUERY('{ "a": 1 }');
+SELECT JSON_QUERY('{ "a": 1 }', '$.a');
+SELECT JSON_QUERY('{ "a": [1,2,3] }', '$.a') WITH ARRAY WRAPPER;
+
+GO
+CREATE VIEW dbo.jsonfunctest AS
+ SELECT JSON_OBJECTAGG( c1:c2 ) as jsoncontents
+ FROM (
+ VALUES('key1', 'c'), ('key2', 'b'), ('key3','a')
+ ) AS t(c1, c2);
+
+GO
+SELECT TOP(5) c.object_id, JSON_OBJECTAGG(c.name:c.column_id) AS columns
+ FROM sys.columns AS c
+ GROUP BY c.object_id;