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

io.snappydata.thrift.internal.ClientConnection Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * 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. See accompanying
 * LICENSE file.
 */
/*
 * Changes for SnappyData data platform.
 *
 * Portions Copyright (c) 2017-2022 TIBCO Software Inc. All rights reserved.
 *
 * 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. See accompanying
 * LICENSE file.
 */

package io.snappydata.thrift.internal;

import java.io.PrintWriter;
import java.net.SocketException;
import java.sql.*;
import java.util.Collections;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;

import com.pivotal.gemfirexd.internal.shared.common.error.ExceptionSeverity;
import com.pivotal.gemfirexd.internal.shared.common.reference.SQLState;
import io.snappydata.thrift.*;
import io.snappydata.thrift.common.SocketTimeout;
import io.snappydata.thrift.common.ThriftExceptionUtil;
import io.snappydata.thrift.common.ThriftUtils;
import io.snappydata.thrift.internal.types.InternalSavepoint;
import org.apache.thrift.transport.TTransport;

/**
 * Implementation of JDBC {@link Connection} for the thrift JDBC driver.
 */
@SuppressWarnings("serial")
public final class ClientConnection extends ReentrantLock implements Connection {

  private final ClientService clientService;
  private final ClientPooledConnection clientServiceOwner;
  private volatile boolean isClosed;

  // records last transaction host
  // all operations fail in transactional mode, so no need to check for the host
  // in every operation under transactional context
  private HostConnection txHost;

  // connection properties
  private volatile int rsHoldability = DEFAULT_RS_HOLDABILITY;
  final EnumSet pendingTXFlags = EnumSet
      .noneOf(TransactionAttribute.class);
  StatementAttrs commonAttrs;
  private String lastCommitResults = "";

  private volatile SnappyExceptionData warnings;
  private int xaState;

  private ClientFinalizer finalizer;

  // defaults for connection properties
  static final int DEFAULT_RS_HOLDABILITY =
      snappydataConstants.DEFAULT_RESULTSET_HOLD_CURSORS_OVER_COMMIT
          ? ResultSet.HOLD_CURSORS_OVER_COMMIT : ResultSet.CLOSE_CURSORS_AT_COMMIT;

  private int generatedSavepointId;

  ClientConnection(ClientService service,
      ClientPooledConnection serviceOwner) throws SQLException {
    this.clientService = service;
    this.clientServiceOwner = serviceOwner;
    initTXHost(this.clientService);
    this.finalizer = serviceOwner != null ? null : new ClientFinalizer(this,
        this.clientService, snappydataConstants.BULK_CLOSE_CONNECTION);
    // don't need to call updateReferentData on finalizer for connection
    // since ClientFinalizer will extract the same from current host
    // information in ClientService for the special case of connection
  }

  public static ClientConnection create(String host, int port,
      Properties connProperties, PrintWriter logWriter) throws SQLException {
    return new ClientConnection(ClientService.create(
        host, port, false, connProperties, logWriter), null);
  }

  public final ClientService getClientService() {
    return this.clientService;
  }

  public final Map getConnectionProperties() {
    return this.clientService.connectionProps;
  }

  final ClientPooledConnection getOwnerPooledConnection() {
    return this.clientServiceOwner;
  }

  final void checkClosedConnection() throws SQLException {
    if (this.isClosed || this.clientService.isClosed()) {
      throw ThriftExceptionUtil.newSQLException(SQLState.NO_CURRENT_CONNECTION,
          null);
    }
  }

  final SQLException informListeners(SQLException sqle) {
    // report only fatal errors
    if (this.clientServiceOwner != null &&
        sqle.getErrorCode() >= ExceptionSeverity.SESSION_SEVERITY) {
      this.clientServiceOwner.onConnectionError(sqle);
    }
    return sqle;
  }

  final Map getPendingTXFlags() {
    if (this.pendingTXFlags.isEmpty()) {
      return null;
    } else {
      final EnumMap txFlags = ThriftUtils
          .newTransactionFlags();
      for (TransactionAttribute pendingFlag : this.pendingTXFlags) {
        // default value sent as false in call to isTXFlagSet does not matter
        // since the flag is guaranteed to be set (to true or false) in any case
        txFlags.put(pendingFlag, this.clientService.isTXFlagSet(
            pendingFlag, false));
      }
      return txFlags;
    }
  }

