/** add session to the end of queue when a transaction starts (depending on isolation mode) */
  public void beginAction(Session session, Statement cs) {

    synchronized (liveTransactionTimestamps) {
      session.actionTimestamp = nextChangeTimestamp();

      if (!session.isTransaction) {
        session.transactionTimestamp = session.actionTimestamp;
        session.isTransaction = true;

        liveTransactionTimestamps.addLast(session.actionTimestamp);
      }
    }

    if (session.hasLocks()) {
      return;
    }

    try {
      writeLock.lock();

      boolean canProceed = beginActionTPL(session, cs);

      if (!canProceed) {
        session.abortTransaction = true;
      }
    } finally {
      writeLock.unlock();
    }
  }
  /** add session to the end of queue when a transaction starts (depending on isolation mode) */
  public void beginAction(Session session, Statement cs) {

    if (session.hasLocks(cs)) {
      return;
    }

    writeLock.lock();

    try {
      if (cs.getCompileTimestamp() < database.schemaManager.getSchemaChangeTimestamp()) {
        cs = session.statementManager.getStatement(session, cs);
        session.sessionContext.currentStatement = cs;

        if (cs == null) {
          return;
        }
      }

      boolean canProceed = setWaitedSessionsTPL(session, cs);

      if (canProceed) {
        if (session.tempSet.isEmpty()) {
          lockTablesTPL(session, cs);

          // we don't set other sessions that would now be waiting for this one too
        } else {
          setWaitingSessionTPL(session);
        }
      } else {
        session.abortTransaction = true;
      }
    } finally {
      writeLock.unlock();
    }
  }
  /**
   * Drops the index with the specified name from this database.
   *
   * @param indexname the name of the index to drop
   * @param ifExists if true and if the Index to drop does not exist, fail silently, else throw
   * @param session the execution context
   * @throws HsqlException if the index does not exist, the session lacks the permission or the
   *     operation violates database integrity
   */
  void dropIndex(Session session, String indexname, String tableName, boolean ifExists)
      throws HsqlException {

    Table t = findUserTableForIndex(session, indexname);

    if (t == null) {
      if (ifExists) {
        return;
      } else {
        throw Trace.error(Trace.INDEX_NOT_FOUND, indexname);
      }
    }

    if (tableName != null && !t.getName().name.equals(tableName)) {
      throw Trace.error(Trace.INDEX_NOT_FOUND, indexname);
    }

    t.checkDropIndex(indexname, null);

    // fredt@users 20020405 - patch 1.7.0 by fredt - drop index bug
    // see Table.moveDefinition();
    session.commit();
    session.setScripting(!t.isTemp());

    TableWorks tw = new TableWorks(session, t);

    tw.dropIndex(indexname);
  }
