Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions SqlScriptDom/Parser/TSql/Ast.xml
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,7 @@
<Member Name="JsonParameters" Type="JsonKeyValue" Collection="true" Summary="The Json parameters to the function."/>
<Member Name="AbsentOrNullOnNull" Type="Identifier" Collection="true" Summary="The Absent or Null on Null will convert or remove sql null to json null"/>
<Member Name="ReturnType" Type="Identifier" Collection="true" Summary="Return type of function. Used by json_arrayagg, json_objectagg, json_array, json_object and json_value"/>
<Member Name="WithArrayWrapper" Type="bool" Summary="Indicates whether WITH ARRAY WRAPPER clause is specified for JSON_QUERY function."/>
</Class>
<Class Name="CallTarget" Abstract="true" Summary="Represents a target of a function call.">
</Class>
Expand Down
3 changes: 3 additions & 0 deletions SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
38 changes: 38 additions & 0 deletions SqlScriptDom/Parser/TSql/TSql170.g
Original file line number Diff line number Diff line change
Expand Up @@ -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))}?
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
16 changes: 16 additions & 0 deletions Test/SqlDom/Baselines170/JsonFunctionTests170.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Test/SqlDom/Only170SyntaxTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
37 changes: 37 additions & 0 deletions Test/SqlDom/ParserErrorsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,43 @@ public void JsonArrayAggSyntaxNegativeTest()
new ParserErrorInfo(34, "SQL46010", "NULL"));
}

/// <summary>
/// Negative tests for JSON_QUERY syntax in functions
/// </summary>
[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"));
}

/// <summary>
/// Negative tests for Data Masking Alter Column syntax.
/// </summary>
Expand Down
67 changes: 40 additions & 27 deletions Test/SqlDom/TestScripts/JsonFunctionTests170.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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;
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;
Loading