Skip to content
This repository was archived by the owner on Mar 23, 2026. It is now read-only.

Commit 6709e7e

Browse files
committed
chore: address pr feedback
1 parent 423594a commit 6709e7e

3 files changed

Lines changed: 80 additions & 56 deletions

File tree

google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcUrlUtility.java

Lines changed: 59 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@
1919
import com.google.api.client.util.escape.CharEscapers;
2020
import com.google.cloud.bigquery.BigQueryOptions;
2121
import com.google.cloud.bigquery.exception.BigQueryJdbcRuntimeException;
22+
import com.google.common.collect.ImmutableList;
23+
import com.google.common.net.UrlEscapers;
2224
import java.io.UnsupportedEncodingException;
2325
import java.net.URLDecoder;
24-
import java.net.URLEncoder;
2526
import java.nio.charset.StandardCharsets;
2627
import java.util.Arrays;
2728
import java.util.Collections;
2829
import java.util.HashMap;
2930
import java.util.HashSet;
31+
import java.util.LinkedHashMap;
3032
import java.util.List;
3133
import java.util.Map;
3234
import java.util.Map.Entry;
@@ -43,6 +45,14 @@
4345
*/
4446
final class BigQueryJdbcUrlUtility {
4547

48+
private static final Map<String, Map<String, String>> PARSE_CACHE =
49+
Collections.synchronizedMap(
50+
new LinkedHashMap<String, Map<String, String>>(50, 0.75f, true) {
51+
protected boolean removeEldestEntry(Map.Entry<String, Map<String, String>> eldest) {
52+
return size() > 50; // bound cache size
53+
}
54+
});
55+
4656
// TODO: Add all Connection options
4757
static final String ALLOW_LARGE_RESULTS_PROPERTY_NAME = "AllowLargeResults";
4858
static final String LARGE_RESULTS_TABLE_PROPERTY_NAME = "LargeResultTable";
@@ -127,7 +137,10 @@ final class BigQueryJdbcUrlUtility {
127137
static final String BYOID_TOKEN_URI_PROPERTY_NAME = "BYOID_TokenUri";
128138
static final String PARTNER_TOKEN_PROPERTY_NAME = "PartnerToken";
129139
private static final Pattern PARTNER_TOKEN_PATTERN =
130-
Pattern.compile("(?i)" + PARTNER_TOKEN_PROPERTY_NAME + "=\\s*\\(([^)]*)\\)");
140+
Pattern.compile(
141+
"(?i)(?:^|(?<=;))\\s*"
142+
+ PARTNER_TOKEN_PROPERTY_NAME
143+
+ "\\s*=\\s*\\(([^)]*)\\)\\s*(?=;|$)");
131144
static final String METADATA_FETCH_THREAD_COUNT_PROPERTY_NAME = "MetaDataFetchThreadCount";
132145
static final int DEFAULT_METADATA_FETCH_THREAD_COUNT_VALUE = 32;
133146
static final String RETRY_TIMEOUT_IN_SECS_PROPERTY_NAME = "Timeout";
@@ -597,6 +610,12 @@ final class BigQueryJdbcUrlUtility {
597610
+ " header.")
598611
.build())));
599612

613+
private static final List<String> NETWORK_PROPERTIES =
614+
ImmutableList.of(
615+
PARTNER_TOKEN_PROPERTY_NAME,
616+
ENDPOINT_OVERRIDES_PROPERTY_NAME,
617+
PRIVATE_SERVICE_CONNECT_PROPERTY_NAME);
618+
600619
private static final Map<String, String> PROPERTY_NAME_MAP;
601620

