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

org.hsqldb.Database Maven / Gradle / Ivy

There is a newer version: 2.7.2
Show newest version
/* Copyright (c) 2001-2019, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


package org.hsqldb;

import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.dbinfo.DatabaseInformation;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.FrameworkLogger;
import org.hsqldb.lib.HashMappedList;
import org.hsqldb.lib.HsqlArrayList;
import org.hsqldb.lib.HsqlTimer;
import org.hsqldb.lib.OrderedHashSet;
import org.hsqldb.map.ValuePool;
import org.hsqldb.persist.HsqlDatabaseProperties;
import org.hsqldb.persist.HsqlProperties;
import org.hsqldb.persist.LobManager;
import org.hsqldb.persist.Logger;
import org.hsqldb.persist.PersistentStoreCollectionDatabase;
import org.hsqldb.result.Result;
import org.hsqldb.rights.GranteeManager;
import org.hsqldb.rights.User;
import org.hsqldb.rights.UserManager;
import org.hsqldb.types.Collation;

// incorporates following contributions
// campbell-burnet@users - javadoc comments

/**
 * Database is the root class for HSQL Database Engine database. 

* * It holds the data structures that form an HSQLDB database instance. * * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 2.5.0 * @since 1.9.0 */ public class Database { int databaseID; HsqlName databaseUniqueName; DatabaseType databaseType; private final String canonicalPath; public HsqlProperties urlProperties; private final String path; public Collation collation; public DatabaseInformation dbInfo; /** indicates the state of the database */ private volatile int dbState; public Logger logger; /** true means that all tables are readonly. */ boolean databaseReadOnly; /** * true means that all CACHED and TEXT tables are readonly. * MEMORY tables are updatable but updates are not persisted. */ private boolean filesReadOnly; /** true means filesReadOnly */ private boolean filesInJar; /** * Defaults are used in version upgrades, but overridden by * databaseProperties or URL properties for new databases. */ public int sqlAvgScale = 0; public boolean sqlRestrictExec = false; public boolean sqlCharLiteral = true; public boolean sqlConcatNulls = true; public boolean sqlConvertTruncate = true; public boolean sqlDoubleNaN = true; public boolean sqlEnforceTypes = false; public boolean sqlEnforceRefs = false; public boolean sqlEnforceSize = true; public boolean sqlEnforceNames = false; public boolean sqlEnforceTDCD = true; public boolean sqlEnforceTDCU = true; public boolean sqlIgnoreCase = false; public boolean sqlLiveObject = false; public boolean sqlLongvarIsLob = false; public boolean sqlNullsFirst = true; public boolean sqlNullsOrder = true; public boolean sqlSysIndexNames = false; public boolean sqlRegularNames = true; public boolean sqlTranslateTTI = true; public boolean sqlUniqueNulls = true; public boolean sqlSyntaxDb2 = false; public boolean sqlSyntaxMss = false; public boolean sqlSyntaxMys = false; public boolean sqlSyntaxOra = false; public boolean sqlSyntaxPgs = false; public int recoveryMode = 0; private boolean isReferentialIntegrity = true; public HsqlDatabaseProperties databaseProperties; private final boolean shutdownOnNoConnection; int resultMaxMemoryRows; // schema invariant objects public UserManager userManager; public GranteeManager granteeManager; public HsqlNameManager nameManager; // session related objects public SessionManager sessionManager; public TransactionManager txManager; public int defaultIsolationLevel = SessionInterface.TX_READ_COMMITTED; public boolean txConflictRollback = true; public boolean txInterruptRollback = false; // schema objects public SchemaManager schemaManager; // public PersistentStoreCollectionDatabase persistentStoreCollection; // public LobManager lobManager; // public CheckpointRunner checkpointRunner; public TimeoutRunner timeoutRunner; // Result updateZeroResult = Result.updateZeroResult; // public static final int DATABASE_ONLINE = 1; public static final int DATABASE_OPENING = 2; public static final int DATABASE_CLOSING = 3; public static final int DATABASE_SHUTDOWN = 4; public static final int CLOSEMODE_IMMEDIATELY = 1; public static final int CLOSEMODE_NORMAL = 2; public static final int CLOSEMODE_COMPACT = 3; public static final int CLOSEMODE_SCRIPT = 4; /** * Constructs a new Database object. * * @param type is the type of the database: "mem:", "file:", "res:" * @param path is the given path to the database files * @param canonicalPath is the canonical path * @param props property overrides placed on the connect URL * @exception HsqlException if the specified name and path * combination is illegal or unavailable, or the database files the * name and path resolves to are in use by another process */ Database(DatabaseType type, String path, String canonicalPath, HsqlProperties props) { setState(Database.DATABASE_SHUTDOWN); this.databaseType = type; this.path = path; this.canonicalPath = canonicalPath; this.urlProperties = props; if (databaseType == DatabaseType.DB_RES) { filesInJar = true; filesReadOnly = true; } logger = new Logger(this); shutdownOnNoConnection = urlProperties.isPropertyTrue(HsqlDatabaseProperties.url_shutdown); recoveryMode = urlProperties.getIntegerProperty( HsqlDatabaseProperties.url_recover, 0); } /** * Opens this database. The database should be opened after construction. */ synchronized void open() { if (!isShutdown()) { return; } reopen(); } /** * 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() { boolean isNew = false; setState(DATABASE_OPENING); try { createObjectStructures(); // completed metadata logger.open(); isNew = logger.isNewDatabase; if (isNew) { String username = urlProperties.getProperty("user", "SA"); String password = urlProperties.getProperty("password", ""); userManager.createFirstUser(username, password); schemaManager.createPublicSchema(); logger.checkpoint(null, false, false); } lobManager.open(); dbInfo.setWithContent(true); checkpointRunner = new CheckpointRunner(); timeoutRunner = new TimeoutRunner(); } catch (Throwable e) { logger.close(Database.CLOSEMODE_IMMEDIATELY); logger.releaseLock(); setState(DATABASE_SHUTDOWN); clearStructures(); DatabaseManager.removeDatabase(this); if (!(e instanceof HsqlException)) { e = Error.error(ErrorCode.GENERAL_ERROR, e); } logger.logSevereEvent("could not reopen database", e); throw (HsqlException) e; } setState(DATABASE_ONLINE); } /** * Clears the data structures, making them elligible for garbage collection. */ void clearStructures() { if (schemaManager != null) { schemaManager.release(); } if (checkpointRunner != null) { checkpointRunner.stop(); } if (timeoutRunner != null) { timeoutRunner.stop(); } lobManager = null; granteeManager = null; userManager = null; nameManager = null; schemaManager = null; sessionManager = null; dbInfo = null; checkpointRunner = null; timeoutRunner = null; } public void createObjectStructures() { nameManager = new HsqlNameManager(this); databaseUniqueName = nameManager.newHsqlName("", false, SchemaObject.DATABASE); lobManager = new LobManager(this); granteeManager = new GranteeManager(this); userManager = new UserManager(this); schemaManager = new SchemaManager(this); persistentStoreCollection = new PersistentStoreCollectionDatabase(this); isReferentialIntegrity = true; sessionManager = new SessionManager(this); collation = Collation.newDatabaseInstance(); dbInfo = DatabaseInformation.newDatabaseInformation(this); txManager = new TransactionManager2PL(this); lobManager.createSchema(); sessionManager.getSysLobSession().setSchema(SqlInvariants.LOBS_SCHEMA); schemaManager.setSchemaChangeTimestamp(); schemaManager.createSystemTables(); } /** * Returns the database ID. */ public int getDatabaseID() { return this.databaseID; } public HsqlName getName() { return databaseUniqueName; } /** * Returns a unique String identifier for the database. */ public String getNameString() { return databaseUniqueName.name; } public void setDatabaseName(String name) { databaseUniqueName.rename(name, false); } /** * Returns the type of the database: "mem", "file", "res" */ public DatabaseType getType() { return databaseType; } /** * Returns the path of the database */ public String getPath() { return path; } public HsqlName getCatalogName() { return nameManager.getCatalogName(); } /** * Returns the database properties. */ public HsqlDatabaseProperties getProperties() { return databaseProperties; } /** * Returns the SessionManager for the database. */ public SessionManager getSessionManager() { return sessionManager; } public boolean isReadOnly() { return databaseReadOnly; } /** * Returns true if database has been shut down, false otherwise */ boolean isShutdown() { return dbState == DATABASE_SHUTDOWN; } /** * Constructs a new Session that operates within (is connected to) the * context of this Database object.

* * If successful, the new Session object initially operates on behalf of * the user specified by the supplied user name. * * Throws if username or password is invalid. */ synchronized Session connect(String username, String password, String zoneString, int timeZoneSeconds) { if (getState() != DATABASE_ONLINE) { throw Error.error(ErrorCode.X_08001); } if (username.equalsIgnoreCase("SA")) { username = "SA"; } User user = userManager.getUser(username, password); Session session = sessionManager.newSession(this, user, databaseReadOnly, true, zoneString, timeZoneSeconds); return session; } /** * Puts this Database object in global read-only mode. After * this call, all existing and future sessions are limited to read-only * transactions. Any following attempts to update the state of the * database will result in throwing an HsqlException. */ public void setReadOnly() { databaseReadOnly = true; filesReadOnly = true; } /** * After this call all CACHED and TEXT tables will be set to read-only * mode. Changes to MEMORY tables will NOT * be stored or updated in the script file. This mode is intended for * use with read-only media where data should not be persisted. */ public void setFilesReadOnly() { filesReadOnly = true; } /** * Is this in filesReadOnly mode? */ public boolean isFilesReadOnly() { return filesReadOnly; } /** * Is this in filesInJar mode? */ public boolean isFilesInJar() { return filesInJar; } /** * Returns the UserManager for this Database. */ public UserManager getUserManager() { return userManager; } /** * Returns the GranteeManager for this Database. */ public GranteeManager getGranteeManager() { return granteeManager; } public void setLiveObject(boolean mode) { sqlLiveObject = mode; } /** * Sets the isReferentialIntegrity attribute. */ public void setReferentialIntegrity(boolean ref) { isReferentialIntegrity = ref; } /** * Is referential integrity currently enforced? */ public boolean isReferentialIntegrity() { return isReferentialIntegrity; } public int getResultMaxMemoryRows() { return resultMaxMemoryRows; } public void setResultMaxMemoryRows(int size) { resultMaxMemoryRows = size; } public void setRestrictExec(boolean mode) { sqlRestrictExec = mode; } public void setStrictNames(boolean mode) { sqlEnforceNames = mode; } public void setRegularNames(boolean mode) { sqlRegularNames = mode; nameManager.setSqlRegularNames(mode); } public void setStrictColumnSize(boolean mode) { sqlEnforceSize = mode; } public void setStrictReferences(boolean mode) { sqlEnforceRefs = mode; } public void setStrictTypes(boolean mode) { sqlEnforceTypes = mode; } public void setStrictTDCD(boolean mode) { sqlEnforceTDCD = mode; } public void setStrictTDCU(boolean mode) { sqlEnforceTDCU = mode; } public void setTranslateTTI(boolean mode) { sqlTranslateTTI = mode; } public void setNullsFirst(boolean mode) { sqlNullsFirst = mode; } public void setNullsOrder(boolean mode) { sqlNullsOrder = mode; } public void setCharacterLiteral(boolean mode) { sqlCharLiteral = mode; } public void setConcatNulls(boolean mode) { sqlConcatNulls = mode; } public void setUniqueNulls(boolean mode) { sqlUniqueNulls = mode; } public void setConvertTrunc(boolean mode) { sqlConvertTruncate = mode; } public void setDoubleNaN(boolean mode) { sqlDoubleNaN = mode; } public void setAvgScale(int scale) { sqlAvgScale = scale; } public void setLongVarIsLob(boolean mode) { sqlLongvarIsLob = mode; } public void setIgnoreCase(boolean mode) { sqlIgnoreCase = mode; } public void setSysIndexNames(boolean mode) { sqlSysIndexNames = mode; } public void setSyntaxDb2(boolean mode) { sqlSyntaxDb2 = mode; } public void setSyntaxMss(boolean mode) { sqlSyntaxMss = mode; } public void setSyntaxMys(boolean mode) { sqlSyntaxMys = mode; } public void setSyntaxOra(boolean mode) { sqlSyntaxOra = mode; } public void setSyntaxPgs(boolean mode) { sqlSyntaxPgs = mode; } void closeIfLast() { if (sessionManager.isEmpty() && dbState == DATABASE_ONLINE) { if (shutdownOnNoConnection) { try { close(CLOSEMODE_NORMAL); } catch (HsqlException e) {} } else { logger.synchLog(); } } } /** * Closes this Database using the specified mode.

* *

    *
  1. closemode -1 performs SHUTDOWN IMMEDIATELY, equivalent * to a poweroff or crash. * *
  2. closemode 0 performs a normal SHUTDOWN that * checkpoints the database normally. * *
  3. 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. *
*/ public void close(int closemode) { HsqlException he = null; // multiple simultaneous close synchronized (this) { if (getState() != DATABASE_ONLINE) { return; } setState(DATABASE_CLOSING); } sessionManager.closeAllSessions(); if (filesReadOnly) { closemode = CLOSEMODE_IMMEDIATELY; } /** * impact of possible error conditions in closing the log * for the CLOSEMODE_COMPACT mode */ boolean result = logger.close(closemode); lobManager.close(); sessionManager.close(); try { if (result && closemode == CLOSEMODE_COMPACT) { clearStructures(); reopen(); txManager.setGlobalChangeTimestamp( txManager.getGlobalChangeTimestamp() + 1); setState(DATABASE_CLOSING); sessionManager.closeAllSessions(); logger.close(CLOSEMODE_NORMAL); lobManager.close(); sessionManager.close(); } } catch (Throwable t) { if (t instanceof HsqlException) { he = (HsqlException) t; } else { he = Error.error(ErrorCode.GENERAL_ERROR, t); } } 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); // todo - when hsqldb.sql. framework logging is supported, add another call FrameworkLogger.clearLoggers("hsqldb.db." + getNameString()); if (he != null) { throw he; } } private void setState(int state) { dbState = state; } int getState() { return dbState; } String getStateString() { int state = getState(); switch (state) { case DATABASE_CLOSING : return "DATABASE_CLOSING"; case DATABASE_ONLINE : return "DATABASE_ONLINE"; case DATABASE_OPENING : return "DATABASE_OPENING"; case DATABASE_SHUTDOWN : return "DATABASE_SHUTDOWN"; default : return "UNKNOWN"; } } public String[] getSettingsSQL() { HsqlArrayList list = new HsqlArrayList(); StringBuilder sb = new StringBuilder(); if (!getCatalogName().name.equals( SqlInvariants.DEFAULT_CATALOG_NAME)) { String name = getCatalogName().statementName; sb.append("ALTER CATALOG PUBLIC RENAME TO ").append(name); list.add(sb.toString()); sb.setLength(0); } if (!collation.isDefaultCollation() || !collation.isPadSpace()) { list.add(collation.getDatabaseCollationSQL()); } HashMappedList lobTables = schemaManager.getTables(SqlInvariants.LOBS_SCHEMA); for (int i = 0; i < lobTables.size(); i++) { Table table = (Table) lobTables.get(i); if (table.isCached()) { sb.append(Tokens.T_SET).append(' ').append(Tokens.T_TABLE); sb.append(' '); sb.append(table.getName().getSchemaQualifiedStatementName()); sb.append(' ').append(Tokens.T_TYPE).append(' '); sb.append(Tokens.T_CACHED); list.add(sb.toString()); sb.setLength(0); } } String[] array = new String[list.size()]; list.toArray(array); return array; } /** * Returns the schema and authorisation statements for the database. */ public Result getScript(boolean indexRoots) { Result r = Result.newSingleColumnResult("COMMAND"); // properties String[] list = logger.getPropertiesSQL(indexRoots); r.addRows(list); list = getSettingsSQL(); r.addRows(list); list = granteeManager.getSQL(); r.addRows(list); // schemas and schema objects such as tables, sequences, etc. list = schemaManager.getSQLArray(); r.addRows(list); // optional comments on tables etc. list = schemaManager.getCommentsSQL(); r.addRows(list); list = schemaManager.getTableSpaceSQL(); r.addRows(list); // index roots if (indexRoots) { list = schemaManager.getIndexRootsSQL(); r.addRows(list); } // text headers - readonly - clustered list = schemaManager.getTablePropsSQL(!indexRoots); r.addRows(list); // password complexity list = userManager.getAuthenticationSQL(); r.addRows(list); // user session start schema names list = userManager.getInitialSchemaSQL(); r.addRows(list); // grantee rights list = granteeManager.getRightsSQL(); r.addRows(list); return r; } public String getURI() { return databaseType.value() + canonicalPath; } public String getCanonicalPath() { return canonicalPath; } public HsqlProperties getURLProperties() { return urlProperties; } public TimeoutRunner getTimeoutRunner() { return timeoutRunner; } class CheckpointRunner implements Runnable { private volatile boolean waiting; private Object timerTask; public void run() { Statement checkpoint = ParserCommand.getAutoCheckpointStatement(Database.this); Session sysSession = sessionManager.newSysSession(); try { sysSession.executeCompiledStatement(checkpoint, ValuePool.emptyObjectArray, 0); } catch (Throwable e) { // ignore exceptions // may be InterruptedException or IOException } finally { sysSession.commit(false); sysSession.close(); waiting = false; } } public void start() { if (!logger.isLogged()) { return; } synchronized (this) { if (waiting) { return; } waiting = true; } timerTask = DatabaseManager.getTimer().scheduleAfter(0, this); } public void stop() { HsqlTimer.cancel(timerTask); timerTask = null; waiting = false; } } static class TimeoutRunner implements Runnable { private Object timerTask; OrderedHashSet sessionList; 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 void stop() { synchronized (this) { if (timerTask == null) { return; } HsqlTimer.cancel(timerTask); sessionList.clear(); timerTask = null; sessionList = null; } } public void addSession(Session session) { synchronized (this) { if (timerTask == null) { start(); } sessionList.add(session); } } private void start() { sessionList = new OrderedHashSet(); timerTask = DatabaseManager.getTimer().schedulePeriodicallyAfter(0, 1000, this, true); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy