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

com.scalar.db.sql.SqlSession Maven / Gradle / Ivy

There is a newer version: 3.14.0
Show newest version
package com.scalar.db.sql;

import com.google.common.annotations.VisibleForTesting;
import com.scalar.db.sql.common.SqlError;
import com.scalar.db.sql.exception.SqlException;
import com.scalar.db.sql.exception.TransactionRetryableException;
import com.scalar.db.sql.exception.UnknownTransactionStatusException;
import com.scalar.db.sql.metadata.Metadata;
import com.scalar.db.sql.statement.BindableStatement;
import com.scalar.db.sql.statement.CommandStatement;
import com.scalar.db.sql.statement.Statement;
import com.scalar.db.sql.util.CommandStatementParser;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;

/** An SQL session in which users can execute all the operations (DDL, DML, etc.). */
@NotThreadSafe
public class SqlSession implements AutoCloseable {

  private final SqlTransactionSessionStrategy sqlTransactionSessionStrategy;

  @Nullable
  private final SqlTwoPhaseCommitTransactionSessionStrategy
      sqlTwoPhaseCommitTransactionSessionStrategy;

  private final CommandStatementExecutor commandStatementExecutor;

  private TransactionMode transactionMode;
  @Nullable private String defaultNamespaceName;
  private boolean closed;

  SqlSession(
      SqlTransactionManager sqlTransactionManager,
      @Nullable SqlTwoPhaseCommitTransactionManager sqlTwoPhaseCommitTransactionManager,
      TransactionMode transactionMode,
      @Nullable String defaultNamespaceName,
      boolean defaultNamespaceNameExistenceCheckEnabled) {
    this.sqlTransactionSessionStrategy =
        new SqlTransactionSessionStrategy(Objects.requireNonNull(sqlTransactionManager));

    if (sqlTwoPhaseCommitTransactionManager == null) {
      this.sqlTwoPhaseCommitTransactionSessionStrategy = null;
    } else {
      this.sqlTwoPhaseCommitTransactionSessionStrategy =
          new SqlTwoPhaseCommitTransactionSessionStrategy(sqlTwoPhaseCommitTransactionManager);
    }

    commandStatementExecutor = new CommandStatementExecutor();

    checkTransactionMode(Objects.requireNonNull(transactionMode));
    this.transactionMode = transactionMode;

    setDefaultNamespaceName(defaultNamespaceName, defaultNamespaceNameExistenceCheckEnabled);
  }

  @VisibleForTesting
  SqlSession(
      SqlTransactionSessionStrategy sqlTransactionSessionStrategy,
      @Nullable
          SqlTwoPhaseCommitTransactionSessionStrategy sqlTwoPhaseCommitTransactionSessionStrategy,
      CommandStatementExecutor commandStatementExecutor,
      TransactionMode transactionMode,
      @Nullable String defaultNamespaceName) {
    this.sqlTransactionSessionStrategy = sqlTransactionSessionStrategy;
    this.sqlTwoPhaseCommitTransactionSessionStrategy = sqlTwoPhaseCommitTransactionSessionStrategy;
    this.commandStatementExecutor = commandStatementExecutor;
    this.transactionMode = transactionMode;
    this.defaultNamespaceName = defaultNamespaceName;
  }

  /**
   * Begins a transaction.
   *
   * @return a transaction ID associated with the begun transaction
   * @throws TransactionRetryableException if the transaction fails to begin due to retryable
   *     faults. You can retry the transaction.
   * @throws SqlException if the transaction fails to begin due to transient or nontransient faults.
   *     You can try retrying the transaction, but you may not be able to begin the transaction due
   *     to nontransient faults
   */
  public String begin() {
    checkIfClosed();
    checkIfTransactionInProgress();
    return getSqlSessionStrategy().begin();
  }

  /**
   * Starts a transaction. This method is an alias of {@link #begin()}.
   *
   * @return a transaction ID associated with the started transaction
   * @throws TransactionRetryableException if the transaction fails to start due to retryable
   *     faults. You can retry the transaction.
   * @throws SqlException if the transaction fails to start due to transient or nontransient faults.
   *     You can try retrying the transaction, but you may not be able to start the transaction due
   *     to nontransient faults
   */
  public String start() {
    checkIfClosed();
    checkIfTransactionInProgress();
    return getSqlSessionStrategy().start();
  }

