Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: #680 PostgreSQL statements split implemented #19

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Tests/ApprovalFiles/NoPublicApiChanges.Run.approved.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public PostgresqlConnectionManager(string connectionString) { }
public PostgresqlConnectionManager(Npgsql.NpgsqlDataSource datasource) { }
public PostgresqlConnectionManager(string connectionString, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { }
public PostgresqlConnectionManager(string connectionString, DbUp.Postgresql.PostgresqlConnectionOptions connectionOptions) { }
public bool StandardConformingStrings { get; set; }
public override System.Collections.Generic.IEnumerable<string> SplitScriptIntoCommands(string scriptContents) { }
}
public class PostgresqlConnectionOptions
Expand Down
76 changes: 76 additions & 0 deletions src/Tests/PostgresqlQueryParserTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System.Collections.Generic;
using System.Linq;
using Xunit;

namespace DbUp.Postgresql.Tests;

public class PostgresqlQueryParserTests
{
[Theory]
[InlineData("SELECT 1\n;\nSELECT 2", 2, "SELECT 1", "SELECT 2")]
[InlineData(";;SELECT 1", 1, "SELECT 1")]
[InlineData("SELECT 1;", 1, "SELECT 1")]
[InlineData("", 0)]
[InlineData("CREATE OR REPLACE RULE test AS ON UPDATE TO test DO (SELECT 1; SELECT 1)",
1,
"CREATE OR REPLACE RULE test AS ON UPDATE TO test DO (SELECT 1; SELECT 1)")]
[InlineData("CREATE OR REPLACE RULE test AS ON UPDATE TO test DO (SELECT 1); SELECT 2",
2,
"CREATE OR REPLACE RULE test AS ON UPDATE TO test DO (SELECT 1)", "SELECT 2")]
[InlineData("SELECT 1 /* block comment; */", 1, "SELECT 1 /* block comment; */")]
[InlineData(
"""
SELECT 1;
-- Line comment; with semicolon
SELECT 2;
""", 2,
"SELECT 1",
"""
-- Line comment; with semicolon
SELECT 2
""")]
[InlineData("SELECT 'string with; semicolon'", 1, "SELECT 'string with; semicolon'")]
[InlineData("SELECT 'string with'' quote and; semicolon'", 1, "SELECT 'string with'' quote and; semicolon'")]
[InlineData("""
CREATE FUNCTION TXT()
LANGUAGE PLPGSQL AS
$BODY$
BEGIN
SELECT 'string with'' quote and; semicolon';
END
$BODY$
""", 1)]
[InlineData("SELECT 1 as \"QUOTED;IDENT\"", 1)]
[InlineData("SELECT E'\\041'; SELECT '1'", 2, "SELECT E'\\041'", "SELECT '1'")]
[InlineData("""
SELECT 'some'
'text';
SELECT '1'
""", 2)]
public void split_into_statements(string sql, int statementCount, params string[] expected)
{
var results = ParseCommand(sql);
Assert.Equal(statementCount, results.Count);
if (expected.Length > 0)
Assert.Equal(expected, results);
}

[Fact]
public void split_into_statements_non_sql_standard()
{
const string sql = "SELECT 'string with\\' quote and; semicolon'";
var results = ParseCommand(sql, false);
Assert.Single(results);
Assert.Equal(sql, results[0]);
}

private List<string> ParseCommand(string sql)
=> ParseCommand(sql, true);

private static List<string> ParseCommand(string sql, bool standardConformingStrings)
{
var manager = new PostgresqlConnectionManager("") { StandardConformingStrings = standardConformingStrings };
var commands = manager.SplitScriptIntoCommands(sql);
return commands.ToList();
}
}
10 changes: 7 additions & 3 deletions src/dbup-postgresql/PostgresqlConnectionManager.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;
using DbUp.Engine.Transactions;
using Npgsql;

Expand All @@ -12,6 +11,11 @@ namespace DbUp.Postgresql
/// </summary>
public class PostgresqlConnectionManager : DatabaseConnectionManager
{
/// <summary>
/// Disallow single quotes to be escaped with a backslash (\')
/// </summary>
public bool StandardConformingStrings { get; set; } = true;

/// <summary>
/// Creates a new PostgreSQL database connection.
/// </summary>
Expand Down Expand Up @@ -47,7 +51,7 @@ public PostgresqlConnectionManager(string connectionString, PostgresqlConnection

return databaseConnection;
}
))
))
{
}

Expand All @@ -67,7 +71,7 @@ public PostgresqlConnectionManager(NpgsqlDataSource datasource)
public override IEnumerable<string> SplitScriptIntoCommands(string scriptContents)
{
var scriptStatements =
Regex.Split(scriptContents, "^\\s*;\\s*$", RegexOptions.IgnoreCase | RegexOptions.Multiline)
PostgresqlQueryParser.ParseRawQuery(scriptContents, StandardConformingStrings)
.Select(x => x.Trim())
.Where(x => x.Length > 0)
.ToArray();
Expand Down
Loading
Loading