-
Notifications
You must be signed in to change notification settings - Fork 41
Add support for JSON Index creation syntax in SQL Server 2025 (TSql170) #147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
269592c
90ad82c
3e23f8b
9d7e0db
753a74b
73a1645
9de9b7f
4865dd4
373dc4d
ff38699
53a2aba
28c5564
667ca5c
9656c7e
89c0efc
50a1bff
d0e2993
b6073da
ab3a74f
eee2f48
04e7f67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -356,4 +356,7 @@ MigrationBackup/ | |
| out/ | ||
|
|
||
| # Project packages folder | ||
| .packages/ | ||
| .packages/ | ||
|
|
||
| # Temporary build artifacts | ||
| tmp/ | ||
|
llali marked this conversation as resolved.
Outdated
|
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4596,6 +4596,14 @@ | |
| <Member Name="OnFileGroupOrPartitionScheme" Type="FileGroupOrPartitionScheme" Summary="The filegroup or partition scheme. Might be null."/> | ||
| <Member Name="OrderedColumns" Type="ColumnReferenceExpression" Collection="true" Summary="The columns which ordered columnstore indexes should be ordered on." /> | ||
| </Class> | ||
| <Class Name="CreateJsonIndexStatement" Base="TSqlStatement" Summary="Represents the create JSON index statement."> | ||
| <Member Name="Name" Type="Identifier" Summary="The name of the index."/> | ||
| <Member Name="OnName" Type="SchemaObjectName" Summary="The table or view name that comes after the ON keyword."/> | ||
| <Member Name="JsonColumn" Type="ColumnReferenceExpression" Summary="The JSON column for the index."/> | ||
| <Member Name="JsonPaths" Type="StringLiteral" Collection="true" Summary="The JSON paths specified in the FOR clause. Optional may have zero elements."/> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Renamed JsonPaths property to ForJsonPaths in Ast.xml to include the actual SQL keyword name, following the suggested naming convention. Updated corresponding references in grammar and script generator files. |
||
| <Member Name="IndexOptions" Type="IndexOption" Collection="true" Summary="The index options. Optional may have zero elements."/> | ||
| <Member Name="OnFileGroupOrPartitionScheme" Type="FileGroupOrPartitionScheme" Summary="The filegroup or partition scheme. Might be null."/> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks like file group is not supported for json index. remove file group from ast.xml and from TSQL170.g and apply the same change in tests
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed filegroup support from JSON Index syntax as requested. The changes include:
JSON indexes now support only the core syntax without ON filegroup clause. (commit eee2f48) |
||
| </Class> | ||
| <Class Name="WindowFrameClause" Summary="Represents the specification of window bounds for windowing aggregates."> | ||
| <Member Name="Top" Type="WindowDelimiter" Summary="Top boundary of the window."/> | ||
| <Member Name="Bottom" Type="WindowDelimiter" Summary="Bottom boundary of the window. Optional may be null."/> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -883,6 +883,9 @@ create2005Statements returns [TSqlStatement vResult = null] | |
| | | ||
| {NextTokenMatches(CodeGenerationSupporter.ColumnStore)}? | ||
| vResult=createColumnStoreIndexStatement[null, null] | ||
| | | ||
| {NextTokenMatches(CodeGenerationSupporter.Json)}? | ||
| vResult=createJsonIndexStatement[null, null] | ||
| | | ||
| {NextTokenMatches(CodeGenerationSupporter.Contract)}? | ||
| vResult=createContractStatement | ||
|
|
@@ -16824,6 +16827,7 @@ createIndexStatement returns [TSqlStatement vResult = null] | |
| ( | ||
| vResult=createRelationalIndexStatement[tUnique, isClustered] | ||
| | vResult=createColumnStoreIndexStatement[tUnique, isClustered] | ||
| | vResult=createJsonIndexStatement[tUnique, isClustered] | ||
| ) | ||
| ) | ||
| | | ||
|
|
@@ -16960,6 +16964,65 @@ createColumnStoreIndexStatement [IToken tUnique, bool? isClustered] returns [Cre | |
| )? | ||
| ; | ||
|
|
||
| createJsonIndexStatement [IToken tUnique, bool? isClustered] returns [CreateJsonIndexStatement vResult = FragmentFactory.CreateFragment<CreateJsonIndexStatement>()] | ||
| { | ||
| Identifier vIdentifier; | ||
| SchemaObjectName vSchemaObjectName; | ||
| ColumnReferenceExpression vJsonColumn; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use Identifier instead of ColumnReferenceExpression for vJsonColumn.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed JsonColumn from ColumnReferenceExpression to Identifier type in both the AST definition and grammar rule, following the same pattern used by XML indexes. This is more semantically correct since we're specifying a simple column name rather than a complex column reference expression. (commit 9656c7e) |
||
| FileGroupOrPartitionScheme vFileGroupOrPartitionScheme; | ||
| StringLiteral vPath; | ||
|
|
||
| if (tUnique != null) | ||
| { | ||
| ThrowIncorrectSyntaxErrorException(tUnique); | ||
| } | ||
| if (isClustered.HasValue) | ||
| { | ||
| ThrowIncorrectSyntaxErrorException(LT(1)); | ||
| } | ||
| } | ||
| : tJson:Identifier tIndex:Index vIdentifier=identifier | ||
| { | ||
| Match(tJson, CodeGenerationSupporter.Json); | ||
| vResult.Name = vIdentifier; | ||
| } | ||
| tOn:On vSchemaObjectName=schemaObjectThreePartName | ||
| { | ||
| vResult.OnName = vSchemaObjectName; | ||
| } | ||
| LeftParenthesis vJsonColumn=column tRParen:RightParenthesis | ||
| { | ||
| vResult.JsonColumn = vJsonColumn; | ||
| UpdateTokenInfo(vResult, tRParen); | ||
| } | ||
| ( | ||
| tFor:For LeftParenthesis | ||
| vPath=stringLiteral | ||
| { | ||
| AddAndUpdateTokenInfo(vResult, vResult.JsonPaths, vPath); | ||
| } | ||
| ( | ||
| Comma vPath=stringLiteral | ||
| { | ||
| AddAndUpdateTokenInfo(vResult, vResult.JsonPaths, vPath); | ||
| } | ||
| )* | ||
| RightParenthesis | ||
| )? | ||
| ( | ||
| // Greedy due to conflict with withCommonTableExpressionsAndXmlNamespaces | ||
| options {greedy = true; } : | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's this for?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The "options {greedy = true; }" directive is used to resolve parser conflicts with common table expressions and XML namespaces. This is a standard ANTLR pattern used throughout the grammar file (appears in 30+ locations) to ensure the WITH clause is properly recognized in CREATE JSON INDEX statements. The comment explains this is needed due to conflict with withCommonTableExpressionsAndXmlNamespaces. |
||
| With | ||
| indexOptionList[IndexAffectingStatement.CreateIndex, vResult.IndexOptions, vResult] | ||
| )? | ||
| ( | ||
| On vFileGroupOrPartitionScheme=filegroupOrPartitionScheme | ||
| { | ||
| vResult.OnFileGroupOrPartitionScheme = vFileGroupOrPartitionScheme; | ||
| } | ||
| )? | ||
| ; | ||
|
|
||
| indexKeyColumnList[CreateIndexStatement vParent] | ||
| { | ||
| ColumnWithSortOrder vColumnWithSortOrder; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| //------------------------------------------------------------------------------ | ||
| // <copyright file="SqlScriptGeneratorVisitor.CreateJsonIndexStatement.cs" company="Microsoft"> | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // </copyright> | ||
| //------------------------------------------------------------------------------ | ||
| using System.Collections.Generic; | ||
| using Microsoft.SqlServer.TransactSql.ScriptDom; | ||
|
|
||
| namespace Microsoft.SqlServer.TransactSql.ScriptDom.ScriptGenerator | ||
| { | ||
| partial class SqlScriptGeneratorVisitor | ||
| { | ||
| public override void ExplicitVisit(CreateJsonIndexStatement node) | ||
| { | ||
| GenerateKeyword(TSqlTokenType.Create); | ||
|
|
||
| GenerateSpaceAndIdentifier(CodeGenerationSupporter.Json); | ||
|
|
||
| GenerateSpaceAndKeyword(TSqlTokenType.Index); | ||
|
|
||
| // name | ||
| GenerateSpaceAndFragmentIfNotNull(node.Name); | ||
|
|
||
| NewLineAndIndent(); | ||
| GenerateKeyword(TSqlTokenType.On); | ||
| GenerateSpaceAndFragmentIfNotNull(node.OnName); | ||
|
|
||
| // JSON column | ||
| if (node.JsonColumn != null) | ||
| { | ||
| GenerateSpace(); | ||
| GenerateSymbol(TSqlTokenType.LeftParenthesis); | ||
| GenerateFragmentIfNotNull(node.JsonColumn); | ||
| GenerateSymbol(TSqlTokenType.RightParenthesis); | ||
| } | ||
|
|
||
| // FOR clause with JSON paths | ||
| if (node.JsonPaths != null && node.JsonPaths.Count > 0) | ||
| { | ||
| NewLineAndIndent(); | ||
| GenerateKeyword(TSqlTokenType.For); | ||
| GenerateSpace(); | ||
| GenerateParenthesisedCommaSeparatedList(node.JsonPaths); | ||
| } | ||
|
|
||
| GenerateIndexOptions(node.IndexOptions); | ||
|
|
||
| if (node.OnFileGroupOrPartitionScheme != null) | ||
| { | ||
| NewLineAndIndent(); | ||
| GenerateKeyword(TSqlTokenType.On); | ||
|
|
||
| GenerateSpaceAndFragmentIfNotNull(node.OnFileGroupOrPartitionScheme); | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| CREATE JSON INDEX IX_JSON_Basic | ||
| ON dbo.Users (JsonData); | ||
|
|
||
| CREATE JSON INDEX IX_JSON_SinglePath | ||
| ON dbo.Users (JsonData) | ||
| FOR ('$.name'); | ||
|
|
||
| CREATE JSON INDEX IX_JSON_MultiplePaths | ||
| ON dbo.Users (JsonData) | ||
| FOR ('$.name', '$.email', '$.age'); | ||
|
|
||
| CREATE JSON INDEX IX_JSON_WithOptions | ||
| ON dbo.Users (JsonData) WITH (FILLFACTOR = 90, ONLINE = OFF); | ||
|
|
||
| CREATE JSON INDEX IX_JSON_Complete | ||
| ON dbo.Users (JsonData) | ||
| FOR ('$.profile.name', '$.profile.email') WITH (MAXDOP = 4, DATA_COMPRESSION = ROW); | ||
|
|
||
| CREATE JSON INDEX IX_JSON_Filegroup | ||
| ON dbo.Users (JsonData) | ||
| ON [PRIMARY]; | ||
|
llali marked this conversation as resolved.
Outdated
|
||
|
|
||
| CREATE JSON INDEX IX_JSON_Full | ||
| ON dbo.Users (JsonData) | ||
| FOR ('$.orders[*].amount', '$.orders[*].date') WITH (DROP_EXISTING = OFF, ALLOW_ROW_LOCKS = ON) | ||
|
ssreerama marked this conversation as resolved.
Outdated
|
||
| ON [JsonIndexes]; | ||
|
|
||
| CREATE JSON INDEX IX_JSON_Schema | ||
| ON MySchema.MyTable (JsonColumn) | ||
| FOR ('$.properties.value'); | ||
|
|
||
| CREATE JSON INDEX [IX JSON Index] | ||
| ON [dbo].[Users] ([Json Data]) | ||
| FOR ('$.data.attributes'); | ||
|
|
||
| CREATE JSON INDEX IX_JSON_Complex | ||
| ON dbo.Documents (Content) | ||
| FOR ('$.metadata.title', '$.content.sections[*].text', '$.tags[*]'); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,7 +9,8 @@ public partial class SqlDomTests | |
| // Note: These filenames are case sensitive, make sure they match the checked-in file exactly | ||
| private static readonly ParserTest[] Only170TestInfos = | ||
| { | ||
| new ParserTest170("RegexpTVFTests170.sql", nErrors80: 1, nErrors90: 1, nErrors100: 0, nErrors110: 0, nErrors120: 0, nErrors130: 0, nErrors140: 0, nErrors150: 0, nErrors160: 0) | ||
| new ParserTest170("RegexpTVFTests170.sql", nErrors80: 1, nErrors90: 1, nErrors100: 0, nErrors110: 0, nErrors120: 0, nErrors130: 0, nErrors140: 0, nErrors150: 0, nErrors160: 0), | ||
| new ParserTest170("JsonIndexTests170.sql", nErrors80: 2, nErrors90: 10, nErrors100: 10, nErrors110: 10, nErrors120: 10, nErrors130: 10, nErrors140: 10, nErrors150: 10, nErrors160: 10) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a comma at the end of this line
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added the missing comma at the end of line 13 in Only170SyntaxTests.cs to fix the array syntax. (commit 89c0efc) |
||
| }; | ||
|
|
||
| private static readonly ParserTest[] SqlAzure170_TestInfos = | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| -- Basic JSON index creation | ||
| CREATE JSON INDEX IX_JSON_Basic ON dbo.Users (JsonData); | ||
|
|
||
| -- JSON index with FOR clause (single path) | ||
| CREATE JSON INDEX IX_JSON_SinglePath ON dbo.Users (JsonData) | ||
| FOR ('$.name'); | ||
|
|
||
| -- JSON index with FOR clause (multiple paths) | ||
| CREATE JSON INDEX IX_JSON_MultiplePaths ON dbo.Users (JsonData) | ||
| FOR ('$.name', '$.email', '$.age'); | ||
|
|
||
| -- JSON index with WITH options | ||
| CREATE JSON INDEX IX_JSON_WithOptions ON dbo.Users (JsonData) | ||
| WITH (FILLFACTOR = 90, ONLINE = OFF); | ||
|
|
||
| -- JSON index with FOR clause and WITH options | ||
| CREATE JSON INDEX IX_JSON_Complete ON dbo.Users (JsonData) | ||
| FOR ('$.profile.name', '$.profile.email') | ||
| WITH (MAXDOP = 4, DATA_COMPRESSION = ROW); | ||
|
|
||
| -- JSON index with ON filegroup | ||
| CREATE JSON INDEX IX_JSON_Filegroup ON dbo.Users (JsonData) | ||
| ON [PRIMARY]; | ||
|
|
||
| -- JSON index with FOR clause, WITH options, and ON filegroup | ||
| CREATE JSON INDEX IX_JSON_Full ON dbo.Users (JsonData) | ||
| FOR ('$.orders[*].amount', '$.orders[*].date') | ||
| WITH (DROP_EXISTING = OFF, ALLOW_ROW_LOCKS = ON) | ||
| ON [JsonIndexes]; | ||
|
|
||
| -- JSON index on schema-qualified table | ||
| CREATE JSON INDEX IX_JSON_Schema ON MySchema.MyTable (JsonColumn) | ||
| FOR ('$.properties.value'); | ||
|
|
||
| -- JSON index with quoted identifiers | ||
| CREATE JSON INDEX [IX JSON Index] ON [dbo].[Users] ([Json Data]) | ||
| FOR ('$.data.attributes'); | ||
|
|
||
| -- JSON index with complex path expressions | ||
| CREATE JSON INDEX IX_JSON_Complex ON dbo.Documents (Content) | ||
| FOR ('$.metadata.title', '$.content.sections[*].text', '$.tags[*]'); |
Uh oh!
There was an error while loading. Please reload this page.