  /**
   * Joins an ongoing transaction associated with the specified transaction ID.
   *
   * @param transactionId a transaction ID associated with the transaction to join
   * @throws TransactionRetryableException if the transaction fails to join due to retryable faults
   *     (e.g., the transaction not found). You can retry the transaction from the beginning.
   * @throws SqlException if the transaction fails to join due to transient or nontransient faults.
   *     You can try retrying the transaction from the beginning, but the transaction may still fail
   *     if the cause is nontranient
   */
  public void join(String transactionId) {
    checkIfClosed();
    checkIfTransactionInProgress();
    getSqlSessionStrategy().join(transactionId);
  }

  /**
   * Suspends the current transaction.
   *
   * @throws SqlException if an unexpected error occurs
   */
  public void suspend() {
    checkIfClosed();
    if (getSqlSessionStrategy().isTransactionInProgress()) {
      getSqlSessionStrategy().suspend();
    }
  }

  /**
   * Resumes an ongoing transaction associated with the specified transaction ID.
   *
   * @param transactionId a transaction ID associated with the transaction to resume
   * @throws TransactionRetryableException if the transaction fails to resume due to retryable
   *     faults (e.g., the transaction not found). You can retry the transaction from the beginning.
   * @throws SqlException if the transaction fails to resume due to transient or nontransient
   *     faults. You can try retrying the transaction from the beginning, but the transaction may
   *     still fail if the cause is nontranient
   */
  public void resume(String transactionId) {
    checkIfClosed();
    checkIfTransactionInProgress();
    getSqlSessionStrategy().resume(transactionId);
  }

  /**
   * Executes the specified SQL statement.
   *
   * @param sql an SQL statement to execute
   * @return a {@link ResultSet} object that contains the data produced by the query
   * @throws TransactionRetryableException if the transaction operation execution fails due to
   *     retryable faults (e.g., a transaction conflict). You can retry the transaction from the
   *     beginning.
   * @throws UnknownTransactionStatusException if the transaction status (committed or aborted) is
   *     unknown when executing the COMMIT statement
   * @throws SqlException if the transaction operation execution fails due to transient or
   *     nontransient faults. You can try retrying the transaction from the beginning, but the
   *     transaction may still fail if the cause is nontranient
   */
  public ResultSet execute(String sql) {
    checkIfClosed();

    // if the SQL is a command statement, execute it
    CommandStatement commandStatement = CommandStatementParser.parse(sql);
    if (commandStatement != null) {
      return commandStatementExecutor.execute(commandStatement, this);
    }

    return getSqlSessionStrategy().execute(sql, (List) null, defaultNamespaceName);
  }

  /**
   * Executes the specified statement.
   *
   * @param statement a statement to execute
   * @return a {@link ResultSet} object that contains the data produced by the query
   * @throws TransactionRetryableException if the transaction operation execution fails due to
   *     retryable faults (e.g., a transaction conflict). You can retry the transaction from the
   *     beginning.
   * @throws UnknownTransactionStatusException if the transaction status (committed or aborted) is
   *     unknown when executing the COMMIT statement
   * @throws SqlException if the transaction operation execution fails due to transient or
   *     nontransient faults. You can try retrying the transaction from the beginning, but the
   *     transaction may still fail if the cause is nontranient
   */
  public ResultSet execute(Statement statement) {
    checkIfClosed();

    // if the statement is a command statement, execute it
    if (statement instanceof CommandStatement) {
      return commandStatementExecutor.execute((CommandStatement) statement, this);
    }

    return getSqlSessionStrategy().execute(statement, (List) null, defaultNamespaceName);
  }

  /**
   * Returns a {@link PreparedStatement} object with the specified SQL statement.
   *
   * @param sql an SQL statement to prepare
   * @return a {@link PreparedStatement} object
   */
  public PreparedStatement prepareStatement(String sql) {
    checkIfClosed();
    return new PreparedStatement(this, sql);
  }

  /**
   * Returns a {@link PreparedStatement} object with the specified statement.
   *
   * @param bindableStatement a statement to prepare
   * @return a {@link PreparedStatement} object
   */
  public PreparedStatement prepareStatement(BindableStatement bindableStatement) {
    checkIfClosed();
    return new PreparedStatement(this, bindableStatement);
  }

  ResultSet execute(String sql, List positionalValues) {
    checkIfClosed();

    // if the SQL is a command statement, execute it
    CommandStatement commandStatement = CommandStatementParser.parse(sql);
    if (commandStatement != null) {
      return commandStatementExecutor.execute(commandStatement, this);
    }

    return getSqlSessionStrategy().execute(sql, positionalValues, defaultNamespaceName);
  }

