From 122e7db20bac85fbaa389959eac675f4d02f6e69 Mon Sep 17 00:00:00 2001 From: sagnghos Date: Wed, 1 Jul 2026 09:55:06 +0000 Subject: [PATCH 1/3] chore: deprecate experimental host option/parameter --- .../pgadapter/metadata/OptionsMetadata.java | 89 +++++++++++-------- .../cloud/spanner/pgadapter/ITAuthTest.java | 2 +- .../pgadapter/ITOpenTelemetryTest.java | 2 +- .../spanner/pgadapter/IntegrationTest.java | 8 +- .../spanner/pgadapter/PgAdapterTestEnv.java | 59 +++++++----- .../pgadapter/liquibase/ITLiquibaseTest.java | 10 +-- .../metadata/OptionsMetadataTest.java | 56 +++++++++++- 7 files changed, 153 insertions(+), 73 deletions(-) diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadata.java b/src/main/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadata.java index 9f9817c715..196bee9315 100644 --- a/src/main/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadata.java +++ b/src/main/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadata.java @@ -65,10 +65,15 @@ public class OptionsMetadata { static Duration DEFAULT_STARTUP_TIMEOUT = Duration.ofSeconds(30L); - private static final String EXTERNAL_HOST_PROJECT = "default"; - private static final String EXTERNAL_HOST_INSTANCE = "default"; + private static final String SPANNER_OMNI_PROJECT = "default"; + private static final String SPANNER_OMNI_INSTANCE = "default"; - private static final String IS_EXPERIMENTAL_HOST_PROPERTY_NAME = "isExperimentalHost"; + /** + * @deprecated use {@link #TYPE_PROPERTY_NAME} instead. + */ + private static final String TYPE_PROPERTY_NAME = "type"; + + private static final String SPANNER_OMNI_TYPE = "omni"; /** * Builder class for creating an instance of {@link OptionsMetadata}. @@ -116,7 +121,8 @@ public static class Builder { private Duration startupTimeout = DEFAULT_STARTUP_TIMEOUT; private String clientCertificate; private String clientKey; - private boolean isExperimentalHost = false; + + private String type = null; private Long describeCacheExpireMinutes; private Long describeCacheMaxSize; @@ -431,27 +437,38 @@ Builder setStartupTimeout(Duration timeout) { /** * Configures mTLS authentication using the provided client certificate and key files. mTLS is - * only supported for experimental spanner hosts. + * only supported for Spanner Omni endpoints. * * @param clientCertificate Path to the client certificate file. * @param clientKey Path to the client private key file. */ - @ExperimentalApi("https://github.com/googleapis/java-spanner/pull/3574") Builder useClientCert(String clientCertificate, String clientKey) { this.clientCertificate = clientCertificate; this.clientKey = clientKey; return this; } - /* + /** * Configures connection to an experimental host endpoint * - * @params experimentalHost url of the experimental host endpoint + * @param experimentalHost url of the experimental host endpoint + * @deprecated Use {@link #setType(String)} and {@link #setEndpoint(String)} instead. */ @ExperimentalApi("https://github.com/googleapis/java-spanner/pull/3574") + @Deprecated Builder setExperimentalHost(String experimentalHost) { setEndpoint(experimentalHost); - this.isExperimentalHost = true; + setType(SPANNER_OMNI_TYPE); + return this; + } + + /** + * Configures the connection instance type. + * + * @param type connection type, e.g., "OMNI" + */ + Builder setType(String type) { + this.type = type; return this; } @@ -546,7 +563,7 @@ private String[] toCommandLineArguments() { || useVirtualGrpcTransportThreads || enableEndToEndTracing || (clientKey != null && clientCertificate != null) - || isExperimentalHost) { + || SPANNER_OMNI_TYPE.equalsIgnoreCase(this.type)) { StringBuilder jdbcOptionBuilder = new StringBuilder(); if (usePlainText) { jdbcOptionBuilder.append("usePlainText=true;"); @@ -572,8 +589,8 @@ private String[] toCommandLineArguments() { jdbcOptionBuilder.append("clientCertificate=").append(clientCertificate).append(";"); jdbcOptionBuilder.append("clientKey=").append(clientKey).append(";"); } - if (isExperimentalHost) { - jdbcOptionBuilder.append("isExperimentalHost=true;"); + if (SPANNER_OMNI_TYPE.equalsIgnoreCase(this.type)) { + jdbcOptionBuilder.append("type=omni;"); } addOption(args, OPTION_JDBC_PROPERTIES, jdbcOptionBuilder.toString()); } @@ -789,7 +806,7 @@ private OptionsMetadata(Builder builder) { if (!propertyMap.containsKey("defaultSequenceKind")) { propertyMap.put("defaultSequenceKind", "bit_reversed_positive"); } - boolean usesExperimentalHost = isExperimentalHost(commandLine, propertyMap); + boolean usesOmniType = hasOmniType(commandLine, propertyMap); this.environment = environment; this.osName = osName; @@ -807,7 +824,7 @@ private OptionsMetadata(Builder builder) { + "OR use -c to set the credentials in PGAdapter and use these credentials for all connections."); } if (this.commandLine.hasOption(OPTION_DATABASE_NAME) - && !usesExperimentalHost + && !usesOmniType && !(this.commandLine.hasOption(OPTION_PROJECT_ID) && this.commandLine.hasOption(OPTION_INSTANCE_ID))) { throw SpannerExceptionFactory.newSpannerException( @@ -816,7 +833,7 @@ private OptionsMetadata(Builder builder) { + "Use the options -p -i -d to specify the " + "database that all connections to this instance of PGAdapter should use."); } - if ((usesExperimentalHost + if ((usesOmniType || (this.commandLine.hasOption(OPTION_PROJECT_ID) && this.commandLine.hasOption(OPTION_INSTANCE_ID))) && this.commandLine.hasOption(OPTION_DATABASE_NAME)) { @@ -1109,7 +1126,7 @@ public SessionPoolOptions getSessionPoolOptions() { */ public String buildCredentialsFile() { // Skip if a com.google.auth.Credentials instance has been set. - if (isExperimentalHost() || credentials != null) { + if (hasOmniType() || credentials != null) { return null; } if (!commandLine.hasOption(OPTION_CREDENTIALS_FILE)) { @@ -1199,23 +1216,23 @@ private static boolean usesEmulator( || isAutoConfigEmulator(propertyMap); } - private boolean isExperimentalHost() { + private boolean hasOmniType() { if (this.propertyMap == null) { - return isExperimentalHost( + return hasOmniType( this.commandLine, parseProperties(this.commandLine.getOptionValue(OPTION_JDBC_PROPERTIES, ""))); } - return isExperimentalHost(this.commandLine, this.propertyMap); + return hasOmniType(this.commandLine, this.propertyMap); } - private static boolean isExperimentalHost( - CommandLine commandLine, Map propertyMap) { + private static boolean hasOmniType(CommandLine commandLine, Map propertyMap) { if (propertyMap == null) { return false; } return commandLine.hasOption(OPTION_SPANNER_ENDPOINT) - && commandLine.hasOption(OPTION_JDBC_PROPERTIES) - && propertyMap.containsKey(IS_EXPERIMENTAL_HOST_PROPERTY_NAME); + && (commandLine.hasOption(OPTION_JDBC_PROPERTIES) + && (propertyMap.containsKey("isExperimentalHost") + || SPANNER_OMNI_TYPE.equalsIgnoreCase(propertyMap.get(TYPE_PROPERTY_NAME)))); } /** Returns the fully qualified database name based on the given database id or name. */ @@ -1227,8 +1244,8 @@ public DatabaseName getDatabaseName(String database) { String projectId; if (commandLine.hasOption(OPTION_PROJECT_ID)) { projectId = commandLine.getOptionValue(OPTION_PROJECT_ID); - } else if (isExperimentalHost()) { - projectId = EXTERNAL_HOST_PROJECT; + } else if (hasOmniType()) { + projectId = SPANNER_OMNI_PROJECT; } else { projectId = getDefaultProjectId(); } @@ -1242,8 +1259,8 @@ public DatabaseName getDatabaseName(String database) { String instanceId; if (commandLine.hasOption(OPTION_INSTANCE_ID)) { instanceId = commandLine.getOptionValue(OPTION_INSTANCE_ID); - } else if (isExperimentalHost()) { - instanceId = EXTERNAL_HOST_INSTANCE; + } else if (hasOmniType()) { + instanceId = SPANNER_OMNI_INSTANCE; } else { throw SpannerExceptionFactory.newSpannerException( ErrorCode.FAILED_PRECONDITION, @@ -1682,11 +1699,11 @@ public boolean hasDefaultConnectionUrl() { public DatabaseId getDefaultDatabaseId() { return this.hasDefaultConnectionUrl() ? DatabaseId.of( - !commandLine.hasOption(OPTION_PROJECT_ID) && isExperimentalHost() - ? EXTERNAL_HOST_PROJECT + !commandLine.hasOption(OPTION_PROJECT_ID) && hasOmniType() + ? SPANNER_OMNI_PROJECT : commandLine.getOptionValue(OPTION_PROJECT_ID), - !commandLine.hasOption(OPTION_INSTANCE_ID) && isExperimentalHost() - ? EXTERNAL_HOST_INSTANCE + !commandLine.hasOption(OPTION_INSTANCE_ID) && hasOmniType() + ? SPANNER_OMNI_INSTANCE : commandLine.getOptionValue(OPTION_INSTANCE_ID), commandLine.getOptionValue(OPTION_DATABASE_NAME)) : null; @@ -1694,7 +1711,7 @@ public DatabaseId getDefaultDatabaseId() { /** Returns true if these options contain a default instance id. */ public boolean hasDefaultInstanceId() { - return isExperimentalHost() + return hasOmniType() || (commandLine.hasOption(OPTION_PROJECT_ID) && commandLine.hasOption(OPTION_INSTANCE_ID)); } @@ -1702,11 +1719,11 @@ public boolean hasDefaultInstanceId() { public InstanceId getDefaultInstanceId() { if (hasDefaultInstanceId()) { return InstanceId.of( - !commandLine.hasOption(OPTION_PROJECT_ID) && isExperimentalHost() - ? EXTERNAL_HOST_PROJECT + !commandLine.hasOption(OPTION_PROJECT_ID) && hasOmniType() + ? SPANNER_OMNI_PROJECT : commandLine.getOptionValue(OPTION_PROJECT_ID), - !commandLine.hasOption(OPTION_INSTANCE_ID) && isExperimentalHost() - ? EXTERNAL_HOST_INSTANCE + !commandLine.hasOption(OPTION_INSTANCE_ID) && hasOmniType() + ? SPANNER_OMNI_INSTANCE : commandLine.getOptionValue(OPTION_INSTANCE_ID)); } return null; diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/ITAuthTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/ITAuthTest.java index b7c7e69b3c..edad11ffc7 100644 --- a/src/test/java/com/google/cloud/spanner/pgadapter/ITAuthTest.java +++ b/src/test/java/com/google/cloud/spanner/pgadapter/ITAuthTest.java @@ -55,7 +55,7 @@ public String getCredentials() { @BeforeClass public static void setup() throws ClassNotFoundException { - IntegrationTest.skipOnExperimentalHost("Cloud auth is not applicable for experimental host"); + IntegrationTest.skipOnSpannerOmni("Cloud auth is not applicable for Spanner Omni"); // Make sure the PG JDBC driver is loaded. Class.forName("org.postgresql.Driver"); diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/ITOpenTelemetryTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/ITOpenTelemetryTest.java index 3b5a505379..ebba9dbeee 100644 --- a/src/test/java/com/google/cloud/spanner/pgadapter/ITOpenTelemetryTest.java +++ b/src/test/java/com/google/cloud/spanner/pgadapter/ITOpenTelemetryTest.java @@ -56,7 +56,7 @@ public class ITOpenTelemetryTest implements IntegrationTest { @BeforeClass public static void setup() throws IOException { IntegrationTest.skipOnEmulator("This test requires credentials"); - IntegrationTest.skipOnExperimentalHost("Cloud auth is not applicable for experimental host"); + IntegrationTest.skipOnSpannerOmni("Cloud auth is not applicable for Spanner Omni"); OptionsMetadata.Builder openTelemetryOptionsBuilder = OptionsMetadata.newBuilder() diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/IntegrationTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/IntegrationTest.java index 33775fafd9..1d33521e71 100644 --- a/src/test/java/com/google/cloud/spanner/pgadapter/IntegrationTest.java +++ b/src/test/java/com/google/cloud/spanner/pgadapter/IntegrationTest.java @@ -22,15 +22,15 @@ static void skipOnEmulator(String reason) { assumeFalse(reason, isRunningOnEmulator()); } - static void skipOnExperimentalHost(String reason) { - assumeFalse(reason, isRunningOnExperimentalHost()); + static void skipOnSpannerOmni(String reason) { + assumeFalse(reason, isRunningOnSpannerOmni()); } static boolean isRunningOnEmulator() { return System.getenv("SPANNER_EMULATOR_HOST") != null; } - static boolean isRunningOnExperimentalHost() { - return System.getProperty("SPANNER_EXPERIMENTAL_HOST") != null; + static boolean isRunningOnSpannerOmni() { + return System.getProperty("SPANNER_OMNI_HOST") != null; } } diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/PgAdapterTestEnv.java b/src/test/java/com/google/cloud/spanner/pgadapter/PgAdapterTestEnv.java index ded4c5468f..994216ed64 100644 --- a/src/test/java/com/google/cloud/spanner/pgadapter/PgAdapterTestEnv.java +++ b/src/test/java/com/google/cloud/spanner/pgadapter/PgAdapterTestEnv.java @@ -43,7 +43,6 @@ import com.google.common.primitives.Bytes; import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; -import io.grpc.ManagedChannelBuilder; import io.opentelemetry.api.OpenTelemetry; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -114,8 +113,13 @@ public class PgAdapterTestEnv { // Default Spanner url. Null indicates that the default URL should be used. static final String DEFAULT_SPANNER_URL = null; - public static final String SPANNER_EXPERIMENTAL_HOST = - System.getProperty("SPANNER_EXPERIMENTAL_HOST", null); + public static final String SPANNER_OMNI_HOST = System.getProperty("SPANNER_OMNI_HOST", null); + private static final boolean USE_PLAIN_TEXT = + Boolean.parseBoolean(System.getProperty("USE_PLAIN_TEXT", "false")); + public static final String SPANNER_OMNI_CLIENT_KEY = + System.getProperty("SPANNER_OMNI_CLIENT_KEY", null); + public static final String SPANNER_OMNI_CLIENT_CERT = + System.getProperty("SPANNER_OMNI_CLIENT_CERT", null); public static final ImmutableList DEFAULT_DATA_MODEL = ImmutableList.of( @@ -239,7 +243,7 @@ public void startPGAdapterServerWithDefaultDatabase( private void startPGAdapterServer( String databaseId, Iterable additionalPGAdapterOptions, OpenTelemetry openTelemetry) { - if (PG_ADAPTER_ADDRESS == null && SPANNER_EXPERIMENTAL_HOST == null) { + if (PG_ADAPTER_ADDRESS == null && SPANNER_OMNI_HOST == null) { additionalPGAdapterOptions = maybeAddAutoConfigEmulator(additionalPGAdapterOptions); String credentials = getCredentials(); ImmutableList.Builder argsListBuilder = @@ -262,14 +266,20 @@ private void startPGAdapterServer( String[] args = argsListBuilder.build().toArray(new String[0]); server = new ProxyServer(new OptionsMetadata(args), openTelemetry); server.startServer(); - } else if (SPANNER_EXPERIMENTAL_HOST != null) { + } else if (SPANNER_OMNI_HOST != null) { + StringBuilder rOption = new StringBuilder(); + if (USE_PLAIN_TEXT) { + rOption.append("usePlainText=true;"); + } + if (SPANNER_OMNI_CLIENT_CERT != null && SPANNER_OMNI_CLIENT_KEY != null) { + rOption.append( + String.format( + "clientCertificate=%s;clientKey=%s;", + SPANNER_OMNI_CLIENT_CERT, SPANNER_OMNI_CLIENT_KEY)); + } + rOption.append("type=omni"); ImmutableList.Builder argsListBuilder = - ImmutableList.builder() - .add( - "-e", - SPANNER_EXPERIMENTAL_HOST, - "-r", - "isExperimentalHost=true;usePlainText=true"); + ImmutableList.builder().add("-e", SPANNER_OMNI_HOST, "-r", rOption.toString()); if (databaseId != null) { argsListBuilder.add("-d", databaseId); } @@ -369,8 +379,8 @@ public String getSpannerUrl() { if (hostUrl == null) { hostUrl = System.getProperty(TEST_SPANNER_URL_PROPERTY, DEFAULT_SPANNER_URL); } - if (SPANNER_EXPERIMENTAL_HOST != null) { - hostUrl = SPANNER_EXPERIMENTAL_HOST; + if (SPANNER_OMNI_HOST != null) { + hostUrl = SPANNER_OMNI_HOST; } return hostUrl; } @@ -383,7 +393,7 @@ public String getProjectId() { if (projectId == null) { projectId = System.getProperty(TEST_PROJECT_PROPERTY, DEFAULT_PROJECT_ID); } - if (SPANNER_EXPERIMENTAL_HOST != null) { + if (SPANNER_OMNI_HOST != null) { projectId = "default"; } return projectId; @@ -393,7 +403,7 @@ public String getInstanceId() { if (instanceId == null) { instanceId = System.getProperty(TEST_INSTANCE_PROPERTY, DEFAULT_INSTANCE_ID); } - if (SPANNER_EXPERIMENTAL_HOST != null) { + if (SPANNER_OMNI_HOST != null) { instanceId = "default"; } return instanceId; @@ -545,7 +555,7 @@ private SpannerOptions createSpannerOptions() { Map env = System.getenv(); gcpCredentials = env.get(GCP_CREDENTIALS); GoogleCredentials credentials = null; - if (!Strings.isNullOrEmpty(gcpCredentials) && SPANNER_EXPERIMENTAL_HOST == null) { + if (!Strings.isNullOrEmpty(gcpCredentials) && SPANNER_OMNI_HOST == null) { try { credentials = GoogleCredentials.fromStream(new FileInputStream(gcpCredentials)); } catch (IOException e) { @@ -580,15 +590,18 @@ private SpannerOptions createSpannerOptions() { builder.setHost(spannerHost); } - if (SPANNER_EXPERIMENTAL_HOST != null) { - if (!SPANNER_EXPERIMENTAL_HOST.startsWith("http")) { - builder.setExperimentalHost("http://" + SPANNER_EXPERIMENTAL_HOST); + if (SPANNER_OMNI_HOST != null) { + if (!SPANNER_OMNI_HOST.startsWith("http")) { + builder.setHost("http://" + SPANNER_OMNI_HOST).setType(SpannerOptions.InstanceType.OMNI); } else { - builder.setExperimentalHost(SPANNER_EXPERIMENTAL_HOST); + builder.setHost(SPANNER_OMNI_HOST).setType(SpannerOptions.InstanceType.OMNI); + } + if (USE_PLAIN_TEXT) { + builder.usePlainText(); + } + if (SPANNER_OMNI_CLIENT_CERT != null && SPANNER_OMNI_CLIENT_KEY != null) { + builder.useClientCert(SPANNER_OMNI_CLIENT_CERT, SPANNER_OMNI_CLIENT_KEY); } - builder - .setChannelConfigurator(ManagedChannelBuilder::usePlaintext) - .setCredentials(NoCredentials.getInstance()); credentials = null; } diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/liquibase/ITLiquibaseTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/liquibase/ITLiquibaseTest.java index a78e3205bf..b58ac6881d 100644 --- a/src/test/java/com/google/cloud/spanner/pgadapter/liquibase/ITLiquibaseTest.java +++ b/src/test/java/com/google/cloud/spanner/pgadapter/liquibase/ITLiquibaseTest.java @@ -224,24 +224,20 @@ void runLiquibaseCommand(String... commands) throws IOException, InterruptedExce ImmutableList.builder().add("mvn", "-B").add(commands).build(); builder.command(liquibaseCommand); builder.directory(new File(LIQUIBASE_SAMPLE_DIRECTORY)); + builder.redirectErrorStream(true); Process process = builder.start(); - String errors; String output; try (BufferedReader reader = - new BufferedReader(new InputStreamReader(process.getInputStream())); - BufferedReader errorReader = - new BufferedReader(new InputStreamReader(process.getErrorStream()))) { - errors = errorReader.lines().collect(Collectors.joining("\n")); + new BufferedReader(new InputStreamReader(process.getInputStream()))) { output = reader.lines().collect(Collectors.joining("\n")); } - LOGGER.warning(errors); LOGGER.info(output); int res = process.waitFor(); - assertEquals(errors, 0, res); + assertEquals(output, 0, res); } private static int getJavaMajorVersion() { diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadataTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadataTest.java index e3e7cddbdd..1d1efa2918 100644 --- a/src/test/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadataTest.java +++ b/src/test/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadataTest.java @@ -669,7 +669,7 @@ public void testUseClientCertParameters() { } @Test - public void testExternalHostConfigurations() { + public void testExperimentalHostConfigurations() { assertEquals( DatabaseId.of("default", "default", "test_db"), (new OptionsMetadata( @@ -721,4 +721,58 @@ public void testExternalHostConfigurations() { .build() .getDefaultDatabaseId()); } + + @Test + public void testSpannerOmniConfigurations() { + assertEquals( + DatabaseId.of("default", "default", "test_db"), + (new OptionsMetadata( + new String[] {"-d", "test_db", "-e", "localhost:8000", "-r", "type=omni"})) + .getDefaultDatabaseId()); + SpannerException spannerException = + assertThrows( + SpannerException.class, + () -> + new OptionsMetadata( + new String[] { + "-d", "test_db", "-e", "spanner.googleapis.com:443", "-c", "credentials.json" + })); + assertEquals(ErrorCode.INVALID_ARGUMENT, spannerException.getErrorCode()); + assertEquals( + DatabaseId.of("default", "default", "test_db"), + OptionsMetadata.newBuilder() + .setEndpoint("localhost:8000") + .setType("OMNI") + .setDatabase("test_db") + .build() + .getDefaultDatabaseId()); + spannerException = + assertThrows( + SpannerException.class, + () -> + OptionsMetadata.newBuilder() + .setEndpoint("spanner.googleapis.com") + .setDatabase("test_db") + .build()); + assertEquals(ErrorCode.INVALID_ARGUMENT, spannerException.getErrorCode()); + spannerException = + assertThrows( + SpannerException.class, + () -> + OptionsMetadata.newBuilder() + .setEnvironment(ImmutableMap.of("SPANNER_EMULATOR_HOST", "localhost:9010")) + .setEndpoint("localhost:8000") + .setDatabase("test_db") + .build()); + assertEquals(ErrorCode.INVALID_ARGUMENT, spannerException.getErrorCode()); + assertEquals( + DatabaseId.of("default", "default", "test_db"), + OptionsMetadata.newBuilder() + .setEnvironment(ImmutableMap.of("SPANNER_EMULATOR_HOST", "localhost:9010")) + .setEndpoint("localhost:8000") + .setType("OMNI") + .setDatabase("test_db") + .build() + .getDefaultDatabaseId()); + } } From 47339c527df16c7b533668c53ee0a338a41d9702 Mon Sep 17 00:00:00 2001 From: sagnghos Date: Wed, 1 Jul 2026 10:34:37 +0000 Subject: [PATCH 2/3] test: Add TLS and plain-text configuration support for Spanner Omni in PgAdapterTestEnv - Dynamically handle USE_PLAIN_TEXT to toggle http vs https. - Add support for injecting SPANNER_OMNI_CLIENT_CERT and SPANNER_OMNI_CLIENT_KEY properties to the client configuration. --- .../cloud/spanner/pgadapter/PgAdapterTestEnv.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/PgAdapterTestEnv.java b/src/test/java/com/google/cloud/spanner/pgadapter/PgAdapterTestEnv.java index 994216ed64..3d175ada02 100644 --- a/src/test/java/com/google/cloud/spanner/pgadapter/PgAdapterTestEnv.java +++ b/src/test/java/com/google/cloud/spanner/pgadapter/PgAdapterTestEnv.java @@ -17,6 +17,7 @@ import static org.junit.Assert.assertEquals; import com.google.api.gax.longrunning.OperationFuture; +import com.google.auth.Credentials; import com.google.auth.oauth2.GoogleCredentials; import com.google.cloud.NoCredentials; import com.google.cloud.spanner.Database; @@ -554,7 +555,7 @@ private SpannerOptions createSpannerOptions() { Map env = System.getenv(); gcpCredentials = env.get(GCP_CREDENTIALS); - GoogleCredentials credentials = null; + Credentials credentials = null; if (!Strings.isNullOrEmpty(gcpCredentials) && SPANNER_OMNI_HOST == null) { try { credentials = GoogleCredentials.fromStream(new FileInputStream(gcpCredentials)); @@ -591,18 +592,16 @@ private SpannerOptions createSpannerOptions() { } if (SPANNER_OMNI_HOST != null) { - if (!SPANNER_OMNI_HOST.startsWith("http")) { - builder.setHost("http://" + SPANNER_OMNI_HOST).setType(SpannerOptions.InstanceType.OMNI); - } else { - builder.setHost(SPANNER_OMNI_HOST).setType(SpannerOptions.InstanceType.OMNI); - } + builder + .setHost((USE_PLAIN_TEXT ? "http://" : "https://") + SPANNER_OMNI_HOST) + .setType(SpannerOptions.InstanceType.OMNI); if (USE_PLAIN_TEXT) { builder.usePlainText(); } if (SPANNER_OMNI_CLIENT_CERT != null && SPANNER_OMNI_CLIENT_KEY != null) { builder.useClientCert(SPANNER_OMNI_CLIENT_CERT, SPANNER_OMNI_CLIENT_KEY); } - credentials = null; + credentials = NoCredentials.getInstance(); } if (System.getProperty(CHANNEL_PROVIDER_PROPERTY) == null && credentials != null) { From b09608196aa16029d15e13c984115bef650e6ac7 Mon Sep 17 00:00:00 2001 From: sagnghos Date: Thu, 2 Jul 2026 16:43:00 +0000 Subject: [PATCH 3/3] fix: use case-insensitive propertyMap for JDBC connection string parameters - Fixes bug where 'Type=omni' or other mixed-cased keys were ignored. - Replace HashMap with TreeMap(String.CASE_INSENSITIVE_ORDER) when parsing JDBC properties. - Remove deprecated setExperimentalHost. --- .../pgadapter/metadata/OptionsMetadata.java | 21 +++---------------- .../metadata/OptionsMetadataTest.java | 18 ++++++++++++++-- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadata.java b/src/main/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadata.java index 196bee9315..99dc9929a1 100644 --- a/src/main/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadata.java +++ b/src/main/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadata.java @@ -33,14 +33,13 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.spanner.v1.DatabaseName; -import io.grpc.ExperimentalApi; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.time.Duration; -import java.util.HashMap; import java.util.Map; +import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Nullable; @@ -448,20 +447,6 @@ Builder useClientCert(String clientCertificate, String clientKey) { return this; } - /** - * Configures connection to an experimental host endpoint - * - * @param experimentalHost url of the experimental host endpoint - * @deprecated Use {@link #setType(String)} and {@link #setEndpoint(String)} instead. - */ - @ExperimentalApi("https://github.com/googleapis/java-spanner/pull/3574") - @Deprecated - Builder setExperimentalHost(String experimentalHost) { - setEndpoint(experimentalHost); - setType(SPANNER_OMNI_TYPE); - return this; - } - /** * Configures the connection instance type. * @@ -947,7 +932,7 @@ public OptionsMetadata( this.ddlTransactionMode = DdlTransactionMode.AutocommitImplicitTransaction; this.replaceJdbcMetadataQueries = replaceJdbcMetadataQueries; this.commandMetadataJSON = commandMetadata; - this.propertyMap = new HashMap<>(); + this.propertyMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); this.disableLocalhostCheck = false; this.serverVersion = DEFAULT_SERVER_VERSION; this.debugMode = false; @@ -958,7 +943,7 @@ public OptionsMetadata( } private Map parseProperties(String propertyOptions) { - Map properties = new HashMap<>(); + Map properties = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); if (!propertyOptions.isEmpty()) { String[] propertyList = propertyOptions.split(";"); for (int i = 0; i < propertyList.length; ++i) { diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadataTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadataTest.java index 1d1efa2918..270fba2830 100644 --- a/src/test/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadataTest.java +++ b/src/test/java/com/google/cloud/spanner/pgadapter/metadata/OptionsMetadataTest.java @@ -689,7 +689,8 @@ public void testExperimentalHostConfigurations() { assertEquals( DatabaseId.of("default", "default", "test_db"), OptionsMetadata.newBuilder() - .setExperimentalHost("localhost:8000") + .setEndpoint("localhost:8000") + .setType("omni") .setDatabase("test_db") .build() .getDefaultDatabaseId()); @@ -716,7 +717,8 @@ public void testExperimentalHostConfigurations() { DatabaseId.of("default", "default", "test_db"), OptionsMetadata.newBuilder() .setEnvironment(ImmutableMap.of("SPANNER_EMULATOR_HOST", "localhost:9010")) - .setExperimentalHost("localhost:8000") + .setEndpoint("localhost:8000") + .setType("omni") .setDatabase("test_db") .build() .getDefaultDatabaseId()); @@ -729,6 +731,18 @@ public void testSpannerOmniConfigurations() { (new OptionsMetadata( new String[] {"-d", "test_db", "-e", "localhost:8000", "-r", "type=omni"})) .getDefaultDatabaseId()); + assertEquals( + DatabaseId.of("default", "default", "test_db"), + (new OptionsMetadata( + new String[] {"-d", "test_db", "-e", "localhost:8000", "-r", "Type=OMNI"})) + .getDefaultDatabaseId()); + assertEquals( + DatabaseId.of("default", "default", "test_db"), + (new OptionsMetadata( + new String[] { + "-d", "test_db", "-e", "localhost:8000", "-r", "IsExperimentalHost=true" + })) + .getDefaultDatabaseId()); SpannerException spannerException = assertThrows( SpannerException.class,