Skip to content

Commit 260bbe3

Browse files
authored
fix: PostgreSQL parser should not treat \ as an escape char (#1921)
The PostgreSQL parser that removes comments and checks the type of statement should not consider a backslash inside a quoted literal or identifier as an escape character. Instead, only double occurrences of the same quotes as the begin/end quote should be considered as an escape inside a quoted literal or identifier. Fixes #1920
1 parent be8b50b commit 260bbe3

File tree

2 files changed

+41
-18
lines changed

2 files changed

+41
-18
lines changed

google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/PostgreSQLStatementParser.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,6 @@ private int skipQuoted(
226226
char startQuote,
227227
String dollarTag,
228228
@Nullable StringBuilder result) {
229-
boolean lastCharWasEscapeChar = false;
230229
int currentIndex = startIndex + 1;
231230
while (currentIndex < sql.length()) {
232231
char currentChar = sql.charAt(currentIndex);
@@ -238,8 +237,6 @@ private int skipQuoted(
238237
appendIfNotNull(result, currentChar, dollarTag, currentChar);
239238
return currentIndex + tag.length() + 2;
240239
}
241-
} else if (lastCharWasEscapeChar) {
242-
lastCharWasEscapeChar = false;
243240
} else if (sql.length() > currentIndex + 1 && sql.charAt(currentIndex + 1) == startQuote) {
244241
// This is an escaped quote (e.g. 'foo''bar')
245242
appendIfNotNull(result, currentChar);
@@ -250,8 +247,6 @@ private int skipQuoted(
250247
appendIfNotNull(result, currentChar);
251248
return currentIndex + 1;
252249
}
253-
} else {
254-
lastCharWasEscapeChar = currentChar == '\\';
255250
}
256251
currentIndex++;
257252
appendIfNotNull(result, currentChar);

google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementParserTest.java

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,34 @@ public void testPostgresSQLDialectDollarQuoted() {
270270
.isEqualTo("SELECT FOO, $BAR \nFROM SOME_TABLE");
271271
}
272272

273+
@Test
274+
public void testPostgreSQLDialectUnicodeEscapedIdentifiers() {
275+
assumeTrue(dialect == Dialect.POSTGRESQL);
276+
277+
assertEquals(
278+
"SELECT 'tricky' AS \"\\\"", parser.removeCommentsAndTrim("SELECT 'tricky' AS \"\\\""));
279+
assertEquals(
280+
"SELECT 'tricky' AS U&\"\\\" UESCAPE '!'",
281+
parser.removeCommentsAndTrim("SELECT 'tricky' AS U&\"\\\" UESCAPE '!'"));
282+
assertEquals(
283+
"SELECT '\\' AS \"tricky\"", parser.removeCommentsAndTrim("SELECT '\\' AS \"tricky\""));
284+
assertEquals("SELECT 'foo''bar'", parser.removeCommentsAndTrim("SELECT 'foo''bar'"));
285+
assertEquals("SELECT 'foo\"bar'", parser.removeCommentsAndTrim("SELECT 'foo\"bar'"));
286+
assertEquals("SELECT 'foo\"\"bar'", parser.removeCommentsAndTrim("SELECT 'foo\"\"bar'"));
287+
assertEquals(
288+
"SELECT 'foo'", parser.removeCommentsAndTrim("SELECT /* This is a 'comment' */ 'foo'"));
289+
assertEquals(
290+
"SELECT 'foo'",
291+
parser.removeCommentsAndTrim("SELECT /* This is a '''comment''' */ 'foo'"));
292+
assertEquals(
293+
"SELECT '''foo''' FROM bar",
294+
parser.removeCommentsAndTrim("SELECT /* This is a '''comment''' */ '''foo''' FROM bar"));
295+
assertEquals(
296+
"SELECT '''foo''' FROM \"\"\"\\bar\\\"\"\"",
297+
parser.removeCommentsAndTrim(
298+
"SELECT /* This is a '''comment''' */ '''foo''' FROM \"\"\"\\bar\\\"\"\""));
299+
}
300+
273301
@Test
274302
public void testPostgreSQLDialectSupportsEmbeddedComments() {
275303
assumeTrue(dialect == Dialect.POSTGRESQL);
@@ -1109,25 +1137,25 @@ public void testPostgreSQLDialectDialectConvertPositionalParametersToNamedParame
11091137
.sqlWithNamedParameters)
11101138
.isEqualTo("$1'?test?\"?test?\"?'$2");
11111139
assertThat(
1112-
parser.convertPositionalParametersToNamedParameters('?', "?'?it\\'?s'?")
1140+
parser.convertPositionalParametersToNamedParameters('?', "?'?it\\''?s'?")
11131141
.sqlWithNamedParameters)
1114-
.isEqualTo("$1'?it\\'?s'$2");
1142+
.isEqualTo("$1'?it\\''?s'$2");
11151143
assertThat(
11161144
parser.convertPositionalParametersToNamedParameters('?', "?'?it\\\"?s'?")
11171145
.sqlWithNamedParameters)
11181146
.isEqualTo("$1'?it\\\"?s'$2");
11191147
assertThat(
1120-
parser.convertPositionalParametersToNamedParameters('?', "?\"?it\\\"?s\"?")
1148+
parser.convertPositionalParametersToNamedParameters('?', "?\"?it\\\"\"?s\"?")
11211149
.sqlWithNamedParameters)
1122-
.isEqualTo("$1\"?it\\\"?s\"$2");
1150+
.isEqualTo("$1\"?it\\\"\"?s\"$2");
11231151
assertThat(
1124-
parser.convertPositionalParametersToNamedParameters('?', "?'''?it\\'?s'''?")
1152+
parser.convertPositionalParametersToNamedParameters('?', "?'''?it\\''?s'''?")
11251153
.sqlWithNamedParameters)
1126-
.isEqualTo("$1'''?it\\'?s'''$2");
1154+
.isEqualTo("$1'''?it\\''?s'''$2");
11271155
assertThat(
1128-
parser.convertPositionalParametersToNamedParameters('?', "?\"\"\"?it\\\"?s\"\"\"?")
1156+
parser.convertPositionalParametersToNamedParameters('?', "?\"\"\"?it\\\"\"?s\"\"\"?")
11291157
.sqlWithNamedParameters)
1130-
.isEqualTo("$1\"\"\"?it\\\"?s\"\"\"$2");
1158+
.isEqualTo("$1\"\"\"?it\\\"\"?s\"\"\"$2");
11311159

11321160
assertThat(
11331161
parser.convertPositionalParametersToNamedParameters('?', "?$$?it$?s$$?")
@@ -1144,13 +1172,13 @@ public void testPostgreSQLDialectDialectConvertPositionalParametersToNamedParame
11441172

11451173
// Note: PostgreSQL allows a single-quoted string literal to contain line feeds.
11461174
assertEquals(
1147-
"$1'?it\\'?s \n ?it\\'?s'$2",
1148-
parser.convertPositionalParametersToNamedParameters('?', "?'?it\\'?s \n ?it\\'?s'?")
1175+
"$1'?it\\''?s \n ?it\\''?s'$2",
1176+
parser.convertPositionalParametersToNamedParameters('?', "?'?it\\''?s \n ?it\\''?s'?")
11491177
.sqlWithNamedParameters);
1150-
assertUnclosedLiteral("?'?it\\'?s \n ?it\\'?s?");
1178+
assertUnclosedLiteral("?'?it\\''?s \n ?it\\''?s?");
11511179
assertEquals(
1152-
"$1'''?it\\'?s \n ?it\\'?s'$2",
1153-
parser.convertPositionalParametersToNamedParameters('?', "?'''?it\\'?s \n ?it\\'?s'?")
1180+
"$1'''?it\\''?s \n ?it\\''?s'$2",
1181+
parser.convertPositionalParametersToNamedParameters('?', "?'''?it\\''?s \n ?it\\''?s'?")
11541182
.sqlWithNamedParameters);
11551183

11561184
assertThat(

0 commit comments

Comments
 (0)