Skip to content

Commit e70e9c7

Browse files
fix: #680 PostgreSQL statements split implemented
Co-authored-by: shokurov <[email protected]>
1 parent 9603453 commit e70e9c7

File tree

3 files changed

+447
-3
lines changed

3 files changed

+447
-3
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Xunit;
4+
5+
namespace DbUp.Postgresql.Tests;
6+
7+
public class PostgresqlQueryParserTests
8+
{
9+
[Theory]
10+
[InlineData("SELECT 1\n;\nSELECT 2", 2, "SELECT 1", "SELECT 2")]
11+
[InlineData(";;SELECT 1", 1, "SELECT 1")]
12+
[InlineData("SELECT 1;", 1, "SELECT 1")]
13+
[InlineData("", 0)]
14+
[InlineData("CREATE OR REPLACE RULE test AS ON UPDATE TO test DO (SELECT 1; SELECT 1)",
15+
1,
16+
"CREATE OR REPLACE RULE test AS ON UPDATE TO test DO (SELECT 1; SELECT 1)")]
17+
[InlineData("CREATE OR REPLACE RULE test AS ON UPDATE TO test DO (SELECT 1); SELECT 2",
18+
2,
19+
"CREATE OR REPLACE RULE test AS ON UPDATE TO test DO (SELECT 1)", "SELECT 2")]
20+
[InlineData("SELECT 1 /* block comment; */", 1, "SELECT 1 /* block comment; */")]
21+
[InlineData(
22+
"""
23+
SELECT 1;
24+
-- Line comment; with semicolon
25+
SELECT 2;
26+
""", 2,
27+
"SELECT 1",
28+
"""
29+
-- Line comment; with semicolon
30+
SELECT 2
31+
""")]
32+
[InlineData("SELECT 'string with; semicolon'", 1, "SELECT 'string with; semicolon'")]
33+
[InlineData("SELECT 'string with'' quote and; semicolon'", 1, "SELECT 'string with'' quote and; semicolon'")]
34+
[InlineData("""
35+
CREATE FUNCTION TXT()
36+
LANGUAGE PLPGSQL AS
37+
$BODY$
38+
BEGIN
39+
SELECT 'string with'' quote and; semicolon';
40+
END
41+
$BODY$
42+
""", 1)]
43+
[InlineData("SELECT 1 as \"QUOTED;IDENT\"", 1)]
44+
[InlineData("SELECT E'\\041'; SELECT '1'", 2, "SELECT E'\\041'", "SELECT '1'")]
45+
[InlineData("""
46+
SELECT 'some'
47+
'text';
48+
SELECT '1'
49+
""", 2)]
50+
public void split_into_statements(string sql, int statementCount, params string[] expected)
51+
{
52+
var results = ParseCommand(sql);
53+
Assert.Equal(statementCount, results.Count);
54+
if (expected.Length > 0)
55+
Assert.Equal(expected, results);
56+
}
57+
58+
[Fact]
59+
public void split_into_statements_non_sql_standard()
60+
{
61+
const string sql = "SELECT 'string with\\' quote and; semicolon'";
62+
var results = ParseCommand(sql, false);
63+
Assert.Single(results);
64+
Assert.Equal(sql, results[0]);
65+
}
66+
67+
private List<string> ParseCommand(string sql)
68+
=> ParseCommand(sql, true);
69+
70+
private static List<string> ParseCommand(string sql, bool standardConformingStrings)
71+
{
72+
var manager = new PostgresqlConnectionManager("") { StandardConformingStrings = standardConformingStrings };
73+
var commands = manager.SplitScriptIntoCommands(sql);
74+
return commands.ToList();
75+
}
76+
}

src/dbup-postgresql/PostgresqlConnectionManager.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System.Collections.Generic;
22
using System.Linq;
33
using System.Security.Cryptography.X509Certificates;
4-
using System.Text.RegularExpressions;
54
using DbUp.Engine.Transactions;
65
using Npgsql;
76

@@ -12,6 +11,11 @@ namespace DbUp.Postgresql
1211
/// </summary>
1312
public class PostgresqlConnectionManager : DatabaseConnectionManager
1413
{
14+
/// <summary>
15+
/// Disallow single quotes to be escaped with a backslash (\')
16+
/// </summary>
17+
public bool StandardConformingStrings { get; set; } = true;
18+
1519
/// <summary>
1620
/// Creates a new PostgreSQL database connection.
1721
/// </summary>
@@ -35,7 +39,7 @@ public PostgresqlConnectionManager(string connectionString, X509Certificate2 cer
3539

3640
return databaseConnection;
3741
}
38-
))
42+
))
3943
{
4044
}
4145

@@ -46,7 +50,7 @@ public PostgresqlConnectionManager(string connectionString, X509Certificate2 cer
4650
public override IEnumerable<string> SplitScriptIntoCommands(string scriptContents)
4751
{
4852
var scriptStatements =
49-
Regex.Split(scriptContents, "^\\s*;\\s*$", RegexOptions.IgnoreCase | RegexOptions.Multiline)
53+
PostgresqlQueryParser.ParseRawQuery(scriptContents, StandardConformingStrings)
5054
.Select(x => x.Trim())
5155
.Where(x => x.Length > 0)
5256
.ToArray();

0 commit comments

Comments
 (0)