diff --git a/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java b/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java index 5c5d5bc84c..75d472de9e 100644 --- a/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java +++ b/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java @@ -32,6 +32,7 @@ import com.google.cloud.bigquery.Job; import com.google.cloud.bigquery.JobInfo; import com.google.cloud.bigquery.QueryJobConfiguration; +import com.google.cloud.bigquery.QueryJobConfiguration.JobCreationMode; import com.google.cloud.bigquery.exception.BigQueryJdbcException; import com.google.cloud.bigquery.exception.BigQueryJdbcRuntimeException; import com.google.cloud.bigquery.exception.BigQueryJdbcSqlFeatureNotSupportedException; @@ -159,10 +160,10 @@ public class BigQueryConnection extends BigQueryNoOpsConnection { this.authProperties = BigQueryJdbcOAuthUtility.parseOAuthProperties(url, this.connectionClassName); this.catalog = - BigQueryJdbcUrlUtility.parseStringProperty( + BigQueryJdbcUrlUtility.parseStringPropertyLazyDefault( url, BigQueryJdbcUrlUtility.PROJECT_ID_PROPERTY_NAME, - BigQueryOptions.getDefaultProjectId(), + () -> BigQueryOptions.getDefaultProjectId(), this.connectionClassName); this.universeDomain = BigQueryJdbcUrlUtility.parseStringProperty( @@ -1064,7 +1065,10 @@ private BigQuery getBigQueryConnection() { } BigQueryOptions options = bigQueryOptions.setHeaderProvider(HEADER_PROVIDER).build(); - options.setQueryPreviewEnabled(String.valueOf(this.useStatelessQueryMode)); + options.setDefaultJobCreationMode( + this.useStatelessQueryMode + ? JobCreationMode.JOB_CREATION_OPTIONAL + : JobCreationMode.JOB_CREATION_REQUIRED); return options.getService(); } diff --git a/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnectionProperty.java b/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnectionProperty.java index 17be15d866..3b94f2fef7 100644 --- a/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnectionProperty.java +++ b/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnectionProperty.java @@ -17,12 +17,14 @@ package com.google.cloud.bigquery.jdbc; import java.util.List; +import java.util.function.Supplier; class BigQueryConnectionProperty { private final String name; private final String description; private final String defaultValue; + private final Supplier defaultValueSupplier; private final List validValues; public String getName() { @@ -34,6 +36,9 @@ public String getDescription() { } public String getDefaultValue() { + if (defaultValueSupplier != null) { + return defaultValueSupplier.get(); + } return defaultValue; } @@ -43,6 +48,7 @@ public List getValidValues() { BigQueryConnectionProperty(Builder builder) { this.name = builder.name; + this.defaultValueSupplier = builder.defaultValueSupplier; this.defaultValue = builder.defaultValue; this.description = builder.description; this.validValues = builder.validValues; @@ -79,6 +85,7 @@ static final class Builder { private String name; private String description; private String defaultValue; + private Supplier defaultValueSupplier = null; private List validValues; private Builder(BigQueryConnectionProperty bigQueryConnectionProperty) { @@ -105,6 +112,11 @@ Builder setDefaultValue(String defaultValue) { return this; } + Builder setLazyDefaultValue(Supplier defaultValueSupplier) { + this.defaultValueSupplier = defaultValueSupplier; + return this; + } + Builder setValidValues(List validValues) { this.validValues = validValues; return this; diff --git a/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOAuthUtility.java b/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOAuthUtility.java index dcf31a7fff..11d79ae4b8 100644 --- a/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOAuthUtility.java +++ b/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOAuthUtility.java @@ -38,6 +38,7 @@ import java.awt.Desktop; import java.io.BufferedReader; import java.io.ByteArrayInputStream; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -331,11 +332,11 @@ private static boolean isFileExists(String filename) { } } - private static boolean isJson(String value) { + private static boolean isJson(byte[] value) { try { // This is done this way to ensure strict Json parsing // https://github.com/google/gson/issues/1208#issuecomment-2120764686 - InputStream stream = new ByteArrayInputStream(value.getBytes()); + InputStream stream = new ByteArrayInputStream(value); InputStreamReader reader = new InputStreamReader(stream); JsonReader jsonReader = new JsonReader(reader); jsonReader.setStrictness(Strictness.STRICT); @@ -365,17 +366,24 @@ private static GoogleCredentials getGoogleServiceAccountCredentials( final String keyPath = pvtKeyPath != null ? pvtKeyPath : pvtKey; PrivateKey key = null; - InputStream stream = null; + byte[] keyBytes = pvtKey != null ? pvtKey.getBytes() : null; if (isFileExists(keyPath)) { - key = privateKeyFromP12File(keyPath, p12Password); - if (key == null) { - stream = Files.newInputStream(Paths.get(keyPath)); + try (InputStream stream = new FileInputStream(keyPath)) { + int bufferSize = 1024 * 1024; + byte[] buffer = new byte[bufferSize]; + stream.read(buffer, 0, bufferSize); + keyBytes = buffer; } - } else if (isJson(pvtKey)) { - stream = new ByteArrayInputStream(pvtKey.getBytes()); + } + + InputStream stream = null; + if (isJson(keyBytes)) { + stream = new ByteArrayInputStream(keyBytes); } else if (pvtKey != null) { key = privateKeyFromPkcs8(pvtKey); + } else if (keyBytes != null) { + key = privateKeyFromP12Bytes(keyBytes, p12Password); } if (stream != null) { @@ -703,9 +711,9 @@ private static GoogleCredentials getServiceAccountImpersonatedCredentials( impersonationLifetimeInt); } - static PrivateKey privateKeyFromP12File(String privateKeyFile, String password) { + static PrivateKey privateKeyFromP12Bytes(byte[] privateKey, String password) { try { - InputStream stream = Files.newInputStream(Paths.get(privateKeyFile)); + InputStream stream = new ByteArrayInputStream(privateKey); return SecurityUtils.loadPrivateKeyFromKeyStore( SecurityUtils.getPkcs12KeyStore(), stream, "notasecret", "privatekey", password); } catch (IOException | GeneralSecurityException e) { diff --git a/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcUrlUtility.java b/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcUrlUtility.java index 5ffa6c254f..f240d3d11a 100644 --- a/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcUrlUtility.java +++ b/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcUrlUtility.java @@ -34,6 +34,7 @@ import java.util.Map.Entry; import java.util.Properties; import java.util.Set; +import java.util.function.Supplier; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -358,7 +359,7 @@ protected boolean removeEldestEntry(Map.Entry> eldes BigQueryConnectionProperty.newBuilder() .setName(PROJECT_ID_PROPERTY_NAME) .setDescription("A globally unique identifier for your project.") - .setDefaultValue(BigQueryOptions.getDefaultProjectId()) + .setLazyDefaultValue(() -> BigQueryOptions.getDefaultProjectId()) .build(), BigQueryConnectionProperty.newBuilder() .setName(LOG_PATH_PROPERTY_NAME) @@ -790,6 +791,19 @@ static String parseStringProperty( return defaultValue; } + static String parseStringPropertyLazyDefault( + String url, + String propertyName, + Supplier defaultValueSupplier, + String callerClassName) { + LOG.finest("++enter++\t" + callerClassName); + String parsedValue = BigQueryJdbcUrlUtility.parseUriProperty(url, propertyName); + if (parsedValue != null) { + return parsedValue; + } + return defaultValueSupplier.get(); + } + static List parseStringListProperty( String url, String propertyName, String callerClassName) { LOG.finest("++enter++\t" + callerClassName); diff --git a/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryConnectionTest.java b/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryConnectionTest.java index 1cc0ad7bb5..4da6165b20 100644 --- a/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryConnectionTest.java +++ b/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryConnectionTest.java @@ -21,6 +21,8 @@ import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; import com.google.api.gax.rpc.HeaderProvider; import com.google.api.gax.rpc.TransportChannelProvider; +import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.QueryJobConfiguration.JobCreationMode; import com.google.cloud.bigquery.exception.BigQueryJdbcException; import com.google.cloud.bigquery.storage.v1.BigQueryReadClient; import com.google.cloud.bigquery.storage.v1.BigQueryWriteClient; @@ -38,6 +40,9 @@ public class BigQueryConnectionTest { private static final String DEFAULT_VERSION = "0.0.0"; private static final String DEFAULT_JDBC_TOKEN_VALUE = "Google-BigQuery-JDBC-Driver"; + private static final String BASE_URL = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" + + "OAuthType=2;OAuthAccessToken=redacted;ProjectId=project;"; private String expectedVersion; @Before @@ -381,4 +386,34 @@ public void testBigQueryReadClientKeepAliveSettings() throws SQLException, IOExc assertTrue(grpcProvider.getKeepAliveWithoutCalls()); } } + + @Test + public void testBigQueryJobCreationMode_required() throws Exception { + String url = BASE_URL + "JobCreationMode=1;"; + try (BigQueryConnection connection = new BigQueryConnection(url)) { + BigQuery bq = connection.getBigQuery(); + assertEquals( + bq.getOptions().getDefaultJobCreationMode(), JobCreationMode.JOB_CREATION_REQUIRED); + } + } + + @Test + public void testBigQueryJobCreationMode_optional() throws Exception { + String url = BASE_URL + "JobCreationMode=2;"; + try (BigQueryConnection connection = new BigQueryConnection(url)) { + BigQuery bq = connection.getBigQuery(); + assertEquals( + bq.getOptions().getDefaultJobCreationMode(), JobCreationMode.JOB_CREATION_OPTIONAL); + } + } + + @Test + public void testBigQueryJobCreationMode_default() throws Exception { + String url = BASE_URL; + try (BigQueryConnection connection = new BigQueryConnection(url)) { + BigQuery bq = connection.getBigQuery(); + assertEquals( + bq.getOptions().getDefaultJobCreationMode(), JobCreationMode.JOB_CREATION_OPTIONAL); + } + } } diff --git a/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOAuthUtilityTest.java b/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOAuthUtilityTest.java index b7fc031621..5129a74961 100644 --- a/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOAuthUtilityTest.java +++ b/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOAuthUtilityTest.java @@ -32,6 +32,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.Files; import java.nio.file.Paths; import java.security.PrivateKey; import java.util.Collections; @@ -472,12 +473,12 @@ public void testPrivateKeyFromPkcs8_wrong() { // keytool -genkey -alias privatekey -keyalg RSA -keysize 2048 -storepass notasecret \ // -keypass notasecret -storetype pkcs12 -keystore ./fake.p12 @Test - public void testPrivateKeyFromP12File() { + public void testPrivateKeyFromP12Bytes() { URL resource = BigQueryJdbcOAuthUtilityTest.class.getResource("/fake.p12"); try { PrivateKey pk = - BigQueryJdbcOAuthUtility.privateKeyFromP12File( - Paths.get(resource.toURI()).toAbsolutePath().toString(), "notasecret"); + BigQueryJdbcOAuthUtility.privateKeyFromP12Bytes( + Files.readAllBytes(Paths.get(resource.toURI())), "notasecret"); assertNotNull(pk); } catch (Exception e) { assertTrue(false); @@ -485,18 +486,12 @@ public void testPrivateKeyFromP12File() { } @Test - public void testPrivateKeyFromP12File_missing_file() { - PrivateKey pk = BigQueryJdbcOAuthUtility.privateKeyFromP12File("", ""); - assertNull(pk); - } - - @Test - public void testPrivateKeyFromP12File_wrong_password() { + public void testPrivateKeyFromP12Bytes_wrong_password() { URL resource = BigQueryJdbcOAuthUtilityTest.class.getResource("/fake.p12"); try { PrivateKey pk = - BigQueryJdbcOAuthUtility.privateKeyFromP12File( - Paths.get(resource.toURI()).toAbsolutePath().toString(), "fake"); + BigQueryJdbcOAuthUtility.privateKeyFromP12Bytes( + Files.readAllBytes(Paths.get(resource.toURI())), "fake"); assertNull(pk); } catch (Exception e) { assertTrue(false);