Skip to content

Commit 875ff6a

Browse files
fix(bqjdbc): validate integer connection properties (#13174)
- **Property Validation:** The driver now rejects negative integers for properties like pool sizes and timeouts. - **Consistent Enforcement:** This validation applies to programmatic DataSource setters. - **Remove Double Parsing:** Removed the `BigQueryJdbcUrlUtility.parseUrl(connectionUri)` usage in `BigQueryDriver.connect()` to eliminate double parsing. - **Better Error Handling:** Invalid inputs now throw clear, driver-specific exceptions instead of low-level runtime errors. - **Expanded Testing:** Added tests to verify behavior for negative values, non-numeric inputs, and unrecognized properties. --------- Co-authored-by: cloud-java-bot <cloud-java-bot@google.com>
1 parent 8cf6b07 commit 875ff6a

5 files changed

Lines changed: 130 additions & 6 deletions

File tree

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,13 @@ public Connection connect(String url, Properties info) throws SQLException {
130130
// strip 'jdbc:' from the URL, add any extra properties
131131
String connectionUri =
132132
BigQueryJdbcUrlUtility.appendPropertiesToURL(url.substring(5), this.toString(), info);
133+
DataSource ds;
133134
try {
134-
BigQueryJdbcUrlUtility.parseUrl(connectionUri);
135+
ds = DataSource.fromUrl(connectionUri);
135136
} catch (BigQueryJdbcRuntimeException e) {
136137
throw new BigQueryJdbcException("Failed to parse connection URL", e);
137138
}
138139

139-
DataSource ds = DataSource.fromUrl(connectionUri);
140-
141140
// LogLevel
142141
String logLevelStr = ds.getLogLevel();
143142
if (logLevelStr == null) {

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,9 @@ private static Map<String, String> parseUrlInternal(String url) {
721721
continue;
722722
}
723723
}
724-
map.put(PROPERTY_NAME_MAP.get(key), CharEscapers.decodeUriPath(kv[1].replace("+", "%2B")));
724+
String propertyName = PROPERTY_NAME_MAP.get(key);
725+
String value = CharEscapers.decodeUriPath(kv[1].replace("+", "%2B"));
726+
map.put(propertyName, value);
725727
}
726728
return Collections.unmodifiableMap(map);
727729
}

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

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.google.cloud.bigquery.jdbc;
1818