  ResultSet execute(String sql, Map namedValues) {
    checkIfClosed();

    // if the SQL is a command statement, execute it
    CommandStatement commandStatement = CommandStatementParser.parse(sql);
    if (commandStatement != null) {
      return commandStatementExecutor.execute(commandStatement, this);
    }

    return getSqlSessionStrategy().execute(sql, namedValues, defaultNamespaceName);
  }

  ResultSet execute(BindableStatement bindableStatement, List positionalValues) {
    checkIfClosed();

    // if the statement is a command statement, execute it
    if (bindableStatement instanceof CommandStatement) {
      return commandStatementExecutor.execute((CommandStatement) bindableStatement, this);
    }

    return getSqlSessionStrategy()
        .execute(bindableStatement, positionalValues, defaultNamespaceName);
  }

  ResultSet execute(BindableStatement bindableStatement, Map namedValues) {
    checkIfClosed();

    // if the statement is a command statement, execute it
    if (bindableStatement instanceof CommandStatement) {
      return commandStatementExecutor.execute((CommandStatement) bindableStatement, this);
    }

    return getSqlSessionStrategy().execute(bindableStatement, namedValues, defaultNamespaceName);
  }

  /**
   * Prepares the current transaction. This method is for the {@code TWO_PHASE_COMMIT_TRANSACTION}
   * mode.
   *
   * @throws TransactionRetryableException if the transaction fails to prepare due to retryable
   *     faults (e.g., a transaction conflict). You can retry the transaction from the beginning.
   * @throws SqlException if the transaction fails to prepare due to transient or nontransient
   *     faults. You can try retrying the transaction from the beginning, but the transaction may
   *     still fail if the cause is nontranient
   */
  public void prepare() {
    checkIfClosed();
    if (transactionMode != TransactionMode.TWO_PHASE_COMMIT_TRANSACTION) {
      throw new IllegalStateException(SqlError.PREPARE_NOT_SUPPORTED.buildMessage());
    }
    checkIfTransactionBegun();
    getSqlSessionStrategy().prepare();
  }

  /**
   * Validates the current transaction. This method is for the {@code TWO_PHASE_COMMIT_TRANSACTION}
   * mode.
   *
   * @throws TransactionRetryableException if the transaction fails to validate due to retryable
   *     faults (e.g., a transaction conflict). You can retry the transaction from the beginning.
   * @throws SqlException if the transaction fails to validate due to transient or nontransient
   *     faults. You can try retrying the transaction from the beginning, but the transaction may
   *     still fail if the cause is nontranient
   */
  public void validate() {
    checkIfClosed();
    if (transactionMode != TransactionMode.TWO_PHASE_COMMIT_TRANSACTION) {
      throw new IllegalStateException(SqlError.VALIDATE_NOT_SUPPORTED.buildMessage());
    }
    checkIfTransactionBegun();
    getSqlSessionStrategy().validate();
  }

  /**
   * Commits the current transaction.
   *
   * @throws TransactionRetryableException if the transaction fails to commit due to retryable
   *     faults (e.g., a transaction conflict). You can retry the transaction from the beginning.
   * @throws UnknownTransactionStatusException if the transaction status (committed or aborted) is
   *     unknown
   * @throws SqlException if the transaction fails to commit due to transient or nontransient
   *     faults. You can try retrying the transaction from the beginning, but the transaction may
   *     still fail if the cause is nontranient
   */
  public void commit() {
    checkIfClosed();
    checkIfTransactionBegun();
    getSqlSessionStrategy().commit();
  }

  /** Rolls back the current transaction. If a transaction is not begun, does nothing. */
  public void rollback() {
    checkIfClosed();
    if (getSqlSessionStrategy().isTransactionInProgress()) {
      getSqlSessionStrategy().rollback();
    }
  }

  /** Aborts the current transaction. This method is an alias of {@link #rollback()}. */
  public void abort() {
    checkIfClosed();
    if (getSqlSessionStrategy().isTransactionInProgress()) {
      getSqlSessionStrategy().abort();
    }
  }

  /**
   * Returns whether a transaction is in progress.
   *
   * @return whether a transaction is in progress
   */
  public boolean isTransactionInProgress() {
    checkIfClosed();
    return getSqlSessionStrategy().isTransactionInProgress();
  }

