/**
   * Opens this database. The database should be opened after construction. or reopened by the
   * close(int closemode) method during a "shutdown compact". Closes the log if there is an error.
   */
  void reopen() throws HsqlException {

    setState(DATABASE_OPENING);

    try {
      User sysUser;

      isNew =
          (sType == DatabaseManager.S_MEM
              || !HsqlProperties.checkFileExists(sPath, isFilesInJar(), getClass()));
      databaseProperties = new HsqlDatabaseProperties(this);

      databaseProperties.load();
      databaseProperties.setURLProperties(urlProperties);
      compiledStatementManager.reset();

      tTable = new HsqlArrayList();
      userManager = new UserManager();
      hAlias = Library.getAliasMap();
      nameManager = new HsqlNameManager();
      triggerNameList = new DatabaseObjectNames();
      indexNameList = new DatabaseObjectNames();
      constraintNameList = new DatabaseObjectNames();
      sequenceManager = new SequenceManager();
      bReferentialIntegrity = true;
      sysUser = userManager.createSysUser(this);
      sessionManager = new SessionManager(this, sysUser);
      dInfo = DatabaseInformation.newDatabaseInformation(this);

      if (sType != DatabaseManager.S_MEM) {
        logger.openLog(this);
      }

      if (isNew) {
        sessionManager
            .getSysSession()
            .sqlExecuteDirectNoPreChecks("CREATE USER SA PASSWORD \"\" ADMIN");
      }

      dInfo.setWithContent(true);
    } catch (Throwable e) {
      logger.closeLog(Database.CLOSEMODE_IMMEDIATELY);
      logger.releaseLock();
      setState(DATABASE_SHUTDOWN);
      clearStructures();

      if (!(e instanceof HsqlException)) {
        e = Trace.error(Trace.GENERAL_ERROR, e.toString());
      }

      throw (HsqlException) e;
    }

    setState(DATABASE_ONLINE);
  }
  /**
   * Constructs a new Session that operates within (is connected to) the context of this Database
   * object.
   *
   * <p>If successful, the new Session object initially operates on behalf of the user specified by
   * the supplied user name.
   *
   * <p>Throws if username or password is invalid.
   */
  synchronized Session connect(String username, String password) throws HsqlException {

    User user = userManager.getUser(username, password);
    Session session = sessionManager.newSession(this, user, databaseReadOnly);

    logger.logConnectUser(session);

    return session;
  }
  /**
   * Constructs a new Session that operates within (is connected to) the context of this Database
   * object.
   *
   * <p>If successful, the new Session object initially operates on behalf of the user specified by
   * the supplied user name.
   *
   * @param username the name of the initial user of this session. The user must already exist in
   *     this Database object.
   * @param password the password of the specified user. This must match the password, as known to
   *     this Database object, of the specified user
   * @return a new Session object that initially that initially operates on behalf of the specified
   *     user
   * @throws SQLException if the specified user does not exist or a bad password is specified
   */
  synchronized Session connect(String username, String password) throws SQLException {

    User user = aAccess.getUser(username.toUpperCase(), password.toUpperCase());
    Session session = sessionManager.newSession(this, user, databaseReadOnly);

    logger.writeToLog(session, "CONNECT USER " + username + " PASSWORD \"" + password + "\"");

    return session;
  }
  /**
   * Closes this Database using the specified mode.
   *
   * <p>
   *
   * <ol>
   *   <LI>closemode -1 performs SHUTDOWN IMMEDIATELY, equivalent to a poweroff or crash.
   *   <LI>closemode 0 performs a normal SHUTDOWN that checkpoints the database normally.
   *   <LI>closemode 1 performs a shutdown compact that scripts out the contents of any CACHED
   *       tables to the log then deletes the existing *.data file that contains the data for all
   *       CACHED table before the normal checkpoint process which in turn creates a new, compact
   *       *.data file.
   * </ol>
   */
  void close(int closemode) throws HsqlException {

    HsqlException he = null;

    setState(DATABASE_CLOSING);
    sessionManager.closeAllSessions();
    sessionManager.clearAll();

    // fredt - impact of possible error conditions in closing the log
    // should be investigated for the CLOSEMODE_COMPACT mode
    logger.closeLog(closemode);

    try {
      if (closemode == CLOSEMODE_COMPACT && !filesReadOnly) {
        clearStructures();
        reopen();
        setState(DATABASE_CLOSING);
        logger.closeLog(CLOSEMODE_NORMAL);
      }
    } catch (Throwable t) {
      if (t instanceof HsqlException) {
        he = (HsqlException) t;
      } else {
        he = Trace.error(Trace.GENERAL_ERROR, t.toString());
      }
    }

    classLoader = null;

    logger.releaseLock();
    setState(DATABASE_SHUTDOWN);
    clearStructures();

    // fredt - this could change to avoid removing a db from the
    // DatabaseManager repository if there are pending getDatabase()
    // calls
    DatabaseManager.removeDatabase(this);

    if (he != null) {
      throw he;
    }
  }
  /**
   * Closes this Database using the specified mode.
   *
   * <p>
   *
   * <ol>
   *   <LI>closemode -1 performs SHUTDOWN IMMEDIATELY, equivalent to a poweroff or crash.
   *   <LI>closemode 0 performs a normal SHUTDOWN that checkpoints the database normally.
   *   <LI>closemode 1 performs a shutdown compact that scripts out the contents of any CACHED
   *       tables to the log then deletes the existing *.data file that contains the data for all
   *       CACHED table before the normal checkpoint process which in turn creates a new, compact
   *       *.data file.
   * </ol>
   *
   * @param closemode which type of close to perform
   * @throws SQLException if a database access error occurs
   * @see Logger#closeLog(int)
   */
  void close(int closemode) throws SQLException {

    SQLException se;
    HsqlRuntime rt;

    se = null;
    rt = HsqlRuntime.getHsqlRuntime();

    synchronized (rt.findOrCreateDatabaseMutex(sName)) {
      setState(DATABASE_CLOSING);

      try {
        logger.closeLog(closemode);

        // tony_lai@users 20020820
        // The database re-open and close has been moved from
        // Log#close(int closemode) for saving memory usage.
        // Doing so the instances of Log and other objects are no longer
        // referenced, and therefore can be garbage collected if necessary.
        if (closemode == 1) {
          open();
          logger.closeLog(0);
        }
      } catch (Throwable t) {
        if (t instanceof SQLException) {
          se = (SQLException) t;
        } else {
          se = Trace.error(Trace.GENERAL_ERROR, t.toString());
        }
      }

      classLoader = null;

      logger.releaseLock();
      rt.removeDatabase(this);
      this.setState(DATABASE_SHUTDOWN);
    }

    if (se != null) {
      throw se;
    }
  }
  /**
   * Opens this database. The database can be opened by the constructor, or reopened by the
   * close(int closemode) method during a "shutdown compact".
   *
   * @see #close(int closemode)
   * @throws SQLException if a database access error occurs
   */
  private void open() throws SQLException {

    boolean newdatabase;
    User sysUser;

    if (sName.length() == 0) {
      throw Trace.error(Trace.GENERAL_ERROR, "bad database name");
    }

    setState(DATABASE_OPENING);
    compiledStatementManager.reset();

    tTable = new HsqlArrayList();
    aAccess = new UserManager();
    hAlias = Library.getAliasMap();
    triggerNameList = new DatabaseObjectNames();
    indexNameList = new DatabaseObjectNames();
    bReferentialIntegrity = true;
    newdatabase = false;
    sysUser = aAccess.createSysUser(this);
    sessionManager = new SessionManager(this, sysUser);
    databaseProperties = new HsqlDatabaseProperties(this);
    dInfo = DatabaseInformation.newDatabaseInformation(this);

    if (sName.equals(".")) {
      newdatabase = true;
    } else {

      // create properties file if not exits and report if new file
      newdatabase = !databaseProperties.load();

      logger.openLog(this, sName);
    }

    if (newdatabase) {
      execute("CREATE USER SA PASSWORD \"\" ADMIN", sessionManager.getSysSession());
    }

    setState(DATABASE_ONLINE);
    dInfo.setWithContent(true);
  }