All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.cloud.spanner.jdbc.JdbcConnection Maven / Gradle / Ivy

There is a newer version: 2.25.2
Show newest version
/*
 * Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.cloud.spanner.jdbc;

import static com.google.cloud.spanner.jdbc.JdbcStatement.ALL_COLUMNS;
import static com.google.cloud.spanner.jdbc.JdbcStatement.isNullOrEmpty;

import com.google.api.client.util.Preconditions;
import com.google.cloud.ByteArray;
import com.google.cloud.spanner.CommitResponse;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Mutation;
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.TimestampBound;
import com.google.cloud.spanner.connection.AutocommitDmlMode;
import com.google.cloud.spanner.connection.Connection;
import com.google.cloud.spanner.connection.ConnectionOptions;
import com.google.cloud.spanner.connection.SavepointSupport;
import com.google.cloud.spanner.connection.TransactionMode;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import java.util.function.BiConsumer;
import java.util.function.Function;
import javax.annotation.Nonnull;

/** Jdbc Connection class for Google Cloud Spanner */
class JdbcConnection extends AbstractJdbcConnection {
  private static final String ONLY_RS_FORWARD_ONLY =
      "Only result sets of type TYPE_FORWARD_ONLY are supported";
  private static final String ONLY_CONCUR_READ_ONLY =
      "Only result sets with concurrency CONCUR_READ_ONLY are supported";
  private static final String ONLY_CLOSE_CURSORS_AT_COMMIT =
      "Only result sets with holdability CLOSE_CURSORS_AT_COMMIT are supported";