Example #4
0
  /**
   * Retrieves the <code>User</code> objects representing the database users that are visible to the
   * <code>User</code> object represented by the <code>session</code> argument.
   *
   * <p>If the <code>session</code> argument's <code>User</code> object attribute has isAdmin() true
   * (directly or by virtue of a Role), then all of the <code>User</code> objects in this collection
   * are considered visible. Otherwise, only this object's special <code>PUBLIC</code> <code>User
   * </code> object attribute and the session <code>User</code> object, if it exists in this
   * collection, are considered visible.
   *
   * <p>
   *
   * @param session The <code>Session</code> object used to determine visibility
   * @return a list of <code>User</code> objects visible to the <code>User</code> object contained
   *     by the <code>session</code> argument.
   */
  public HsqlArrayList listVisibleUsers(Session session) {

    HsqlArrayList list;
    User user;
    boolean isAdmin;
    String sessionName;
    String userName;

    list = new HsqlArrayList();
    isAdmin = session.isAdmin();
    sessionName = session.getUsername();

    if (userList == null || userList.size() == 0) {
      return list;
    }

    for (int i = 0; i < userList.size(); i++) {
      user = (User) userList.get(i);

      if (user == null) {
        continue;
      }

      userName = user.getName().getNameString();

      if (isAdmin) {
        list.add(user);
      } else if (sessionName.equals(userName)) {
        list.add(user);
      }
    }

    return list;
  }
  public void readLobResults(
      SessionInterface session, DataInputStream inputStream, RowInputBinary in)
      throws IOException, HsqlException {

    Result currentResult = this;
    boolean hasLob = false;

    setSession(session);

    while (true) {
      int addedResultMode = inputStream.readByte();

      if (addedResultMode == ResultConstants.LARGE_OBJECT_OP) {
        ResultLob resultLob = ResultLob.newLob(inputStream, false);

        if (session instanceof Session) {
          ((Session) session).allocateResultLob(resultLob, inputStream);
        } else {
          currentResult.addLobResult(resultLob);
        }

        hasLob = true;

        continue;
      } else if (addedResultMode == ResultConstants.NONE) {
        break;
      } else {
        throw Error.runtimeError(ErrorCode.U_S0500, "Result");
      }
    }

    if (hasLob) {
      ((Session) session).registerResultLobs(currentResult);
    }
  }
    public void run() {

      try {
        for (int i = sessionList.size() - 1; i >= 0; i--) {
          Session session = (Session) sessionList.get(i);

          if (session.isClosed()) {
            synchronized (this) {
              sessionList.remove(i);
            }

            continue;
          }

          boolean result = session.timeoutManager.checkTimeout();
          /*
                              if (result) {
                                  synchronized (this) {
                                      sessionList.remove(i);
                                  }
                              }
          */
        }
      } catch (Throwable e) {

        // ignore exceptions
        // may be InterruptedException or IOException
      }
    }
  public boolean commitTransaction(Session session) {

    if (session.abortTransaction) {
      return false;
    }

    int limit = session.rowActionList.size();
    Object[] list = session.rowActionList.getArray();

    writeLock.lock();

    try {

      // new actionTimestamp used for commitTimestamp
      session.actionTimestamp = nextChangeTimestamp();
      session.transactionEndTimestamp = session.actionTimestamp;

      endTransaction(session);

      for (int i = 0; i < limit; i++) {
        RowAction action = (RowAction) list[i];

        action.commit(session);
      }

      persistCommit(session, list, limit);

      int newLimit = session.rowActionList.size();

      if (newLimit > limit) {
        list = session.rowActionList.getArray();

        mergeTransaction(session, list, limit, newLimit, session.actionTimestamp);
        finaliseRows(session, list, limit, newLimit, true);
        session.rowActionList.setSize(limit);
      }

      // session.actionTimestamp is the committed tx timestamp
      if (getFirstLiveTransactionTimestamp() > session.actionTimestamp || session == lobSession) {
        mergeTransaction(session, list, 0, limit, session.actionTimestamp);
        finaliseRows(session, list, 0, limit, true);
      } else {
        list = session.rowActionList.toArray();

        addToCommittedQueue(session, list);
      }

      endTransactionTPL(session);
    } finally {
      writeLock.unlock();
    }

    session.tempSet.clear();

    return true;
  }
  public void beginTransaction(Session session) {

    synchronized (liveTransactionTimestamps) {
      session.actionTimestamp = nextChangeTimestamp();
      session.transactionTimestamp = session.actionTimestamp;
      session.isTransaction = true;

      liveTransactionTimestamps.addLast(session.transactionTimestamp);
    }
  }
  /**
   * Executes an UPDATE statement. It is assumed that the argument is of the correct type.
   *
   * @param cs a CompiledStatement of type CompiledStatement.UPDATE
   * @throws HsqlException if a database access error occurs
   * @return the result of executing the statement
   */
  private Result executeUpdateStatement(CompiledStatement cs) throws HsqlException {

    Table table = cs.targetTable;
    TableFilter filter = cs.tf;
    int count = 0;

    if (filter.findFirst()) {
      int[] colmap = cs.columnMap; // column map
      Expression[] colvalues = cs.columnValues;
      Expression condition = cs.condition; // update condition
      int len = colvalues.length;
      HashMappedList rowset = new HashMappedList();
      int size = table.getColumnCount();
      int[] coltypes = table.getColumnTypes();
      boolean success = false;

      do {
        if (condition == null || condition.test(session)) {
          try {
            Row row = filter.currentRow;
            Object[] ni = table.getNewRow();

            System.arraycopy(row.getData(), 0, ni, 0, size);

            for (int i = 0; i < len; i++) {
              int ci = colmap[i];

              ni[ci] = colvalues[i].getValue(session, coltypes[ci]);
            }

            rowset.add(row, ni);
          } catch (HsqlInternalException e) {
          }
        }
      } while (filter.next());

      session.beginNestedTransaction();

      try {
        count = table.update(session, rowset, colmap);
        success = true;
      } finally {

        // update failed (constraint violation) or succeeded
        session.endNestedTransaction(!success);
      }
    }

    updateResult.iUpdateCount = count;

    return updateResult;
  }
  public void rollback(Session session) {

    session.abortTransaction = false;
    session.actionTimestamp = nextChangeTimestamp();

    rollbackPartial(session, 0, session.transactionTimestamp);
    endTransaction(session);

    try {
      writeLock.lock();
      endTransactionTPL(session);
    } finally {
      writeLock.unlock();
    }
  }
  /**
   * Drops the specified user-defined view or table from this Database object.
   *
   * <p>The process of dropping a table or view includes:
   *
   * <OL>
   *   <LI>checking that the specified Session's currently connected User has the right to perform
   *       this operation and refusing to proceed if not by throwing.
   *   <LI>checking for referential constraints that conflict with this operation and refusing to
   *       proceed if they exist by throwing.
   *   <LI>removing the specified Table from this Database object.
   *   <LI>removing any exported foreign keys Constraint objects held by any tables referenced by
   *       the table to be dropped. This is especially important so that the dropped Table ceases to
   *       be referenced, eventually allowing its full garbage collection.
   *   <LI>
   * </OL>
   *
   * <p>
   *
   * @param name of the table or view to drop
   * @param ifExists if true and if the Table to drop does not exist, fail silently, else throw
   * @param isView true if the name argument refers to a View
   * @param session the connected context in which to perform this operation
   * @throws HsqlException if any of the checks listed above fail
   */
  void dropTable(Session session, String name, boolean ifExists, boolean isView)
      throws HsqlException {

    Table toDrop = null;
    int dropIndex = -1;

    for (int i = 0; i < tTable.size(); i++) {
      toDrop = (Table) tTable.get(i);

      if (toDrop.equals(session, name) && isView == toDrop.isView()) {
        dropIndex = i;

        break;
      } else {
        toDrop = null;
      }
    }

    if (dropIndex == -1) {
      if (ifExists) {
        return;
      } else {
        throw Trace.error(isView ? Trace.VIEW_NOT_FOUND : Trace.TABLE_NOT_FOUND, name);
      }
    }

    if (!toDrop.isTemp()) {
      session.checkDDLWrite();
    }

    if (isView) {
      checkViewIsInView((View) toDrop);
    } else {
      checkTableIsReferenced(toDrop);
      checkTableIsInView(toDrop.tableName.name);
    }

    tTable.remove(dropIndex);
    removeExportedKeys(toDrop);
    userManager.removeDbObject(toDrop.getName());
    triggerNameList.removeOwner(toDrop.tableName);
    indexNameList.removeOwner(toDrop.tableName);
    constraintNameList.removeOwner(toDrop.tableName);
    toDrop.dropTriggers();
    toDrop.drop();
    session.setScripting(!toDrop.isTemp());
    session.commit();
  }
  protected void readExistingData(Session session) throws IOException, HsqlException {

    for (; ; ) {
      String s = readTableInit();

      if (s == null) {
        break;
      }

      String schema = session.getSchemaName(currentSchema);
      Table t = db.schemaManager.getUserTable(session, s, schema);
      int j = 0;

      for (j = 0; ; j++) {
        if (readRow(t) == false) {
          break;
        }
      }

      int checkCount = readTableTerm();

      if (j != checkCount) {
        throw Trace.error(
            Trace.ERROR_IN_SCRIPT_FILE,
            Trace.ERROR_IN_BINARY_SCRIPT_1,
            new Object[] {s, new Integer(j), new Integer(checkCount)});
      }
    }
  }
  /** Instantiate this as a SET statement. */
  StatementSet(
      Session session,
      Table table,
      RangeVariable rangeVars[],
      int[] updateColumnMap,
      Expression[] colExpressions,
      CompileContext compileContext) {

    super(
        StatementTypes.ASSIGNMENT,
        StatementTypes.X_SQL_DATA_CHANGE,
        session.getCurrentSchemaHsqlName());

    this.operationType = TRIGGER_SET;
    this.targetTable = table;
    this.baseTable = targetTable.getBaseTable();
    this.updateColumnMap = updateColumnMap;
    this.updateExpressions = colExpressions;
    this.updateCheckColumns = targetTable.getColumnCheckList(updateColumnMap);
    this.targetRangeVariables = rangeVars;
    isTransactionStatement = false;

    setDatabseObjects(compileContext);
    checkAccessRights(session);
  }
  public Result execute(Session session) {

    Result result;

    if (targetTable != null && session.isReadOnly() && !targetTable.isTemp()) {
      HsqlException e = Error.error(ErrorCode.X_25006);

      return Result.newErrorResult(e);
    }

    if (isExplain) {
      return getExplainResult(session);
    }

    try {
      if (subqueries.length > 0) {
        materializeSubQueries(session);
      }

      result = getResult(session);
    } catch (Throwable t) {
      result = Result.newErrorResult(t, null);

      result.getException().setStatementType(group, type);
    }

    session.sessionContext.clearStructures(this);

    return result;
  }
  /** Initializes this connection. */
  private void init() {

    runnerThread = Thread.currentThread();
    keepAlive = true;

    try {
      socket.setTcpNoDelay(true);

      dataInput = new DataInputStream(socket.getInputStream());
      dataOutput = new BufferedOutputStream(socket.getOutputStream());

      Result resultIn = Result.read(rowIn, dataInput);
      Result resultOut;

      try {
        int dbIndex = ArrayUtil.find(server.dbAlias, resultIn.subSubString);

        dbID = server.dbID[dbIndex];
        user = resultIn.getMainString();
        password = resultIn.getSubString();

        if (!server.isSilent()) {
          server.printWithThread(mThread + ":trying to connect user " + user);
        }

        session =
            DatabaseManager.newSession(dbID, resultIn.getMainString(), resultIn.getSubString());
        resultOut = new Result(ResultConstants.UPDATECOUNT);
        resultOut.databaseID = session.getDatabase().databaseID;
        resultOut.sessionID = session.getId();
      } catch (HsqlException e) {
        session = null;
        resultOut = new Result(e, null);
      } catch (ArrayIndexOutOfBoundsException e) {
        session = null;
        resultOut = new Result(Trace.error(Trace.DATABASE_NOT_EXISTS), resultIn.subSubString);
      }

      Result.write(resultOut, rowOut, dataOutput);

      return;
    } catch (Exception e) {
      server.printWithThread(mThread + ":couldn't connect " + user);
    }

    close();
  }
    public void run() {

      try {
        Session sysSession = sessionManager.newSysSession();
        Statement checkpoint = ParserCommand.getAutoCheckpointStatement(Database.this);

        sysSession.executeCompiledStatement(checkpoint, ValuePool.emptyObjectArray, 0);
        sysSession.commit(false);
        sysSession.close();

        waiting = false;
      } catch (Throwable e) {

        // ignore exceptions
        // may be InterruptedException or IOException
      }
    }
  Node getRoot(Session session) {

    if (isTemp) {
      return session.getIndexRoot(indexName, onCommitPreserve);
    } else {
      return root;
    }
  }
  void setRoot(Session session, Node node) {

    if (isTemp) {
      session.setIndexRoot(indexName, onCommitPreserve, node);
    } else {
      root = node;
    }
  }
  // result
  Result getDataResultHead(Result command, Result result, boolean isNetwork) {

    int fetchSize = command.getFetchSize();

    result.setResultId(session.actionTimestamp);

    if (command.rsConcurrency == ResultConstants.CONCUR_READ_ONLY) {
      result.setDataResultConcurrency(ResultConstants.CONCUR_READ_ONLY);
      result.setDataResultHoldability(command.rsHoldability);
    } else {
      if (result.rsConcurrency == ResultConstants.CONCUR_READ_ONLY) {
        result.setDataResultHoldability(command.rsHoldability);

        // add warning for concurrency conflict
      } else {
        if (session.isAutoCommit()) {
          result.setDataResultConcurrency(ResultConstants.CONCUR_READ_ONLY);
          result.setDataResultHoldability(ResultConstants.HOLD_CURSORS_OVER_COMMIT);
        } else {
          result.setDataResultHoldability(ResultConstants.CLOSE_CURSORS_AT_COMMIT);
        }
      }
    }

    result.setDataResultScrollability(command.rsScrollability);

    boolean hold = false;
    boolean copy = false;

    if (result.rsConcurrency == ResultConstants.CONCUR_UPDATABLE) {
      hold = true;
    }

    if (isNetwork) {
      if (fetchSize != 0 && result.getNavigator().getSize() > fetchSize) {
        copy = true;
        hold = true;
      }
    } else {
      if (result.getNavigator().isDiskBased()) {
        hold = true;
      }
    }

    if (hold) {
      if (resultMap == null) {
        resultMap = new LongKeyHashMap();
      }

      resultMap.put(result.getResultId(), result);
    }

    if (copy) {
      result = Result.newDataHeadResult(session, result, 0, fetchSize);
    }

    return result;
  }
  /** Initializes this connection. */
  private void init() {

    runnerThread = Thread.currentThread();
    keepAlive = true;

    try {
      socket.setTcpNoDelay(true);

      dataInput = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
      dataOutput = new BufferedOutputStream(socket.getOutputStream());

      Result resultIn = Result.read(rowIn, dataInput);
      Result resultOut;

      try {
        dbID = server.getDBID(resultIn.subSubString);
        user = resultIn.getMainString();
        password = resultIn.getSubString();

        if (!server.isSilent()) {
          server.printWithThread(mThread + ":trying to connect user " + user);
        }

        session =
            DatabaseManager.newSession(dbID, resultIn.getMainString(), resultIn.getSubString());
        resultOut = new Result(ResultConstants.UPDATECOUNT);
        resultOut.databaseID = session.getDatabase().databaseID;
        resultOut.sessionID = session.getId();
      } catch (HsqlException e) {
        session = null;
        resultOut = new Result(e, null);
      } catch (RuntimeException e) {
        session = null;
        resultOut = new Result(e, null);
      }

      Result.write(resultOut, rowOut, dataOutput);

      return;
    } catch (Exception e) {
      server.printWithThread(mThread + ":couldn't connect " + user);
    }

    close();
  }
  public boolean prepareCommitActions(Session session) {

    Object[] list = session.rowActionList.getArray();
    int limit = session.rowActionList.size();

    if (session.abortTransaction) {

      //            System.out.println("cascade fail " + session + " " + session.actionTimestamp);
      return false;
    }

    try {
      writeLock.lock();

      for (int i = 0; i < limit; i++) {
        RowAction rowact = (RowAction) list[i];

        if (!rowact.canCommit(session, session.tempSet)) {

          //                System.out.println("commit conflicts " + session + " " +
          // session.actionTimestamp);
          return false;
        }
      }

      session.actionTimestamp = nextChangeTimestamp();

      for (int i = 0; i < limit; i++) {
        RowAction action = (RowAction) list[i];

        action.prepareCommit(session);
      }

      for (int i = 0; i < session.tempSet.size(); i++) {
        Session current = (Session) session.tempSet.get(i);

        current.abortTransaction = true;
      }

      return true;
    } finally {
      writeLock.unlock();
      session.tempSet.clear();
    }
  }
  public void beginTransaction(Session session) {

    writeLock.lock();

    try {
      if (!session.isTransaction) {
        session.actionTimestamp = nextChangeTimestamp();
        session.transactionTimestamp = session.actionTimestamp;
        session.isTransaction = true;

        transactionCount++;

        liveTransactionTimestamps.addLast(session.transactionTimestamp);
      }
    } finally {
      writeLock.unlock();
    }
  }
  /**
   * Executes an INSERT_SELECT statement. It is assumed that the argument is of the correct type.
   *
   * @param cs a CompiledStatement of type CompiledStatement.INSERT_SELECT
   * @throws HsqlException if a database access error occurs
   * @return the result of executing the statement
   */
  private Result executeInsertSelectStatement(CompiledStatement cs) throws HsqlException {

    Table t = cs.targetTable;
    Select s = cs.select;
    int[] ct = t.getColumnTypes(); // column types
    Result r = s.getResult(session.getMaxRows(), session);
    Record rc = r.rRoot;
    int[] cm = cs.columnMap; // column map
    boolean[] ccl = cs.checkColumns; // column check list
    int len = cm.length;
    Object[] row;
    int count;
    boolean success = false;

    session.beginNestedTransaction();

    try {
      while (rc != null) {
        row = t.getNewRowData(session, ccl);

        for (int i = 0; i < len; i++) {
          int j = cm[i];

          if (ct[j] != r.metaData.colType[i]) {
            row[j] = Column.convertObject(rc.data[i], ct[j]);
          } else {
            row[j] = rc.data[i];
          }
        }

        rc.data = row;
        rc = rc.next;
      }

      count = t.insert(session, r);
      success = true;
    } finally {
      session.endNestedTransaction(!success);
    }

    updateResult.iUpdateCount = count;

    return updateResult;
  }
  /**
   * The main SQL statement executor.
   *
   * <p>All requests to execute SQL statements against this Database object eventually go through
   * this method.
   *
   * @param statement the SQL statement to execute
   * @param session an object representing a connected user and a collection of session state
   *     attributes
   * @return the result of executing the specified statement, in a form suitable for either wrapping
   *     in a local ResultSet object or for transmitting to a remote client via the native HSQLDB
   *     protocol
   * @deprecated from 1.7.2; Session is now the HSQLDB execution hub
   */
  Result execute(String statement, Session session) {

    try {
      Trace.check(session != null, Trace.ACCESS_IS_DENIED);
    } catch (SQLException e) {
      return new Result(e, statement);
    }

    return session.sqlExecuteDirect(statement);
  }
  /**
   * Drops the index with the specified name from this database.
   *
   * @param indexname the name of the index to drop
   * @param session the execution context
   * @throws SQLException if the index does not exist, the session lacks the permission or the
   *     operation violates database integrity
   */
  void dropIndex(String indexname, Session session) throws SQLException {

    Table t = findUserTableForIndex(indexname, session);

    if (t == null) {
      throw Trace.error(Trace.INDEX_NOT_FOUND, indexname);
    }

    t.checkDropIndex(indexname, null);

    // fredt@users 20020405 - patch 1.7.0 by fredt - drop index bug
    // see Table.moveDefinition();
    session.commit();
    session.setScripting(!t.isTemp());

    TableWorks tw = new TableWorks(t);

    tw.dropIndex(indexname);
  }
  Result getWriteAccessResult(Session session) {

    try {
      if (targetTable != null && !targetTable.isTemp()) {
        session.checkReadWrite();
      }
    } catch (HsqlException e) {
      return Result.newErrorResult(e);
    }

    return null;
  }
  /** Drops a trigger with the specified name in the given context. */
  void dropTrigger(Session session, String name) throws HsqlException {

    boolean found = triggerNameList.containsName(name);

    Trace.check(found, Trace.TRIGGER_NOT_FOUND, name);

    HsqlName tableName = (HsqlName) triggerNameList.removeName(name);
    Table t = this.findUserTable(session, tableName.name);

    t.dropTrigger(name);
    session.setScripting(!t.isTemp());
  }
  /** Drops from this Database any temporary tables owned by the specified Session. */
  void dropTempTables(Session ownerSession) {

    int i = tTable.size();

    while (i-- > 0) {
      Table toDrop = (Table) tTable.get(i);

      if (toDrop.isTemp() && toDrop.getOwnerSessionId() != ownerSession.getId()) {
        tTable.remove(i);
      }
    }
  }
  /**
   * Executes a SELECT statement. It is assumed that the argument is of the correct type.
   *
   * @param cs a CompiledStatement of type CompiledStatement.SELECT
   * @throws HsqlException if a database access error occurs
   * @return the result of executing the statement
   */
  private Result executeSelectStatement(CompiledStatement cs) throws HsqlException {

    Select select = cs.select;
    Result result;

    if (select.sIntoTable != null) {

      // session level user rights
      session.checkDDLWrite();

      if (session.getDatabase().findUserTable(session, select.sIntoTable.name) != null
          || session.getDatabase().dInfo.getSystemTable(session, select.sIntoTable.name) != null) {
        throw Trace.error(Trace.TABLE_ALREADY_EXISTS, select.sIntoTable.name);
      }

      result = select.getResult(session.getMaxRows(), session);
      result =
          session.dbCommandInterpreter.processSelectInto(
              result, select.sIntoTable, select.intoType);

      session.getDatabase().setMetaDirty(false);
    } else {
      result = select.getResult(session.getMaxRows(), session);
    }

    return result;
  }
  void endTransactionTPL(Session session) {

    int unlockedCount = 0;

    unlockTablesTPL(session);

    final int waitingCount = session.waitingSessions.size();

    if (waitingCount == 0) {
      return;
    }

    for (int i = 0; i < waitingCount; i++) {
      Session current = (Session) session.waitingSessions.get(i);

      current.tempUnlocked = false;

      long count = current.latch.getCount();

      if (count == 1) {
        boolean canProceed = setWaitedSessionsTPL(current, current.currentStatement);

        if (!canProceed) {
          current.abortTransaction = true;
        }

        if (current.tempSet.isEmpty()) {
          lockTablesTPL(current, current.currentStatement);

          current.tempUnlocked = true;

          unlockedCount++;
        }
      }
    }

    for (int i = 0; i < waitingCount; i++) {
      Session current = (Session) session.waitingSessions.get(i);

      if (!current.tempUnlocked) {

        // this can introduce additional waits for the sessions
        boolean canProceed = setWaitedSessionsTPL(current, current.currentStatement);

        if (!canProceed) {
          current.abortTransaction = true;
        }
      }
    }

    for (int i = 0; i < waitingCount; i++) {
      Session current = (Session) session.waitingSessions.get(i);

      setWaitingSessionTPL(current);
    }

    session.tempSet.clear();
    session.waitingSessions.clear();
  }