From 71488b2766d780c476bb099f3584750f2f58c26b Mon Sep 17 00:00:00 2001 From: Kirill Logachev Date: Fri, 22 May 2026 20:53:19 +0000 Subject: [PATCH 1/2] fix(bigquery-jdbc): Add escape character support for pattern matching --- .../jdbc/BigQueryDatabaseMetaData.java | 68 ++++++++++++------- .../jdbc/BigQueryDatabaseMetaDataTest.java | 11 +++ 2 files changed, 55 insertions(+), 24 deletions(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java index 1c6834feb27d..6ef01e2168b9 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java @@ -5080,40 +5080,60 @@ Pattern compileSqlLikePattern(String sqlLikePattern) { } StringBuilder regex = new StringBuilder(sqlLikePattern.length() * 2); regex.append('^'); + boolean escaped = false; for (int i = 0; i < sqlLikePattern.length(); i++) { char c = sqlLikePattern.charAt(i); - switch (c) { - case '%': - regex.append(".*"); - break; - case '_': - regex.append('.'); - break; - case '\\': - case '.': - case '[': - case ']': - case '(': - case ')': - case '{': - case '}': - case '*': - case '+': - case '?': - case '^': - case '$': - case '|': + if (escaped) { + if (isRegexMetacharacter(c)) { regex.append('\\').append(c); - break; - default: + } else { regex.append(c); - break; + } + escaped = false; + } else if (c == '\\') { + escaped = true; + } else { + switch (c) { + case '%': + regex.append(".*"); + break; + case '_': + regex.append('.'); + break; + default: + if (isRegexMetacharacter(c)) { + regex.append('\\').append(c); + } else { + regex.append(c); + } + break; + } } } + if (escaped) { + regex.append('\\').append('\\'); + } regex.append('$'); return Pattern.compile(regex.toString(), Pattern.CASE_INSENSITIVE); } + private boolean isRegexMetacharacter(char c) { + return c == '\\' + || c == '.' + || c == '[' + || c == ']' + || c == '(' + || c == ')' + || c == '{' + || c == '}' + || c == '*' + || c == '+' + || c == '?' + || c == '^' + || c == '$' + || c == '|'; + } + boolean needsListing(String pattern) { return pattern == null || pattern.contains("%") || pattern.contains("_"); } diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaDataTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaDataTest.java index 654cdbe0ff7c..78adda7b2dc6 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaDataTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaDataTest.java @@ -243,6 +243,17 @@ public void testCompileSqlLikePattern() { assertNotNull(bracketPattern); assertTrue(bracketPattern.matcher("array[0]").matches()); assertFalse(bracketPattern.matcher("array_0_").matches()); + + // Escaped wildcards + Pattern escapedUnderscore = dbMetadata.compileSqlLikePattern("test\\_table"); + assertNotNull(escapedUnderscore); + assertTrue(escapedUnderscore.matcher("test_table").matches()); + assertFalse(escapedUnderscore.matcher("test1table").matches()); + + Pattern escapedPercent = dbMetadata.compileSqlLikePattern("100\\%discount"); + assertNotNull(escapedPercent); + assertTrue(escapedPercent.matcher("100%discount").matches()); + assertFalse(escapedPercent.matcher("100PERCENTdiscount").matches()); } @Test From 9b3650256802fc0cdc4d35e14a81d934087fad08 Mon Sep 17 00:00:00 2001 From: Kirill Logachev Date: Fri, 22 May 2026 21:17:13 +0000 Subject: [PATCH 2/2] feedback --- .../jdbc/BigQueryDatabaseMetaData.java | 52 +++++-------------- 1 file changed, 12 insertions(+), 40 deletions(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java index 6ef01e2168b9..86fa0d84cec1 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java @@ -5083,55 +5083,27 @@ Pattern compileSqlLikePattern(String sqlLikePattern) { boolean escaped = false; for (int i = 0; i < sqlLikePattern.length(); i++) { char c = sqlLikePattern.charAt(i); - if (escaped) { - if (isRegexMetacharacter(c)) { - regex.append('\\').append(c); - } else { - regex.append(c); - } - escaped = false; - } else if (c == '\\') { + if (!escaped && c == '\\') { escaped = true; + continue; + } else if (!escaped && c == '%') { + regex.append(".*"); + } else if (!escaped && c == '_') { + regex.append('.'); } else { - switch (c) { - case '%': - regex.append(".*"); - break; - case '_': - regex.append('.'); - break; - default: - if (isRegexMetacharacter(c)) { - regex.append('\\').append(c); - } else { - regex.append(c); - } - break; + if (isRegexMetacharacter(c)) { + regex.append('\\'); } + regex.append(c); + escaped = false; } } - if (escaped) { - regex.append('\\').append('\\'); - } regex.append('$'); return Pattern.compile(regex.toString(), Pattern.CASE_INSENSITIVE); } - private boolean isRegexMetacharacter(char c) { - return c == '\\' - || c == '.' - || c == '[' - || c == ']' - || c == '(' - || c == ')' - || c == '{' - || c == '}' - || c == '*' - || c == '+' - || c == '?' - || c == '^' - || c == '$' - || c == '|'; + private static boolean isRegexMetacharacter(char c) { + return "\\.[]{}()*+?^$|".indexOf(c) != -1; } boolean needsListing(String pattern) {