  /** a set of attributes shared by all statements of this connection */
  public void updateCommonStatementAttributes(Consumer changeAttrs) {
    super.lock();
    try {
      if (this.commonAttrs == null) {
        this.commonAttrs = new StatementAttrs();
      }
      changeAttrs.accept(this.commonAttrs);
    } finally {
      super.unlock();
    }
  }

  public void clearCommonStatementAttributes() {
    super.lock();
    try {
      this.commonAttrs = null;
    } finally {
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ClientStatement createStatement() throws SQLException {
    super.lock();
    try {
      checkClosedConnection();
      return new ClientStatement(this);
    } finally {
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ClientPreparedStatement prepareStatement(String sql)
      throws SQLException {
    super.lock();
    try {
      checkClosedConnection();
      return new ClientPreparedStatement(this, sql);
    } finally {
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ClientCallableStatement prepareCall(String sql) throws SQLException {
    checkClosedConnection();
    super.lock();
    try {
      return new ClientCallableStatement(this, sql);
    } finally {
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String nativeSQL(String sql) throws SQLException {
    checkClosedConnection();
    // no changes
    return sql;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void setAutoCommit(boolean autoCommit) throws SQLException {
    super.lock();
    try {
      checkClosedConnection();
      if (autoCommit != autoCommit()) {
        this.pendingTXFlags.add(TransactionAttribute.AUTOCOMMIT);
        this.clientService.setTXFlag(TransactionAttribute.AUTOCOMMIT,
            autoCommit);
      }
    } finally {
      super.unlock();
    }
  }

  private boolean autoCommit() {
    return this.clientService.isTXFlagSet(TransactionAttribute.AUTOCOMMIT,
        snappydataConstants.DEFAULT_AUTOCOMMIT);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean getAutoCommit() throws SQLException {
    super.lock();
    try {
      checkClosedConnection();
      return autoCommit();
    } finally {
      super.unlock();
    }
  }

  private void initTXHost(final ClientService service) {
    if (service != null) {
      this.txHost = service.getCurrentHostConnection();
    } else {
      this.txHost = null;
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void commit() throws SQLException {
    final ClientService service = this.clientService;
    super.lock();
    try {
      checkClosedConnection();
      if (getTransactionIsolation() == TRANSACTION_NONE) {
        this.lastCommitResults = service.commitTransaction(
            service.getCurrentHostConnection(), true, null);
      } else {
        this.lastCommitResults = service.commitTransaction(txHost, true, null);
      }
      initTXHost(service);
    } catch (SnappyException se) {
      throw informListeners(ThriftExceptionUtil.newSQLException(se));
    } finally {
      super.unlock();
    }
  }

  public String getLastCommitResults() {
    super.lock();
    try {
      return this.lastCommitResults;
    } finally {
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void rollback() throws SQLException {
    final ClientService service = this.clientService;
    super.lock();
    try {
      checkClosedConnection();
      if (getTransactionIsolation() == TRANSACTION_NONE) {
        service.rollbackTransaction(service.getCurrentHostConnection(), true, null);
      } else {
        service.rollbackTransaction(txHost, true, null);
      }
      initTXHost(service);
    } catch (SnappyException se) {
      throw informListeners(ThriftExceptionUtil.newSQLException(se));
    } finally {
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void close() throws SQLException {
    // fire callbacks for pooled connection and return
    if (this.clientServiceOwner != null) {
      if (isClosed()) {
        this.clientServiceOwner.onConnectionError(ThriftExceptionUtil
            .newSQLException(SQLState.PHYSICAL_CONNECTION_ALREADY_CLOSED));
      } else {
        this.clientServiceOwner.onConnectionClose();
      }
      this.isClosed = true;
      return;
    }
    super.lock();
    try {
      final ClientFinalizer finalizer = this.finalizer;
      if (finalizer != null) {
        finalizer.clearAll();
        this.finalizer = null;
      }
      // closing an already closed Connection is a no-op as per JDBC spec
      if (isClosed()) {
        return;
      }
      this.clientService.closeConnection(0);
      isClosed = true;
    } catch (SnappyException se) {
      throw ThriftExceptionUtil.newSQLException(se);
    } finally {
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public final boolean isClosed() throws SQLException {
    return this.isClosed || this.clientService.isClosed();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public DatabaseMetaData getMetaData() throws SQLException {
    checkClosedConnection();
    return new ClientDBMetaData(this);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void setReadOnly(boolean readOnly) throws SQLException {
    super.lock();
    try {
      checkClosedConnection();
      final ClientService service = this.clientService;
      if (readOnly != service.isTXFlagSet(
          TransactionAttribute.READ_ONLY_CONNECTION, false)) {
        this.pendingTXFlags.add(TransactionAttribute.READ_ONLY_CONNECTION);
        service.setTXFlag(TransactionAttribute.READ_ONLY_CONNECTION, readOnly);
      }
    } finally {
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isReadOnly() throws SQLException {
    super.lock();
    try {
      checkClosedConnection();
      return this.clientService.isTXFlagSet(
          TransactionAttribute.READ_ONLY_CONNECTION, false);
    } finally {
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void setCatalog(String catalog) throws SQLException {
    // Per jdbc spec: if the driver does not support catalogs, it will silently
    // ignore this request.
    checkClosedConnection();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String getCatalog() throws SQLException {
    // not supported by SnappyData
    return null;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void setTransactionIsolation(int level) throws SQLException {
    checkClosedConnection();
    // go to server only if there is a change in isolation level
    final ClientService service = this.clientService;
    if (level != service.isolationLevel) {
      super.lock();
      if (level != service.isolationLevel) {
        try {
          service.beginTransaction(level, getPendingTXFlags());
          initTXHost(service);
          // clear the pending transaction flags
          this.pendingTXFlags.clear();
        } catch (SnappyException se) {
          throw informListeners(ThriftExceptionUtil.newSQLException(se));
        } finally {
          super.unlock();
        }
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public int getTransactionIsolation() throws SQLException {
    return this.clientService.isolationLevel;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public SQLWarning getWarnings() throws SQLException {
    if (this.warnings != null) {
      super.lock();
      try {
        final SnappyExceptionData warnings = this.warnings;
        if (warnings != null) {
          return ThriftExceptionUtil.newSQLWarning(warnings, null);
        }
      } finally {
        super.unlock();
      }
    }
    return null;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void clearWarnings() throws SQLException {
    this.warnings = null;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ClientStatement createStatement(int resultSetType,
      int resultSetConcurrency) throws SQLException {
    return createStatement(resultSetType, resultSetConcurrency,
        this.rsHoldability);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ClientPreparedStatement prepareStatement(String sql,
      int resultSetType, int resultSetConcurrency) throws SQLException {
    return prepareStatement(sql, resultSetType, resultSetConcurrency,
        this.rsHoldability);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ClientCallableStatement prepareCall(String sql, int resultSetType,
      int resultSetConcurrency) throws SQLException {
    return prepareCall(sql, resultSetType, resultSetConcurrency,
        this.rsHoldability);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Map> getTypeMap() throws SQLException {
    // nothing in SnappyData
    return new HashMap<>();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void setTypeMap(Map> map) throws SQLException {
    checkClosedConnection();
    if (map == null) {
      throw ThriftExceptionUtil.newSQLException(SQLState.INVALID_API_PARAMETER,
          null, null, "map", "setTypeMap");
    }
    if (!map.isEmpty()) {
      throw ThriftExceptionUtil.newSQLException(SQLState.NOT_IMPLEMENTED, null,
          "setTypeMap(Map)");
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void setHoldability(int holdability) throws SQLException {
    checkClosedConnection();
    this.rsHoldability = holdability;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public int getHoldability() {
    return this.rsHoldability;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Savepoint setSavepoint() throws SQLException {
    super.lock();
    try {
      if (autoCommit()) { // Throw exception if auto-commit is on
        throw ThriftExceptionUtil
            .newSQLException(SQLState.NO_SAVEPOINT_WHEN_AUTO);
      }
      if (++this.generatedSavepointId < 0) {
        this.generatedSavepointId = 1; // restart from 1 on overflow
      }
      InternalSavepoint savepoint = new InternalSavepoint(this,
          this.generatedSavepointId);
      setSavepoint(savepoint);
      return savepoint;
    } finally {
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Savepoint setSavepoint(String name) throws SQLException {
    super.lock();
    try {
      if (name == null) {
        throw ThriftExceptionUtil
            .newSQLException(SQLState.NULL_NAME_FOR_SAVEPOINT);
      }
      if (autoCommit()) { // Throw exception if auto-commit is on
        throw ThriftExceptionUtil
            .newSQLException(SQLState.NO_SAVEPOINT_WHEN_AUTO);
      }
      InternalSavepoint savepoint = new InternalSavepoint(this, name);
      setSavepoint(savepoint);
      return savepoint;
    } finally {
      super.unlock();
    }
  }

  private void setSavepoint(InternalSavepoint savepoint) throws SQLException {
    ClientStatement stmt = null;
    try {
      stmt = createStatement(ResultSet.TYPE_FORWARD_ONLY,
          ResultSet.CONCUR_READ_ONLY, getHoldability());
      final String savepointName = savepoint.getRealSavepointName();
      String sql = "SAVEPOINT \"" + savepointName
          + "\" ON ROLLBACK RETAIN CURSORS";
      stmt.execute(sql, false, null, null);
    } finally {
      if (stmt != null) {
        try {
          stmt.close();
        } catch (Throwable ignored) {
        }
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void rollback(Savepoint savepoint) throws SQLException {
    ClientStatement stmt = null;
    int saveXaState = 0;
    super.lock();
    try {
      saveXaState = this.xaState;
      if (savepoint == null) { // Throw exception if savepoint is null
        throw ThriftExceptionUtil
            .newSQLException(SQLState.XACT_SAVEPOINT_RELEASE_ROLLBACK_FAIL);
      }
      if (autoCommit()) { // Throw exception if auto-commit is on
        throw ThriftExceptionUtil
            .newSQLException(SQLState.NO_SAVEPOINT_ROLLBACK_OR_RELEASE_WHEN_AUTO);
      }
      // Only allow to rollback to a savepoint from the connection that create
      // the savepoint.
      InternalSavepoint intSP;
      if (!(savepoint instanceof InternalSavepoint)
          || (intSP = (InternalSavepoint)savepoint).getConnection() != this) {
        throw ThriftExceptionUtil
            .newSQLException(SQLState.SAVEPOINT_NOT_CREATED_BY_CONNECTION);
      }

      // Construct and flow a savepoint rollback statement to server.
      stmt = createStatement(ResultSet.TYPE_FORWARD_ONLY,
          ResultSet.CONCUR_READ_ONLY, getHoldability());
      final String savepointName = intSP.getRealSavepointName();
      String sql = "ROLLBACK TO SAVEPOINT \"" + savepointName + "\"";
      stmt.execute(sql, false, null, null);
    } finally {
      if (stmt != null) {
        try {
          stmt.close();
        } catch (Throwable ignored) {
        }
      }
      this.xaState = saveXaState;
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void releaseSavepoint(Savepoint savepoint) throws SQLException {
    ClientStatement stmt = null;
    int saveXaState = 0;
    super.lock();
    try {
      saveXaState = this.xaState;
      if (savepoint == null) { // Throw exception if savepoint is null
        throw ThriftExceptionUtil
            .newSQLException(SQLState.XACT_SAVEPOINT_RELEASE_ROLLBACK_FAIL);
      }
      if (autoCommit()) { // Throw exception if auto-commit is on
        throw ThriftExceptionUtil
            .newSQLException(SQLState.NO_SAVEPOINT_ROLLBACK_OR_RELEASE_WHEN_AUTO);
      }
      // Only allow to rollback to a savepoint from the connection that create
      // the savepoint.
      InternalSavepoint intSP;
      if (!(savepoint instanceof InternalSavepoint)
          || (intSP = (InternalSavepoint)savepoint).getConnection() != this) {
        throw ThriftExceptionUtil
            .newSQLException(SQLState.SAVEPOINT_NOT_CREATED_BY_CONNECTION);
      }

      // Construct and flow a savepoint rollback statement to server.
      stmt = createStatement(ResultSet.TYPE_FORWARD_ONLY,
          ResultSet.CONCUR_READ_ONLY, getHoldability());
      final String savepointName = intSP.getRealSavepointName();
      String sql = "RELEASE SAVEPOINT \"" + savepointName + "\"";
      stmt.execute(sql, false, null, null);
    } finally {
      if (stmt != null) {
        try {
          stmt.close();
        } catch (Throwable ignored) {
        }
      }
      this.xaState = saveXaState;
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ClientStatement createStatement(int resultSetType,
      int resultSetConcurrency, int resultSetHoldability) throws SQLException {
    super.lock();
    try {
      checkClosedConnection();
      return new ClientStatement(this, resultSetType, resultSetConcurrency,
          resultSetHoldability);
    } finally {
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ClientPreparedStatement prepareStatement(String sql,
      int resultSetType, int resultSetConcurrency, int resultSetHoldability)
      throws SQLException {
    super.lock();
    try {
      checkClosedConnection();
      return new ClientPreparedStatement(this, sql, resultSetType,
          resultSetConcurrency, resultSetHoldability);
    } finally {
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ClientCallableStatement prepareCall(String sql, int resultSetType,
      int resultSetConcurrency, int resultSetHoldability) throws SQLException {
    super.lock();
    try {
      checkClosedConnection();
      return new ClientCallableStatement(this, sql, resultSetType,
          resultSetConcurrency, resultSetHoldability);
    } finally {
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ClientPreparedStatement prepareStatement(String sql,
      int autoGeneratedKeys) throws SQLException {
    super.lock();
    try {
      checkClosedConnection();
      return new ClientPreparedStatement(this, sql,
          autoGeneratedKeys == Statement.RETURN_GENERATED_KEYS);
    } finally {
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ClientPreparedStatement prepareStatement(String sql,
      int[] columnIndexes) throws SQLException {
    super.lock();
    try {
      checkClosedConnection();
      return new ClientPreparedStatement(this, sql, columnIndexes);
    } finally {
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ClientPreparedStatement prepareStatement(String sql,
      String[] columnNames) throws SQLException {
    super.lock();
    try {
      checkClosedConnection();
      return new ClientPreparedStatement(this, sql, columnNames);
    } finally {
      super.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Blob createBlob() throws SQLException {
    return new ClientBlob(this.clientService);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Clob createClob() throws SQLException {
    return new ClientClob(this.clientService);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public NClob createNClob() throws SQLException {
    throw ThriftExceptionUtil.newSQLException(SQLState.NOT_IMPLEMENTED, null,
        "createNClob()");
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public SQLXML createSQLXML() throws SQLException {
    throw ThriftExceptionUtil.newSQLException(SQLState.NOT_IMPLEMENTED, null,
        "createSQLXML()");
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isValid(final int timeout) throws SQLException {
    if (timeout < 0) {
      throw ThriftExceptionUtil.newSQLException(SQLState.INVALID_API_PARAMETER,
          null, timeout, "timeout", "java.sql.Connection.isValid");
    }

    // Check if the connection is closed
    if (isClosed()) {
      return false;
    }

    // Do a simple query against the database
    super.lock();
    try {
      // Save the current network timeout value
      final int oldTimeout = getTimeout();
      // Set the required timeout value on the network connection
      if (oldTimeout != timeout) {
        setTimeout(timeout);
      }

      // Run a simple validation query against the database
      this.clientService.executeQuery("VALUES(1)", null);

      // Restore the previous timeout value
      if (oldTimeout != timeout) {
        setTimeout(oldTimeout);
      }
    } catch (SnappyException se) {
      // If an SQL exception is thrown the connection is not valid,
      // we ignore the exception and return false.
      return false;
    } finally {
      super.unlock();
    }
    return true; // The connection is valid
  }

  /**
   * setClientInfo will always throw a
   * SQLClientInfoException since SnappyData does not support any
   * properties.
   */
  @Override
  public void setClientInfo(String name, String value)
      throws SQLClientInfoException {
    if (name != null || value != null) {
      HashMap failedProperties = new HashMap<>(1);
      if (name != null) {
        failedProperties.put(name, ClientInfoStatus.REASON_UNKNOWN_PROPERTY);
      }
      throw ThriftExceptionUtil.newSQLClientInfoException(
          SQLState.PROPERTY_UNSUPPORTED_CHANGE, failedProperties, null, name,
          value);
    }
  }

  /**
   * setClientInfo will throw a SQLClientInfoException
   * unless the properties paramenter is empty, since SnappyData does
   * not support any properties.
   */
  @Override
  public void setClientInfo(Properties properties)
      throws SQLClientInfoException {
    if (properties != null && !properties.isEmpty()) {
      HashMap failedProperties =
          new HashMap<>(properties.size());
      String firstKey = null;
      for (String key : properties.stringPropertyNames()) {
        if (firstKey == null) {
          firstKey = key;
        }
        failedProperties.put(key, ClientInfoStatus.REASON_UNKNOWN_PROPERTY);
      }
      if (firstKey != null) {
        throw ThriftExceptionUtil.newSQLClientInfoException(
            SQLState.PROPERTY_UNSUPPORTED_CHANGE, failedProperties, null,
            firstKey, properties.getProperty(firstKey));
      }
    }
  }

  /**
   * getClientInfo always returns a null String since
   * SnappyData doesn't support ClientInfoProperties.
   */
  @Override
  public String getClientInfo(String name) throws SQLException {
    checkClosedConnection();
    return null;
  }

  /**
   * getClientInfo always returns an empty Properties
   * object since SnappyData doesn't support ClientInfoProperties.
   */
  @Override
  public Properties getClientInfo() throws SQLException {
    checkClosedConnection();
    return new Properties();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Array createArrayOf(String typeName, Object[] elements)
      throws SQLException {
    throw ThriftExceptionUtil.newSQLException(SQLState.NOT_IMPLEMENTED, null,
        "createArrayOf(String,Object[])");
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Struct createStruct(String typeName, Object[] attributes)
      throws SQLException {
    throw ThriftExceptionUtil.newSQLException(SQLState.NOT_IMPLEMENTED, null,
        "createStruct(String,Object[])");
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public  T unwrap(Class iface) throws SQLException {
    checkClosedConnection();
    try {
      return iface.cast(this);
    } catch (ClassCastException cce) {
      throw ThriftExceptionUtil.newSQLException(SQLState.UNABLE_TO_UNWRAP, cce,
          iface);
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isWrapperFor(Class iface) throws SQLException {
    checkClosedConnection();
    return iface.isInstance(this);
  }

  // for JDBC 4.1

  @Override
  public void setSchema(String schema) throws SQLException {
    super.lock();
    try {
      checkClosedConnection();
      UpdateResult ur = this.clientService.executeUpdate(
          Collections.singletonList("SET SCHEMA \"" + schema + '"'), null);
      this.warnings = ur.warnings;
    } catch (SnappyException se) {
      throw informListeners(ThriftExceptionUtil.newSQLException(se));
    } finally {
      super.unlock();
    }
  }

  @Override
  public String getSchema() throws SQLException {
    String defaultSchema = this.clientService.getCurrentDefaultSchema();
    if (defaultSchema != null) {
      return defaultSchema;
    }

    super.lock();
    try {
      checkClosedConnection();
      RowSet rs = this.clientService
          .executeQuery("VALUES CURRENT SCHEMA", null);
      List rows = rs.getRows();
      if (rows != null && rows.size() > 0) {
        return (String)rows.get(0).getObject(0);
      } else {
        return null;
      }
    } catch (SnappyException se) {
      throw informListeners(ThriftExceptionUtil.newSQLException(se));
    } finally {
      super.unlock();
    }
  }

  @Override
  public void abort(Executor executor) throws SQLException {
    // no locking here since it is supposed to be used by admins when the socket
    // may be already in use
    checkClosedConnection();
    // check permission
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
      sm.checkPermission(new SQLPermission("callAbort"));
    }
    if (executor == null) {
      throw ThriftExceptionUtil.newSQLException(
          SQLState.LANG_UNEXPECTED_USER_EXCEPTION, null,
          "ClientConnection.abort: null executor passed");
    }
    executor.execute(new Runnable() {
      @Override
      public void run() {
        // input and output protocol are identical in our usage
        clientService.getInputProtocol().getTransport().close();
      }
    });
  }

  @Override
  public void setNetworkTimeout(Executor executor, int milliseconds)
      throws SQLException {
    // no locking here since it is supposed to be used by admins when the socket
    // may be already in use
    checkClosedConnection();
    // check permission
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
      sm.checkPermission(new SQLPermission("setNetworkTimeout"));
    }
    setTimeout(milliseconds);
  }

  @Override
  public int getNetworkTimeout() throws SQLException {
    super.lock();
    try {
      checkClosedConnection();
      return getTimeout();
    } finally {
      super.unlock();
    }
  }

  private void setTimeout(int milliseconds) throws SQLException {
    // input and output protocol are identical in our usage
    TTransport socket = this.clientService.getInputProtocol().getTransport();
    if (socket instanceof SocketTimeout) {
      try {
        ((SocketTimeout)socket).setSoTimeout(milliseconds);
      } catch (SocketException se) {
        throw informListeners(ThriftExceptionUtil.newSQLException(
            SQLState.SOCKET_EXCEPTION, se, se.getMessage()));
      }
    }
  }

  private int getTimeout() throws SQLException {
    // input and output protocol are identical in our usage
    TTransport socket = this.clientService.getInputProtocol().getTransport();
    if (socket instanceof SocketTimeout) {
      try {
        return ((SocketTimeout)socket).getSoTimeout();
      } catch (SocketException se) {
        throw informListeners(ThriftExceptionUtil.newSQLException(
            SQLState.SOCKET_EXCEPTION, se, se.getMessage()));
      }
    } else {
      return 0;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy