From c8cdd1b29739c48ed5a897eecea17c0390c711b5 Mon Sep 17 00:00:00 2001 From: Aasim Khan Date: Thu, 21 May 2026 09:33:16 -0700 Subject: [PATCH] Fix PERSIST_SAMPLE_PERCENT statistics option parsing --- .../Parser/TSql/CodeGenerationSupporter.cs | 1 + .../Parser/TSql/StatisticsOptionKind.cs | 1 + SqlScriptDom/Parser/TSql/TSql130.g | 18 +++++++++++++++ SqlScriptDom/Parser/TSql/TSql140.g | 18 +++++++++++++++ SqlScriptDom/Parser/TSql/TSql150.g | 18 +++++++++++++++ SqlScriptDom/Parser/TSql/TSql160.g | 18 +++++++++++++++ SqlScriptDom/Parser/TSql/TSql170.g | 18 +++++++++++++++ SqlScriptDom/Parser/TSql/TSql180.g | 18 +++++++++++++++ ...ScriptGeneratorVisitor.StatisticsOption.cs | 5 ++++- ...PersistSamplePercentStatisticsTests130.sql | 22 +++++++++++++++++++ ...PersistSamplePercentStatisticsTests130.sql | 22 +++++++++++++++++++ Test/SqlDom/Only130SyntaxTests.cs | 3 ++- Test/SqlDom/Only180SyntaxTests.cs | 3 ++- ...PersistSamplePercentStatisticsTests130.sql | 8 +++++++ 14 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 Test/SqlDom/Baselines130/PersistSamplePercentStatisticsTests130.sql create mode 100644 Test/SqlDom/Baselines180/PersistSamplePercentStatisticsTests130.sql create mode 100644 Test/SqlDom/TestScripts/PersistSamplePercentStatisticsTests130.sql diff --git a/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs b/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs index a7e52ae..e271285 100644 --- a/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs +++ b/SqlScriptDom/Parser/TSql/CodeGenerationSupporter.cs @@ -778,6 +778,7 @@ internal static class CodeGenerationSupporter internal const string Period = "PERIOD"; internal const string PermissionSet = "PERMISSION_SET"; internal const string PerNode = "PER_NODE"; + internal const string PersistSamplePercent = "PERSIST_SAMPLE_PERCENT"; internal const string Persisted = "PERSISTED"; internal const string PhysicalOnly = "PHYSICAL_ONLY"; internal const string PhysName = "PHYSNAME"; diff --git a/SqlScriptDom/Parser/TSql/StatisticsOptionKind.cs b/SqlScriptDom/Parser/TSql/StatisticsOptionKind.cs index acc8eb0..9e5404b 100644 --- a/SqlScriptDom/Parser/TSql/StatisticsOptionKind.cs +++ b/SqlScriptDom/Parser/TSql/StatisticsOptionKind.cs @@ -30,6 +30,7 @@ public enum StatisticsOptionKind Rows = 11, Incremental = 12, AutoDrop = 13, + PersistSamplePercent = 14, } #pragma warning restore 1591 diff --git a/SqlScriptDom/Parser/TSql/TSql130.g b/SqlScriptDom/Parser/TSql/TSql130.g index 1212d2b..4ef84a3 100644 --- a/SqlScriptDom/Parser/TSql/TSql130.g +++ b/SqlScriptDom/Parser/TSql/TSql130.g @@ -12267,10 +12267,26 @@ incrementalStatisticsOption returns [OnOffStatisticsOption vResult = this.Fragme } ; +persistSamplePercentStatisticsOption returns [OnOffStatisticsOption vResult = this.FragmentFactory.CreateFragment()] +{ + OptionState vOptionState; +} + : tOption:Identifier EqualsSign vOptionState=optionOnOff[vResult] + { + Match(tOption, CodeGenerationSupporter.PersistSamplePercent); + vResult.OptionKind = StatisticsOptionKind.PersistSamplePercent; + vResult.OptionState = vOptionState; + UpdateTokenInfo(vResult, tOption); + } + ; + createStatisticsStatementWithOption[ref bool isConflictingOption] returns [StatisticsOption vResult] : {NextTokenMatches(CodeGenerationSupporter.Incremental)}? vResult = incrementalStatisticsOption + | + {NextTokenMatches(CodeGenerationSupporter.PersistSamplePercent)}? + vResult = persistSamplePercentStatisticsOption | vResult = sampleStatisticsOption[ref isConflictingOption] | @@ -12327,6 +12343,8 @@ updateStatisticsStatementWithOption [ref bool isConflictingOption] returns [Stat vResult = resampleStatisticsOption | {NextTokenMatches(CodeGenerationSupporter.Incremental)}? vResult = incrementalStatisticsOption + | {NextTokenMatches(CodeGenerationSupporter.PersistSamplePercent)}? + vResult = persistSamplePercentStatisticsOption | vResult = sampleStatisticsOption[ref isConflictingOption] | {NextTokenMatches(CodeGenerationSupporter.StatsStream)}? diff --git a/SqlScriptDom/Parser/TSql/TSql140.g b/SqlScriptDom/Parser/TSql/TSql140.g index 65ccc86..323a91a 100644 --- a/SqlScriptDom/Parser/TSql/TSql140.g +++ b/SqlScriptDom/Parser/TSql/TSql140.g @@ -12673,10 +12673,26 @@ incrementalStatisticsOption returns [OnOffStatisticsOption vResult = this.Fragme } ; +persistSamplePercentStatisticsOption returns [OnOffStatisticsOption vResult = this.FragmentFactory.CreateFragment()] +{ + OptionState vOptionState; +} + : tOption:Identifier EqualsSign vOptionState=optionOnOff[vResult] + { + Match(tOption, CodeGenerationSupporter.PersistSamplePercent); + vResult.OptionKind = StatisticsOptionKind.PersistSamplePercent; + vResult.OptionState = vOptionState; + UpdateTokenInfo(vResult, tOption); + } + ; + createStatisticsStatementWithOption[ref bool isConflictingOption] returns [StatisticsOption vResult] : {NextTokenMatches(CodeGenerationSupporter.Incremental)}? vResult = incrementalStatisticsOption + | + {NextTokenMatches(CodeGenerationSupporter.PersistSamplePercent)}? + vResult = persistSamplePercentStatisticsOption | vResult = sampleStatisticsOption[ref isConflictingOption] | @@ -12733,6 +12749,8 @@ updateStatisticsStatementWithOption [ref bool isConflictingOption] returns [Stat vResult = resampleStatisticsOption | {NextTokenMatches(CodeGenerationSupporter.Incremental)}? vResult = incrementalStatisticsOption + | {NextTokenMatches(CodeGenerationSupporter.PersistSamplePercent)}? + vResult = persistSamplePercentStatisticsOption | vResult = sampleStatisticsOption[ref isConflictingOption] | {NextTokenMatches(CodeGenerationSupporter.StatsStream)}? diff --git a/SqlScriptDom/Parser/TSql/TSql150.g b/SqlScriptDom/Parser/TSql/TSql150.g index 2b01332..6fc72df 100644 --- a/SqlScriptDom/Parser/TSql/TSql150.g +++ b/SqlScriptDom/Parser/TSql/TSql150.g @@ -13340,10 +13340,26 @@ incrementalStatisticsOption returns [OnOffStatisticsOption vResult = this.Fragme } ; +persistSamplePercentStatisticsOption returns [OnOffStatisticsOption vResult = this.FragmentFactory.CreateFragment()] +{ + OptionState vOptionState; +} + : tOption:Identifier EqualsSign vOptionState=optionOnOff[vResult] + { + Match(tOption, CodeGenerationSupporter.PersistSamplePercent); + vResult.OptionKind = StatisticsOptionKind.PersistSamplePercent; + vResult.OptionState = vOptionState; + UpdateTokenInfo(vResult, tOption); + } + ; + createStatisticsStatementWithOption[ref bool isConflictingOption] returns [StatisticsOption vResult] : {NextTokenMatches(CodeGenerationSupporter.Incremental)}? vResult = incrementalStatisticsOption + | + {NextTokenMatches(CodeGenerationSupporter.PersistSamplePercent)}? + vResult = persistSamplePercentStatisticsOption | vResult = sampleStatisticsOption[ref isConflictingOption] | @@ -13400,6 +13416,8 @@ updateStatisticsStatementWithOption [ref bool isConflictingOption] returns [Stat vResult = resampleStatisticsOption | {NextTokenMatches(CodeGenerationSupporter.Incremental)}? vResult = incrementalStatisticsOption + | {NextTokenMatches(CodeGenerationSupporter.PersistSamplePercent)}? + vResult = persistSamplePercentStatisticsOption | vResult = sampleStatisticsOption[ref isConflictingOption] | {NextTokenMatches(CodeGenerationSupporter.StatsStream)}? diff --git a/SqlScriptDom/Parser/TSql/TSql160.g b/SqlScriptDom/Parser/TSql/TSql160.g index c45d6ff..9bedb72 100644 --- a/SqlScriptDom/Parser/TSql/TSql160.g +++ b/SqlScriptDom/Parser/TSql/TSql160.g @@ -13385,6 +13385,19 @@ autoDropStatisticsOption returns [OnOffStatisticsOption vResult = this.FragmentF } ; +persistSamplePercentStatisticsOption returns [OnOffStatisticsOption vResult = this.FragmentFactory.CreateFragment()] +{ + OptionState vOptionState; +} + : tOption:Identifier EqualsSign vOptionState=optionOnOff[vResult] + { + Match(tOption, CodeGenerationSupporter.PersistSamplePercent); + vResult.OptionKind = StatisticsOptionKind.PersistSamplePercent; + vResult.OptionState = vOptionState; + UpdateTokenInfo(vResult, tOption); + } + ; + createStatisticsStatementWithOption[ref bool isConflictingOption] returns [StatisticsOption vResult] : {NextTokenMatches(CodeGenerationSupporter.Incremental)}? @@ -13392,6 +13405,9 @@ createStatisticsStatementWithOption[ref bool isConflictingOption] returns [Stati | {NextTokenMatches(CodeGenerationSupporter.AutoDrop)}? vResult = autoDropStatisticsOption + | + {NextTokenMatches(CodeGenerationSupporter.PersistSamplePercent)}? + vResult = persistSamplePercentStatisticsOption | vResult = sampleStatisticsOption[ref isConflictingOption] | @@ -13450,6 +13466,8 @@ updateStatisticsStatementWithOption [ref bool isConflictingOption] returns [Stat vResult = incrementalStatisticsOption | {NextTokenMatches(CodeGenerationSupporter.AutoDrop)}? vResult = autoDropStatisticsOption + | {NextTokenMatches(CodeGenerationSupporter.PersistSamplePercent)}? + vResult = persistSamplePercentStatisticsOption | vResult = sampleStatisticsOption[ref isConflictingOption] | {NextTokenMatches(CodeGenerationSupporter.StatsStream)}? diff --git a/SqlScriptDom/Parser/TSql/TSql170.g b/SqlScriptDom/Parser/TSql/TSql170.g index 65bb5a9..be8d318 100644 --- a/SqlScriptDom/Parser/TSql/TSql170.g +++ b/SqlScriptDom/Parser/TSql/TSql170.g @@ -13440,6 +13440,19 @@ autoDropStatisticsOption returns [OnOffStatisticsOption vResult = this.FragmentF } ; +persistSamplePercentStatisticsOption returns [OnOffStatisticsOption vResult = this.FragmentFactory.CreateFragment()] +{ + OptionState vOptionState; +} + : tOption:Identifier EqualsSign vOptionState=optionOnOff[vResult] + { + Match(tOption, CodeGenerationSupporter.PersistSamplePercent); + vResult.OptionKind = StatisticsOptionKind.PersistSamplePercent; + vResult.OptionState = vOptionState; + UpdateTokenInfo(vResult, tOption); + } + ; + createStatisticsStatementWithOption[ref bool isConflictingOption] returns [StatisticsOption vResult] : {NextTokenMatches(CodeGenerationSupporter.Incremental)}? @@ -13447,6 +13460,9 @@ createStatisticsStatementWithOption[ref bool isConflictingOption] returns [Stati | {NextTokenMatches(CodeGenerationSupporter.AutoDrop)}? vResult = autoDropStatisticsOption + | + {NextTokenMatches(CodeGenerationSupporter.PersistSamplePercent)}? + vResult = persistSamplePercentStatisticsOption | vResult = sampleStatisticsOption[ref isConflictingOption] | @@ -13505,6 +13521,8 @@ updateStatisticsStatementWithOption [ref bool isConflictingOption] returns [Stat vResult = incrementalStatisticsOption | {NextTokenMatches(CodeGenerationSupporter.AutoDrop)}? vResult = autoDropStatisticsOption + | {NextTokenMatches(CodeGenerationSupporter.PersistSamplePercent)}? + vResult = persistSamplePercentStatisticsOption | vResult = sampleStatisticsOption[ref isConflictingOption] | {NextTokenMatches(CodeGenerationSupporter.StatsStream)}? diff --git a/SqlScriptDom/Parser/TSql/TSql180.g b/SqlScriptDom/Parser/TSql/TSql180.g index 70ddca4..46131b0 100644 --- a/SqlScriptDom/Parser/TSql/TSql180.g +++ b/SqlScriptDom/Parser/TSql/TSql180.g @@ -13440,6 +13440,19 @@ autoDropStatisticsOption returns [OnOffStatisticsOption vResult = this.FragmentF } ; +persistSamplePercentStatisticsOption returns [OnOffStatisticsOption vResult = this.FragmentFactory.CreateFragment()] +{ + OptionState vOptionState; +} + : tOption:Identifier EqualsSign vOptionState=optionOnOff[vResult] + { + Match(tOption, CodeGenerationSupporter.PersistSamplePercent); + vResult.OptionKind = StatisticsOptionKind.PersistSamplePercent; + vResult.OptionState = vOptionState; + UpdateTokenInfo(vResult, tOption); + } + ; + createStatisticsStatementWithOption[ref bool isConflictingOption] returns [StatisticsOption vResult] : {NextTokenMatches(CodeGenerationSupporter.Incremental)}? @@ -13447,6 +13460,9 @@ createStatisticsStatementWithOption[ref bool isConflictingOption] returns [Stati | {NextTokenMatches(CodeGenerationSupporter.AutoDrop)}? vResult = autoDropStatisticsOption + | + {NextTokenMatches(CodeGenerationSupporter.PersistSamplePercent)}? + vResult = persistSamplePercentStatisticsOption | vResult = sampleStatisticsOption[ref isConflictingOption] | @@ -13505,6 +13521,8 @@ updateStatisticsStatementWithOption [ref bool isConflictingOption] returns [Stat vResult = incrementalStatisticsOption | {NextTokenMatches(CodeGenerationSupporter.AutoDrop)}? vResult = autoDropStatisticsOption + | {NextTokenMatches(CodeGenerationSupporter.PersistSamplePercent)}? + vResult = persistSamplePercentStatisticsOption | vResult = sampleStatisticsOption[ref isConflictingOption] | {NextTokenMatches(CodeGenerationSupporter.StatsStream)}? diff --git a/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.StatisticsOption.cs b/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.StatisticsOption.cs index 708583c..d28bb30 100644 --- a/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.StatisticsOption.cs +++ b/SqlScriptDom/ScriptDom/SqlServer/ScriptGenerator/SqlScriptGeneratorVisitor.StatisticsOption.cs @@ -81,6 +81,9 @@ public override void ExplicitVisit(OnOffStatisticsOption node) case StatisticsOptionKind.AutoDrop: optionName = CodeGenerationSupporter.AutoDrop; break; + case StatisticsOptionKind.PersistSamplePercent: + optionName = CodeGenerationSupporter.PersistSamplePercent; + break; default: System.Diagnostics.Debug.Assert(false, "invalid option encountered"); break; @@ -110,4 +113,4 @@ public override void ExplicitVisit(ResampleStatisticsOption node) } } } -} \ No newline at end of file +} diff --git a/Test/SqlDom/Baselines130/PersistSamplePercentStatisticsTests130.sql b/Test/SqlDom/Baselines130/PersistSamplePercentStatisticsTests130.sql new file mode 100644 index 0000000..92f20c0 --- /dev/null +++ b/Test/SqlDom/Baselines130/PersistSamplePercentStatisticsTests130.sql @@ -0,0 +1,22 @@ +UPDATE STATISTICS dbo.MyTable + WITH FULLSCAN, PERSIST_SAMPLE_PERCENT = ON; + +UPDATE STATISTICS dbo.T + WITH FULLSCAN, PERSIST_SAMPLE_PERCENT = OFF; + +UPDATE STATISTICS dbo.T + WITH PERSIST_SAMPLE_PERCENT = ON; + +UPDATE STATISTICS dbo.T + WITH PERSIST_SAMPLE_PERCENT = ON, FULLSCAN; + +UPDATE STATISTICS dbo.T + WITH SAMPLE 50 PERCENT, PERSIST_SAMPLE_PERCENT = ON; + +CREATE STATISTICS stat1 + ON dbo.T(c1) + WITH FULLSCAN, PERSIST_SAMPLE_PERCENT = ON; + +CREATE STATISTICS stat2 + ON dbo.T(c1) + WITH SAMPLE 25 PERCENT, PERSIST_SAMPLE_PERCENT = OFF; diff --git a/Test/SqlDom/Baselines180/PersistSamplePercentStatisticsTests130.sql b/Test/SqlDom/Baselines180/PersistSamplePercentStatisticsTests130.sql new file mode 100644 index 0000000..92f20c0 --- /dev/null +++ b/Test/SqlDom/Baselines180/PersistSamplePercentStatisticsTests130.sql @@ -0,0 +1,22 @@ +UPDATE STATISTICS dbo.MyTable + WITH FULLSCAN, PERSIST_SAMPLE_PERCENT = ON; + +UPDATE STATISTICS dbo.T + WITH FULLSCAN, PERSIST_SAMPLE_PERCENT = OFF; + +UPDATE STATISTICS dbo.T + WITH PERSIST_SAMPLE_PERCENT = ON; + +UPDATE STATISTICS dbo.T + WITH PERSIST_SAMPLE_PERCENT = ON, FULLSCAN; + +UPDATE STATISTICS dbo.T + WITH SAMPLE 50 PERCENT, PERSIST_SAMPLE_PERCENT = ON; + +CREATE STATISTICS stat1 + ON dbo.T(c1) + WITH FULLSCAN, PERSIST_SAMPLE_PERCENT = ON; + +CREATE STATISTICS stat2 + ON dbo.T(c1) + WITH SAMPLE 25 PERCENT, PERSIST_SAMPLE_PERCENT = OFF; diff --git a/Test/SqlDom/Only130SyntaxTests.cs b/Test/SqlDom/Only130SyntaxTests.cs index 700629b..0665459 100644 --- a/Test/SqlDom/Only130SyntaxTests.cs +++ b/Test/SqlDom/Only130SyntaxTests.cs @@ -74,7 +74,8 @@ public partial class SqlDomTests new ParserTest130("AlterWorkloadGroupStatementSqlDwTests.sql", 12, 6, 5, 5, 5), new ParserTest130("CreateWorkloadClassifierStatementSqlDwTests.sql", 2, 1, 1, 1, 1), new ParserTest130("PredictSqlDwTests.sql", 3, 3, 3, 3, 3), - new ParserTest130("CreateEventSessionNotLikePredicate.sql", 2, 1, 1, 1, 1) + new ParserTest130("CreateEventSessionNotLikePredicate.sql", 2, 1, 1, 1, 1), + new ParserTest130("PersistSamplePercentStatisticsTests130.sql") }; [TestMethod] diff --git a/Test/SqlDom/Only180SyntaxTests.cs b/Test/SqlDom/Only180SyntaxTests.cs index b68b9ba..cd8746d 100644 --- a/Test/SqlDom/Only180SyntaxTests.cs +++ b/Test/SqlDom/Only180SyntaxTests.cs @@ -13,7 +13,8 @@ public partial class SqlDomTests private static readonly ParserTest[] Only180TestInfos = { new ParserTest180("AutomaticIndexCompactionTests180.sql", nErrors80: 2, nErrors90: 2, nErrors100: 2, nErrors110: 2, nErrors120: 2, nErrors130: 2, nErrors140: 2, nErrors150: 2, nErrors160: 2, nErrors170: 2), - new ParserTest180("TrimFromReturnTests160.sql") + new ParserTest180("TrimFromReturnTests160.sql"), + new ParserTest180("PersistSamplePercentStatisticsTests130.sql") }; private static readonly ParserTest[] SqlAzure180_TestInfos = diff --git a/Test/SqlDom/TestScripts/PersistSamplePercentStatisticsTests130.sql b/Test/SqlDom/TestScripts/PersistSamplePercentStatisticsTests130.sql new file mode 100644 index 0000000..3c66b11 --- /dev/null +++ b/Test/SqlDom/TestScripts/PersistSamplePercentStatisticsTests130.sql @@ -0,0 +1,8 @@ +UPDATE STATISTICS dbo.MyTable WITH FULLSCAN, PERSIST_SAMPLE_PERCENT = ON; +UPDATE STATISTICS dbo.T WITH FULLSCAN, PERSIST_SAMPLE_PERCENT = OFF; +UPDATE STATISTICS dbo.T WITH PERSIST_SAMPLE_PERCENT = ON; +UPDATE STATISTICS dbo.T WITH PERSIST_SAMPLE_PERCENT = ON, FULLSCAN; +UPDATE STATISTICS dbo.T WITH SAMPLE 50 PERCENT, PERSIST_SAMPLE_PERCENT = ON; + +CREATE STATISTICS stat1 ON dbo.T(c1) WITH FULLSCAN, PERSIST_SAMPLE_PERCENT = ON; +CREATE STATISTICS stat2 ON dbo.T(c1) WITH SAMPLE 25 PERCENT, PERSIST_SAMPLE_PERCENT = OFF;