From d605dbfbdbf19a19456d64c7887c20e067c9c76b Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Feb 2026 08:43:56 +1100 Subject: [PATCH 1/2] Format IN clauses and add tests Break long IN/NOT IN value lists into multi-line, indented form when generating SQL scripts by adding InPredicateCollector and updating FormattedScriptGenerator to replace single-line value lists. Add tests CommandInClause and CommandNotInClause plus their verified outputs. Update readme snippet anchors to match new source line ranges. --- readme.md | 10 ++--- src/Tests/Tests.CommandInClause.verified.txt | 11 ++++++ .../Tests.CommandNotInClause.verified.txt | 10 +++++ src/Tests/Tests.cs | 20 ++++++++++ .../FormattedScriptGenerator.cs | 37 +++++++++++++++++++ src/Verify.SqlServer/InPredicateCollector.cs | 7 ++++ 6 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 src/Tests/Tests.CommandInClause.verified.txt create mode 100644 src/Tests/Tests.CommandNotInClause.verified.txt create mode 100644 src/Verify.SqlServer/InPredicateCollector.cs diff --git a/readme.md b/readme.md index 3ab1ebb..dbfa43d 100644 --- a/readme.md +++ b/readme.md @@ -65,7 +65,7 @@ await Verify(connection) // include only tables and views .SchemaIncludes(DbObjects.Tables | DbObjects.Views); ``` -snippet source | anchor +snippet source | anchor Available values: @@ -103,7 +103,7 @@ await Verify(connection) _ => _ is TableViewBase || _.Name == "MyTrigger"); ``` -snippet source | anchor +snippet source | anchor @@ -125,7 +125,7 @@ command.CommandText = "select Value from MyTable"; var value = await command.ExecuteScalarAsync(); await Verify(value!); ``` -snippet source | anchor +snippet source | anchor Will result in the following verified file: @@ -180,7 +180,7 @@ await Verify( sqlEntries = entries }); ``` -snippet source | anchor +snippet source | anchor @@ -208,7 +208,7 @@ var sqlErrorsViaType = entries .Select(_ => _.Data) .OfType(); ``` -snippet source | anchor +snippet source | anchor diff --git a/src/Tests/Tests.CommandInClause.verified.txt b/src/Tests/Tests.CommandInClause.verified.txt new file mode 100644 index 0000000..f0823a1 --- /dev/null +++ b/src/Tests/Tests.CommandInClause.verified.txt @@ -0,0 +1,11 @@ +{ + Text: +select Id, + Name +from MyTable +where Id in (1, + 2, + 4, + 6), + HasTransaction: false +} \ No newline at end of file diff --git a/src/Tests/Tests.CommandNotInClause.verified.txt b/src/Tests/Tests.CommandNotInClause.verified.txt new file mode 100644 index 0000000..057bccd --- /dev/null +++ b/src/Tests/Tests.CommandNotInClause.verified.txt @@ -0,0 +1,10 @@ +{ + Text: +select Id, + Name +from MyTable +where Id not in (1, + 2, + 4), + HasTransaction: false +} \ No newline at end of file diff --git a/src/Tests/Tests.cs b/src/Tests/Tests.cs index 5c574e8..c5afaff 100644 --- a/src/Tests/Tests.cs +++ b/src/Tests/Tests.cs @@ -194,6 +194,26 @@ public async Task CommandOrderByMultipleColumnsDesc() await Verify(command); } + [Test] + public async Task CommandInClause() + { + var command = new SqlCommand + { + CommandText = "select Id, Name from MyTable where Id in (1, 2, 4, 6)" + }; + await Verify(command); + } + + [Test] + public async Task CommandNotInClause() + { + var command = new SqlCommand + { + CommandText = "select Id, Name from MyTable where Id not in (1, 2, 4)" + }; + await Verify(command); + } + [Test] public async Task Exception() { diff --git a/src/Verify.SqlServer/FormattedScriptGenerator.cs b/src/Verify.SqlServer/FormattedScriptGenerator.cs index 26cc059..662f7cb 100644 --- a/src/Verify.SqlServer/FormattedScriptGenerator.cs +++ b/src/Verify.SqlServer/FormattedScriptGenerator.cs @@ -50,6 +50,43 @@ public string GenerateScript(TSqlFragment fragment) #endif } + var inCollector = new InPredicateCollector(); + fragment.Accept(inCollector); + + foreach (var inPredicate in inCollector.Predicates) + { + if (inPredicate.Values.Count <= 1) + { + continue; + } + + var values = new List(inPredicate.Values.Count); + foreach (var value in inPredicate.Values) + { + generator.GenerateScript(value, out var text); + values.Add(text); + } + + var singleLine = string.Join(", ", values); + + var pos = script.IndexOf(singleLine, StringComparison.Ordinal); + if (pos == -1) + { + continue; + } + + var lineStart = script.LastIndexOf('\n', pos) + 1; + var indent = new string(' ', pos - lineStart); + +#if NET48 + var multiLine = string.Join(",\n" + indent, values); + script = script.Substring(0, pos) + multiLine + script.Substring(pos + singleLine.Length); +#else + var multiLine = string.Join(",\n" + indent, values); + script = string.Concat(script.AsSpan(0, pos), multiLine, script.AsSpan(pos + singleLine.Length)); +#endif + } + return script; } } \ No newline at end of file diff --git a/src/Verify.SqlServer/InPredicateCollector.cs b/src/Verify.SqlServer/InPredicateCollector.cs new file mode 100644 index 0000000..8291a90 --- /dev/null +++ b/src/Verify.SqlServer/InPredicateCollector.cs @@ -0,0 +1,7 @@ +class InPredicateCollector : TSqlFragmentVisitor +{ + public List Predicates { get; } = []; + + public override void Visit(InPredicate node) => + Predicates.Add(node); +} From d65b62967d30f22c54a041fee0b2cc336d1b54ae Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 6 Feb 2026 08:44:09 +1100 Subject: [PATCH 2/2] Update Directory.Build.props --- src/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 13c1622..6f172e4 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ CS1591;CS0649;CS8632;NU1608;NU1109 - 11.3.0 + 11.3.1 preview 1.0.0 SqlServer, Verify