Skip to content

Commit

Permalink
fix: #680 PostgreSQL statements split implemented (#19)
Browse files Browse the repository at this point in the history
* fix: #680 PostgreSQL statements split implemented



Co-authored-by: shokurov <egor.shokurov@gmail.com>

* Updated approval file

---------

Co-authored-by: shokurov <egor.shokurov@gmail.com>
Co-authored-by: Robert Wagner <robert@wagner.id.au>
  • Loading branch information
3 people authored Dec 10, 2024
1 parent b33151c commit 80c9a50
Show file tree
Hide file tree
Showing 4 changed files with 448 additions and 3 deletions.
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

0 comments on commit 80c9a50

Please sign in to comment.