1919
import com.google.cloud.bigquery.exception.BigQueryJdbcException;
20+
import com.google.cloud.bigquery.exception.BigQueryJdbcRuntimeException;
2021
import com.google.common.base.Joiner;
2122
import com.google.common.collect.ImmutableMap;
2223
import com.google.common.collect.ImmutableSet;
@@ -340,7 +341,13 @@ public static DataSource fromUrl(String url) {
340341
for (Map.Entry<String, String> entry : properties.entrySet()) {
341342
BiConsumer<DataSource, String> setter = PROPERTY_SETTERS.get(entry.getKey());
342343
if (setter != null) {
343-
setter.accept(dataSource, entry.getValue());
344+
try {
345+
setter.accept(dataSource, entry.getValue());
346+
} catch (NumberFormatException e) {
347+
throw new BigQueryJdbcRuntimeException(
348+
String.format("Invalid value for %s. It must be a valid integer.", entry.getKey()),
349+
e);
350+
}
344351
}
345352
}
346353
return dataSource;
@@ -664,6 +671,10 @@ public void setProjectId(String projectId) {
664671
}
665672

666673
public void setMaxResults(Long maxResults) {
674+
if (maxResults != null && maxResults <= 0) {
675+
throw new BigQueryJdbcRuntimeException(
676+
"Invalid value for MaxResults. It must be greater than 0.");
677+
}
667678
this.maxResults = maxResults;
668679
}
669680

@@ -736,6 +747,10 @@ public Long getConnectionPoolSize() {
736747
}
737748

738749
public void setConnectionPoolSize(Long connectionPoolSize) {
750+
if (connectionPoolSize != null) {
751+
validateNonNegative(
752+
connectionPoolSize, BigQueryJdbcUrlUtility.CONNECTION_POOL_SIZE_PROPERTY_NAME);
753+
}
739754
this.connectionPoolSize = connectionPoolSize;
740755
}
741756

@@ -746,14 +761,27 @@ public Long getListenerPoolSize() {
746761
}
747762

748763
public void setListenerPoolSize(Long listenerPoolSize) {
764+
if (listenerPoolSize != null) {
765+
validateNonNegative(
766+
listenerPoolSize, BigQueryJdbcUrlUtility.LISTENER_POOL_SIZE_PROPERTY_NAME);
767+
}
749768
this.listenerPoolSize = listenerPoolSize;
750769
}
751770

752771
public void setHighThroughputMinTableSize(Integer highThroughputMinTableSize) {
772+
if (highThroughputMinTableSize != null) {
773+
validateNonNegative(
774+
highThroughputMinTableSize, BigQueryJdbcUrlUtility.HTAPI_MIN_TABLE_SIZE_PROPERTY_NAME);
775+
}
753776
this.highThroughputMinTableSize = highThroughputMinTableSize;
754777
}
755778

756779
public void setHighThroughputActivationRatio(Integer highThroughputActivationRatio) {
780+
if (highThroughputActivationRatio != null) {
781+
validateNonNegative(
782+
highThroughputActivationRatio,
783+
BigQueryJdbcUrlUtility.HTAPI_ACTIVATION_RATIO_PROPERTY_NAME);
784+
}
757785
this.highThroughputActivationRatio = highThroughputActivationRatio;
758786
}
759787

@@ -1048,6 +1076,11 @@ public Integer getMetadataFetchThreadCount() {
10481076
}
10491077

10501078
public void setMetadataFetchThreadCount(Integer metadataFetchThreadCount) {
1079+
if (metadataFetchThreadCount != null) {
1080+
validateNonNegative(
1081+
metadataFetchThreadCount,
1082+
BigQueryJdbcUrlUtility.METADATA_FETCH_THREAD_COUNT_PROPERTY_NAME);
1083+
}
10511084
this.metadataFetchThreadCount = metadataFetchThreadCount;
10521085
}
10531086

@@ -1106,18 +1139,31 @@ public Integer getRetryMaxDelay() {
11061139
}
11071140

11081141
public void setJobTimeout(Integer jobTimeout) {
1142+
if (jobTimeout != null) {
1143+
validateNonNegative(jobTimeout, BigQueryJdbcUrlUtility.JOB_TIMEOUT_PROPERTY_NAME);
1144+
}
11091145
this.jobTimeout = jobTimeout;
11101146
}
11111147

11121148
public void setRetryInitialDelay(Integer retryInitialDelay) {
1149+
if (retryInitialDelay != null) {
1150+
validateNonNegative(
1151+
retryInitialDelay, BigQueryJdbcUrlUtility.RETRY_INITIAL_DELAY_PROPERTY_NAME);
1152+
}
11131153
this.retryInitialDelay = retryInitialDelay;
11141154
}
11151155

11161156
public void setRetryMaxDelay(Integer retryMaxDelay) {
1157+
if (retryMaxDelay != null) {
1158+
validateNonNegative(retryMaxDelay, BigQueryJdbcUrlUtility.RETRY_MAX_DELAY_PROPERTY_NAME);
1159+
}
11171160
this.retryMaxDelay = retryMaxDelay;
11181161
}
11191162

11201163
public void setTimeout(Integer timeout) {
1164+
if (timeout != null) {
1165+
validateNonNegative(timeout, BigQueryJdbcUrlUtility.RETRY_TIMEOUT_IN_SECS_PROPERTY_NAME);
1166+
}
11211167
this.timeout = timeout;
11221168
}
11231169

@@ -1126,6 +1172,10 @@ public Integer getHttpConnectTimeout() {
11261172
}
11271173

11281174
public void setHttpConnectTimeout(Integer httpConnectTimeout) {
1175+
if (httpConnectTimeout != null) {
1176+
validateNonNegative(
1177+
httpConnectTimeout, BigQueryJdbcUrlUtility.HTTP_CONNECT_TIMEOUT_PROPERTY_NAME);
1178+
}
11291179
this.httpConnectTimeout = httpConnectTimeout;
11301180
}
11311181

@@ -1134,6 +1184,9 @@ public Integer getHttpReadTimeout() {
11341184
}
11351185

11361186
public void setHttpReadTimeout(Integer httpReadTimeout) {
1187+
if (httpReadTimeout != null) {
1188+
validateNonNegative(httpReadTimeout, BigQueryJdbcUrlUtility.HTTP_READ_TIMEOUT_PROPERTY_NAME);
1189+
}
11371190
this.httpReadTimeout = httpReadTimeout;
11381191
}
11391192

@@ -1154,6 +1207,10 @@ public Integer getSwaActivationRowCount() {
11541207
}
11551208

11561209
public void setSwaActivationRowCount(Integer swaActivationRowCount) {
1210+
if (swaActivationRowCount != null) {
1211+
validateNonNegative(
1212+
swaActivationRowCount, BigQueryJdbcUrlUtility.SWA_ACTIVATION_ROW_COUNT_PROPERTY_NAME);
1213+
}
11571214
this.swaActivationRowCount = swaActivationRowCount;
11581215
}
11591216

@@ -1164,6 +1221,10 @@ public Integer getSwaAppendRowCount() {
11641221
}
11651222

11661223
public void setSwaAppendRowCount(Integer swaAppendRowCount) {
1224+
if (swaAppendRowCount != null) {
1225+
validateNonNegative(
1226+
swaAppendRowCount, BigQueryJdbcUrlUtility.SWA_APPEND_ROW_COUNT_PROPERTY_NAME);
1227+
}
11671228
this.swaAppendRowCount = swaAppendRowCount;
11681229
}
11691230

@@ -1315,4 +1376,12 @@ public <T> T unwrap(Class<T> iface) {
13151376
public boolean isWrapperFor(Class<?> iface) {
13161377
return false;
13171378
}
1379+
1380+
private static void validateNonNegative(long val, String propertyName) {
1381+
if (val < 0) {
1382+
throw new BigQueryJdbcRuntimeException(
1383+
String.format(
1384+
"Invalid value for %s. It must be greater than or equal to 0.", propertyName));
1385+
}
1386+
}
13181387
}

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

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
2121
import static org.junit.jupiter.api.Assertions.assertFalse;
2222
import static org.junit.jupiter.api.Assertions.assertNull;
23+
import static org.junit.jupiter.api.Assertions.assertThrows;
2324

25+
import com.google.cloud.bigquery.exception.BigQueryJdbcRuntimeException;
2426
import java.util.Collections;
2527
import java.util.Map;
2628
import java.util.Properties;
@@ -206,4 +208,56 @@ public void testAppendPropertiesToURL_propertyWithSemicolon_isEscaped() throws E
206208
assertThat(parsedProperties.get("ProjectId")).isEqualTo(complexValue);
207209
assertFalse(parsedProperties.containsKey("ExtraProperty"));
208210
}
211+
212+
@Test
213+
public void testInvalidConnectionProperties() {
214+
String url = "jdbc:bigquery://;MaxResults=-1";
215+
assertThrows(BigQueryJdbcRuntimeException.class, () -> DataSource.fromUrl(url));
216+
217+
String url2 = "jdbc:bigquery://;ConnectionPoolSize=-2";
218+
assertThrows(BigQueryJdbcRuntimeException.class, () -> DataSource.fromUrl(url2));
219+
220+
String url3 = "jdbc:bigquery://;Timeout=-1";
221+
assertThrows(BigQueryJdbcRuntimeException.class, () -> DataSource.fromUrl(url3));
222+
223+
String url4 = "jdbc:bigquery://;JobTimeout=-1";
224+
assertThrows(BigQueryJdbcRuntimeException.class, () -> DataSource.fromUrl(url4));
225+
226+
String url5 = "jdbc:bigquery://;RetryInitialDelay=-1";
227+
assertThrows(BigQueryJdbcRuntimeException.class, () -> DataSource.fromUrl(url5));
228+
229+
String url6 = "jdbc:bigquery://;RetryMaxDelay=-1";
230+
assertThrows(BigQueryJdbcRuntimeException.class, () -> DataSource.fromUrl(url6));
231+
232+
String url7 = "jdbc:bigquery://;MaxResults=0";
233+
assertThrows(BigQueryJdbcRuntimeException.class, () -> DataSource.fromUrl(url7));
234+
}
235+
236+
@Test
237+
public void testInvalidSetterValues() {
238+
DataSource ds = new DataSource();
239+
assertThrows(BigQueryJdbcRuntimeException.class, () -> ds.setMaxResults(-1L));
240+
assertThrows(BigQueryJdbcRuntimeException.class, () -> ds.setMaxResults(0L));
241+
assertThrows(BigQueryJdbcRuntimeException.class, () -> ds.setTimeout(-1));
242+
assertThrows(BigQueryJdbcRuntimeException.class, () -> ds.setJobTimeout(-1));
243+
assertThrows(BigQueryJdbcRuntimeException.class, () -> ds.setRetryInitialDelay(-1));
244+
assertThrows(BigQueryJdbcRuntimeException.class, () -> ds.setRetryMaxDelay(-1));
245+
}
246+
247+
@Test
248+
public void testNonNumericConnectionProperties() {
249+
String url = "jdbc:bigquery://;MaxResults=abc";
250+
assertThrows(BigQueryJdbcRuntimeException.class, () -> DataSource.fromUrl(url));
251+
}
252+
253+
@Test
254+
public void testUnrecognizedConnectionProperties() {
255+
// Unrecognized key-value pair should be ignored (log warning, no exception)
256+
String url = "jdbc:bigquery://;UnknownProperty=value";
257+
assertDoesNotThrow(() -> DataSource.fromUrl(url));
258+
259+
// Malformed property (not key-value) should throw exception
260+
String url2 = "jdbc:bigquery://;MalformedProperty";
261+
assertThrows(BigQueryJdbcRuntimeException.class, () -> DataSource.fromUrl(url2));
262+
}
209263
}

java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/ITConnectionPoolingTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public void testPooledConnectionDataSourceFailInvalidConnectionURl() {
6868
PooledConnectionDataSource pooledDataSource = new PooledConnectionDataSource();
6969
pooledDataSource.setURL(connectionUrl);
7070

71-
assertThrows(NumberFormatException.class, () -> pooledDataSource.getPooledConnection());
71+
assertThrows(BigQueryJdbcException.class, () -> pooledDataSource.getPooledConnection());
7272
}
7373

7474
@Test

0 commit comments

Comments
 (0)