  /**
   * This query is used to check the aliveness of the connection if legacy alive check has been
   * enabled. As Cloud Spanner JDBC connections do not maintain a physical or logical connection to
   * Cloud Spanner, there is also no point in repeatedly executing a simple query to check whether a
   * connection is alive. Instead, we rely on the result from the initial query to Spanner that
   * determines the dialect to determine whether the connection is alive or not. This result is
   * cached for all JDBC connections using the same {@link com.google.cloud.spanner.Spanner}
   * instance.
   *
   * 

The legacy {@link #isValid(int)} check using a SELECT 1 statement can be enabled by setting * the System property spanner.jdbc.use_legacy_is_valid_check to true or setting the environment * variable SPANNER_JDBC_USE_LEGACY_IS_VALID_CHECK to true. */ static final String LEGACY_IS_VALID_QUERY = "SELECT 1"; static final ImmutableList NO_GENERATED_KEY_COLUMNS = ImmutableList.of(); private Map> typeMap = new HashMap<>(); private final boolean useLegacyIsValidCheck; private final Metrics metrics; private final Attributes openTelemetryMetricsAttributes; JdbcConnection(String connectionUrl, ConnectionOptions options) throws SQLException { super(connectionUrl, options); this.useLegacyIsValidCheck = useLegacyValidCheck(); OpenTelemetry openTelemetry = getSpanner().getOptions().getOpenTelemetry(); this.openTelemetryMetricsAttributes = createOpenTelemetryAttributes(getConnectionOptions().getDatabaseId(), false); this.metrics = new Metrics(openTelemetry); } static boolean useLegacyValidCheck() { String value = System.getProperty("spanner.jdbc.use_legacy_is_valid_check"); if (Strings.isNullOrEmpty(value)) { value = System.getenv("SPANNER_JDBC_USE_LEGACY_IS_VALID_CHECK"); } if (!Strings.isNullOrEmpty(value)) { return Boolean.parseBoolean(value); } return false; } @VisibleForTesting static Attributes createOpenTelemetryAttributes( DatabaseId databaseId, boolean includeConnectionId) { AttributesBuilder attributesBuilder = Attributes.builder(); // A unique connection ID should only be included for tracing and not for metrics. if (includeConnectionId) { attributesBuilder.put("connection_id", UUID.randomUUID().toString()); } attributesBuilder.put("database", databaseId.getDatabase()); attributesBuilder.put("instance_id", databaseId.getInstanceId().getInstance()); attributesBuilder.put("project_id", databaseId.getInstanceId().getProject()); return attributesBuilder.build(); } public void recordClientLibLatencyMetric(long value) { metrics.recordClientLibLatency(value, openTelemetryMetricsAttributes); } @Override public Statement createStatement() throws SQLException { checkClosed(); return new JdbcStatement(this); } @Override public JdbcPreparedStatement prepareStatement(String sql) throws SQLException { return prepareStatement(sql, NO_GENERATED_KEY_COLUMNS); } private JdbcPreparedStatement prepareStatement( String sql, ImmutableList generatedKeyColumns) throws SQLException { checkClosed(); return new JdbcPreparedStatement(this, sql, generatedKeyColumns); } @Override public String nativeSQL(String sql) throws SQLException { checkClosed(); return getParser() .convertPositionalParametersToNamedParameters('?', getParser().removeCommentsAndTrim(sql)) .sqlWithNamedParameters; } @Override public String getStatementTag() throws SQLException { checkClosed(); return getSpannerConnection().getStatementTag(); } @Override public void setStatementTag(String tag) throws SQLException { checkClosed(); try { getSpannerConnection().setStatementTag(tag); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public String getTransactionTag() throws SQLException { checkClosed(); return getSpannerConnection().getTransactionTag(); } @Override public void setTransactionTag(String tag) throws SQLException { checkClosed(); try { getSpannerConnection().setTransactionTag(tag); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public void setTransactionMode(TransactionMode mode) throws SQLException { checkClosed(); getSpannerConnection().setTransactionMode(mode); } @Override public TransactionMode getTransactionMode() throws SQLException { checkClosed(); return getSpannerConnection().getTransactionMode(); } @Override public void setAutocommitDmlMode(AutocommitDmlMode mode) throws SQLException { checkClosed(); getSpannerConnection().setAutocommitDmlMode(mode); } @Override public AutocommitDmlMode getAutocommitDmlMode() throws SQLException { checkClosed(); return getSpannerConnection().getAutocommitDmlMode(); } @Override public void setReadOnlyStaleness(TimestampBound staleness) throws SQLException { checkClosed(); getSpannerConnection().setReadOnlyStaleness(staleness); } @Override public TimestampBound getReadOnlyStaleness() throws SQLException { checkClosed(); return getSpannerConnection().getReadOnlyStaleness(); } @Override public void setOptimizerVersion(String optimizerVersion) throws SQLException { checkClosed(); getSpannerConnection().setOptimizerVersion(optimizerVersion); } @Override public String getOptimizerVersion() throws SQLException { checkClosed(); return getSpannerConnection().getOptimizerVersion(); } @Override public boolean isInTransaction() throws SQLException { checkClosed(); return getSpannerConnection().isInTransaction(); } @Override public boolean isTransactionStarted() throws SQLException { checkClosed(); return getSpannerConnection().isTransactionStarted(); } @Override public void setAutoCommit(boolean autoCommit) throws SQLException { checkClosed(); try { // According to the JDBC spec's we need to commit the current transaction when changing // autocommit mode. if (getSpannerConnection().isAutocommit() != autoCommit && getSpannerConnection().isTransactionStarted()) { commit(); } getSpannerConnection().setAutocommit(autoCommit); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public boolean getAutoCommit() throws SQLException { checkClosed(); return getSpannerConnection().isAutocommit(); } @Override public void commit() throws SQLException { checkClosed(); try { getSpannerConnection().commit(); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public void rollback() throws SQLException { checkClosed(); try { getSpannerConnection().rollback(); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public void close() throws SQLException { try { getSpannerConnection().close(); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public boolean isClosed() { return getSpannerConnection().isClosed(); } @Override public DatabaseMetaData getMetaData() throws SQLException { checkClosed(); return new JdbcDatabaseMetaData(this); } @Override public void setReadOnly(boolean readOnly) throws SQLException { checkClosed(); try { getSpannerConnection().setReadOnly(readOnly); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public boolean isReadOnly() throws SQLException { checkClosed(); return getSpannerConnection().isReadOnly(); } @Override public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { checkClosed(); JdbcPreconditions.checkSqlFeatureSupported( resultSetType == ResultSet.TYPE_FORWARD_ONLY, ONLY_RS_FORWARD_ONLY); JdbcPreconditions.checkSqlFeatureSupported( resultSetConcurrency == ResultSet.CONCUR_READ_ONLY, ONLY_CONCUR_READ_ONLY); return createStatement(); } @Override public Statement createStatement( int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { checkClosed(); JdbcPreconditions.checkSqlFeatureSupported( resultSetType == ResultSet.TYPE_FORWARD_ONLY, ONLY_RS_FORWARD_ONLY); JdbcPreconditions.checkSqlFeatureSupported( resultSetConcurrency == ResultSet.CONCUR_READ_ONLY, ONLY_CONCUR_READ_ONLY); JdbcPreconditions.checkSqlFeatureSupported( resultSetHoldability == ResultSet.CLOSE_CURSORS_AT_COMMIT, ONLY_CLOSE_CURSORS_AT_COMMIT); return createStatement(); } @Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { checkClosed(); JdbcPreconditions.checkSqlFeatureSupported( resultSetType == ResultSet.TYPE_FORWARD_ONLY, ONLY_RS_FORWARD_ONLY); JdbcPreconditions.checkSqlFeatureSupported( resultSetConcurrency == ResultSet.CONCUR_READ_ONLY, ONLY_CONCUR_READ_ONLY); return prepareStatement(sql); } @Override public PreparedStatement prepareStatement( String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { checkClosed(); JdbcPreconditions.checkSqlFeatureSupported( resultSetType == ResultSet.TYPE_FORWARD_ONLY, ONLY_RS_FORWARD_ONLY); JdbcPreconditions.checkSqlFeatureSupported( resultSetConcurrency == ResultSet.CONCUR_READ_ONLY, ONLY_CONCUR_READ_ONLY); JdbcPreconditions.checkSqlFeatureSupported( resultSetHoldability == ResultSet.CLOSE_CURSORS_AT_COMMIT, ONLY_CLOSE_CURSORS_AT_COMMIT); return prepareStatement(sql); } @Override public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { return prepareStatement( sql, autoGeneratedKeys == Statement.RETURN_GENERATED_KEYS ? ALL_COLUMNS : NO_GENERATED_KEY_COLUMNS); } @Override public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { // This should preferably have returned an error, but the initial version of the driver just // accepted and ignored this. Starting to throw an error now would be a breaking change. // TODO: Consider throwing an Unsupported error for the next major version bump. return prepareStatement(sql, NO_GENERATED_KEY_COLUMNS); } @Override public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { return prepareStatement( sql, isNullOrEmpty(columnNames) ? NO_GENERATED_KEY_COLUMNS : ImmutableList.copyOf(columnNames)); } @Override public Map> getTypeMap() throws SQLException { checkClosed(); return new HashMap<>(typeMap); } @Override public void setTypeMap(Map> map) throws SQLException { checkClosed(); this.typeMap = new HashMap<>(map); } boolean isUseLegacyIsValidCheck() { return useLegacyIsValidCheck; } @Override public boolean isValid(int timeout) throws SQLException { JdbcPreconditions.checkArgument(timeout >= 0, "timeout must be >= 0"); if (!isClosed()) { if (isUseLegacyIsValidCheck()) { return legacyIsValid(timeout); } try { return getDialect() != null; } catch (Exception ignore) { // ignore and fall through. } } return false; } private boolean legacyIsValid(int timeout) throws SQLException { try (Statement statement = createStatement()) { statement.setQueryTimeout(timeout); try (ResultSet rs = statement.executeQuery(LEGACY_IS_VALID_QUERY)) { if (rs.next()) { if (rs.getLong(1) == 1L) { return true; } } } } catch (SQLException e) { // ignore and fall through. } return false; } @Override public Blob createBlob() throws SQLException { checkClosed(); return new JdbcBlob(); } @Override public Clob createClob() throws SQLException { checkClosed(); return new JdbcClob(); } @Override public NClob createNClob() throws SQLException { checkClosed(); return new JdbcClob(); } @Override public Array createArrayOf(String typeName, Object[] elements) throws SQLException { checkClosed(); return JdbcArray.createArray(typeName, elements); } @Override public void setCatalog(String catalog) throws SQLException { // This method could be changed to allow the user to change to another database. // For now, we only support setting the default catalog in order to support frameworks // and applications that set this when no catalog has been specified in the connection // URL. checkClosed(); checkValidCatalog(catalog); } void checkValidCatalog(String catalog) throws SQLException { String defaultCatalog = getDefaultCatalog(); JdbcPreconditions.checkArgument( defaultCatalog.equals(catalog), String.format("Only catalog %s is supported", defaultCatalog)); } @Override public String getCatalog() throws SQLException { checkClosed(); return getDefaultCatalog(); } @Nonnull String getDefaultCatalog() { switch (getDialect()) { case POSTGRESQL: String database = getConnectionOptions().getDatabaseName(); // It should not be possible that database is null, but it's better to be safe than sorry. return database == null ? "" : database; case GOOGLE_STANDARD_SQL: default: return ""; } } @Override public void setSchema(String schema) throws SQLException { checkClosed(); checkValidSchema(schema); } void checkValidSchema(String schema) throws SQLException { String defaultSchema = getDefaultSchema(); JdbcPreconditions.checkArgument( defaultSchema.equals(schema), String.format("Only schema %s is supported", defaultSchema)); } @Override public String getSchema() throws SQLException { checkClosed(); return getDefaultSchema(); } @Nonnull String getDefaultSchema() { return getDialect().getDefaultSchema(); } @Override public SavepointSupport getSavepointSupport() throws SQLException { checkClosed(); return getSpannerConnection().getSavepointSupport(); } @Override public void setSavepointSupport(SavepointSupport savepointSupport) throws SQLException { checkClosed(); try { getSpannerConnection().setSavepointSupport(savepointSupport); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public Savepoint setSavepoint() throws SQLException { checkClosed(); try { JdbcSavepoint savepoint = JdbcSavepoint.unnamed(); getSpannerConnection().savepoint(savepoint.internalGetSavepointName()); return savepoint; } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public Savepoint setSavepoint(String name) throws SQLException { checkClosed(); try { JdbcSavepoint savepoint = JdbcSavepoint.named(name); getSpannerConnection().savepoint(savepoint.internalGetSavepointName()); return savepoint; } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public void rollback(Savepoint savepoint) throws SQLException { checkClosed(); JdbcPreconditions.checkArgument(savepoint instanceof JdbcSavepoint, savepoint); JdbcSavepoint jdbcSavepoint = (JdbcSavepoint) savepoint; try { getSpannerConnection().rollbackToSavepoint(jdbcSavepoint.internalGetSavepointName()); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public void releaseSavepoint(Savepoint savepoint) throws SQLException { checkClosed(); JdbcPreconditions.checkArgument(savepoint instanceof JdbcSavepoint, savepoint); JdbcSavepoint jdbcSavepoint = (JdbcSavepoint) savepoint; try { getSpannerConnection().releaseSavepoint(jdbcSavepoint.internalGetSavepointName()); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public Timestamp getCommitTimestamp() throws SQLException { checkClosed(); try { return getSpannerConnection().getCommitTimestamp().toSqlTimestamp(); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public CommitResponse getCommitResponse() throws SQLException { checkClosed(); try { return getSpannerConnection().getCommitResponse(); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public void setReturnCommitStats(boolean returnCommitStats) throws SQLException { checkClosed(); try { getSpannerConnection().setReturnCommitStats(returnCommitStats); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public boolean isReturnCommitStats() throws SQLException { checkClosed(); try { return getSpannerConnection().isReturnCommitStats(); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public Timestamp getReadTimestamp() throws SQLException { checkClosed(); try { return getSpannerConnection().getReadTimestamp().toSqlTimestamp(); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public boolean isRetryAbortsInternally() throws SQLException { checkClosed(); try { return getSpannerConnection().isRetryAbortsInternally(); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public void setRetryAbortsInternally(boolean retryAbortsInternally) throws SQLException { checkClosed(); try { getSpannerConnection().setRetryAbortsInternally(retryAbortsInternally); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public void write(Mutation mutation) throws SQLException { checkClosed(); try { getSpannerConnection().write(mutation); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public void write(Iterable mutations) throws SQLException { checkClosed(); try { getSpannerConnection().write(mutations); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public void bufferedWrite(Mutation mutation) throws SQLException { checkClosed(); try { getSpannerConnection().bufferedWrite(mutation); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public void bufferedWrite(Iterable mutations) throws SQLException { checkClosed(); try { getSpannerConnection().bufferedWrite(mutations); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } /** * Convenience method for calling a setter and translating any {@link SpannerException} to a * {@link SQLException}. */ private void set(BiConsumer setter, T value) throws SQLException { checkClosed(); try { setter.accept(getSpannerConnection(), value); } catch (SpannerException spannerException) { throw JdbcSqlExceptionFactory.of(spannerException); } } /** * Convenience method for calling a getter and translating any {@link SpannerException} to a * {@link SQLException}. */ private R get(Function getter) throws SQLException { checkClosed(); try { return getter.apply(getSpannerConnection()); } catch (SpannerException spannerException) { throw JdbcSqlExceptionFactory.of(spannerException); } } @Override public void setDataBoostEnabled(boolean dataBoostEnabled) throws SQLException { set(Connection::setDataBoostEnabled, dataBoostEnabled); } @Override public boolean isDataBoostEnabled() throws SQLException { return get(Connection::isDataBoostEnabled); } @Override public void setAutoPartitionMode(boolean autoPartitionMode) throws SQLException { set(Connection::setAutoPartitionMode, autoPartitionMode); } @Override public boolean isAutoPartitionMode() throws SQLException { return get(Connection::isAutoPartitionMode); } @Override public void setMaxPartitions(int maxPartitions) throws SQLException { set(Connection::setMaxPartitions, maxPartitions); } @Override public int getMaxPartitions() throws SQLException { return get(Connection::getMaxPartitions); } @Override public void setMaxPartitionedParallelism(int maxThreads) throws SQLException { set(Connection::setMaxPartitionedParallelism, maxThreads); } @Override public int getMaxPartitionedParallelism() throws SQLException { return get(Connection::getMaxPartitionedParallelism); } @Override public void setAutoBatchDml(boolean autoBatchDml) throws SQLException { set(Connection::setAutoBatchDml, autoBatchDml); } @Override public boolean isAutoBatchDml() throws SQLException { return get(Connection::isAutoBatchDml); } @Override public void setAutoBatchDmlUpdateCount(long updateCount) throws SQLException { set(Connection::setAutoBatchDmlUpdateCount, updateCount); } @Override public long getAutoBatchDmlUpdateCount() throws SQLException { return get(Connection::getAutoBatchDmlUpdateCount); } @Override public void setAutoBatchDmlUpdateCountVerification(boolean verification) throws SQLException { set(Connection::setAutoBatchDmlUpdateCountVerification, verification); } @Override public boolean isAutoBatchDmlUpdateCountVerification() throws SQLException { return get(Connection::isAutoBatchDmlUpdateCountVerification); } @SuppressWarnings("deprecation") private static final class JdbcToSpannerTransactionRetryListener implements com.google.cloud.spanner.connection.TransactionRetryListener { private final TransactionRetryListener delegate; JdbcToSpannerTransactionRetryListener(TransactionRetryListener delegate) { this.delegate = Preconditions.checkNotNull(delegate); } @Override public void retryStarting( com.google.cloud.Timestamp transactionStarted, long transactionId, int retryAttempt) { delegate.retryStarting(transactionStarted, transactionId, retryAttempt); } @Override public void retryFinished( com.google.cloud.Timestamp transactionStarted, long transactionId, int retryAttempt, RetryResult result) { delegate.retryFinished( transactionStarted, transactionId, retryAttempt, TransactionRetryListener.RetryResult.valueOf(result.name())); } @Override public boolean equals(Object o) { if (!(o instanceof JdbcToSpannerTransactionRetryListener)) { return false; } JdbcToSpannerTransactionRetryListener other = (JdbcToSpannerTransactionRetryListener) o; return this.delegate.equals(other.delegate); } @Override public int hashCode() { return delegate.hashCode(); } } @SuppressWarnings("deprecation") @Override public void addTransactionRetryListener(TransactionRetryListener listener) throws SQLException { checkClosed(); getSpannerConnection() .addTransactionRetryListener(new JdbcToSpannerTransactionRetryListener(listener)); } @Override public void addTransactionRetryListener( com.google.cloud.spanner.connection.TransactionRetryListener listener) throws SQLException { checkClosed(); getSpannerConnection().addTransactionRetryListener(listener); } @SuppressWarnings("deprecation") @Override public boolean removeTransactionRetryListener(TransactionRetryListener listener) throws SQLException { checkClosed(); return getSpannerConnection() .removeTransactionRetryListener(new JdbcToSpannerTransactionRetryListener(listener)); } @Override public boolean removeTransactionRetryListener( com.google.cloud.spanner.connection.TransactionRetryListener listener) throws SQLException { checkClosed(); return getSpannerConnection().removeTransactionRetryListener(listener); } @SuppressWarnings("deprecation") @Override public Iterator getTransactionRetryListeners() throws SQLException { checkClosed(); return Iterators.transform( getSpannerConnection().getTransactionRetryListeners(), input -> { if (input instanceof JdbcToSpannerTransactionRetryListener) { return ((JdbcToSpannerTransactionRetryListener) input).delegate; } return null; }); } @Override public Iterator getTransactionRetryListenersFromConnection() throws SQLException { checkClosed(); return getSpannerConnection().getTransactionRetryListeners(); } @Override public void setProtoDescriptors(@Nonnull byte[] protoDescriptors) throws SQLException { Preconditions.checkNotNull(protoDescriptors); checkClosed(); try { getSpannerConnection().setProtoDescriptors(protoDescriptors); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public void setProtoDescriptors(@Nonnull InputStream protoDescriptors) throws SQLException, IOException { Preconditions.checkNotNull(protoDescriptors); checkClosed(); try { getSpannerConnection() .setProtoDescriptors(ByteArray.copyFrom(protoDescriptors).toByteArray()); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } } @Override public byte[] getProtoDescriptors() throws SQLException { checkClosed(); return getSpannerConnection().getProtoDescriptors(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy