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);