602621
static {
@@ -616,10 +635,9 @@ final class BigQueryJdbcUrlUtility {
616635
for (String p : BYOID_PROPERTIES) {
617636
map.put(p.toUpperCase(), p);
618637
}
619-
map.put(PARTNER_TOKEN_PROPERTY_NAME.toUpperCase(), PARTNER_TOKEN_PROPERTY_NAME);
620-
map.put(ENDPOINT_OVERRIDES_PROPERTY_NAME.toUpperCase(), ENDPOINT_OVERRIDES_PROPERTY_NAME);
621-
map.put(
622-
PRIVATE_SERVICE_CONNECT_PROPERTY_NAME.toUpperCase(), PRIVATE_SERVICE_CONNECT_PROPERTY_NAME);
638+
for (String p : NETWORK_PROPERTIES) {
639+
map.put(p.toUpperCase(), p);
640+
}
623641
PROPERTY_NAME_MAP = Collections.unmodifiableMap(map);
624642
}
625643

@@ -648,24 +666,33 @@ static String parseUriProperty(String uri, String property) {
648666
* @throws BigQueryJdbcRuntimeException if an unknown property is found or the URL is malformed.
649667
*/
650668
static Map<String, String> parseUrl(String url) {
669+
if (url == null) {
670+
return Collections.emptyMap();
671+
}
672+
return PARSE_CACHE.computeIfAbsent(url, BigQueryJdbcUrlUtility::parseUrlInternal);
673+
}
674+
675+
private static Map<String, String> parseUrlInternal(String url) {
651676
Map<String, String> map = new HashMap<>();
652677
if (url == null) {
653678
return map;
654679
}
655680

656-
int start = url.indexOf(';');
657-
if (start == -1) {
681+
String[] urlParts = url.split(";", 2);
682+
if (urlParts.length < 2) {
658683
return map;
659684
}
660685

661-
String urlToParse = url.substring(start + 1);
686+
String urlToParse = urlParts[1];
662687

663688
// Parse PartnerToken separately as it contains ';'
664-
StringBuilder urlBuilder = new StringBuilder(urlToParse);
665-
String partnerToken = parseAndRemovePartnerTokenProperty(urlBuilder, "parseUrl");
666-
urlToParse = urlBuilder.toString();
667-
if (partnerToken != null) {
668-
map.put(PARTNER_TOKEN_PROPERTY_NAME, partnerToken);
689+
Matcher matcher = PARTNER_TOKEN_PATTERN.matcher(urlToParse);
690+
if (matcher.find()) {
691+
String partnerToken = matcher.group(1).trim();
692+
if (partnerToken.toUpperCase().startsWith("GPN:")) {
693+
map.put(PARTNER_TOKEN_PROPERTY_NAME, " (" + partnerToken + ")");
694+
urlToParse = matcher.replaceFirst("");
695+
}
669696
}
670697

671698
String[] parts = urlToParse.split(";");
@@ -674,25 +701,17 @@ static Map<String, String> parseUrl(String url) {
674701
continue;
675702
}
676703
String[] kv = part.split("=", 2);
677-
String key = kv[0].trim();
678-
if (kv.length == 1) {
679-
if (!key.isEmpty()) {
680-
String safeKey = key.length() > 32 ? key.substring(0, 32) + "..." : key;
681-
throw new BigQueryJdbcRuntimeException("Property '" + safeKey + "' has no value.");
682-
}
683-
} else {
684-
String value = kv[1]; // Value might be empty string if "Key="
685-
if (!key.isEmpty()) {
686-
String upperCaseKey = key.toUpperCase();
687-
if (!PROPERTY_NAME_MAP.containsKey(upperCaseKey)) {
688-
String safeKey = key.length() > 32 ? key.substring(0, 32) + "..." : key;
689-
throw new BigQueryJdbcRuntimeException("Unknown property: " + safeKey);
690-
}
691-
map.put(PROPERTY_NAME_MAP.get(upperCaseKey), CharEscapers.decodeUriPath(value));
692-
}
704+
String key = kv[0].trim().toUpperCase();
705+
if (kv.length != 2 || !PROPERTY_NAME_MAP.containsKey(key)) {
706+
String ref = (kv.length == 2) ? key : part;
707+
String safeRef = ref.length() > 32 ? ref.substring(0, 32) + "..." : ref;
708+
throw new BigQueryJdbcRuntimeException(
709+
String.format("Wrong value or unknown setting: %s", safeRef));
693710
}
711+
712+
map.put(PROPERTY_NAME_MAP.get(key), CharEscapers.decodeUriPath(kv[1]));
694713
}
695-
return map;
714+
return Collections.unmodifiableMap(map);
696715
}
697716

698717
/**
@@ -708,14 +727,11 @@ static String appendPropertiesToURL(String url, String callerClassName, Properti
708727
for (Entry<Object, Object> entry : properties.entrySet()) {
709728
if (entry.getValue() != null && !"".equals(entry.getValue())) {
710729
LOG.finest("Appending %s with value %s to URL", entry.getKey(), entry.getValue());
711-
try {
712-
String encodedValue =
713-
URLEncoder.encode((String) entry.getValue(), StandardCharsets.UTF_8.name())
714-
.replace("+", "%20");
715-
urlBuilder.append(";").append(entry.getKey()).append("=").append(encodedValue);
716-
} catch (UnsupportedEncodingException e) {
717-
throw new BigQueryJdbcRuntimeException(e);
718-
}
730+
String encodedValue =
731+
UrlEscapers.urlFormParameterEscaper()
732+
.escape((String) entry.getValue())
733+
.replace("+", "%20");
734+
urlBuilder.append(";").append(entry.getKey()).append("=").append(encodedValue);
719735
}
720736
}
721737
return urlBuilder.toString();
@@ -790,18 +806,11 @@ public static String parsePartnerTokenProperty(String url, String callerClassNam
790806
LOG.finest("++enter++\t" + callerClassName);
791807
// This property is expected to be set by partners only. For more details on exact format
792808
// supported, refer b/396086960
793-
return parseAndRemovePartnerTokenProperty(new StringBuilder(url), callerClassName);
794-
}
795-
796-
private static String parseAndRemovePartnerTokenProperty(
797-
StringBuilder urlBuilder, String callerClassName) {
798-
Matcher matcher = PARTNER_TOKEN_PATTERN.matcher(urlBuilder);
799-
809+
Matcher matcher = PARTNER_TOKEN_PATTERN.matcher(url);
800810
if (matcher.find()) {
801-
String content = matcher.group(1).trim();
802-
urlBuilder.delete(matcher.start(), matcher.end());
803-
if (content.toUpperCase().startsWith("GPN:")) {
804-
return " (" + content + ")";
811+
String partnerToken = matcher.group(1).trim();
812+
if (partnerToken.toUpperCase().startsWith("GPN:")) {
813+
return " (" + partnerToken + ")";
805814
}
806815
}
807816
return null;

google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/DataSource.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public class DataSource implements javax.sql.DataSource {
7878
private Integer metadataFetchThreadCount;
7979
private String sslTrustStorePath;
8080
private String sslTrustStorePassword;
81-
private String labels;
81+
private Map<String, String> labels;
8282
private String requestReason;
8383
private Integer timeout;
8484
private Integer jobTimeout;
@@ -156,7 +156,7 @@ private Properties createProperties() {
156156
}
157157
if (this.queryProperties != null) {
158158
connectionProperties.setProperty(
159-
BigQueryJdbcUrlUtility.QUERY_PROPERTIES_NAME, this.queryProperties.toString());
159+
BigQueryJdbcUrlUtility.QUERY_PROPERTIES_NAME, serializeMap(this.queryProperties));
160160
}
161161
if (this.enableSession != null) {
162162
connectionProperties.setProperty(
@@ -295,7 +295,8 @@ private Properties createProperties() {
295295
String.valueOf(this.sslTrustStorePassword));
296296
}
297297
if (this.labels != null) {
298-
connectionProperties.setProperty(BigQueryJdbcUrlUtility.LABELS_PROPERTY_NAME, this.labels);
298+
connectionProperties.setProperty(
299+
BigQueryJdbcUrlUtility.LABELS_PROPERTY_NAME, serializeMap(this.labels));
299300
}
300301
if (this.requestReason != null) {
301302
connectionProperties.setProperty(
@@ -346,6 +347,20 @@ private Properties createProperties() {
346347
return connectionProperties;
347348
}
348349

350+
private String serializeMap(Map<String, String> map) {
351+
if (map == null || map.isEmpty()) {
352+
return "";
353+
}
354+
StringBuilder sb = new StringBuilder();
355+
for (Map.Entry<String, String> entry : map.entrySet()) {
356+
if (sb.length() > 0) {
357+
sb.append(",");
358+
}
359+
sb.append(entry.getKey()).append("=").append(entry.getValue());
360+
}
361+
return sb.toString();
362+
}
363+
349364
@Override
350365
public Connection getConnection(String username, String password) throws SQLException {
351366
LOG.warning(
@@ -691,11 +706,11 @@ public void setSSLTrustStorePassword(String sslTrustStorePassword) {
691706
this.sslTrustStorePassword = sslTrustStorePassword;
692707
}
693708

694-
public String getLabels() {
709+
public Map<String, String> getLabels() {
695710
return labels;
696711
}
697712

698-
public void setLabels(String labels) {
713+
public void setLabels(Map<String, String> labels) {
699714
this.labels = labels;
700715
}
701716

google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcUrlUtilityTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ public void testParseUrl_longUnknownProperty_sanitized() {
154154
assertThrows(
155155
BigQueryJdbcRuntimeException.class, () -> BigQueryJdbcUrlUtility.parseUrl(url));
156156

157-
assertThat(e.getMessage()).contains("Unknown property:");
157+
assertThat(e.getMessage()).contains("Wrong value or unknown setting: ");
158158
assertThat(e.getMessage()).contains("...");
159159
assertThat(e.getMessage()).doesNotContain(longKey);
160160
assertThat(e.getMessage().length()).isLessThan(100);

0 commit comments

Comments
 (0)