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 5c5d5bc84..4c97af217 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,7 @@ 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 17be15d86..361627661 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 dcf31a7ff..01f9b9f9a 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 @@ -331,11 +331,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); @@ -366,16 +366,18 @@ 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)); - } - } else if (isJson(pvtKey)) { - stream = new ByteArrayInputStream(pvtKey.getBytes()); + keyBytes = Files.readAllBytes(Paths.get(keyPath)); + } + + if (isJson(keyBytes)) { + stream = new ByteArrayInputStream(keyBytes); } else if (pvtKey != null) { key = privateKeyFromPkcs8(pvtKey); + } else { + key = privateKeyFromP12Bytes(keyBytes, p12Password); } if (stream != null) { @@ -703,9 +705,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 50d0d33bb..9c01fd948 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 @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.function.Supplier; import java.util.Properties; import java.util.Set; import java.util.logging.Level; @@ -340,7 +341,7 @@ final class BigQueryJdbcUrlUtility { 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) @@ -680,6 +681,16 @@ 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 1cc0ad7bb..b14428790 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; @@ -381,4 +383,37 @@ public void testBigQueryReadClientKeepAliveSettings() throws SQLException, IOExc assertTrue(grpcProvider.getKeepAliveWithoutCalls()); } } + + @Test + public void testBigQueryJobCreationMode_required() throws Exception { + String url = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" + + "OAuthType=3;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 = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" + + "OAuthType=3;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 = + "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;" + + "OAuthType=3;"; + 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 b7fc03162..5129a7496 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);