diff --git a/SqlScriptDom/Parser/TSql/TSql130.g b/SqlScriptDom/Parser/TSql/TSql130.g index bc917aa6..a5df40d5 100644 --- a/SqlScriptDom/Parser/TSql/TSql130.g +++ b/SqlScriptDom/Parser/TSql/TSql130.g @@ -26976,6 +26976,10 @@ regularColumnBody [IndexAffectingStatement statementType, ColumnDefinition vPare } )? )? + ( + {NextTokenMatches(CodeGenerationSupporter.Masked) && !vParent.IsMasked}? + maskedClause[vParent] + )? { VerifyColumnDataType(vParent); } diff --git a/SqlScriptDom/Parser/TSql/TSql140.g b/SqlScriptDom/Parser/TSql/TSql140.g index 323a91ab..c619261e 100644 --- a/SqlScriptDom/Parser/TSql/TSql140.g +++ b/SqlScriptDom/Parser/TSql/TSql140.g @@ -27714,6 +27714,10 @@ regularColumnBody [IndexAffectingStatement statementType, ColumnDefinition vPare } )? )? + ( + {NextTokenMatches(CodeGenerationSupporter.Masked) && !vParent.IsMasked}? + maskedClause[vParent] + )? ( {NextTokenMatches(CodeGenerationSupporter.Hidden)}? tHidden:Identifier diff --git a/SqlScriptDom/Parser/TSql/TSql150.g b/SqlScriptDom/Parser/TSql/TSql150.g index 6fc72dfe..f6bf9024 100644 --- a/SqlScriptDom/Parser/TSql/TSql150.g +++ b/SqlScriptDom/Parser/TSql/TSql150.g @@ -28554,6 +28554,10 @@ regularColumnBody [IndexAffectingStatement statementType, ColumnDefinition vPare } )? )? + ( + {NextTokenMatches(CodeGenerationSupporter.Masked) && !vParent.IsMasked}? + maskedClause[vParent] + )? ( {NextTokenMatches(CodeGenerationSupporter.Hidden)}? tHidden:Identifier diff --git a/SqlScriptDom/Parser/TSql/TSql160.g b/SqlScriptDom/Parser/TSql/TSql160.g index 9bedb727..b8b3dec9 100644 --- a/SqlScriptDom/Parser/TSql/TSql160.g +++ b/SqlScriptDom/Parser/TSql/TSql160.g @@ -29034,6 +29034,10 @@ regularColumnBody [IndexAffectingStatement statementType, ColumnDefinition vPare } )? )? + ( + {NextTokenMatches(CodeGenerationSupporter.Masked) && !vParent.IsMasked}? + maskedClause[vParent] + )? ( {NextTokenMatches(CodeGenerationSupporter.Hidden)}? tHidden:Identifier diff --git a/SqlScriptDom/Parser/TSql/TSql170.g b/SqlScriptDom/Parser/TSql/TSql170.g index ceb78600..87a8483d 100644 --- a/SqlScriptDom/Parser/TSql/TSql170.g +++ b/SqlScriptDom/Parser/TSql/TSql170.g @@ -29730,6 +29730,10 @@ regularColumnBody [IndexAffectingStatement statementType, ColumnDefinition vPare } )? )? + ( + {NextTokenMatches(CodeGenerationSupporter.Masked) && !vParent.IsMasked}? + maskedClause[vParent] + )? ( {NextTokenMatches(CodeGenerationSupporter.Hidden)}? tHidden:Identifier diff --git a/SqlScriptDom/Parser/TSql/TSql180.g b/SqlScriptDom/Parser/TSql/TSql180.g index 934911db..0e6ab819 100644 --- a/SqlScriptDom/Parser/TSql/TSql180.g +++ b/SqlScriptDom/Parser/TSql/TSql180.g @@ -29968,6 +29968,10 @@ regularColumnBody [IndexAffectingStatement statementType, ColumnDefinition vPare } )? )? + ( + {NextTokenMatches(CodeGenerationSupporter.Masked) && !vParent.IsMasked}? + maskedClause[vParent] + )? ( {NextTokenMatches(CodeGenerationSupporter.Hidden)}? tHidden:Identifier diff --git a/SqlScriptDom/Parser/TSql/TSqlFabricDW.g b/SqlScriptDom/Parser/TSql/TSqlFabricDW.g index 4ad40b85..7fafaec2 100644 --- a/SqlScriptDom/Parser/TSql/TSqlFabricDW.g +++ b/SqlScriptDom/Parser/TSql/TSqlFabricDW.g @@ -29104,6 +29104,10 @@ regularColumnBody [IndexAffectingStatement statementType, ColumnDefinition vPare } )? )? + ( + {NextTokenMatches(CodeGenerationSupporter.Masked) && !vParent.IsMasked}? + maskedClause[vParent] + )? ( {NextTokenMatches(CodeGenerationSupporter.Hidden)}? tHidden:Identifier diff --git a/Test/SqlDom/Baselines130/SparseMaskedColumnOrderTests130.sql b/Test/SqlDom/Baselines130/SparseMaskedColumnOrderTests130.sql new file mode 100644 index 00000000..36ff7ead --- /dev/null +++ b/Test/SqlDom/Baselines130/SparseMaskedColumnOrderTests130.sql @@ -0,0 +1,11 @@ +CREATE TABLE t ( + c VARCHAR (100) SPARSE MASKED WITH (FUNCTION = 'default()') NULL +); + +CREATE TABLE t ( + c VARCHAR (100) SPARSE MASKED WITH (FUNCTION = 'default()') NULL +); + +CREATE TABLE t ( + c VARBINARY (MAX) SPARSE FILESTREAM MASKED WITH (FUNCTION = 'default()') NULL +); diff --git a/Test/SqlDom/Only130SyntaxTests.cs b/Test/SqlDom/Only130SyntaxTests.cs index 0665459f..945d90ef 100644 --- a/Test/SqlDom/Only130SyntaxTests.cs +++ b/Test/SqlDom/Only130SyntaxTests.cs @@ -27,6 +27,7 @@ public partial class SqlDomTests new ParserTest130("ColumnStoreInlineIndex130.sql", 10, 10, 10, 10, 10), new ParserTest130("CreateIndexStatementTests130.sql", 6, 6, 6, 6, 6), new ParserTest130("CreateTableTests130.sql", 62, 62, 62, 62, 62), + new ParserTest130("SparseMaskedColumnOrderTests130.sql"), new ParserTest130("CreateAlterSecurityPolicyStatementTests130.sql", 2, 33, 33, 33, 33), new ParserTest130("JsonForClauseTests130.sql", 14, 14, 14, 14, 14), new ParserTest130("DropStatementsTests130.sql", 10, 10, 9, 9, 9), diff --git a/Test/SqlDom/ParserErrorsTests.cs b/Test/SqlDom/ParserErrorsTests.cs index faeb5a68..45b7daf3 100644 --- a/Test/SqlDom/ParserErrorsTests.cs +++ b/Test/SqlDom/ParserErrorsTests.cs @@ -206,6 +206,21 @@ public void CreateTableLedgerGeneratedAlwaysNegativeTest() new ParserErrorInfo(106, "SQL46010", "HIDEN")); } + /// + /// Negative test for GitHub issue #216: MASKED WITH must not be specified twice on the + /// same column, even though SPARSE and MASKED WITH are now accepted in either order. + /// + [TestMethod] + [Priority(0)] + [SqlStudioTestCategory(Category.UnitTest)] + public void SparseMaskedColumnNegativeTest() + { + string doubleMasked = + "CREATE TABLE t (c VARCHAR(100) MASKED WITH (FUNCTION = 'default()') SPARSE MASKED WITH (FUNCTION = 'email()') NULL)"; + ParserTestUtils.ErrorTest140(doubleMasked, + new ParserErrorInfo(doubleMasked.IndexOf("SPARSE MASKED") + 7, "SQL46010", "MASKED")); + } + /// /// Negative tests for HIDDEN columns /// diff --git a/Test/SqlDom/TestScripts/SparseMaskedColumnOrderTests130.sql b/Test/SqlDom/TestScripts/SparseMaskedColumnOrderTests130.sql new file mode 100644 index 00000000..8a29c18c --- /dev/null +++ b/Test/SqlDom/TestScripts/SparseMaskedColumnOrderTests130.sql @@ -0,0 +1,12 @@ +-- Regression tests for GitHub issue #216: SPARSE combined with MASKED WITH must parse in +-- either clause order. The order documented by the CREATE TABLE reference is SPARSE first, +-- then MASKED WITH. + +-- Documented order: SPARSE before MASKED WITH (this is the case that used to fail). +CREATE TABLE t (c VARCHAR(100) SPARSE MASKED WITH (FUNCTION = 'default()') NULL); + +-- Reversed order: MASKED WITH before SPARSE (this already worked). +CREATE TABLE t (c VARCHAR(100) MASKED WITH (FUNCTION = 'default()') SPARSE NULL); + +-- SPARSE FILESTREAM storage followed by MASKED WITH. +CREATE TABLE t (c VARBINARY(MAX) FILESTREAM SPARSE MASKED WITH (FUNCTION = 'default()') NULL);