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

com.google.cloud.spanner.jdbc.AbstractJdbcConnection 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 com.google.cloud.spanner.Dialect;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.connection.AbstractStatementParser;
import com.google.cloud.spanner.connection.ConnectionOptions;
import com.google.common.annotations.VisibleForTesting;
import com.google.rpc.Code;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Struct;
import java.util.Properties;
import java.util.concurrent.Executor;

/** Base class for Cloud Spanner JDBC connections. */
abstract class AbstractJdbcConnection extends AbstractJdbcWrapper
    implements CloudSpannerJdbcConnection {
  private static final String CALLABLE_STATEMENTS_UNSUPPORTED =
      "Callable statements are not supported";
  private static final String ONLY_SERIALIZABLE =
      "Only isolation level TRANSACTION_SERIALIZABLE is supported";
  private static final String ONLY_CLOSE_ALLOWED =
      "Only holdability CLOSE_CURSORS_AT_COMMIT is supported";
  private static final String SQLXML_UNSUPPORTED = "SQLXML is not supported";
  private static final String STRUCTS_UNSUPPORTED = "Structs are not supported";
  private static final String ABORT_UNSUPPORTED = "Abort is not supported";
  private static final String NETWORK_TIMEOUT_UNSUPPORTED = "Network timeout is not supported";
  static final String CLIENT_INFO_NOT_SUPPORTED =
      "Cloud Spanner does not support ClientInfo property %s";

  private final String connectionUrl;
  private final ConnectionOptions options;
  private final com.google.cloud.spanner.connection.Connection spanner;
  private final Properties clientInfo;
  private AbstractStatementParser parser;

  private SQLWarning firstWarning = null;
  private SQLWarning lastWarning = null;

  AbstractJdbcConnection(String connectionUrl, ConnectionOptions options) throws SQLException {
    this.connectionUrl = connectionUrl;
    this.options = options;
    this.spanner = options.getConnection();
    this.clientInfo = new Properties(JdbcDatabaseMetaData.getDefaultClientInfoProperties());
  }

  /** Return the corresponding {@link com.google.cloud.spanner.connection.Connection} */
  com.google.cloud.spanner.connection.Connection getSpannerConnection() {
    return spanner;
  }

  @Override
  public String getConnectionUrl() {
    return connectionUrl;
  }

  ConnectionOptions getConnectionOptions() {
    return options;
  }

  Spanner getSpanner() {
    return this.spanner.getSpanner();
  }

  @Override
  public Dialect getDialect() {
    return spanner.getDialect();
  }

  protected AbstractStatementParser getParser() throws SQLException {
    if (parser == null) {
      try {
        parser = AbstractStatementParser.getInstance(spanner.getDialect());
      } catch (SpannerException e) {
        throw JdbcSqlExceptionFactory.of(e);
      }
    }
    return parser;
  }

  @Override
  public CallableStatement prepareCall(String sql) throws SQLException {
    return checkClosedAndThrowUnsupported(CALLABLE_STATEMENTS_UNSUPPORTED);
  }

  @Override
  public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
      throws SQLException {
    return checkClosedAndThrowUnsupported(CALLABLE_STATEMENTS_UNSUPPORTED);
  }

  @Override
  public CallableStatement prepareCall(
      String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
      throws SQLException {
    return checkClosedAndThrowUnsupported(CALLABLE_STATEMENTS_UNSUPPORTED);
  }

  @Override
  public void setTransactionIsolation(int level) throws SQLException {
    checkClosed();
    JdbcPreconditions.checkArgument(
        level == TRANSACTION_SERIALIZABLE
            || level == TRANSACTION_REPEATABLE_READ
            || level == TRANSACTION_READ_UNCOMMITTED
            || level == TRANSACTION_READ_COMMITTED,
        "Not a transaction isolation level");
    JdbcPreconditions.checkSqlFeatureSupported(
        level == TRANSACTION_SERIALIZABLE, ONLY_SERIALIZABLE);
  }

  @Override
  public int getTransactionIsolation() throws SQLException {
    checkClosed();
    return TRANSACTION_SERIALIZABLE;
  }

  @Override
  public void setHoldability(int holdability) throws SQLException {
    checkClosed();
    JdbcPreconditions.checkArgument(
        holdability == ResultSet.HOLD_CURSORS_OVER_COMMIT
            || holdability == ResultSet.CLOSE_CURSORS_AT_COMMIT,
        "Not a holdability value");
    JdbcPreconditions.checkSqlFeatureSupported(
        holdability == ResultSet.CLOSE_CURSORS_AT_COMMIT, ONLY_CLOSE_ALLOWED);
  }

  @Override
  public int getHoldability() throws SQLException {
    checkClosed();
    return ResultSet.CLOSE_CURSORS_AT_COMMIT;
  }

  @Override
  public SQLWarning getWarnings() throws SQLException {
    checkClosed();
    return firstWarning;
  }

  @Override
  public void clearWarnings() throws SQLException {
    checkClosed();
    firstWarning = null;
    lastWarning = null;
  }

  @Override
  public SQLXML createSQLXML() throws SQLException {
    return checkClosedAndThrowUnsupported(SQLXML_UNSUPPORTED);
  }

  @Override
  public void setClientInfo(String name, String value) throws SQLClientInfoException {
    Properties supported;
    try {
      checkClosed();
      supported = JdbcDatabaseMetaData.getDefaultClientInfoProperties();
    } catch (SQLException e) {
      if (e instanceof JdbcSqlException) {
        throw JdbcSqlExceptionFactory.clientInfoException(
            e.getMessage(), ((JdbcSqlException) e).getCode());
      } else {
        throw JdbcSqlExceptionFactory.clientInfoException(e.getMessage(), Code.UNKNOWN);
      }
    }
    if (value == null) {
      throw JdbcSqlExceptionFactory.clientInfoException(
          "Null-value is not allowed for client info.", Code.INVALID_ARGUMENT);
    }
    if (value.length() > JdbcDatabaseMetaData.MAX_CLIENT_INFO_VALUE_LENGTH) {
      throw JdbcSqlExceptionFactory.clientInfoException(
          String.format(
              "Max length of value is %d characters.",
              JdbcDatabaseMetaData.MAX_CLIENT_INFO_VALUE_LENGTH),
          Code.INVALID_ARGUMENT);
    }
    name = name.toUpperCase();
    if (supported.containsKey(name)) {
      clientInfo.setProperty(name, value);
    } else {
      pushWarning(new SQLWarning(String.format(CLIENT_INFO_NOT_SUPPORTED, name)));
    }
  }

  @Override
  public void setClientInfo(Properties properties) throws SQLClientInfoException {
    try {
      checkClosed();
    } catch (SQLException e) {
      if (e instanceof JdbcSqlException) {
        throw JdbcSqlExceptionFactory.clientInfoException(
            e.getMessage(), ((JdbcSqlException) e).getCode());
      } else {
        throw JdbcSqlExceptionFactory.clientInfoException(e.getMessage(), Code.UNKNOWN);
      }
    }
    clientInfo.clear();
    for (String property : properties.stringPropertyNames()) {
      setClientInfo(property, properties.getProperty(property));
    }
  }

  @Override
  public String getClientInfo(String name) throws SQLException {
    checkClosed();
    return clientInfo.getProperty(name.toUpperCase());
  }

  @Override
  public Properties getClientInfo() throws SQLException {
    checkClosed();
    return (Properties) clientInfo.clone();
  }

  @Override
  public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
    return checkClosedAndThrowUnsupported(STRUCTS_UNSUPPORTED);
  }

  @Override
  public void abort(Executor executor) throws SQLException {
    checkClosedAndThrowUnsupported(ABORT_UNSUPPORTED);
  }

  @Override
  public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
    checkClosedAndThrowUnsupported(NETWORK_TIMEOUT_UNSUPPORTED);
  }

  @Override
  public int getNetworkTimeout() throws SQLException {
    return checkClosedAndThrowUnsupported(NETWORK_TIMEOUT_UNSUPPORTED);
  }

  @VisibleForTesting
  void pushWarning(SQLWarning warning) {
    if (lastWarning == null) {
      firstWarning = warning;
    } else {
      lastWarning.setNextWarning(warning);
    }
    lastWarning = warning;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy