diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDriver.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDriver.java index 5b138873b399..15058ff01bd9 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDriver.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDriver.java @@ -127,35 +127,46 @@ public Connection connect(String url, Properties info) throws SQLException { LOG.finest("++enter++"); try { if (acceptsURL(url)) { - // strip 'jdbc:' from the URL, add any extra properties String connectionUri = BigQueryJdbcUrlUtility.appendPropertiesToURL(url.substring(5), this.toString(), info); + Level logLevel; + String logPath; + try { + // LogLevel + String logLevelStr = + BigQueryJdbcUrlUtility.parseUriPropertyWithoutValidation( + connectionUri, BigQueryJdbcUrlUtility.LOG_LEVEL_PROPERTY_NAME); + if (logLevelStr == null) { + logLevelStr = System.getenv(BigQueryJdbcUrlUtility.LOG_LEVEL_ENV_VAR); + } + logLevel = BigQueryJdbcUrlUtility.parseLogLevel(logLevelStr); + + // LogPath + logPath = + BigQueryJdbcUrlUtility.parseUriPropertyWithoutValidation( + connectionUri, BigQueryJdbcUrlUtility.LOG_PATH_PROPERTY_NAME); + if (logPath == null) { + logPath = System.getenv(BigQueryJdbcUrlUtility.LOG_PATH_ENV_VAR); + } + if (logPath == null) { + logPath = BigQueryJdbcUrlUtility.DEFAULT_LOG_PATH; + } + + BigQueryJdbcRootLogger.setLevel(logLevel, logPath); + } catch (RuntimeException e) { + LOG.log(Level.SEVERE, "Failed to parse connection URL properties", e); + throw new BigQueryJdbcException("Failed to parse connection URL properties", e); + } + + // Logging starts from here. DataSource ds; try { ds = DataSource.fromUrl(connectionUri); } catch (BigQueryJdbcRuntimeException e) { + LOG.log(Level.SEVERE, "Failed to parse connection URL", e); throw new BigQueryJdbcException("Failed to parse connection URL", e); } - // LogLevel - String logLevelStr = ds.getLogLevel(); - if (logLevelStr == null) { - logLevelStr = System.getenv(BigQueryJdbcUrlUtility.LOG_LEVEL_ENV_VAR); - } - Level logLevel = BigQueryJdbcUrlUtility.parseLogLevel(logLevelStr); - - // LogPath - String logPath = ds.getLogPath(); - if (logPath == null) { - logPath = System.getenv(BigQueryJdbcUrlUtility.LOG_PATH_ENV_VAR); - } - if (logPath == null) { - logPath = BigQueryJdbcUrlUtility.DEFAULT_LOG_PATH; - } - - BigQueryJdbcRootLogger.setLevel(logLevel, logPath); - - // Logging starts from here. BigQueryConnection connection = new BigQueryConnection(connectionUri, ds); LOG.info( "Driver info : { {Database Product Name : %s}, " diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcUrlUtility.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcUrlUtility.java index c0dfcbd1f4aa..0a19bed7a2c8 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcUrlUtility.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcUrlUtility.java @@ -662,6 +662,37 @@ static String parseUriProperty(String uri, String property) { return map.get(property); } + /** + * Parses a URI property from the given URI without validating any other properties. + * + * @param uri The URI to parse. + * @param property The name of the property to parse. + * @return The String value of the property, or null if the property is not found. + */ + static String parseUriPropertyWithoutValidation(String uri, String property) { + if (uri == null) { + return null; + } + int start = 0; + int len = uri.length(); + while (start < len) { + int nextSemi = uri.indexOf(';', start); + int end = (nextSemi == -1) ? len : nextSemi; + + int eqIndex = uri.indexOf('=', start); + if (eqIndex > start && eqIndex < end) { + String key = uri.substring(start, eqIndex).trim(); + if (key.equalsIgnoreCase(property)) { + String value = uri.substring(eqIndex + 1, end); + return CharEscapers.decodeUriPath(value.replace("+", "%2B")); + } + } + + start = end + 1; + } + return null; + } + /** * Parses the URL into a map of key-value pairs, validating that all keys are known properties. * diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDriverTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDriverTest.java index 85eb254038f3..8a637ac1e3be 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDriverTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDriverTest.java @@ -21,11 +21,13 @@ import java.sql.DriverPropertyInfo; import java.sql.SQLException; import java.util.Properties; +import java.util.logging.Level; import java.util.logging.Logger; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class BigQueryDriverTest { +public class BigQueryDriverTest extends BigQueryJdbcLoggingBaseTest { static BigQueryDriver bigQueryDriver; @@ -104,4 +106,63 @@ public void testConnectWithInvalidUrlChainsNoException() throws SQLException { new Properties()); assertThat(connection.isClosed()).isFalse(); } + + @Test + public void testUnknownPropertyWarningIsLogged() throws SQLException { + Connection connection = + bigQueryDriver.connect( + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" + + "OAuthType=2;OAuthAccessToken=redactedToken;ProjectId=t;LogLevel=3;" + + "MyUnknownSetting=Value", + new Properties()); + assertThat(connection.isClosed()).isFalse(); + + boolean foundWarning = + capturedLogs.stream() + .anyMatch( + r -> + r.getLevel() == Level.WARNING + && r.getMessage() + .contains("Wrong value or unknown setting: MYUNKNOWNSETTING")); + assertThat(foundWarning).isTrue(); + } + + @Test + public void testMalformedUrlExceptionIsLogged() { + Assertions.assertThrows( + SQLException.class, + () -> + bigQueryDriver.connect( + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" + + "OAuthType=2;OAuthAccessToken=redactedToken;ProjectId=t;LogLevel=3;" + + "MalformedPropertyWithoutEquals", + new Properties())); + + boolean foundSevere = + capturedLogs.stream() + .anyMatch( + r -> + r.getLevel() == Level.SEVERE + && r.getMessage().contains("Failed to parse connection URL")); + assertThat(foundSevere).isTrue(); + } + + @Test + public void testInvalidLogLevelExceptionIsLogged() { + Assertions.assertThrows( + SQLException.class, + () -> + bigQueryDriver.connect( + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" + + "OAuthType=2;OAuthAccessToken=redactedToken;ProjectId=t;LogLevel=invalidInt;", + new Properties())); + + boolean foundSevere = + capturedLogs.stream() + .anyMatch( + r -> + r.getLevel() == Level.SEVERE + && r.getMessage().contains("Failed to parse connection URL properties")); + assertThat(foundSevere).isTrue(); + } }