Skip to content

Commit bfa777b

Browse files
authored
chore: allow comments in PG strings (#2604)
Allow comments in PostgreSQL query strings for extracting parameter definitions. The comments need to be preserved, as they could contain query hints.
1 parent 59cec9b commit bfa777b

File tree

3 files changed

+286
-117
lines changed

3 files changed

+286
-117
lines changed

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -539,11 +539,10 @@ public static class ParametersInfo {
539539
* Converts all positional parameters (?) in the given sql string into named parameters. The
540540
* parameters are named @p1, @p2, etc. This method is used when converting a JDBC statement that
541541
* uses positional parameters to a Cloud Spanner {@link Statement} instance that requires named
542-
* parameters. The input SQL string may not contain any comments. There is an exception case if
543-
* the statement starts with a GSQL comment which forces it to be interpreted as a GoogleSql
544-
* statement.
542+
* parameters. The input SQL string may not contain any comments, except for PostgreSQL-dialect
543+
* SQL strings.
545544
*
546-
* @param sql The sql string without comments that should be converted
545+
* @param sql The sql string that should be converted
547546
* @return A {@link ParametersInfo} object containing a string with named parameters instead of
548547
* positional parameters and the number of parameters.
549548
* @throws SpannerException If the input sql string contains an unclosed string/byte literal.

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

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,8 @@ ParametersInfo convertPositionalParametersToNamedParametersInternal(char paramCh
184184
* Note: This is an internal API and breaking changes can be made without prior notice.
185185
*
186186
* <p>Returns the PostgreSQL-style query parameters ($1, $2, ...) in the given SQL string. The
187-
* SQL-string is assumed to not contain any comments. Use {@link #removeCommentsAndTrim(String)}
188-
* to remove all comments before calling this method. Occurrences of query-parameter like strings
189-
* inside quoted identifiers or string literals are ignored.
187+
* SQL-string is allowed to contain comments. Occurrences of query-parameter like strings inside
188+
* quoted identifiers or string literals are ignored.
190189
*
191190
* <p>The following example will return a set containing ("$1", "$2"). <code>
192191
* select col1, col2, "col$4"
@@ -195,7 +194,7 @@ ParametersInfo convertPositionalParametersToNamedParametersInternal(char paramCh
195194
* and not col3=$1 and col4='$3'
196195
* </code>
197196
*
198-
* @param sql the SQL-string to check for parameters. Must not contain comments.
197+
* @param sql the SQL-string to check for parameters.
199198
* @return A set containing all the parameters in the SQL-string.
200199
*/
201200
@InternalApi
@@ -233,12 +232,56 @@ private int skip(String sql, int currentIndex, @Nullable StringBuilder result) {
233232
return skipQuoted(
234233
sql, currentIndex + dollarTag.length() + 1, currentChar, dollarTag, result);
235234
}
235+
} else if (currentChar == HYPHEN
236+
&& sql.length() > (currentIndex + 1)
237+
&& sql.charAt(currentIndex + 1) == HYPHEN) {
238+
return skipSingleLineComment(sql, currentIndex, result);
239+
} else if (currentChar == SLASH
240+
&& sql.length() > (currentIndex + 1)
241+
&& sql.charAt(currentIndex + 1) == ASTERISK) {
242+
return skipMultiLineComment(sql, currentIndex, result);
236243
}
237244

238245
appendIfNotNull(result, currentChar);
239246
return currentIndex + 1;
240247
}
241248

249+
static int skipSingleLineComment(String sql, int currentIndex, @Nullable StringBuilder result) {
250+
int endIndex = sql.indexOf('\n', currentIndex + 2);
251+
if (endIndex == -1) {
252+
endIndex = sql.length();
253+
} else {
254+
// Include the newline character.
255+
endIndex++;
256+
}
257+
appendIfNotNull(result, sql.substring(currentIndex, endIndex));
258+
return endIndex;
259+
}
260+
261+
static int skipMultiLineComment(String sql, int startIndex, @Nullable StringBuilder result) {
262+
// Current position is start + '/*'.length().
263+
int pos = startIndex + 2;
264+
// PostgreSQL allows comments to be nested. That is, the following is allowed:
265+
// '/* test /* inner comment */ still a comment */'
266+
int level = 1;
267+
while (pos < sql.length()) {
268+
if (sql.charAt(pos) == SLASH && sql.length() > (pos + 1) && sql.charAt(pos + 1) == ASTERISK) {
269+
level++;
270+
}
271+
if (sql.charAt(pos) == ASTERISK && sql.length() > (pos + 1) && sql.charAt(pos + 1) == SLASH) {
272+
level--;
273+
if (level == 0) {
274+
pos += 2;
275+
appendIfNotNull(result, sql.substring(startIndex, pos));
276+
return pos;
277+
}
278+
}
279+
pos++;
280+
}
281+
appendIfNotNull(result, sql.substring(startIndex));
282+
return sql.length();
283+
}
284+
242285
private int skipQuoted(
243286
String sql, int startIndex, char startQuote, @Nullable StringBuilder result) {
244287
return skipQuoted(sql, startIndex, startQuote, null, result);
@@ -285,6 +328,12 @@ private void appendIfNotNull(@Nullable StringBuilder result, char currentChar) {
285328
}
286329
}
287330

331+
private static void appendIfNotNull(@Nullable StringBuilder result, String suffix) {
332+
if (result != null) {
333+
result.append(suffix);
334+
}
335+
}
336+
288337
private void appendIfNotNull(
289338
@Nullable StringBuilder result, char prefix, String tag, char suffix) {
290339
if (result != null) {

0 commit comments

Comments
 (0)