From 45c521ac8df8e6c55a19630bd632e1b51e0b59fd Mon Sep 17 00:00:00 2001 From: Isaac Devine Date: Sun, 16 Aug 2026 21:37:39 +1200 Subject: [PATCH 1/4] Add missing tests for sql server batch splitting Help make the SQL Server Batch Splitting more robust by adding test cases which don't work well with the current regex-based replacement. These are taken from https://github.com/chucknorris/roundhouse/pull/82 --- .../Basic_tests/BatchSplitterReplacer_.cs | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/unittests/SqlServer/Basic_tests/BatchSplitterReplacer_.cs b/unittests/SqlServer/Basic_tests/BatchSplitterReplacer_.cs index 61814679..5133c6f1 100644 --- a/unittests/SqlServer/Basic_tests/BatchSplitterReplacer_.cs +++ b/unittests/SqlServer/Basic_tests/BatchSplitterReplacer_.cs @@ -254,6 +254,35 @@ public void go_after_double_dash_comment_with_single_quote_and_single_quote_afte Assert.Equal(expected_scrubbed, sql_statement_scrubbed); } + [Fact] + public void lowercase_go_statement() + { + const string sql_to_match = "\r\nwhere DataName = 'AttributeKeyMap'\r\ngo "; + string expected_scrubbed = "\r\nwhere DataName = 'AttributeKeyMap'\r\n" + + Batch_terminator_replacement_string + " "; + _testOutput.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.Equal(expected_scrubbed, sql_statement_scrubbed); + } + + [Fact] + public void go_after_line_comment_containing_an_apostrophe() + { + const string sql_to_match = @"select 1 -- ' +GO +'' +GO +"; + string expected_scrubbed = @"select 1 -- ' +" + Batch_terminator_replacement_string + @" +'' +" + Batch_terminator_replacement_string + @" +"; + _testOutput.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.Equal(expected_scrubbed, sql_statement_scrubbed); + } + [Fact] public void go_with_comment_after() { @@ -431,6 +460,20 @@ public void go_when_between_tick_marks() Assert.Equal(expected_scrubbed, sql_statement_scrubbed); } + [Fact] + public void go_inside_a_multiline_string_literal() + { + const string sql_to_match = @"select ' 1 -- +GO +'"; + const string expected_scrubbed = @"select ' 1 -- +GO +'"; + _testOutput.WriteLine(sql_to_match); + string sql_statement_scrubbed = _replacer.Replace(sql_to_match); + Assert.Equal(expected_scrubbed, sql_statement_scrubbed); + } + [Fact] public void go_when_between_tick_marks_with_symbols_and_words_before_ending_on_same_line() @@ -514,6 +557,18 @@ public void go_inside_of_comments_with_a_line_break() Assert.Equal(expected_scrubbed, sql_statement_scrubbed); } + [Fact] + public void go_inside_of_nested_comments_with_a_line_break() + { + const string sql_to_match = @"/* /* */ GO +*/"; + const string expected_scrubbed = @"/* /* */ GO +*/"; + _testOutput.WriteLine(sql_to_match); + string sql_statement_scrubbed = _replacer.Replace(sql_to_match); + Assert.Equal(expected_scrubbed, sql_statement_scrubbed); + } + [Fact] public void go_inside_of_comments_with_words_before() { From fc657d6f4ebce8291029ee47fd741f44d552ae27 Mon Sep 17 00:00:00 2001 From: Isaac Devine Date: Mon, 17 Aug 2026 10:41:12 +1200 Subject: [PATCH 2/4] Better encapsulate statement splitting Hide the implementation details that it is implemented by regular expressions to allow easier implementation of different techniques in the future. Also a note on the terminology, it is probably better named batch splitting, but I wanted to keep the implementation as close to how it was originally; hence the same naming. Furthermore, dependent code doesn't care *how* the statements are split, just that they are - so this further encapsulates that decision and is a better abstraction point. Notably some tests have changed from using raw string literals, instead being parameterized to accept the line endings in different environments (i.e. Windows and everything else). This helps the tests be more consistent. (Thanks to @wokket for pointing this issue out: https://github.com/grate-devs/grate/pull/816#discussion_r3844060141 ) --- .../Infrastructure/BatchSplitterReplacer.cs | 27 - .../Infrastructure/IStatementSplitter.cs | 9 + src/grate.core/Infrastructure/ISyntax.cs | 2 +- .../Infrastructure/RegexStatementSplitter.cs | 59 ++ .../Infrastructure/StatementSplitter.cs | 33 - src/grate.core/Migration/AnsiSqlDatabase.cs | 5 +- .../MariaDbStatementSplitter.cs | 11 + .../Infrastructure/MariaDbSyntax.cs | 13 +- .../Infrastructure/OracleStatementSplitter.cs | 11 + .../Infrastructure/OracleSyntax.cs | 13 +- .../PostgreSqlStatementSplitter.cs | 22 + .../Infrastructure/PostgreSqlSyntax.cs | 15 +- .../Infrastructure/SqliteStatementSplitter.cs | 11 + .../Infrastructure/SqliteSyntax.cs | 13 +- .../SqlServerStatementSplitter.cs | 11 + .../Infrastructure/SqlServerSyntax.cs | 13 +- .../Basic_tests/BatchSplitterReplacer_.cs | 580 ---------------- .../Basic_tests/OracleStatementSplitter_.cs | 504 ++++++++++++++ .../Oracle/Basic_tests/StatementSplitter_.cs | 33 - .../OracleSplitterContext.cs | 52 +- ...er_.cs => PostgreSqlStatementSplitter_.cs} | 9 +- .../Basic_tests/BatchSplitterReplacer_.cs | 653 ------------------ .../SqlServerStatementSplitter_.cs | 589 ++++++++++++++++ .../Basic_tests/StatementSplitter_.cs | 33 - .../SqlServerSplitterContext.cs | 51 +- .../Basic_tests/BatchSplitterReplacer_.cs | 597 ---------------- .../SqlServerStatementSplitter_.cs | 539 +++++++++++++++ .../Basic_tests/StatementSplitter_.cs | 32 - .../SqlServerSplitterContext.cs | 51 +- 29 files changed, 1819 insertions(+), 2172 deletions(-) delete mode 100644 src/grate.core/Infrastructure/BatchSplitterReplacer.cs create mode 100644 src/grate.core/Infrastructure/IStatementSplitter.cs create mode 100644 src/grate.core/Infrastructure/RegexStatementSplitter.cs delete mode 100644 src/grate.core/Infrastructure/StatementSplitter.cs create mode 100644 src/grate.mariadb/Infrastructure/MariaDbStatementSplitter.cs create mode 100644 src/grate.oracle/Infrastructure/OracleStatementSplitter.cs create mode 100644 src/grate.postgresql/Infrastructure/PostgreSqlStatementSplitter.cs create mode 100644 src/grate.sqlite/Infrastructure/SqliteStatementSplitter.cs create mode 100644 src/grate.sqlserver/Infrastructure/SqlServerStatementSplitter.cs delete mode 100644 unittests/Oracle/Basic_tests/BatchSplitterReplacer_.cs create mode 100644 unittests/Oracle/Basic_tests/OracleStatementSplitter_.cs delete mode 100644 unittests/Oracle/Basic_tests/StatementSplitter_.cs rename unittests/PostgreSQL/Statement_Splitting/{StatementSplitter_.cs => PostgreSqlStatementSplitter_.cs} (92%) delete mode 100644 unittests/SqlServer/Basic_tests/BatchSplitterReplacer_.cs create mode 100644 unittests/SqlServer/Basic_tests/SqlServerStatementSplitter_.cs delete mode 100644 unittests/SqlServer/Basic_tests/StatementSplitter_.cs delete mode 100644 unittests/SqlServerCaseSensitive/Basic_tests/BatchSplitterReplacer_.cs create mode 100644 unittests/SqlServerCaseSensitive/Basic_tests/SqlServerStatementSplitter_.cs delete mode 100644 unittests/SqlServerCaseSensitive/Basic_tests/StatementSplitter_.cs diff --git a/src/grate.core/Infrastructure/BatchSplitterReplacer.cs b/src/grate.core/Infrastructure/BatchSplitterReplacer.cs deleted file mode 100644 index cb571314..00000000 --- a/src/grate.core/Infrastructure/BatchSplitterReplacer.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Text.RegularExpressions; -using static System.Text.RegularExpressions.RegexOptions; - -namespace grate.Infrastructure; - -internal class BatchSplitterReplacer -{ - private string Replacement { get; } - private readonly Regex _regex; - - public BatchSplitterReplacer(ISyntax syntax) - { - var pattern = syntax.StatementSeparatorRegex; - Replacement = StatementSplitter.BatchTerminatorReplacementString; - - _regex = new Regex(pattern, IgnoreCase | Multiline); - } - - public string Replace(string text) => _regex.Replace(text, ReplaceBatchSeparator); - - private string ReplaceBatchSeparator(Match match) - { - var groups = match.Groups; - var replacement = groups["BATCHSPLITTER"].Success ? Replacement : string.Empty; - return groups["KEEP1"].Value + replacement + groups["KEEP2"].Value; - } -} diff --git a/src/grate.core/Infrastructure/IStatementSplitter.cs b/src/grate.core/Infrastructure/IStatementSplitter.cs new file mode 100644 index 00000000..7426cda0 --- /dev/null +++ b/src/grate.core/Infrastructure/IStatementSplitter.cs @@ -0,0 +1,9 @@ +namespace grate.Infrastructure; + +/// +/// Splits a batch of SQL text into individual statements to be run separately. +/// +public interface IStatementSplitter +{ + IEnumerable Split(string statement); +} diff --git a/src/grate.core/Infrastructure/ISyntax.cs b/src/grate.core/Infrastructure/ISyntax.cs index 7343e2d4..6b973484 100644 --- a/src/grate.core/Infrastructure/ISyntax.cs +++ b/src/grate.core/Infrastructure/ISyntax.cs @@ -5,7 +5,7 @@ /// public interface ISyntax { - string StatementSeparatorRegex { get; } + IStatementSplitter StatementSplitter { get; } string CurrentDatabase { get; } string ListDatabases { get; } string CreateDatabase(string databaseName, string? password); diff --git a/src/grate.core/Infrastructure/RegexStatementSplitter.cs b/src/grate.core/Infrastructure/RegexStatementSplitter.cs new file mode 100644 index 00000000..13cf6dcc --- /dev/null +++ b/src/grate.core/Infrastructure/RegexStatementSplitter.cs @@ -0,0 +1,59 @@ +using System.Text.RegularExpressions; +using static System.Text.RegularExpressions.RegexOptions; + +namespace grate.Infrastructure; + +/// +/// Base class for splitting SQL batches into statements using a dialect-specific regex. +/// The regex must tag the parts to keep as KEEP1/KEEP2 groups, and the separator to remove as BATCHSPLITTER. +/// +public abstract class RegexStatementSplitter : IStatementSplitter +{ + private const string BatchTerminatorReplacementString = @" |{[_REMOVE_]}| "; + + protected abstract string StringsRegex { get; } + protected abstract string DashCommentsRegex { get; } + protected abstract string StarCommentsRegex { get; } + protected abstract string SeparatorRegex { get; } + + /// + /// Additional regex fragments to be inserted between the strings and comments patterns (e.g. for extra string forms). + /// + protected virtual IEnumerable AdditionalRegexes => Enumerable.Empty(); + + private Regex? _regex; + private Regex Regex => _regex ??= new Regex(Pattern, IgnoreCase | Multiline); + + private string Pattern => + string.Join("|", new[] { StringsRegex } + .Concat(AdditionalRegexes) + .Append(DashCommentsRegex) + .Append(StarCommentsRegex) + .Append(SeparatorRegex)); + + public IEnumerable Split(string statement) + { + var replaced = Replace(statement); + + var statements = replaced.Split(BatchTerminatorReplacementString); + return statements.Where(HasScriptsToRun); + } + + private string Replace(string text) => Regex.Replace(text, ReplaceBatchSeparator); + + private static string ReplaceBatchSeparator(Match match) + { + var groups = match.Groups; + var replacement = groups["BATCHSPLITTER"].Success ? BatchTerminatorReplacementString : string.Empty; + return groups["KEEP1"].Value + replacement + groups["KEEP2"].Value; + } + + private static bool HasScriptsToRun(string sqlStatement) + { + var trimmedStatement = sqlStatement.Replace(BatchTerminatorReplacementString, string.Empty, StringComparison.InvariantCultureIgnoreCase); + return !string.IsNullOrEmpty(trimmedStatement.ToLower() + .Replace(Environment.NewLine, string.Empty) + .Replace("\n", string.Empty) // This is necessary to make script with unix-style line endings work on grate on Windows + .Replace(" ", string.Empty)); + } +} diff --git a/src/grate.core/Infrastructure/StatementSplitter.cs b/src/grate.core/Infrastructure/StatementSplitter.cs deleted file mode 100644 index 91c88134..00000000 --- a/src/grate.core/Infrastructure/StatementSplitter.cs +++ /dev/null @@ -1,33 +0,0 @@ -using grate.Migration; - -namespace grate.Infrastructure; - -internal class StatementSplitter -{ - public const string BatchTerminatorReplacementString = @" |{[_REMOVE_]}| "; - - private readonly BatchSplitterReplacer _replacer; - - public StatementSplitter(ISyntax syntax) - { - _replacer = new BatchSplitterReplacer(syntax); - } - - public IEnumerable Split(string statement) - { - var replaced = _replacer.Replace(statement); - - var statements = replaced.Split(BatchTerminatorReplacementString); - return statements.Where(HasScriptsToRun); - } - - private static bool HasScriptsToRun(string sqlStatement) - { - var trimmedStatement = sqlStatement.Replace(BatchTerminatorReplacementString, string.Empty, StringComparison.InvariantCultureIgnoreCase); - return !string.IsNullOrEmpty(trimmedStatement.ToLower() - .Replace(Environment.NewLine, string.Empty) - .Replace("\n", string.Empty) // This is necessary to make script with unix-style line endings work on grate on Windows - .Replace(" ", string.Empty)); - } - -} diff --git a/src/grate.core/Migration/AnsiSqlDatabase.cs b/src/grate.core/Migration/AnsiSqlDatabase.cs index 84362e2a..b18596fc 100644 --- a/src/grate.core/Migration/AnsiSqlDatabase.cs +++ b/src/grate.core/Migration/AnsiSqlDatabase.cs @@ -36,7 +36,6 @@ protected AnsiSqlDatabase(ILogger logger, ISyntax syntax) { Logger = logger; _syntax = syntax; - StatementSplitter = new StatementSplitter(syntax); } public string ServerName => Connection.DataSource; @@ -54,15 +53,13 @@ protected AnsiSqlDatabase(ILogger logger, ISyntax syntax) public abstract bool SupportsSchemas { get; } public virtual bool SplitBatchStatements => true; - private StatementSplitter StatementSplitter { get; } + private IStatementSplitter StatementSplitter => _syntax.StatementSplitter; public virtual IEnumerable GetStatements(string sql) => SplitBatchStatements ? this.StatementSplitter.Split(sql) : new[] { sql }; public abstract void ThrowScriptFailed(MigrationsFolder folder, string file, string? scriptText, Exception exception); - public string StatementSeparatorRegex => _syntax.StatementSeparatorRegex; - public string ScriptsRunTable => _syntax.TableWithSchema(SchemaName, ScriptsRunTableName); public string ScriptsRunErrorsTable => _syntax.TableWithSchema(SchemaName, ScriptsRunErrorsTableName); public string VersionTable => _syntax.TableWithSchema(SchemaName, VersionTableName); diff --git a/src/grate.mariadb/Infrastructure/MariaDbStatementSplitter.cs b/src/grate.mariadb/Infrastructure/MariaDbStatementSplitter.cs new file mode 100644 index 00000000..99c1acb6 --- /dev/null +++ b/src/grate.mariadb/Infrastructure/MariaDbStatementSplitter.cs @@ -0,0 +1,11 @@ +using grate.Infrastructure; + +namespace grate.MariaDb.Infrastructure; + +public class MariaDbStatementSplitter : RegexStatementSplitter +{ + protected override string StringsRegex => @"(?'[^']*')"; + protected override string DashCommentsRegex => @"(?--.*$)"; + protected override string StarCommentsRegex => @"(?/\*[\S\s]*?\*/)"; + protected override string SeparatorRegex => @"(?^|\s)(?GO)(?\s|;|$)"; +} diff --git a/src/grate.mariadb/Infrastructure/MariaDbSyntax.cs b/src/grate.mariadb/Infrastructure/MariaDbSyntax.cs index 5909ecb7..0c739325 100644 --- a/src/grate.mariadb/Infrastructure/MariaDbSyntax.cs +++ b/src/grate.mariadb/Infrastructure/MariaDbSyntax.cs @@ -3,17 +3,8 @@ namespace grate.MariaDb.Infrastructure; public readonly struct MariaDbSyntax : ISyntax { - public string StatementSeparatorRegex - { - get - { - const string strings = @"(?'[^']*')"; - const string dashComments = @"(?--.*$)"; - const string starComments = @"(?/\*[\S\s]*?\*/)"; - const string separator = @"(?^|\s)(?GO)(?\s|;|$)"; - return strings + "|" + dashComments + "|" + starComments + "|" + separator; - } - } + private static readonly IStatementSplitter _statementSplitter = new MariaDbStatementSplitter(); + public IStatementSplitter StatementSplitter => _statementSplitter; public string CurrentDatabase => "SELECT DATABASE()"; public string ListDatabases => "SHOW DATABASES"; diff --git a/src/grate.oracle/Infrastructure/OracleStatementSplitter.cs b/src/grate.oracle/Infrastructure/OracleStatementSplitter.cs new file mode 100644 index 00000000..b04692d6 --- /dev/null +++ b/src/grate.oracle/Infrastructure/OracleStatementSplitter.cs @@ -0,0 +1,11 @@ +using grate.Infrastructure; + +namespace grate.Oracle.Infrastructure; + +public class OracleStatementSplitter : RegexStatementSplitter +{ + protected override string StringsRegex => @"(?'[^']*')"; + protected override string DashCommentsRegex => @"(?--.*$)"; + protected override string StarCommentsRegex => @"(?/\*[\S\s]*?\*/)"; + protected override string SeparatorRegex => @"(?^|\s)(?/)(?\s|;|$)"; +} diff --git a/src/grate.oracle/Infrastructure/OracleSyntax.cs b/src/grate.oracle/Infrastructure/OracleSyntax.cs index b4463d58..cd85873b 100644 --- a/src/grate.oracle/Infrastructure/OracleSyntax.cs +++ b/src/grate.oracle/Infrastructure/OracleSyntax.cs @@ -4,17 +4,8 @@ namespace grate.Oracle.Infrastructure; public readonly struct OracleSyntax : ISyntax { - public string StatementSeparatorRegex - { - get - { - const string strings = @"(?'[^']*')"; - const string dashComments = @"(?--.*$)"; - const string starComments = @"(?/\*[\S\s]*?\*/)"; - const string separator = @"(?^|\s)(?/)(?\s|;|$)"; - return strings + "|" + dashComments + "|" + starComments + "|" + separator; - } - } + private static readonly IStatementSplitter _statementSplitter = new OracleStatementSplitter(); + public IStatementSplitter StatementSplitter => _statementSplitter; public string CurrentDatabase => "select user from dual"; public string ListDatabases => "SELECT * FROM all_users"; diff --git a/src/grate.postgresql/Infrastructure/PostgreSqlStatementSplitter.cs b/src/grate.postgresql/Infrastructure/PostgreSqlStatementSplitter.cs new file mode 100644 index 00000000..19ea814f --- /dev/null +++ b/src/grate.postgresql/Infrastructure/PostgreSqlStatementSplitter.cs @@ -0,0 +1,22 @@ +using grate.Infrastructure; + +namespace grate.PostgreSql.Infrastructure; + +public class PostgreSqlStatementSplitter : RegexStatementSplitter +{ + protected override string StringsRegex => @"(?'([^']|\'\')*')"; + protected override string DashCommentsRegex => "(?--.*$)"; + protected override string StarCommentsRegex => @"(?/\*[\S\s]*?\*/)"; + protected override string SeparatorRegex => "(?.*)(?(;)(?=(?:[^']|'[^']*')*$))(?.*)"; + + protected override IEnumerable AdditionalRegexes + { + get + { + const string backslashEscapedStrings = @"(?E(?\$(?'tag'\w*)\$[\S\s]*?\$\k'tag'\$)"; + yield return backslashEscapedStrings; + yield return dollarQuotedStrings; + } + } +} diff --git a/src/grate.postgresql/Infrastructure/PostgreSqlSyntax.cs b/src/grate.postgresql/Infrastructure/PostgreSqlSyntax.cs index 78c8ad73..d86008b6 100644 --- a/src/grate.postgresql/Infrastructure/PostgreSqlSyntax.cs +++ b/src/grate.postgresql/Infrastructure/PostgreSqlSyntax.cs @@ -3,19 +3,8 @@ namespace grate.PostgreSql.Infrastructure; public readonly struct PostgreSqlSyntax : ISyntax { - public string StatementSeparatorRegex - { - get - { - const string strings = @"(?'([^']|\'\')*')"; - const string backslashEscapedStrings = @"(?E(?\$(?'tag'\w*)\$[\S\s]*?\$\k'tag'\$)"; - const string dashComments = "(?--.*$)"; - const string starComments = @"(?/\*[\S\s]*?\*/)"; - const string separator = "(?.*)(?(;)(?=(?:[^']|'[^']*')*$))(?.*)"; - return strings + "|" + backslashEscapedStrings + "|" + dollarQuotedStrings + "|" + dashComments + "|" + starComments + "|" + separator; - } - } + private static readonly IStatementSplitter _statementSplitter = new PostgreSqlStatementSplitter(); + public IStatementSplitter StatementSplitter => _statementSplitter; public string CurrentDatabase => "SELECT current_database()"; public string ListDatabases => "SELECT datname FROM pg_database"; diff --git a/src/grate.sqlite/Infrastructure/SqliteStatementSplitter.cs b/src/grate.sqlite/Infrastructure/SqliteStatementSplitter.cs new file mode 100644 index 00000000..27bbf153 --- /dev/null +++ b/src/grate.sqlite/Infrastructure/SqliteStatementSplitter.cs @@ -0,0 +1,11 @@ +using grate.Infrastructure; + +namespace grate.Sqlite.Infrastructure; + +public class SqliteStatementSplitter : RegexStatementSplitter +{ + protected override string StringsRegex => @"(?'[^']*')"; + protected override string DashCommentsRegex => @"(?--.*$)"; + protected override string StarCommentsRegex => @"(?/\*[\S\s]*?\*/)"; + protected override string SeparatorRegex => @"(?^|\s)(?GO)(?\s|;|$)"; +} diff --git a/src/grate.sqlite/Infrastructure/SqliteSyntax.cs b/src/grate.sqlite/Infrastructure/SqliteSyntax.cs index 9b80b95e..ccfe1d6b 100644 --- a/src/grate.sqlite/Infrastructure/SqliteSyntax.cs +++ b/src/grate.sqlite/Infrastructure/SqliteSyntax.cs @@ -3,17 +3,8 @@ namespace grate.Sqlite.Infrastructure; public readonly struct SqliteSyntax : ISyntax { - public string StatementSeparatorRegex - { - get - { - const string strings = @"(?'[^']*')"; - const string dashComments = @"(?--.*$)"; - const string starComments = @"(?/\*[\S\s]*?\*/)"; - const string separator = @"(?^|\s)(?GO)(?\s|;|$)"; - return strings + "|" + dashComments + "|" + starComments + "|" + separator; - } - } + private static readonly IStatementSplitter _statementSplitter = new SqliteStatementSplitter(); + public IStatementSplitter StatementSplitter => _statementSplitter; public string CurrentDatabase => "SELECT name FROM pragma_database_list ORDER BY seq DESC LIMIT 1"; public string ListDatabases => "select name from pragma_database_list"; diff --git a/src/grate.sqlserver/Infrastructure/SqlServerStatementSplitter.cs b/src/grate.sqlserver/Infrastructure/SqlServerStatementSplitter.cs new file mode 100644 index 00000000..6ebaeb51 --- /dev/null +++ b/src/grate.sqlserver/Infrastructure/SqlServerStatementSplitter.cs @@ -0,0 +1,11 @@ +using grate.Infrastructure; + +namespace grate.SqlServer.Infrastructure; + +public class SqlServerStatementSplitter : RegexStatementSplitter +{ + protected override string StringsRegex => @"(?'[^']*')"; + protected override string DashCommentsRegex => @"(?--.*$)"; + protected override string StarCommentsRegex => @"(?/\*[\S\s]*?\*/)"; + protected override string SeparatorRegex => @"(?^|\s)(?GO)(?\s|;|$)"; +} diff --git a/src/grate.sqlserver/Infrastructure/SqlServerSyntax.cs b/src/grate.sqlserver/Infrastructure/SqlServerSyntax.cs index a8ec4a5f..65106c2d 100644 --- a/src/grate.sqlserver/Infrastructure/SqlServerSyntax.cs +++ b/src/grate.sqlserver/Infrastructure/SqlServerSyntax.cs @@ -3,17 +3,8 @@ namespace grate.SqlServer.Infrastructure; public readonly struct SqlServerSyntax : ISyntax { - public string StatementSeparatorRegex - { - get - { - const string strings = @"(?'[^']*')"; - const string dashComments = @"(?--.*$)"; - const string starComments = @"(?/\*[\S\s]*?\*/)"; - const string separator = @"(?^|\s)(?GO)(?\s|;|$)"; - return strings + "|" + dashComments + "|" + starComments + "|" + separator; - } - } + private static readonly IStatementSplitter _statementSplitter = new SqlServerStatementSplitter(); + public IStatementSplitter StatementSplitter => _statementSplitter; public string CurrentDatabase => "SELECT DB_NAME()"; public string ListDatabases => "SELECT name FROM sys.databases"; diff --git a/unittests/Oracle/Basic_tests/BatchSplitterReplacer_.cs b/unittests/Oracle/Basic_tests/BatchSplitterReplacer_.cs deleted file mode 100644 index df240446..00000000 --- a/unittests/Oracle/Basic_tests/BatchSplitterReplacer_.cs +++ /dev/null @@ -1,580 +0,0 @@ -using grate.Infrastructure; -using grate.Migration; -using grate.Oracle.Infrastructure; -using Oracle.TestInfrastructure; - -// ReSharper disable InconsistentNaming - -namespace Oracle.Basic_tests; - - -public class BatchSplitterReplacer_ -{ - private const string Batch_terminator_replacement_string = StatementSplitter.BatchTerminatorReplacementString; - - private const string Symbols_to_check = "`~!@#$%^&*()-_+=,.;:'\"[]\\/?<>"; - private const string Words_to_check = "abcdefghijklmnopqrstuvwzyz0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - // private static readonly IDatabase Database = new OracleDatabase(NullLogger.Instance); - - // private static BatchSplitterReplacer Replacer => new(Database.StatementSeparatorRegex, StatementSplitter.BatchTerminatorReplacementString); - - // ReSharper disable once InconsistentNaming - public class should_replace_on - { - private ITestOutputHelper _testOutput; - private BatchSplitterReplacer Replacer; - - public should_replace_on(ITestOutputHelper testOutput) - { - _testOutput = testOutput; - Replacer = new BatchSplitterReplacer(new OracleSyntax()); - } - - [Fact] - public void full_statement_without_issue() - { - string sql_to_match = OracleSplitterContext.FullSplitter.PLSqlStatement; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(OracleSplitterContext.FullSplitter.PLSqlStatementScrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_space() - { - const string sql_to_match = @" / "; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" "; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_tab() - { - string sql_to_match = @" /" + "\t"; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + "\t"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_by_itself() - { - const string sql_to_match = @"/"; - string expected_scrubbed = Batch_terminator_replacement_string; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_starting_file() - { - const string sql_to_match = @"/ -whatever"; - string expected_scrubbed = Batch_terminator_replacement_string + @" -whatever"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_new_line() - { - const string sql_to_match = @" / -"; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_one_new_line_after_double_dash_comments() - { - const string sql_to_match = - @"-- -/ -"; - string expected_scrubbed = - @"-- -" + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_one_new_line_after_double_dash_comments_and_words() - { - string sql_to_match = @"-- " + Words_to_check + @" -/ -"; - string expected_scrubbed = @"-- " + Words_to_check + @" -" + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_new_line_after_double_dash_comments_and_symbols() - { - string sql_to_match = @"-- " + Symbols_to_check + @" -/ -"; - string expected_scrubbed = @"-- " + Symbols_to_check + @" -" + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_on_its_own_line() - { - const string sql_to_match = @" -/ -"; - string expected_scrubbed = @" -" + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_no_line_terminator() - { - const string sql_to_match = @" / "; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" "; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_words_before() - { - string sql_to_match = Words_to_check + @" / -"; - string expected_scrubbed = Words_to_check + @" " + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_symbols_and_words_before() - { - string sql_to_match = Symbols_to_check + Words_to_check + @" / -"; - string expected_scrubbed = Symbols_to_check + Words_to_check + @" " + - Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_words_and_symbols_before() - { - string sql_to_match = Words_to_check + Symbols_to_check + @" / -"; - string expected_scrubbed = Words_to_check + Symbols_to_check + @" " + - Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_words_after_on_the_same_line() - { - string sql_to_match = @" / " + Words_to_check; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" " + Words_to_check; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_words_after_on_the_same_line_including_symbols() - { - string sql_to_match = @" / " + Words_to_check + Symbols_to_check; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" " + Words_to_check + - Symbols_to_check; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_words_before_and_after_on_the_same_line() - { - string sql_to_match = Words_to_check + @" / " + Words_to_check; - string expected_scrubbed = Words_to_check + @" " + Batch_terminator_replacement_string + @" " + - Words_to_check; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_words_before_and_after_on_the_same_line_including_symbols() - { - string sql_to_match = Words_to_check + Symbols_to_check.Replace("'", "").Replace("\"", "") + - " / BOB" + Symbols_to_check; - string expected_scrubbed = Words_to_check + Symbols_to_check.Replace("'", "").Replace("\"", "") + - " " + Batch_terminator_replacement_string + " BOB" + Symbols_to_check; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_after_double_dash_comment_with_single_quote_and_single_quote_after_slash() - { - string sql_to_match = Words_to_check + @" -- ' -/ -select '' -/"; - string expected_scrubbed = Words_to_check + @" -- ' -" + Batch_terminator_replacement_string + @" -select '' -" + Batch_terminator_replacement_string; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_comment_after() - { - string sql_to_match = " / -- comment"; - string expected_scrubbed = " " + Batch_terminator_replacement_string + " -- comment"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_semicolon_directly_after() - { - string sql_to_match = "jalla /;"; - string expected_scrubbed = "jalla " + Batch_terminator_replacement_string + ";"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - } - - public class should_not_replace_on - { - private ITestOutputHelper _testOutput; - private BatchSplitterReplacer Replacer; - - public should_not_replace_on(ITestOutputHelper testOutput) - { - _testOutput = testOutput; - Replacer = new BatchSplitterReplacer(new OracleSyntax()); - } - - [Fact] - public void slash_when_slash_is_the_last_part_of_the_last_word_on_a_line() - { - string sql_to_match = Words_to_check + @"/ -"; - string expected_scrubbed = Words_to_check + @"/ -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_double_dash_comment_starting_line() - { - string sql_to_match = @"--/ -"; - string expected_scrubbed = @"--/ -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_double_dash_comment_and_space_starting_line() - { - string sql_to_match = @"-- / -"; - string expected_scrubbed = @"-- / -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_double_dash_comment_and_space_starting_line_and_words_after_slash() - { - string sql_to_match = @"-- / " + Words_to_check + @" -"; - string expected_scrubbed = @"-- / " + Words_to_check + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_double_dash_comment_and_space_starting_line_and_symbols_after_slash() - { - string sql_to_match = @"-- / " + Symbols_to_check + @" -"; - string expected_scrubbed = @"-- / " + Symbols_to_check + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_double_dash_comment_and_tab_starting_line() - { - string sql_to_match = "--" + "\t" + @"/ -"; - string expected_scrubbed = @"--" + "\t" + @"/ -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_double_dash_comment_and_tab_starting_line_and_words_after_slash() - { - string sql_to_match = @"--" + "\t" + @"/ " + Words_to_check + @" -"; - string expected_scrubbed = @"--" + "\t" + @"/ " + Words_to_check + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_double_dash_comment_and_tab_starting_line_and_symbols_after_slash() - { - string sql_to_match = @"--" + "\t" + @"/ " + Symbols_to_check + @" -"; - string expected_scrubbed = @"--" + "\t" + @"/ " + Symbols_to_check + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_double_dash_comment_starting_line_with_words_before_slash() - { - string sql_to_match = @"-- " + Words_to_check + @" / -"; - string expected_scrubbed = @"-- " + Words_to_check + @" / -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_when_between_tick_marks() - { - const string sql_to_match = @"' / - '"; - const string expected_scrubbed = @"' / - '"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void - slash_when_between_tick_marks_with_symbols_and_words_before_ending_on_same_line() - { - string sql_to_match = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" /'"; - string expected_scrubbed = - @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" /'"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_when_between_tick_marks_with_symbols_and_words_before() - { - string sql_to_match = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" / - '"; - string expected_scrubbed = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" / - '"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_when_between_tick_marks_with_symbols_and_words_after() - { - string sql_to_match = @"' / - " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @"'"; - string expected_scrubbed = @"' / - " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @"'"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_with_double_dash_comment_starting_line_with_symbols_before_slash() - { - string sql_to_match = @"--" + Symbols_to_check + @" / -"; - string expected_scrubbed = @"--" + Symbols_to_check + @" / -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void - slash_with_double_dash_comment_starting_line_with_words_and_symbols_before_slash() - { - string sql_to_match = @"--" + Symbols_to_check + Words_to_check + @" / -"; - string expected_scrubbed = @"--" + Symbols_to_check + Words_to_check + @" / -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_inside_of_comments() - { - string sql_to_match = @"/* / */"; - string expected_scrubbed = @"/* / */"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_inside_of_comments_with_a_line_break() - { - string sql_to_match = @"/* / -*/"; - string expected_scrubbed = @"/* / -*/"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_inside_of_comments_with_words_before() - { - string sql_to_match = - @"/* -" + Words_to_check + @" / - -*/"; - string expected_scrubbed = - @"/* -" + Words_to_check + @" / - -*/"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_inside_of_comments_with_words_before_on_a_different_line() - { - string sql_to_match = - @"/* -" + Words_to_check + @" -/ - -*/"; - string expected_scrubbed = - @"/* -" + Words_to_check + @" -/ - -*/"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_inside_of_comments_with_words_before_and_after_on_different_lines() - { - string sql_to_match = - @"/* -" + Words_to_check + @" -/ - -" + Words_to_check + @" -*/"; - string expected_scrubbed = - @"/* -" + Words_to_check + @" -/ - -" + Words_to_check + @" -*/"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void slash_inside_of_comments_with_symbols_after_on_different_lines() - { - string sql_to_match = - @"/* -/ - -" + Symbols_to_check + @" -*/"; - string expected_scrubbed = - @"/* -/ - -" + Symbols_to_check + @" -*/"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - } - -} diff --git a/unittests/Oracle/Basic_tests/OracleStatementSplitter_.cs b/unittests/Oracle/Basic_tests/OracleStatementSplitter_.cs new file mode 100644 index 00000000..a883375d --- /dev/null +++ b/unittests/Oracle/Basic_tests/OracleStatementSplitter_.cs @@ -0,0 +1,504 @@ +using grate.Infrastructure; +using grate.Oracle.Infrastructure; +using Oracle.TestInfrastructure; + +// ReSharper disable InconsistentNaming + +namespace Oracle.Basic_tests; + + +public class OracleStatementSplitter_ +{ + private const string Symbols_to_check = "`~!@#$%^&*()-_+=,.;:'\"[]\\/?<>"; + private const string Words_to_check = "abcdefghijklmnopqrstuvwzyz0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + // ReSharper disable once InconsistentNaming + public class should_replace_on + { + private ITestOutputHelper _testOutput; + private OracleStatementSplitter Splitter; + + public should_replace_on(ITestOutputHelper testOutput) + { + _testOutput = testOutput; + Splitter = new OracleStatementSplitter(); + } + + [Fact] + public void full_statement_without_issue() + { + string sql_to_match = OracleSplitterContext.FullSplitter.PLSqlStatement; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.NotEmpty(result); + Assert.True(result.Count > 1, "Should split into multiple statements"); + Assert.Equal(result, OracleSplitterContext.FullSplitter.PLSqlStatementScrubbed); + } + + [Fact] + public void slash_with_space() + { + const string sql_to_match = @" / "; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Empty(result); + } + + [Fact] + public void slash_with_tab() + { + string sql_to_match = @" /" + "\t"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal(["\t"],result); + } + + [Fact] + public void slash_by_itself() + { + const string sql_to_match = @"/"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Empty(result); + } + + [Fact] + public void slash_starting_file() + { + const string sql_to_match = @"/ +whatever"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_new_line() + { + const string sql_to_match = @" / +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Empty(result); + } + + [Theory] + [InlineData("\n", "LF")] + public void slash_with_one_new_line_after_double_dash_comments_lf(string line_ending, string _) + { + string sql_to_match = $"--{line_ending}/{line_ending}"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal(["--" + line_ending], result); + } + + [Theory] + [InlineData("\r\n", "CRLF")] + public void slash_with_one_new_line_after_double_dash_comments_crlf(string line_ending, string _) + { + string sql_to_match = $"--{line_ending}/{line_ending}"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal(["--" + line_ending, line_ending], result); + } + + [Fact] + public void slash_with_one_new_line_after_double_dash_comments_and_words() + { + string sql_to_match = @"-- " + Words_to_check + @" +/ +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_new_line_after_double_dash_comments_and_symbols() + { + string sql_to_match = @"-- " + Symbols_to_check + @" +/ +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_on_its_own_line() + { + const string sql_to_match = @" +/ +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Empty(result); + } + + [Fact] + public void slash_with_no_line_terminator() + { + const string sql_to_match = @" / "; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Empty(result); + } + + [Fact] + public void slash_with_words_before() + { + string sql_to_match = Words_to_check + @" / +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_symbols_and_words_before() + { + string sql_to_match = Symbols_to_check + Words_to_check + @" / +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_words_and_symbols_before() + { + string sql_to_match = Words_to_check + Symbols_to_check + @" / +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_words_after_on_the_same_line() + { + string sql_to_match = @" / " + Words_to_check; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_words_after_on_the_same_line_including_symbols() + { + string sql_to_match = @" / " + Words_to_check + Symbols_to_check; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_words_before_and_after_on_the_same_line() + { + string sql_to_match = Words_to_check + @" / " + Words_to_check; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal([Words_to_check + " "," " + Words_to_check], result); + } + + [Fact] + public void slash_with_words_before_and_after_on_the_same_line_including_symbols() + { + string sql_to_match = Words_to_check + Symbols_to_check.Replace("'", "").Replace("\"", "") + + " / BOB" + Symbols_to_check; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal([ Words_to_check + Symbols_to_check.Replace("'", "").Replace("\"", "") + + " ", " BOB" + Symbols_to_check], result); + } + + [Fact] + public void slash_after_double_dash_comment_with_single_quote_and_single_quote_after_slash() + { + string sql_to_match = Words_to_check + @" -- ' +/ +select '' +/"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal([Words_to_check + " -- '\n", "\nselect ''\n"], result); + } + + [Fact] + public void slash_with_comment_after() + { + string sql_to_match = " / -- comment"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_semicolon_directly_after() + { + string sql_to_match = "jalla /;"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal(["jalla ", ";"], result); + } + + } + + public class should_not_replace_on + { + private ITestOutputHelper _testOutput; + private OracleStatementSplitter Splitter; + + public should_not_replace_on(ITestOutputHelper testOutput) + { + _testOutput = testOutput; + Splitter = new OracleStatementSplitter(); + } + + [Fact] + public void slash_when_slash_is_the_last_part_of_the_last_word_on_a_line() + { + string sql_to_match = Words_to_check + @"/ +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_double_dash_comment_starting_line() + { + string sql_to_match = @"--/ +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_double_dash_comment_and_space_starting_line() + { + string sql_to_match = @"-- / +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_double_dash_comment_and_space_starting_line_and_words_after_slash() + { + string sql_to_match = @"-- / " + Words_to_check + @" +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_double_dash_comment_and_space_starting_line_and_symbols_after_slash() + { + string sql_to_match = @"-- / " + Symbols_to_check + @" +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_double_dash_comment_and_tab_starting_line() + { + string sql_to_match = "--" + "\t" + @"/ +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_double_dash_comment_and_tab_starting_line_and_words_after_slash() + { + string sql_to_match = @"--" + "\t" + @"/ " + Words_to_check + @" +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_double_dash_comment_and_tab_starting_line_and_symbols_after_slash() + { + string sql_to_match = @"--" + "\t" + @"/ " + Symbols_to_check + @" +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_double_dash_comment_starting_line_with_words_before_slash() + { + string sql_to_match = @"-- " + Words_to_check + @" / +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_when_between_tick_marks() + { + const string sql_to_match = @"' / + '"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void + slash_when_between_tick_marks_with_symbols_and_words_before_ending_on_same_line() + { + string sql_to_match = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" /'"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_when_between_tick_marks_with_symbols_and_words_before() + { + string sql_to_match = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" / + '"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_when_between_tick_marks_with_symbols_and_words_after() + { + string sql_to_match = @"' / + " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @"'"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_with_double_dash_comment_starting_line_with_symbols_before_slash() + { + string sql_to_match = @"--" + Symbols_to_check + @" / +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void + slash_with_double_dash_comment_starting_line_with_words_and_symbols_before_slash() + { + string sql_to_match = @"--" + Symbols_to_check + Words_to_check + @" / +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_inside_of_comments() + { + string sql_to_match = @"/* / */"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_inside_of_comments_with_a_line_break() + { + string sql_to_match = @"/* / +*/"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_inside_of_comments_with_words_before() + { + string sql_to_match = + @"/* +" + Words_to_check + @" / + +*/"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_inside_of_comments_with_words_before_on_a_different_line() + { + string sql_to_match = + @"/* +" + Words_to_check + @" +/ + +*/"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_inside_of_comments_with_words_before_and_after_on_different_lines() + { + string sql_to_match = + @"/* +" + Words_to_check + @" +/ + +" + Words_to_check + @" +*/"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void slash_inside_of_comments_with_symbols_after_on_different_lines() + { + string sql_to_match = + @"/* +/ + +" + Symbols_to_check + @" +*/"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + } + + public class Split + { + private readonly OracleStatementSplitter _splitter = new(); + + [Fact] + public void Splits_and_removes_GO_statements() + { + var original = @" +SELECT * FROM v$version WHERE banner LIKE 'Oracle%'; + + +/ +SELECT 1 +"; + var batches = _splitter.Split(original); + + Assert.Equal(2, batches.Count()); + } + } +} diff --git a/unittests/Oracle/Basic_tests/StatementSplitter_.cs b/unittests/Oracle/Basic_tests/StatementSplitter_.cs deleted file mode 100644 index 3beae1ef..00000000 --- a/unittests/Oracle/Basic_tests/StatementSplitter_.cs +++ /dev/null @@ -1,33 +0,0 @@ -using grate.Infrastructure; -using grate.Migration; -using grate.Oracle.Infrastructure; - -namespace Oracle.Basic_tests; - - -// ReSharper disable once InconsistentNaming -public class StatementSplitter_ -{ - private readonly StatementSplitter _splitter; - - public StatementSplitter_() - { - _splitter = new StatementSplitter(new OracleSyntax()); - } - - [Fact] - public void Splits_and_removes_GO_statements() - { - var original = @" -SELECT * FROM v$version WHERE banner LIKE 'Oracle%'; - - -/ -SELECT 1 -"; - var batches = _splitter.Split(original); - - Assert.Equal(2, batches.Count()); - } - -} diff --git a/unittests/Oracle/TestInfrastructure/OracleSplitterContext.cs b/unittests/Oracle/TestInfrastructure/OracleSplitterContext.cs index ac1fe7e3..c6adf78a 100644 --- a/unittests/Oracle/TestInfrastructure/OracleSplitterContext.cs +++ b/unittests/Oracle/TestInfrastructure/OracleSplitterContext.cs @@ -99,25 +99,25 @@ INSERT [dbo].[Foo] ([Bar]) VALUES (N'/ speed racer, / speed racer, / speed racer /"; - public static readonly string PLSqlStatementScrubbed = @" + public static readonly string[] PLSqlStatementScrubbed = [@" BOB1 -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" /* COMMENT */ BOB2 -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" -- / -BOB3 " + StatementSplitter.BatchTerminatorReplacementString + @" +BOB3 ", @" --`~!@#$%^&*()-_+=,.;:'""[]\/?<> / BOB5 - " + StatementSplitter.BatchTerminatorReplacementString + @" + ", @" BOB6 -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" /* / */ @@ -132,12 +132,12 @@ INSERT [dbo].[Foo] ([Bar]) VALUES (N'/ speed racer, / speed racer, / speed racer BOB8 -- -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" BOB9 -- `~!@#$%^&*()-_+=,.;:'""[]\/?<> -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" BOB10/ @@ -165,7 +165,7 @@ yeppsasd decimal(20, 6) NULL, slsald varchar(15) NULL, uhasdf varchar(15) NULL, daf_asdfasdf DECIMAL(20,6) NULL; -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Daily job', @step_id=1, @@ -182,40 +182,14 @@ dml statements / dml statements ' -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" INSERT [dbo].[Foo] ([Bar]) VALUES (N'hello--world. Thanks!') INSERT [dbo].[Foo] ([Bar]) VALUES (N'/ speed racer, / speed racer, / speed racer /!!!!! ') -" + StatementSplitter.BatchTerminatorReplacementString + @""; - - public static readonly string plsql_statement = - @" -SQL1; -; -SQL2; -; -tmpSql := 'DROP SEQUENCE mutatieStockID'; -EXECUTE IMMEDIATE tmpSql; -; -BEGIN -INSERT into Table (columnname) values ("";""); -UPDATE Table set columnname="";""; -END; -"; - public static readonly string plsql_statement_scrubbed = @" -SQL1; -" + StatementSplitter.BatchTerminatorReplacementString + @" -SQL2; -" + StatementSplitter.BatchTerminatorReplacementString + @" -tmpSql := 'DROP SEQUENCE mutatieStockID'; -EXECUTE IMMEDIATE tmpSql; -" + StatementSplitter.BatchTerminatorReplacementString + @" -BEGIN -INSERT into Table (columnname) values ("";""); -UPDATE Table set columnname="";""; -END; -"; +"]; + + } } diff --git a/unittests/PostgreSQL/Statement_Splitting/StatementSplitter_.cs b/unittests/PostgreSQL/Statement_Splitting/PostgreSqlStatementSplitter_.cs similarity index 92% rename from unittests/PostgreSQL/Statement_Splitting/StatementSplitter_.cs rename to unittests/PostgreSQL/Statement_Splitting/PostgreSqlStatementSplitter_.cs index 8b6b5439..c3f9ffdb 100644 --- a/unittests/PostgreSQL/Statement_Splitting/StatementSplitter_.cs +++ b/unittests/PostgreSQL/Statement_Splitting/PostgreSqlStatementSplitter_.cs @@ -1,18 +1,17 @@ using grate.Infrastructure; -using grate.Migration; using grate.PostgreSql.Infrastructure; namespace Basic_tests.Infrastructure.PostgreSQL.Statement_Splitting; // ReSharper disable once InconsistentNaming -public class StatementSplitter_ +public class PostgreSqlStatementSplitter_ { - private readonly StatementSplitter _statementSplitter; + private readonly PostgreSqlStatementSplitter _statementSplitter; - public StatementSplitter_() + public PostgreSqlStatementSplitter_() { - _statementSplitter = new StatementSplitter(new PostgreSqlSyntax()); + _statementSplitter = new PostgreSqlStatementSplitter(); } [Fact] diff --git a/unittests/SqlServer/Basic_tests/BatchSplitterReplacer_.cs b/unittests/SqlServer/Basic_tests/BatchSplitterReplacer_.cs deleted file mode 100644 index 5133c6f1..00000000 --- a/unittests/SqlServer/Basic_tests/BatchSplitterReplacer_.cs +++ /dev/null @@ -1,653 +0,0 @@ -using grate.Infrastructure; -using grate.Migration; -using grate.SqlServer.Infrastructure; -using SqlServer.TestInfrastructure; - -// ReSharper disable InconsistentNaming - -namespace SqlServer.Basic_tests; - -public class BatchSplitterReplacer_ -{ - private const string Batch_terminator_replacement_string = StatementSplitter.BatchTerminatorReplacementString; - - private const string Symbols_to_check = "`~!@#$%^&*()-_+=,.;:'\"[]\\/?<>"; - private const string Words_to_check = "abcdefghijklmnopqrstuvwzyz0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - public class should_replace_on - { - private ITestOutputHelper _testOutput; - private BatchSplitterReplacer Replacer; - - public should_replace_on(ITestOutputHelper testOutput) - { - _testOutput = testOutput; - Replacer = new BatchSplitterReplacer(new SqlServerSyntax()); - } - - [Fact] - public void full_statement_without_issue() - { - string sql_to_match = SqlServerSplitterContext.FullSplitter.tsql_statement; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(SqlServerSplitterContext.FullSplitter.tsql_statement_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_space() - { - const string sql_to_match = @" GO "; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" "; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_tab() - { - string sql_to_match = @" GO" + "\t"; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + "\t"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_by_itself() - { - const string sql_to_match = @"GO"; - string expected_scrubbed = Batch_terminator_replacement_string; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_starting_file() - { - const string sql_to_match = @"GO -whatever"; - string expected_scrubbed = Batch_terminator_replacement_string + @" -whatever"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_new_line() - { - const string sql_to_match = @" GO -"; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_on_new_line_after_double_dash_comments() - { - const string sql_to_match = - @"-- -GO -"; - string expected_scrubbed = - @"-- -" + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_on_new_line_after_double_dash_comments_and_words() - { - string sql_to_match = @"-- " + Words_to_check + @" -GO -"; - string expected_scrubbed = @"-- " + Words_to_check + @" -" + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_new_line_after_double_dash_comments_and_symbols() - { - string sql_to_match = @"-- " + Symbols_to_check + @" -GO -"; - string expected_scrubbed = @"-- " + Symbols_to_check + @" -" + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_on_its_own_line() - { - const string sql_to_match = @" -GO -"; - string expected_scrubbed = @" -" + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_no_line_terminator() - { - const string sql_to_match = @" GO "; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" "; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_words_before() - { - string sql_to_match = Words_to_check + @" GO -"; - string expected_scrubbed = Words_to_check + @" " + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_symbols_and_words_before() - { - string sql_to_match = Symbols_to_check + Words_to_check + @" GO -"; - string expected_scrubbed = Symbols_to_check + Words_to_check + @" " + - Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_words_and_symbols_before() - { - string sql_to_match = Words_to_check + Symbols_to_check + @" GO -"; - string expected_scrubbed = Words_to_check + Symbols_to_check + @" " + - Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_words_after_on_the_same_line() - { - string sql_to_match = @" GO " + Words_to_check; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" " + Words_to_check; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_words_after_on_the_same_line_including_symbols() - { - string sql_to_match = @" GO " + Words_to_check + Symbols_to_check; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" " + Words_to_check + - Symbols_to_check; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_words_before_and_after_on_the_same_line() - { - string sql_to_match = Words_to_check + @" GO " + Words_to_check; - string expected_scrubbed = Words_to_check + @" " + Batch_terminator_replacement_string + @" " + - Words_to_check; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_words_before_and_after_on_the_same_line_including_symbols() - { - string sql_to_match = Words_to_check + Symbols_to_check.Replace("'", "").Replace("\"", "") + - " GO BOB" + Symbols_to_check; - string expected_scrubbed = Words_to_check + Symbols_to_check.Replace("'", "").Replace("\"", "") + - " " + Batch_terminator_replacement_string + " BOB" + Symbols_to_check; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_after_double_dash_comment_with_single_quote_and_single_quote_after_go() - { - string sql_to_match = Words_to_check + @" -- ' -GO -select '' -go"; - string expected_scrubbed = Words_to_check + @" -- ' -" + Batch_terminator_replacement_string + @" -select '' -" + Batch_terminator_replacement_string; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void lowercase_go_statement() - { - const string sql_to_match = "\r\nwhere DataName = 'AttributeKeyMap'\r\ngo "; - string expected_scrubbed = "\r\nwhere DataName = 'AttributeKeyMap'\r\n" + - Batch_terminator_replacement_string + " "; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_after_line_comment_containing_an_apostrophe() - { - const string sql_to_match = @"select 1 -- ' -GO -'' -GO -"; - string expected_scrubbed = @"select 1 -- ' -" + Batch_terminator_replacement_string + @" -'' -" + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_comment_after() - { - string sql_to_match = " GO -- comment"; - string expected_scrubbed = " " + Batch_terminator_replacement_string + " -- comment"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_semicolon_directly_after() - { - string sql_to_match = "jalla GO;"; - string expected_scrubbed = "jalla " + Batch_terminator_replacement_string + ";"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - } - - public class should_not_replace_on - { - private readonly ITestOutputHelper _testOutput; - private readonly BatchSplitterReplacer _replacer; - - public should_not_replace_on(ITestOutputHelper testOutput) - { - _testOutput = testOutput; - _replacer = new BatchSplitterReplacer(new SqlServerSyntax()); - } - - [Fact] - public void g() - { - const string sql_to_match = @" G -"; - const string expected_scrubbed = @" G -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void o() - { - const string sql_to_match = @" O -"; - const string expected_scrubbed = @" O -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_when_go_is_the_last_part_of_the_last_word_on_a_line() - { - string sql_to_match = Words_to_check + @"GO -"; - string expected_scrubbed = Words_to_check + @"GO -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_starting_line() - { - string sql_to_match = @"--GO -"; - string expected_scrubbed = @"--GO -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_and_space_starting_line() - { - string sql_to_match = @"-- GO -"; - string expected_scrubbed = @"-- GO -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_and_space_starting_line_and_words_after_go() - { - string sql_to_match = @"-- GO " + Words_to_check + @" -"; - string expected_scrubbed = @"-- GO " + Words_to_check + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_and_space_starting_line_and_symbols_after_go() - { - string sql_to_match = @"-- GO " + Symbols_to_check + @" -"; - string expected_scrubbed = @"-- GO " + Symbols_to_check + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_and_tab_starting_line() - { - string sql_to_match = "--" + "\t" + @"GO -"; - string expected_scrubbed = @"--" + "\t" + @"GO -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_and_tab_starting_line_and_words_after_go() - { - string sql_to_match = @"--" + "\t" + @"GO " + Words_to_check + @" -"; - string expected_scrubbed = @"--" + "\t" + @"GO " + Words_to_check + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_and_tab_starting_line_and_symbols_after_go() - { - string sql_to_match = @"--" + "\t" + @"GO " + Symbols_to_check + @" -"; - string expected_scrubbed = @"--" + "\t" + @"GO " + Symbols_to_check + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_starting_line_with_words_before_go() - { - string sql_to_match = @"-- " + Words_to_check + @" GO -"; - string expected_scrubbed = @"-- " + Words_to_check + @" GO -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_when_between_tick_marks() - { - const string sql_to_match = @"' GO - '"; - const string expected_scrubbed = @"' GO - '"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_inside_a_multiline_string_literal() - { - const string sql_to_match = @"select ' 1 -- -GO -'"; - const string expected_scrubbed = @"select ' 1 -- -GO -'"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void - go_when_between_tick_marks_with_symbols_and_words_before_ending_on_same_line() - { - string sql_to_match = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" GO'"; - string expected_scrubbed = - @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" GO'"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_when_between_tick_marks_with_symbols_and_words_before() - { - string sql_to_match = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" GO - '"; - string expected_scrubbed = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" GO - '"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_when_between_tick_marks_with_symbols_and_words_after() - { - string sql_to_match = @"' GO - " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @"'"; - string expected_scrubbed = @"' GO - " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @"'"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_starting_line_with_symbols_before_go() - { - string sql_to_match = @"--" + Symbols_to_check + @" GO -"; - string expected_scrubbed = @"--" + Symbols_to_check + @" GO -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void - go_with_double_dash_comment_starting_line_with_words_and_symbols_before_go() - { - string sql_to_match = @"--" + Symbols_to_check + Words_to_check + @" GO -"; - string expected_scrubbed = @"--" + Symbols_to_check + Words_to_check + @" GO -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_inside_of_comments() - { - string sql_to_match = @"/* GO */"; - string expected_scrubbed = @"/* GO */"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_inside_of_comments_with_a_line_break() - { - string sql_to_match = @"/* GO -*/"; - string expected_scrubbed = @"/* GO -*/"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_inside_of_nested_comments_with_a_line_break() - { - const string sql_to_match = @"/* /* */ GO -*/"; - const string expected_scrubbed = @"/* /* */ GO -*/"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_inside_of_comments_with_words_before() - { - string sql_to_match = - @"/* -" + Words_to_check + @" GO - -*/"; - string expected_scrubbed = - @"/* -" + Words_to_check + @" GO - -*/"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_inside_of_comments_with_words_before_on_a_different_line() - { - string sql_to_match = - @"/* -" + Words_to_check + @" -GO - -*/"; - string expected_scrubbed = - @"/* -" + Words_to_check + @" -GO - -*/"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_inside_of_comments_with_words_before_and_after_on_different_lines() - { - string sql_to_match = - @"/* -" + Words_to_check + @" -GO - -" + Words_to_check + @" -*/"; - string expected_scrubbed = - @"/* -" + Words_to_check + @" -GO - -" + Words_to_check + @" -*/"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_inside_of_comments_with_symbols_after_on_different_lines() - { - string sql_to_match = - @"/* -GO - -" + Symbols_to_check + @" -*/"; - string expected_scrubbed = - @"/* -GO - -" + Symbols_to_check + @" -*/"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - } - -} diff --git a/unittests/SqlServer/Basic_tests/SqlServerStatementSplitter_.cs b/unittests/SqlServer/Basic_tests/SqlServerStatementSplitter_.cs new file mode 100644 index 00000000..e9948882 --- /dev/null +++ b/unittests/SqlServer/Basic_tests/SqlServerStatementSplitter_.cs @@ -0,0 +1,589 @@ +using grate.Infrastructure; +using grate.SqlServer.Infrastructure; +using SqlServer.TestInfrastructure; + +// ReSharper disable InconsistentNaming + +namespace SqlServer.Basic_tests; + +public class SqlServerStatementSplitter_ +{ + private const string Symbols_to_check = "`~!@#$%^&*()-_+=,.;:'\"[]\\/?<>"; + private const string Words_to_check = "abcdefghijklmnopqrstuvwzyz0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + public class should_replace_on + { + private ITestOutputHelper _testOutput; + private SqlServerStatementSplitter Splitter; + + public should_replace_on(ITestOutputHelper testOutput) + { + _testOutput = testOutput; + Splitter = new SqlServerStatementSplitter(); + } + + [Fact] + public void full_statement_without_issue() + { + string sql_to_match = SqlServerSplitterContext.FullSplitter.tsql_statement; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.NotEmpty(result); + Assert.True(result.Count > 1, "Should split into multiple statements"); + Assert.Equal(result, SqlServerSplitterContext.FullSplitter.tsql_statement_scrubbed); + } + + [Fact] + public void go_with_space() + { + const string sql_to_match = @" GO "; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Empty(result); + } + + [Fact] + public void go_with_tab() + { + string sql_to_match = @" GO" + "\t"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal(["\t"], result); + } + + [Fact] + public void go_by_itself() + { + const string sql_to_match = @"GO"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Empty(result); + } + + [Fact] + public void go_starting_file() + { + const string sql_to_match = @"GO +whatever"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Contains("whatever", result[0]); + } + + [Fact] + public void go_with_new_line() + { + const string sql_to_match = @" GO +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Empty(result); + } + + [Theory] + [InlineData("\r\n", "CRLF")] + [InlineData("\n", "LF")] + public void go_with_on_new_line_after_double_dash_comments(string line_ending, string _) + { + string sql_to_match = $"--{line_ending}GO{line_ending}"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal(["--" + line_ending], result); + } + + [Fact] + public void go_with_on_new_line_after_double_dash_comments_and_words() + { + string sql_to_match = @"-- " + Words_to_check + @" +GO +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void go_with_new_line_after_double_dash_comments_and_symbols() + { + string sql_to_match = @"-- " + Symbols_to_check + @" +GO +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void go_on_its_own_line() + { + const string sql_to_match = @" +GO +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Empty(result); + } + + [Fact] + public void go_with_no_line_terminator() + { + const string sql_to_match = @" GO "; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Empty(result); + } + + [Fact] + public void go_with_words_before() + { + string sql_to_match = Words_to_check + @" GO +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(Words_to_check.Trim(), result[0].Trim()); + } + + [Fact] + public void go_with_symbols_and_words_before() + { + string sql_to_match = Symbols_to_check + Words_to_check + @" GO +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void go_with_words_and_symbols_before() + { + string sql_to_match = Words_to_check + Symbols_to_check + @" GO +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void go_with_words_after_on_the_same_line() + { + string sql_to_match = @" GO " + Words_to_check; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Contains(Words_to_check.Substring(0, 5), result[0]); + } + + [Fact] + public void go_with_words_after_on_the_same_line_including_symbols() + { + string sql_to_match = @" GO " + Words_to_check + Symbols_to_check; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void go_with_words_before_and_after_on_the_same_line() + { + string sql_to_match = Words_to_check + @" GO " + Words_to_check; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal(2, result.Count); + Assert.Equal(Words_to_check.Trim(), result[0].Trim()); + Assert.Equal(Words_to_check.Trim(), result[1].Trim()); + } + + [Fact] + public void go_with_words_before_and_after_on_the_same_line_including_symbols() + { + string sql_to_match = Words_to_check + Symbols_to_check.Replace("'", "").Replace("\"", "") + + " GO BOB" + Symbols_to_check; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal(2, result.Count); + } + + [Fact] + public void go_after_double_dash_comment_with_single_quote_and_single_quote_after_go() + { + string sql_to_match = Words_to_check + @" -- ' +GO +select '' +go"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal(2, result.Count); + } + + [Fact] + public void lowercase_go_statement() + { + const string sql_to_match = "\r\nwhere DataName = 'AttributeKeyMap'\r\ngo "; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Contains("DataName", result[0]); + } + + [Fact] + public void go_after_line_comment_containing_an_apostrophe() + { + const string sql_to_match = @"select 1 -- ' +GO +'' +GO +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal(2, result.Count); + } + + [Fact] + public void go_with_comment_after() + { + string sql_to_match = " GO -- comment"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal([" -- comment"], result); + } + + [Fact] + public void go_with_semicolon_directly_after() + { + string sql_to_match = "jalla GO;"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal(["jalla ", ";" ], result); + } + + } + + public class should_not_replace_on + { + private readonly ITestOutputHelper _testOutput; + private readonly SqlServerStatementSplitter _splitter; + + public should_not_replace_on(ITestOutputHelper testOutput) + { + _testOutput = testOutput; + _splitter = new SqlServerStatementSplitter(); + } + + [Fact] + public void g() + { + const string sql_to_match = @" G +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Contains("G", result[0]); + } + + [Fact] + public void o() + { + const string sql_to_match = @" O +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Contains("O", result[0]); + } + + [Fact] + public void go_when_go_is_the_last_part_of_the_last_word_on_a_line() + { + string sql_to_match = Words_to_check + @"GO +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Contains(Words_to_check.Substring(0, 5), result[0]); + } + + [Fact] + public void go_with_double_dash_comment_starting_line() + { + string sql_to_match = @"--GO +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_with_double_dash_comment_and_space_starting_line() + { + string sql_to_match = @"-- GO +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_with_double_dash_comment_and_space_starting_line_and_words_after_go() + { + string sql_to_match = @"-- GO " + Words_to_check + @" +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_with_double_dash_comment_and_space_starting_line_and_symbols_after_go() + { + string sql_to_match = @"-- GO " + Symbols_to_check + @" +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_with_double_dash_comment_and_tab_starting_line() + { + string sql_to_match = "--" + "\t" + @"GO +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_with_double_dash_comment_and_tab_starting_line_and_words_after_go() + { + string sql_to_match = @"--" + "\t" + @"GO " + Words_to_check + @" +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_with_double_dash_comment_and_tab_starting_line_and_symbols_after_go() + { + string sql_to_match = @"--" + "\t" + @"GO " + Symbols_to_check + @" +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_with_double_dash_comment_starting_line_with_words_before_go() + { + string sql_to_match = @"-- " + Words_to_check + @" GO +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_when_between_tick_marks() + { + const string sql_to_match = @"' GO + '"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_inside_a_multiline_string_literal() + { + const string sql_to_match = @"select ' 1 -- +GO +'"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void + go_when_between_tick_marks_with_symbols_and_words_before_ending_on_same_line() + { + string sql_to_match = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" GO'"; + string expected_scrubbed = + @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" GO'"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_when_between_tick_marks_with_symbols_and_words_before() + { + string sql_to_match = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" GO + '"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_when_between_tick_marks_with_symbols_and_words_after() + { + string sql_to_match = @"' GO + " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @"'"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_with_double_dash_comment_starting_line_with_symbols_before_go() + { + string sql_to_match = @"--" + Symbols_to_check + @" GO +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void + go_with_double_dash_comment_starting_line_with_words_and_symbols_before_go() + { + string sql_to_match = @"--" + Symbols_to_check + Words_to_check + @" GO +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_inside_of_comments() + { + string sql_to_match = @"/* GO */"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_inside_of_comments_with_a_line_break() + { + string sql_to_match = @"/* GO +*/"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_inside_of_nested_comments_with_a_line_break() + { + const string sql_to_match = @"/* /* */ GO +*/"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_inside_of_comments_with_words_before() + { + string sql_to_match = + @"/* +" + Words_to_check + @" GO + +*/"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_inside_of_comments_with_words_before_on_a_different_line() + { + string sql_to_match = + @"/* +" + Words_to_check + @" +GO + +*/"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_inside_of_comments_with_words_before_and_after_on_different_lines() + { + string sql_to_match = + @"/* +" + Words_to_check + @" +GO + +" + Words_to_check + @" +*/"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_inside_of_comments_with_symbols_after_on_different_lines() + { + string sql_to_match = + @"/* +GO + +" + Symbols_to_check + @" +*/"; + + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + } + + public class Split + { + private readonly SqlServerStatementSplitter _splitter = new(); + + [Fact] + public void Splits_and_removes_GO_statements() + { + var original = @" +SELECT @@VERSION; + + +GO +SELECT 1 +"; + var batches = _splitter.Split(original); + + Assert.Equal(2, batches.Count()); + } + } +} diff --git a/unittests/SqlServer/Basic_tests/StatementSplitter_.cs b/unittests/SqlServer/Basic_tests/StatementSplitter_.cs deleted file mode 100644 index 12bbedb7..00000000 --- a/unittests/SqlServer/Basic_tests/StatementSplitter_.cs +++ /dev/null @@ -1,33 +0,0 @@ -using grate.Infrastructure; -using grate.Migration; -using grate.SqlServer.Infrastructure; - -namespace SqlServer.Statement_Splitting; - - -// ReSharper disable once InconsistentNaming -public class StatementSplitter_ -{ - private readonly StatementSplitter _splitter; - - public StatementSplitter_() - { - _splitter = new StatementSplitter(new SqlServerSyntax()); - } - - [Fact] - public void Splits_and_removes_GO_statements() - { - var original = @" -SELECT @@VERSION; - - -GO -SELECT 1 -"; - var batches = _splitter.Split(original); - - Assert.Equal(2, batches.Count()); - } - -} diff --git a/unittests/SqlServer/TestInfrastructure/SqlServerSplitterContext.cs b/unittests/SqlServer/TestInfrastructure/SqlServerSplitterContext.cs index b175d4f3..df733f71 100644 --- a/unittests/SqlServer/TestInfrastructure/SqlServerSplitterContext.cs +++ b/unittests/SqlServer/TestInfrastructure/SqlServerSplitterContext.cs @@ -100,25 +100,25 @@ INSERT [dbo].[Foo] ([Bar]) VALUES (N'Go speed racer, go speed racer, go speed ra GO"; - public static readonly string tsql_statement_scrubbed = @" + public static readonly string[] tsql_statement_scrubbed = [@" BOB1 -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" /* COMMENT */ BOB2 -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" -- GO -BOB3 " + StatementSplitter.BatchTerminatorReplacementString + @" +BOB3 ", @" --`~!@#$%^&*()-_+=,.;:'""[]\/?<> GO BOB5 - " + StatementSplitter.BatchTerminatorReplacementString + @" + ", @" BOB6 -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" /* GO */ @@ -133,12 +133,12 @@ INSERT [dbo].[Foo] ([Bar]) VALUES (N'Go speed racer, go speed racer, go speed ra BOB8 -- -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" BOB9 -- `~!@#$%^&*()-_+=,.;:'""[]\/?<> -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" BOB10GO @@ -166,7 +166,7 @@ yeppsasd decimal(20, 6) NULL, slsald varchar(15) NULL, uhasdf varchar(15) NULL, daf_asdfasdf DECIMAL(20,6) NULL; -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Daily job', @step_id=1, @@ -183,40 +183,13 @@ dml statements GO dml statements ' -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" INSERT [dbo].[Foo] ([Bar]) VALUES (N'hello--world. Thanks!') INSERT [dbo].[Foo] ([Bar]) VALUES (N'Go speed racer, go speed racer, go speed racer go!!!!! ') -" + StatementSplitter.BatchTerminatorReplacementString + @""; - - public static readonly string plsql_statement = - @" -SQL1; -; -SQL2; -; -tmpSql := 'DROP SEQUENCE mutatieStockID'; -EXECUTE IMMEDIATE tmpSql; -; -BEGIN -INSERT into Table (columnname) values ("";""); -UPDATE Table set columnname="";""; -END; -"; - public static readonly string plsql_statement_scrubbed = @" -SQL1; -" + StatementSplitter.BatchTerminatorReplacementString + @" -SQL2; -" + StatementSplitter.BatchTerminatorReplacementString + @" -tmpSql := 'DROP SEQUENCE mutatieStockID'; -EXECUTE IMMEDIATE tmpSql; -" + StatementSplitter.BatchTerminatorReplacementString + @" -BEGIN -INSERT into Table (columnname) values ("";""); -UPDATE Table set columnname="";""; -END; -"; +"]; + } } diff --git a/unittests/SqlServerCaseSensitive/Basic_tests/BatchSplitterReplacer_.cs b/unittests/SqlServerCaseSensitive/Basic_tests/BatchSplitterReplacer_.cs deleted file mode 100644 index 83773904..00000000 --- a/unittests/SqlServerCaseSensitive/Basic_tests/BatchSplitterReplacer_.cs +++ /dev/null @@ -1,597 +0,0 @@ -using grate.Infrastructure; -using grate.SqlServer.Infrastructure; -using SqlServerCaseSensitive.TestInfrastructure; - -// ReSharper disable InconsistentNaming - -namespace SqlServerCaseSensitive.Basic_tests; - -public class BatchSplitterReplacer_ -{ - private const string Batch_terminator_replacement_string = StatementSplitter.BatchTerminatorReplacementString; - - private const string Symbols_to_check = "`~!@#$%^&*()-_+=,.;:'\"[]\\/?<>"; - private const string Words_to_check = "abcdefghijklmnopqrstuvwzyz0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - public class should_replace_on - { - private ITestOutputHelper _testOutput; - private BatchSplitterReplacer Replacer; - - public should_replace_on(ITestOutputHelper testOutput) - { - _testOutput = testOutput; - Replacer = new BatchSplitterReplacer(new SqlServerSyntax()); - } - - [Fact] - public void full_statement_without_issue() - { - string sql_to_match = SqlServerSplitterContext.FullSplitter.tsql_statement; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(SqlServerSplitterContext.FullSplitter.tsql_statement_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_space() - { - const string sql_to_match = @" GO "; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" "; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_tab() - { - string sql_to_match = @" GO" + "\t"; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + "\t"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_by_itself() - { - const string sql_to_match = @"GO"; - string expected_scrubbed = Batch_terminator_replacement_string; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_starting_file() - { - const string sql_to_match = @"GO -whatever"; - string expected_scrubbed = Batch_terminator_replacement_string + @" -whatever"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_new_line() - { - const string sql_to_match = @" GO -"; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_on_new_line_after_double_dash_comments() - { - const string sql_to_match = - @"-- -GO -"; - string expected_scrubbed = - @"-- -" + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_on_new_line_after_double_dash_comments_and_words() - { - string sql_to_match = @"-- " + Words_to_check + @" -GO -"; - string expected_scrubbed = @"-- " + Words_to_check + @" -" + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_new_line_after_double_dash_comments_and_symbols() - { - string sql_to_match = @"-- " + Symbols_to_check + @" -GO -"; - string expected_scrubbed = @"-- " + Symbols_to_check + @" -" + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_on_its_own_line() - { - const string sql_to_match = @" -GO -"; - string expected_scrubbed = @" -" + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_no_line_terminator() - { - const string sql_to_match = @" GO "; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" "; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_words_before() - { - string sql_to_match = Words_to_check + @" GO -"; - string expected_scrubbed = Words_to_check + @" " + Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_symbols_and_words_before() - { - string sql_to_match = Symbols_to_check + Words_to_check + @" GO -"; - string expected_scrubbed = Symbols_to_check + Words_to_check + @" " + - Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_words_and_symbols_before() - { - string sql_to_match = Words_to_check + Symbols_to_check + @" GO -"; - string expected_scrubbed = Words_to_check + Symbols_to_check + @" " + - Batch_terminator_replacement_string + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_words_after_on_the_same_line() - { - string sql_to_match = @" GO " + Words_to_check; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" " + Words_to_check; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_words_after_on_the_same_line_including_symbols() - { - string sql_to_match = @" GO " + Words_to_check + Symbols_to_check; - string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" " + Words_to_check + - Symbols_to_check; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_words_before_and_after_on_the_same_line() - { - string sql_to_match = Words_to_check + @" GO " + Words_to_check; - string expected_scrubbed = Words_to_check + @" " + Batch_terminator_replacement_string + @" " + - Words_to_check; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_words_before_and_after_on_the_same_line_including_symbols() - { - string sql_to_match = Words_to_check + Symbols_to_check.Replace("'", "").Replace("\"", "") + - " GO BOB" + Symbols_to_check; - string expected_scrubbed = Words_to_check + Symbols_to_check.Replace("'", "").Replace("\"", "") + - " " + Batch_terminator_replacement_string + " BOB" + Symbols_to_check; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_after_double_dash_comment_with_single_quote_and_single_quote_after_go() - { - string sql_to_match = Words_to_check + @" -- ' -GO -select '' -go"; - string expected_scrubbed = Words_to_check + @" -- ' -" + Batch_terminator_replacement_string + @" -select '' -" + Batch_terminator_replacement_string; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_comment_after() - { - string sql_to_match = " GO -- comment"; - string expected_scrubbed = " " + Batch_terminator_replacement_string + " -- comment"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_semicolon_directly_after() - { - string sql_to_match = "jalla GO;"; - string expected_scrubbed = "jalla " + Batch_terminator_replacement_string + ";"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - } - - public class should_not_replace_on - { - private readonly ITestOutputHelper _testOutput; - private readonly BatchSplitterReplacer _replacer; - - public should_not_replace_on(ITestOutputHelper testOutput) - { - _testOutput = testOutput; - _replacer = new BatchSplitterReplacer(new SqlServerSyntax()); - } - - [Fact] - public void g() - { - const string sql_to_match = @" G -"; - const string expected_scrubbed = @" G -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void o() - { - const string sql_to_match = @" O -"; - const string expected_scrubbed = @" O -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_when_go_is_the_last_part_of_the_last_word_on_a_line() - { - string sql_to_match = Words_to_check + @"GO -"; - string expected_scrubbed = Words_to_check + @"GO -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_starting_line() - { - string sql_to_match = @"--GO -"; - string expected_scrubbed = @"--GO -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_and_space_starting_line() - { - string sql_to_match = @"-- GO -"; - string expected_scrubbed = @"-- GO -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_and_space_starting_line_and_words_after_go() - { - string sql_to_match = @"-- GO " + Words_to_check + @" -"; - string expected_scrubbed = @"-- GO " + Words_to_check + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_and_space_starting_line_and_symbols_after_go() - { - string sql_to_match = @"-- GO " + Symbols_to_check + @" -"; - string expected_scrubbed = @"-- GO " + Symbols_to_check + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_and_tab_starting_line() - { - string sql_to_match = "--" + "\t" + @"GO -"; - string expected_scrubbed = @"--" + "\t" + @"GO -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_and_tab_starting_line_and_words_after_go() - { - string sql_to_match = @"--" + "\t" + @"GO " + Words_to_check + @" -"; - string expected_scrubbed = @"--" + "\t" + @"GO " + Words_to_check + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_and_tab_starting_line_and_symbols_after_go() - { - string sql_to_match = @"--" + "\t" + @"GO " + Symbols_to_check + @" -"; - string expected_scrubbed = @"--" + "\t" + @"GO " + Symbols_to_check + @" -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_starting_line_with_words_before_go() - { - string sql_to_match = @"-- " + Words_to_check + @" GO -"; - string expected_scrubbed = @"-- " + Words_to_check + @" GO -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_when_between_tick_marks() - { - const string sql_to_match = @"' GO - '"; - const string expected_scrubbed = @"' GO - '"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void - go_when_between_tick_marks_with_symbols_and_words_before_ending_on_same_line() - { - string sql_to_match = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" GO'"; - string expected_scrubbed = - @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" GO'"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_when_between_tick_marks_with_symbols_and_words_before() - { - string sql_to_match = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" GO - '"; - string expected_scrubbed = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" GO - '"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_when_between_tick_marks_with_symbols_and_words_after() - { - string sql_to_match = @"' GO - " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @"'"; - string expected_scrubbed = @"' GO - " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @"'"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_with_double_dash_comment_starting_line_with_symbols_before_go() - { - string sql_to_match = @"--" + Symbols_to_check + @" GO -"; - string expected_scrubbed = @"--" + Symbols_to_check + @" GO -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void - go_with_double_dash_comment_starting_line_with_words_and_symbols_before_go() - { - string sql_to_match = @"--" + Symbols_to_check + Words_to_check + @" GO -"; - string expected_scrubbed = @"--" + Symbols_to_check + Words_to_check + @" GO -"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_inside_of_comments() - { - string sql_to_match = @"/* GO */"; - string expected_scrubbed = @"/* GO */"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_inside_of_comments_with_a_line_break() - { - string sql_to_match = @"/* GO -*/"; - string expected_scrubbed = @"/* GO -*/"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_inside_of_comments_with_words_before() - { - string sql_to_match = - @"/* -" + Words_to_check + @" GO - -*/"; - string expected_scrubbed = - @"/* -" + Words_to_check + @" GO - -*/"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_inside_of_comments_with_words_before_on_a_different_line() - { - string sql_to_match = - @"/* -" + Words_to_check + @" -GO - -*/"; - string expected_scrubbed = - @"/* -" + Words_to_check + @" -GO - -*/"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_inside_of_comments_with_words_before_and_after_on_different_lines() - { - string sql_to_match = - @"/* -" + Words_to_check + @" -GO - -" + Words_to_check + @" -*/"; - string expected_scrubbed = - @"/* -" + Words_to_check + @" -GO - -" + Words_to_check + @" -*/"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - - [Fact] - public void go_inside_of_comments_with_symbols_after_on_different_lines() - { - string sql_to_match = - @"/* -GO - -" + Symbols_to_check + @" -*/"; - string expected_scrubbed = - @"/* -GO - -" + Symbols_to_check + @" -*/"; - _testOutput.WriteLine(sql_to_match); - string sql_statement_scrubbed = _replacer.Replace(sql_to_match); - Assert.Equal(expected_scrubbed, sql_statement_scrubbed); - } - } - -} diff --git a/unittests/SqlServerCaseSensitive/Basic_tests/SqlServerStatementSplitter_.cs b/unittests/SqlServerCaseSensitive/Basic_tests/SqlServerStatementSplitter_.cs new file mode 100644 index 00000000..33a5152f --- /dev/null +++ b/unittests/SqlServerCaseSensitive/Basic_tests/SqlServerStatementSplitter_.cs @@ -0,0 +1,539 @@ +using grate.Infrastructure; +using grate.SqlServer.Infrastructure; +using SqlServerCaseSensitive.TestInfrastructure; + +// ReSharper disable InconsistentNaming + +namespace SqlServerCaseSensitive.Basic_tests; + +public class SqlServerStatementSplitter_ +{ + private const string Symbols_to_check = "`~!@#$%^&*()-_+=,.;:'\"[]\\/?<>"; + private const string Words_to_check = "abcdefghijklmnopqrstuvwzyz0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + public class should_replace_on + { + private ITestOutputHelper _testOutput; + private SqlServerStatementSplitter Splitter; + + public should_replace_on(ITestOutputHelper testOutput) + { + _testOutput = testOutput; + Splitter = new SqlServerStatementSplitter(); + } + + [Fact] + public void full_statement_without_issue() + { + string sql_to_match = SqlServerSplitterContext.FullSplitter.tsql_statement; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.NotEmpty(result); + Assert.True(result.Count > 1, "Should split into multiple statements"); + Assert.Equal(result, SqlServerSplitterContext.FullSplitter.tsql_statement_scrubbed); + } + + [Fact] + public void go_with_space() + { + const string sql_to_match = @" GO "; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Empty(result); + } + + [Fact] + public void go_with_tab() + { + string sql_to_match = @" GO" + "\t"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal(["\t"], result); + } + + [Fact] + public void go_by_itself() + { + const string sql_to_match = @"GO"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Empty(result); + } + + [Fact] + public void go_starting_file() + { + const string sql_to_match = @"GO +whatever"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Contains("whatever", result[0]); + } + + [Fact] + public void go_with_new_line() + { + const string sql_to_match = @" GO +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Empty(result); + } + + + [Theory] + [InlineData("\r\n", "CRLF")] + [InlineData("\n", "LF")] + public void go_with_on_new_line_after_double_dash_comments(string line_ending, string _) + { + string sql_to_match = $"--{line_ending}GO{line_ending}"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal(["--" + line_ending], result); + } + + [Fact] + public void go_with_on_new_line_after_double_dash_comments_and_words() + { + string sql_to_match = @"-- " + Words_to_check + @" +GO +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void go_with_new_line_after_double_dash_comments_and_symbols() + { + string sql_to_match = @"-- " + Symbols_to_check + @" +GO +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void go_on_its_own_line() + { + const string sql_to_match = @" +GO +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Empty(result); + } + + [Fact] + public void go_with_no_line_terminator() + { + const string sql_to_match = @" GO "; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Empty(result); + } + + [Fact] + public void go_with_words_before() + { + string sql_to_match = Words_to_check + @" GO +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(Words_to_check.Trim(), result[0].Trim()); + } + + [Fact] + public void go_with_symbols_and_words_before() + { + string sql_to_match = Symbols_to_check + Words_to_check + @" GO +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void go_with_words_and_symbols_before() + { + string sql_to_match = Words_to_check + Symbols_to_check + @" GO +"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void go_with_words_after_on_the_same_line() + { + string sql_to_match = @" GO " + Words_to_check; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Contains(Words_to_check.Substring(0, 5), result[0]); + } + + [Fact] + public void go_with_words_after_on_the_same_line_including_symbols() + { + string sql_to_match = @" GO " + Words_to_check + Symbols_to_check; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + } + + [Fact] + public void go_with_words_before_and_after_on_the_same_line() + { + string sql_to_match = Words_to_check + @" GO " + Words_to_check; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal([Words_to_check + " ", " " + Words_to_check], result); + } + + [Fact] + public void go_with_words_before_and_after_on_the_same_line_including_symbols() + { + string sql_to_match = Words_to_check + Symbols_to_check.Replace("'", "").Replace("\"", "") + + " GO BOB" + Symbols_to_check; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal(2, result.Count); + } + + [Fact] + public void go_after_double_dash_comment_with_single_quote_and_single_quote_after_go() + { + string sql_to_match = Words_to_check + @" -- ' +GO +select '' +go"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal(2, result.Count); + } + + [Fact] + public void go_with_comment_after() + { + string sql_to_match = " GO -- comment"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal([" -- comment"], result); + } + + [Fact] + public void go_with_semicolon_directly_after() + { + string sql_to_match = "jalla GO;"; + _testOutput.WriteLine(sql_to_match); + var result = Splitter.Split(sql_to_match).ToList(); + Assert.Equal(["jalla ", ";" ], result); + } + + } + + public class should_not_replace_on + { + private readonly ITestOutputHelper _testOutput; + private readonly SqlServerStatementSplitter _splitter; + + public should_not_replace_on(ITestOutputHelper testOutput) + { + _testOutput = testOutput; + _splitter = new SqlServerStatementSplitter(); + } + + [Fact] + public void g() + { + const string sql_to_match = @" G +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Contains("G", result[0]); + } + + [Fact] + public void o() + { + const string sql_to_match = @" O +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Contains("O", result[0]); + } + + [Fact] + public void go_when_go_is_the_last_part_of_the_last_word_on_a_line() + { + string sql_to_match = Words_to_check + @"GO +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Contains(Words_to_check.Substring(0, 5), result[0]); + } + + [Fact] + public void go_with_double_dash_comment_starting_line() + { + string sql_to_match = @"--GO +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_with_double_dash_comment_and_space_starting_line() + { + string sql_to_match = @"-- GO +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_with_double_dash_comment_and_space_starting_line_and_words_after_go() + { + string sql_to_match = @"-- GO " + Words_to_check + @" +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_with_double_dash_comment_and_space_starting_line_and_symbols_after_go() + { + string sql_to_match = @"-- GO " + Symbols_to_check + @" +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_with_double_dash_comment_and_tab_starting_line() + { + string sql_to_match = "--" + "\t" + @"GO +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_with_double_dash_comment_and_tab_starting_line_and_words_after_go() + { + string sql_to_match = @"--" + "\t" + @"GO " + Words_to_check + @" +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_with_double_dash_comment_and_tab_starting_line_and_symbols_after_go() + { + string sql_to_match = @"--" + "\t" + @"GO " + Symbols_to_check + @" +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_with_double_dash_comment_starting_line_with_words_before_go() + { + string sql_to_match = @"-- " + Words_to_check + @" GO +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_when_between_tick_marks() + { + const string sql_to_match = @"' GO + '"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void + go_when_between_tick_marks_with_symbols_and_words_before_ending_on_same_line() + { + string sql_to_match = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" GO'"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_when_between_tick_marks_with_symbols_and_words_before() + { + string sql_to_match = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" GO + '"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_when_between_tick_marks_with_symbols_and_words_after() + { + string sql_to_match = @"' GO + " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @"'"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_with_double_dash_comment_starting_line_with_symbols_before_go() + { + string sql_to_match = @"--" + Symbols_to_check + @" GO +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void + go_with_double_dash_comment_starting_line_with_words_and_symbols_before_go() + { + string sql_to_match = @"--" + Symbols_to_check + Words_to_check + @" GO +"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_inside_of_comments() + { + string sql_to_match = @"/* GO */"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_inside_of_comments_with_a_line_break() + { + string sql_to_match = @"/* GO +*/"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_inside_of_comments_with_words_before() + { + string sql_to_match = + @"/* +" + Words_to_check + @" GO + +*/"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_inside_of_comments_with_words_before_on_a_different_line() + { + string sql_to_match = + @"/* +" + Words_to_check + @" +GO + +*/"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_inside_of_comments_with_words_before_and_after_on_different_lines() + { + string sql_to_match = + @"/* +" + Words_to_check + @" +GO + +" + Words_to_check + @" +*/"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + + [Fact] + public void go_inside_of_comments_with_symbols_after_on_different_lines() + { + string sql_to_match = + @"/* +GO + +" + Symbols_to_check + @" +*/"; + _testOutput.WriteLine(sql_to_match); + var result = _splitter.Split(sql_to_match).ToList(); + Assert.Single(result); + Assert.Equal(sql_to_match, result.Single()); + } + } + + public class Split + { + private readonly SqlServerStatementSplitter _splitter = new(); + + [Fact] + public void Splits_and_removes_GO_statements() + { + var original = @" +SELECT @@VERSION; + + +GO +SELECT 1 +"; + var batches = _splitter.Split(original); + + Assert.Equal(2, batches.Count()); + } + } +} diff --git a/unittests/SqlServerCaseSensitive/Basic_tests/StatementSplitter_.cs b/unittests/SqlServerCaseSensitive/Basic_tests/StatementSplitter_.cs deleted file mode 100644 index 14cbcf84..00000000 --- a/unittests/SqlServerCaseSensitive/Basic_tests/StatementSplitter_.cs +++ /dev/null @@ -1,32 +0,0 @@ -using grate.Infrastructure; -using grate.SqlServer.Infrastructure; - -namespace SqlServerCaseSensitive.Basic_tests; - - -// ReSharper disable once InconsistentNaming -public class StatementSplitter_ -{ - private readonly StatementSplitter _splitter; - - public StatementSplitter_() - { - _splitter = new StatementSplitter(new SqlServerSyntax()); - } - - [Fact] - public void Splits_and_removes_GO_statements() - { - var original = @" -SELECT @@VERSION; - - -GO -SELECT 1 -"; - var batches = _splitter.Split(original); - - Assert.Equal(2, batches.Count()); - } - -} diff --git a/unittests/SqlServerCaseSensitive/TestInfrastructure/SqlServerSplitterContext.cs b/unittests/SqlServerCaseSensitive/TestInfrastructure/SqlServerSplitterContext.cs index 84843edd..18b89a30 100644 --- a/unittests/SqlServerCaseSensitive/TestInfrastructure/SqlServerSplitterContext.cs +++ b/unittests/SqlServerCaseSensitive/TestInfrastructure/SqlServerSplitterContext.cs @@ -100,25 +100,25 @@ INSERT [dbo].[Foo] ([Bar]) VALUES (N'Go speed racer, go speed racer, go speed ra GO"; - public static readonly string tsql_statement_scrubbed = @" + public static readonly string[] tsql_statement_scrubbed =[ @" BOB1 -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" /* COMMENT */ BOB2 -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" -- GO -BOB3 " + StatementSplitter.BatchTerminatorReplacementString + @" +BOB3 ", @" --`~!@#$%^&*()-_+=,.;:'""[]\/?<> GO BOB5 - " + StatementSplitter.BatchTerminatorReplacementString + @" + ", @" BOB6 -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" /* GO */ @@ -133,12 +133,12 @@ INSERT [dbo].[Foo] ([Bar]) VALUES (N'Go speed racer, go speed racer, go speed ra BOB8 -- -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" BOB9 -- `~!@#$%^&*()-_+=,.;:'""[]\/?<> -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" BOB10GO @@ -166,7 +166,7 @@ yeppsasd decimal(20, 6) NULL, slsald varchar(15) NULL, uhasdf varchar(15) NULL, daf_asdfasdf DECIMAL(20,6) NULL; -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Daily job', @step_id=1, @@ -183,40 +183,13 @@ dml statements GO dml statements ' -" + StatementSplitter.BatchTerminatorReplacementString + @" +", @" INSERT [dbo].[Foo] ([Bar]) VALUES (N'hello--world. Thanks!') INSERT [dbo].[Foo] ([Bar]) VALUES (N'Go speed racer, go speed racer, go speed racer go!!!!! ') -" + StatementSplitter.BatchTerminatorReplacementString + @""; - - public static readonly string plsql_statement = - @" -SQL1; -; -SQL2; -; -tmpSql := 'DROP SEQUENCE mutatieStockID'; -EXECUTE IMMEDIATE tmpSql; -; -BEGIN -INSERT into Table (columnname) values ("";""); -UPDATE Table set columnname="";""; -END; -"; - public static readonly string plsql_statement_scrubbed = @" -SQL1; -" + StatementSplitter.BatchTerminatorReplacementString + @" -SQL2; -" + StatementSplitter.BatchTerminatorReplacementString + @" -tmpSql := 'DROP SEQUENCE mutatieStockID'; -EXECUTE IMMEDIATE tmpSql; -" + StatementSplitter.BatchTerminatorReplacementString + @" -BEGIN -INSERT into Table (columnname) values ("";""); -UPDATE Table set columnname="";""; -END; -"; +"]; + } } From e0a5189bed7ad4dbe3e3f512822af4bd838d5b4a Mon Sep 17 00:00:00 2001 From: Isaac Devine Date: Tue, 25 Aug 2026 09:50:22 +1200 Subject: [PATCH 3/4] Make Splitter Unit Tests more consistent Although the Splitter name was inherited from the BatchSplitterReplacer tests, we want to more to more consistent naming used in the rest of grate. Thanks @wokket for the comment: https://github.com/grate-devs/grate/pull/816#discussion_r3844064309 --- .../Basic_tests/OracleStatementSplitter_.cs | 94 +++++++++---------- .../SqlServerStatementSplitter_.cs | 50 +++++----- .../SqlServerStatementSplitter_.cs | 46 ++++----- 3 files changed, 95 insertions(+), 95 deletions(-) diff --git a/unittests/Oracle/Basic_tests/OracleStatementSplitter_.cs b/unittests/Oracle/Basic_tests/OracleStatementSplitter_.cs index a883375d..c5534b7b 100644 --- a/unittests/Oracle/Basic_tests/OracleStatementSplitter_.cs +++ b/unittests/Oracle/Basic_tests/OracleStatementSplitter_.cs @@ -16,12 +16,12 @@ public class OracleStatementSplitter_ public class should_replace_on { private ITestOutputHelper _testOutput; - private OracleStatementSplitter Splitter; + private OracleStatementSplitter _splitter; public should_replace_on(ITestOutputHelper testOutput) { _testOutput = testOutput; - Splitter = new OracleStatementSplitter(); + _splitter = new OracleStatementSplitter(); } [Fact] @@ -29,7 +29,7 @@ public void full_statement_without_issue() { string sql_to_match = OracleSplitterContext.FullSplitter.PLSqlStatement; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.NotEmpty(result); Assert.True(result.Count > 1, "Should split into multiple statements"); Assert.Equal(result, OracleSplitterContext.FullSplitter.PLSqlStatementScrubbed); @@ -40,7 +40,7 @@ public void slash_with_space() { const string sql_to_match = @" / "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Empty(result); } @@ -49,7 +49,7 @@ public void slash_with_tab() { string sql_to_match = @" /" + "\t"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal(["\t"],result); } @@ -58,7 +58,7 @@ public void slash_by_itself() { const string sql_to_match = @"/"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Empty(result); } @@ -68,7 +68,7 @@ public void slash_starting_file() const string sql_to_match = @"/ whatever"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -78,7 +78,7 @@ public void slash_with_new_line() const string sql_to_match = @" / "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Empty(result); } @@ -88,7 +88,7 @@ public void slash_with_one_new_line_after_double_dash_comments_lf(string line_en { string sql_to_match = $"--{line_ending}/{line_ending}"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal(["--" + line_ending], result); } @@ -98,7 +98,7 @@ public void slash_with_one_new_line_after_double_dash_comments_crlf(string line_ { string sql_to_match = $"--{line_ending}/{line_ending}"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal(["--" + line_ending, line_ending], result); } @@ -109,7 +109,7 @@ public void slash_with_one_new_line_after_double_dash_comments_and_words() / "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -120,7 +120,7 @@ public void slash_with_new_line_after_double_dash_comments_and_symbols() / "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -131,7 +131,7 @@ public void slash_on_its_own_line() / "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Empty(result); } @@ -140,7 +140,7 @@ public void slash_with_no_line_terminator() { const string sql_to_match = @" / "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Empty(result); } @@ -150,7 +150,7 @@ public void slash_with_words_before() string sql_to_match = Words_to_check + @" / "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -160,7 +160,7 @@ public void slash_with_symbols_and_words_before() string sql_to_match = Symbols_to_check + Words_to_check + @" / "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -170,7 +170,7 @@ public void slash_with_words_and_symbols_before() string sql_to_match = Words_to_check + Symbols_to_check + @" / "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -179,7 +179,7 @@ public void slash_with_words_after_on_the_same_line() { string sql_to_match = @" / " + Words_to_check; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -188,7 +188,7 @@ public void slash_with_words_after_on_the_same_line_including_symbols() { string sql_to_match = @" / " + Words_to_check + Symbols_to_check; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -197,7 +197,7 @@ public void slash_with_words_before_and_after_on_the_same_line() { string sql_to_match = Words_to_check + @" / " + Words_to_check; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal([Words_to_check + " "," " + Words_to_check], result); } @@ -207,7 +207,7 @@ public void slash_with_words_before_and_after_on_the_same_line_including_symbols string sql_to_match = Words_to_check + Symbols_to_check.Replace("'", "").Replace("\"", "") + " / BOB" + Symbols_to_check; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal([ Words_to_check + Symbols_to_check.Replace("'", "").Replace("\"", "") + " ", " BOB" + Symbols_to_check], result); } @@ -220,7 +220,7 @@ public void slash_after_double_dash_comment_with_single_quote_and_single_quote_a select '' /"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal([Words_to_check + " -- '\n", "\nselect ''\n"], result); } @@ -229,7 +229,7 @@ public void slash_with_comment_after() { string sql_to_match = " / -- comment"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -238,7 +238,7 @@ public void slash_with_semicolon_directly_after() { string sql_to_match = "jalla /;"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal(["jalla ", ";"], result); } @@ -247,12 +247,12 @@ public void slash_with_semicolon_directly_after() public class should_not_replace_on { private ITestOutputHelper _testOutput; - private OracleStatementSplitter Splitter; + private OracleStatementSplitter _splitter; public should_not_replace_on(ITestOutputHelper testOutput) { _testOutput = testOutput; - Splitter = new OracleStatementSplitter(); + _splitter = new OracleStatementSplitter(); } [Fact] @@ -261,7 +261,7 @@ public void slash_when_slash_is_the_last_part_of_the_last_word_on_a_line() string sql_to_match = Words_to_check + @"/ "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -271,7 +271,7 @@ public void slash_with_double_dash_comment_starting_line() string sql_to_match = @"--/ "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -281,7 +281,7 @@ public void slash_with_double_dash_comment_and_space_starting_line() string sql_to_match = @"-- / "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -291,7 +291,7 @@ public void slash_with_double_dash_comment_and_space_starting_line_and_words_aft string sql_to_match = @"-- / " + Words_to_check + @" "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -301,7 +301,7 @@ public void slash_with_double_dash_comment_and_space_starting_line_and_symbols_a string sql_to_match = @"-- / " + Symbols_to_check + @" "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -311,7 +311,7 @@ public void slash_with_double_dash_comment_and_tab_starting_line() string sql_to_match = "--" + "\t" + @"/ "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -321,7 +321,7 @@ public void slash_with_double_dash_comment_and_tab_starting_line_and_words_after string sql_to_match = @"--" + "\t" + @"/ " + Words_to_check + @" "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -331,7 +331,7 @@ public void slash_with_double_dash_comment_and_tab_starting_line_and_symbols_aft string sql_to_match = @"--" + "\t" + @"/ " + Symbols_to_check + @" "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -341,7 +341,7 @@ public void slash_with_double_dash_comment_starting_line_with_words_before_slash string sql_to_match = @"-- " + Words_to_check + @" / "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -351,7 +351,7 @@ public void slash_when_between_tick_marks() const string sql_to_match = @"' / '"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -361,7 +361,7 @@ public void { string sql_to_match = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" /'"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -371,7 +371,7 @@ public void slash_when_between_tick_marks_with_symbols_and_words_before() string sql_to_match = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" / '"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -381,7 +381,7 @@ public void slash_when_between_tick_marks_with_symbols_and_words_after() string sql_to_match = @"' / " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @"'"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -391,7 +391,7 @@ public void slash_with_double_dash_comment_starting_line_with_symbols_before_sla string sql_to_match = @"--" + Symbols_to_check + @" / "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -402,7 +402,7 @@ public void string sql_to_match = @"--" + Symbols_to_check + Words_to_check + @" / "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -411,7 +411,7 @@ public void slash_inside_of_comments() { string sql_to_match = @"/* / */"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -421,7 +421,7 @@ public void slash_inside_of_comments_with_a_line_break() string sql_to_match = @"/* / */"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -434,7 +434,7 @@ public void slash_inside_of_comments_with_words_before() */"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -448,7 +448,7 @@ public void slash_inside_of_comments_with_words_before_on_a_different_line() */"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -463,7 +463,7 @@ public void slash_inside_of_comments_with_words_before_and_after_on_different_li " + Words_to_check + @" */"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -477,7 +477,7 @@ public void slash_inside_of_comments_with_symbols_after_on_different_lines() " + Symbols_to_check + @" */"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } } diff --git a/unittests/SqlServer/Basic_tests/SqlServerStatementSplitter_.cs b/unittests/SqlServer/Basic_tests/SqlServerStatementSplitter_.cs index e9948882..8736c179 100644 --- a/unittests/SqlServer/Basic_tests/SqlServerStatementSplitter_.cs +++ b/unittests/SqlServer/Basic_tests/SqlServerStatementSplitter_.cs @@ -14,12 +14,12 @@ public class SqlServerStatementSplitter_ public class should_replace_on { private ITestOutputHelper _testOutput; - private SqlServerStatementSplitter Splitter; + private SqlServerStatementSplitter _splitter; public should_replace_on(ITestOutputHelper testOutput) { _testOutput = testOutput; - Splitter = new SqlServerStatementSplitter(); + _splitter = new SqlServerStatementSplitter(); } [Fact] @@ -27,7 +27,7 @@ public void full_statement_without_issue() { string sql_to_match = SqlServerSplitterContext.FullSplitter.tsql_statement; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.NotEmpty(result); Assert.True(result.Count > 1, "Should split into multiple statements"); Assert.Equal(result, SqlServerSplitterContext.FullSplitter.tsql_statement_scrubbed); @@ -38,7 +38,7 @@ public void go_with_space() { const string sql_to_match = @" GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Empty(result); } @@ -47,7 +47,7 @@ public void go_with_tab() { string sql_to_match = @" GO" + "\t"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal(["\t"], result); } @@ -56,7 +56,7 @@ public void go_by_itself() { const string sql_to_match = @"GO"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Empty(result); } @@ -66,7 +66,7 @@ public void go_starting_file() const string sql_to_match = @"GO whatever"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); Assert.Contains("whatever", result[0]); } @@ -77,7 +77,7 @@ public void go_with_new_line() const string sql_to_match = @" GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Empty(result); } @@ -88,7 +88,7 @@ public void go_with_on_new_line_after_double_dash_comments(string line_ending, s { string sql_to_match = $"--{line_ending}GO{line_ending}"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal(["--" + line_ending], result); } @@ -99,7 +99,7 @@ public void go_with_on_new_line_after_double_dash_comments_and_words() GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -110,7 +110,7 @@ public void go_with_new_line_after_double_dash_comments_and_symbols() GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -121,7 +121,7 @@ public void go_on_its_own_line() GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Empty(result); } @@ -130,7 +130,7 @@ public void go_with_no_line_terminator() { const string sql_to_match = @" GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Empty(result); } @@ -140,7 +140,7 @@ public void go_with_words_before() string sql_to_match = Words_to_check + @" GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); Assert.Equal(Words_to_check.Trim(), result[0].Trim()); } @@ -151,7 +151,7 @@ public void go_with_symbols_and_words_before() string sql_to_match = Symbols_to_check + Words_to_check + @" GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -161,7 +161,7 @@ public void go_with_words_and_symbols_before() string sql_to_match = Words_to_check + Symbols_to_check + @" GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -170,7 +170,7 @@ public void go_with_words_after_on_the_same_line() { string sql_to_match = @" GO " + Words_to_check; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); Assert.Contains(Words_to_check.Substring(0, 5), result[0]); } @@ -180,7 +180,7 @@ public void go_with_words_after_on_the_same_line_including_symbols() { string sql_to_match = @" GO " + Words_to_check + Symbols_to_check; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -189,7 +189,7 @@ public void go_with_words_before_and_after_on_the_same_line() { string sql_to_match = Words_to_check + @" GO " + Words_to_check; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal(2, result.Count); Assert.Equal(Words_to_check.Trim(), result[0].Trim()); Assert.Equal(Words_to_check.Trim(), result[1].Trim()); @@ -201,7 +201,7 @@ public void go_with_words_before_and_after_on_the_same_line_including_symbols() string sql_to_match = Words_to_check + Symbols_to_check.Replace("'", "").Replace("\"", "") + " GO BOB" + Symbols_to_check; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal(2, result.Count); } @@ -213,7 +213,7 @@ public void go_after_double_dash_comment_with_single_quote_and_single_quote_afte select '' go"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal(2, result.Count); } @@ -222,7 +222,7 @@ public void lowercase_go_statement() { const string sql_to_match = "\r\nwhere DataName = 'AttributeKeyMap'\r\ngo "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); Assert.Contains("DataName", result[0]); } @@ -236,7 +236,7 @@ public void go_after_line_comment_containing_an_apostrophe() GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal(2, result.Count); } @@ -245,7 +245,7 @@ public void go_with_comment_after() { string sql_to_match = " GO -- comment"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal([" -- comment"], result); } @@ -254,7 +254,7 @@ public void go_with_semicolon_directly_after() { string sql_to_match = "jalla GO;"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal(["jalla ", ";" ], result); } diff --git a/unittests/SqlServerCaseSensitive/Basic_tests/SqlServerStatementSplitter_.cs b/unittests/SqlServerCaseSensitive/Basic_tests/SqlServerStatementSplitter_.cs index 33a5152f..81edaa00 100644 --- a/unittests/SqlServerCaseSensitive/Basic_tests/SqlServerStatementSplitter_.cs +++ b/unittests/SqlServerCaseSensitive/Basic_tests/SqlServerStatementSplitter_.cs @@ -14,12 +14,12 @@ public class SqlServerStatementSplitter_ public class should_replace_on { private ITestOutputHelper _testOutput; - private SqlServerStatementSplitter Splitter; + private SqlServerStatementSplitter _splitter; public should_replace_on(ITestOutputHelper testOutput) { _testOutput = testOutput; - Splitter = new SqlServerStatementSplitter(); + _splitter = new SqlServerStatementSplitter(); } [Fact] @@ -27,7 +27,7 @@ public void full_statement_without_issue() { string sql_to_match = SqlServerSplitterContext.FullSplitter.tsql_statement; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.NotEmpty(result); Assert.True(result.Count > 1, "Should split into multiple statements"); Assert.Equal(result, SqlServerSplitterContext.FullSplitter.tsql_statement_scrubbed); @@ -38,7 +38,7 @@ public void go_with_space() { const string sql_to_match = @" GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Empty(result); } @@ -47,7 +47,7 @@ public void go_with_tab() { string sql_to_match = @" GO" + "\t"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal(["\t"], result); } @@ -56,7 +56,7 @@ public void go_by_itself() { const string sql_to_match = @"GO"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Empty(result); } @@ -66,7 +66,7 @@ public void go_starting_file() const string sql_to_match = @"GO whatever"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); Assert.Contains("whatever", result[0]); } @@ -77,7 +77,7 @@ public void go_with_new_line() const string sql_to_match = @" GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Empty(result); } @@ -89,7 +89,7 @@ public void go_with_on_new_line_after_double_dash_comments(string line_ending, s { string sql_to_match = $"--{line_ending}GO{line_ending}"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal(["--" + line_ending], result); } @@ -100,7 +100,7 @@ public void go_with_on_new_line_after_double_dash_comments_and_words() GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -111,7 +111,7 @@ public void go_with_new_line_after_double_dash_comments_and_symbols() GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -122,7 +122,7 @@ public void go_on_its_own_line() GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Empty(result); } @@ -131,7 +131,7 @@ public void go_with_no_line_terminator() { const string sql_to_match = @" GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Empty(result); } @@ -141,7 +141,7 @@ public void go_with_words_before() string sql_to_match = Words_to_check + @" GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); Assert.Equal(Words_to_check.Trim(), result[0].Trim()); } @@ -152,7 +152,7 @@ public void go_with_symbols_and_words_before() string sql_to_match = Symbols_to_check + Words_to_check + @" GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -162,7 +162,7 @@ public void go_with_words_and_symbols_before() string sql_to_match = Words_to_check + Symbols_to_check + @" GO "; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -171,7 +171,7 @@ public void go_with_words_after_on_the_same_line() { string sql_to_match = @" GO " + Words_to_check; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); Assert.Contains(Words_to_check.Substring(0, 5), result[0]); } @@ -181,7 +181,7 @@ public void go_with_words_after_on_the_same_line_including_symbols() { string sql_to_match = @" GO " + Words_to_check + Symbols_to_check; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Single(result); } @@ -190,7 +190,7 @@ public void go_with_words_before_and_after_on_the_same_line() { string sql_to_match = Words_to_check + @" GO " + Words_to_check; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal([Words_to_check + " ", " " + Words_to_check], result); } @@ -200,7 +200,7 @@ public void go_with_words_before_and_after_on_the_same_line_including_symbols() string sql_to_match = Words_to_check + Symbols_to_check.Replace("'", "").Replace("\"", "") + " GO BOB" + Symbols_to_check; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal(2, result.Count); } @@ -212,7 +212,7 @@ public void go_after_double_dash_comment_with_single_quote_and_single_quote_afte select '' go"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal(2, result.Count); } @@ -221,7 +221,7 @@ public void go_with_comment_after() { string sql_to_match = " GO -- comment"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal([" -- comment"], result); } @@ -230,7 +230,7 @@ public void go_with_semicolon_directly_after() { string sql_to_match = "jalla GO;"; _testOutput.WriteLine(sql_to_match); - var result = Splitter.Split(sql_to_match).ToList(); + var result = _splitter.Split(sql_to_match).ToList(); Assert.Equal(["jalla ", ";" ], result); } From 7e077e5da0201b6e16def55ceea58c0fc0a79c8d Mon Sep 17 00:00:00 2001 From: Isaac Devine Date: Tue, 18 Aug 2026 14:54:21 +1200 Subject: [PATCH 4/4] Use a tokenizer for splitting SQLServer batches This handles nested comments and strings more reliably that a solely regex-based approach. It is also clearer. Regular expressions used to tokenize the sql statement, which is then parsed to ensure that it is only split when outside comments and string literals. A small behavior change is that tabs will longer be a batch on their own , i.e. whitespace only batches are dropped. --- .../SqlServerStatementSplitter.cs | 125 +++++++++++++++++- .../SqlServerStatementSplitter_.cs | 2 +- .../SqlServerStatementSplitter_.cs | 2 +- 3 files changed, 122 insertions(+), 7 deletions(-) diff --git a/src/grate.sqlserver/Infrastructure/SqlServerStatementSplitter.cs b/src/grate.sqlserver/Infrastructure/SqlServerStatementSplitter.cs index 6ebaeb51..3cc2a494 100644 --- a/src/grate.sqlserver/Infrastructure/SqlServerStatementSplitter.cs +++ b/src/grate.sqlserver/Infrastructure/SqlServerStatementSplitter.cs @@ -1,11 +1,126 @@ +using System.Diagnostics; +using System.Text.RegularExpressions; using grate.Infrastructure; namespace grate.SqlServer.Infrastructure; -public class SqlServerStatementSplitter : RegexStatementSplitter +/// +/// Splits SQL Server batches on GO separators using a tokenizer rather than a single regex, +/// so nested comments, string literals and batch separators can be mixed in any combination. +/// +public partial class SqlServerStatementSplitter : IStatementSplitter { - protected override string StringsRegex => @"(?'[^']*')"; - protected override string DashCommentsRegex => @"(?--.*$)"; - protected override string StarCommentsRegex => @"(?/\*[\S\s]*?\*/)"; - protected override string SeparatorRegex => @"(?^|\s)(?GO)(?\s|;|$)"; + private enum TokenType + { + BatchSeparator, + StringDelimiter, + MultiLineCommentStart, + MultiLineCommentEnd, + SingleLineCommentStart, + NewLine + } + + private readonly record struct Token(TokenType Type, int Index, int Length); + + [GeneratedRegex( + """ + \bGO\b|'|/\*|\*/|--|$ + """, + RegexOptions.Multiline | RegexOptions.IgnoreCase)] + private static partial Regex TokenPattern(); + + [GeneratedRegex(@"\S")] + private static partial Regex SignificantTextPattern(); + + public IEnumerable Split(string statement) => + BreakIntoBatches(statement).Where(batch => SignificantTextPattern().IsMatch(batch)); + + private static IEnumerable Tokenize(string sql) + { + for (var match = TokenPattern().Match(sql); match.Success; match = match.NextMatch()) + { + // Each alternative in TokenPattern yields distinct literal text, so the matched + // value alone identifies the token type - anything else is the (case-insensitive) GO. + var type = match.Value switch + { + "'" => TokenType.StringDelimiter, + "/*" => TokenType.MultiLineCommentStart, + "*/" => TokenType.MultiLineCommentEnd, + "--" => TokenType.SingleLineCommentStart, + "" => TokenType.NewLine, + _ => TokenType.BatchSeparator + }; + yield return new Token(type, match.Index, match.Length); + } + } + + /// + /// Cuts the sql into batches wherever a GO batch separator token is found outside + /// of any string literal or comment, tracking nested multi-line comments. + /// + private static IEnumerable BreakIntoBatches(string sql) + { + using var tokens = Tokenize(sql).GetEnumerator(); + var cutIndex = 0; + var token = default(Token); + + bool NextToken() + { + if (!tokens.MoveNext()) + { + return false; + } + token = tokens.Current; + return true; + } + + while (NextToken()) + { + switch (token.Type) + { + case TokenType.BatchSeparator: + // great! we have a batch of SQL. + yield return sql[cutIndex..token.Index]; + cutIndex = token.Index + token.Length; + break; + + case TokenType.StringDelimiter: + // Consume until we exit the string + while (NextToken() && token.Type != TokenType.StringDelimiter) + { + } + break; + + case TokenType.MultiLineCommentStart: + // Consume until we exit the top-most comment, + // accounting for nesting + var depth = 1; + while (depth > 0 && NextToken()) + { + depth += token.Type switch + { + TokenType.MultiLineCommentStart => 1, + TokenType.MultiLineCommentEnd => -1, + _ => 0 + }; + } + break; + + case TokenType.SingleLineCommentStart: + while (NextToken() && token.Type != TokenType.NewLine) + { + } + break; + + case TokenType.NewLine: + break; + + default: + throw new UnreachableException($"Unexpected token type: {token.Type}"); + } + } + + // And the final sql batch + yield return sql[cutIndex..]; + } } diff --git a/unittests/SqlServer/Basic_tests/SqlServerStatementSplitter_.cs b/unittests/SqlServer/Basic_tests/SqlServerStatementSplitter_.cs index 8736c179..606dab03 100644 --- a/unittests/SqlServer/Basic_tests/SqlServerStatementSplitter_.cs +++ b/unittests/SqlServer/Basic_tests/SqlServerStatementSplitter_.cs @@ -48,7 +48,7 @@ public void go_with_tab() string sql_to_match = @" GO" + "\t"; _testOutput.WriteLine(sql_to_match); var result = _splitter.Split(sql_to_match).ToList(); - Assert.Equal(["\t"], result); + Assert.Empty(result); } [Fact] diff --git a/unittests/SqlServerCaseSensitive/Basic_tests/SqlServerStatementSplitter_.cs b/unittests/SqlServerCaseSensitive/Basic_tests/SqlServerStatementSplitter_.cs index 81edaa00..ae7c6a61 100644 --- a/unittests/SqlServerCaseSensitive/Basic_tests/SqlServerStatementSplitter_.cs +++ b/unittests/SqlServerCaseSensitive/Basic_tests/SqlServerStatementSplitter_.cs @@ -48,7 +48,7 @@ public void go_with_tab() string sql_to_match = @" GO" + "\t"; _testOutput.WriteLine(sql_to_match); var result = _splitter.Split(sql_to_match).ToList(); - Assert.Equal(["\t"], result); + Assert.Empty(result); } [Fact]