  /**
   * Returns the current transaction ID.
   *
   * @return the current transaction ID
   */
  public Optional getTransactionId() {
    checkIfClosed();
    return getSqlSessionStrategy().getTransactionId();
  }

  /**
   * Sets a transaction mode.
   *
   * @param transactionMode a transaction mode
   */
  public void setTransactionMode(TransactionMode transactionMode) {
    checkIfClosed();

    if (getSqlSessionStrategy().isTransactionInProgress()) {
      throw new IllegalStateException(
          SqlError.PREVIOUS_TRANSACTION_STILL_IN_PROGRESS.buildMessage());
    }

    checkTransactionMode(transactionMode);
    this.transactionMode = transactionMode;
  }

  private void checkTransactionMode(TransactionMode transactionMode) {
    if (transactionMode == TransactionMode.TWO_PHASE_COMMIT_TRANSACTION
        && sqlTwoPhaseCommitTransactionSessionStrategy == null) {
      throw new IllegalArgumentException(
          SqlError.TWO_PHASE_COMMIT_TRANSACTION_MODE_NOT_SUPPORTED.buildMessage());
    }
  }

  /**
   * Returns the current transaction mode.
   *
   * @return the current transaction mode
   */
  public TransactionMode getTransactionMode() {
    checkIfClosed();
    return transactionMode;
  }

  /**
   * Sets a default namespace name with existence check enabled
   *
   * @param defaultNamespaceName a default namespace name
   * @throws SqlException if an unexpected error happens
   */
  public void setDefaultNamespaceName(@Nullable String defaultNamespaceName) {
    setDefaultNamespaceName(defaultNamespaceName, true);
  }

  /**
   * Sets a default namespace name.
   *
   * @param defaultNamespaceName a default namespace name
   * @param defaultNamespaceNameExistenceCheckEnabled true if existence check should be done on the
   *     default namespace name, false otherwise
   * @throws SqlException if an unexpected error happens
   */
  public void setDefaultNamespaceName(
      @Nullable String defaultNamespaceName, boolean defaultNamespaceNameExistenceCheckEnabled) {
    checkIfClosed();

    if (defaultNamespaceName == null) {
      this.defaultNamespaceName = null;
      return;
    }

    if (defaultNamespaceNameExistenceCheckEnabled) {
      this.defaultNamespaceName =
          getSqlSessionStrategy()
              .getMetadata()
              .getNamespace(defaultNamespaceName)
              .orElseThrow(
                  () ->
                      new IllegalArgumentException(
                          SqlError.NAMESPACE_NOT_FOUND.buildMessage(defaultNamespaceName)))
              .getName();
    } else {
      this.defaultNamespaceName = defaultNamespaceName;
    }
  }

  /**
   * Returns the current default namespace name.
   *
   * @return the current default namespace name
   */
  public Optional getDefaultNamespaceName() {
    checkIfClosed();
    return Optional.ofNullable(defaultNamespaceName);
  }

  /**
   * Returns a {@link Metadata} object.
   *
   * @return a {@link Metadata} object.
   */
  public Metadata getMetadata() {
    checkIfClosed();
    return getSqlSessionStrategy().getMetadata();
  }

  /**
   * Returns whether this session is closed.
   *
   * @return whether this session is closed
   */
  public boolean isClosed() {
    return closed;
  }

  /** Closes this session. */
  @Override
  public void close() {
    closed = true;
  }

  private void checkIfClosed() {
    if (closed) {
      throw new IllegalStateException(SqlError.SQL_SESSION_ALREADY_CLOSED.buildMessage());
    }
  }

  private void checkIfTransactionInProgress() {
    if (isTransactionInProgress()) {
      throw new IllegalStateException(
          SqlError.PREVIOUS_TRANSACTION_STILL_IN_PROGRESS.buildMessage());
    }
  }

  private void checkIfTransactionBegun() {
    if (!isTransactionInProgress()) {
      throw new IllegalStateException(SqlError.TRANSACTION_NOT_BEGUN.buildMessage());
    }
  }

  private SqlSessionStrategy getSqlSessionStrategy() {
    switch (transactionMode) {
      case TRANSACTION:
        return sqlTransactionSessionStrategy;
      case TWO_PHASE_COMMIT_TRANSACTION:
        return sqlTwoPhaseCommitTransactionSessionStrategy;
      default:
        throw new AssertionError();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy