/*
 * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.engine;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

import org.h2.api.DatabaseEventListener;
import org.h2.api.ErrorCode;
import org.h2.api.JavaObjectSerializer;
import org.h2.api.TableEngine;
import org.h2.command.CommandInterface;
import org.h2.command.ddl.CreateTableData;
import org.h2.command.dml.SetTypes;
import org.h2.constraint.Constraint;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.message.TraceSystem;
import org.h2.mvstore.MVStore;
import org.h2.mvstore.db.MVTableEngine;
import org.h2.result.LocalResultFactory;
import org.h2.result.Row;
import org.h2.result.RowFactory;
import org.h2.result.SearchRow;
import org.h2.schema.Schema;
import org.h2.schema.SchemaObject;
import org.h2.schema.Sequence;
import org.h2.schema.TriggerObject;
import org.h2.security.auth.Authenticator;
import org.h2.store.DataHandler;
import org.h2.store.FileLock;
import org.h2.store.FileLockMethod;
import org.h2.store.FileStore;
import org.h2.store.InDoubtTransaction;
import org.h2.store.LobStorageBackend;
import org.h2.store.LobStorageFrontend;
import org.h2.store.LobStorageInterface;
import org.h2.store.LobStorageMap;
import org.h2.store.PageStore;
import org.h2.store.WriterThread;
import org.h2.store.fs.FileUtils;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.MetaTable;
import org.h2.table.Table;
import org.h2.table.TableLinkConnection;
import org.h2.table.TableSynonym;
import org.h2.table.TableType;
import org.h2.table.TableView;
import org.h2.tools.DeleteDbFiles;
import org.h2.tools.Server;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
import org.h2.util.SmallLRUCache;
import org.h2.util.SourceCompiler;
import org.h2.util.StringUtils;
import org.h2.util.TempFileDeleter;
import org.h2.util.Utils;
import org.h2.value.CaseInsensitiveMap;
import org.h2.value.CompareMode;
import org.h2.value.CaseInsensitiveConcurrentMap;
import org.h2.value.Value;
import org.h2.value.ValueInt;

There is one database object per open database. The format of the meta data table is: id int, 0, objectType int, sql varchar
Since:2004-04-15 22:49
/** * There is one database object per open database. * * The format of the meta data table is: * id int, 0, objectType int, sql varchar * * @since 2004-04-15 22:49 */
public class Database implements DataHandler { private static int initialPowerOffCount; private static final boolean ASSERT; private static final ThreadLocal<Session> META_LOCK_DEBUGGING; private static final ThreadLocal<Database> META_LOCK_DEBUGGING_DB; private static final ThreadLocal<Throwable> META_LOCK_DEBUGGING_STACK; private static final Session[] EMPTY_SESSION_ARRAY = new Session[0]; static { boolean a = false; // Intentional side-effect assert a = true; ASSERT = a; if (a) { META_LOCK_DEBUGGING = new ThreadLocal<>(); META_LOCK_DEBUGGING_DB = new ThreadLocal<>(); META_LOCK_DEBUGGING_STACK = new ThreadLocal<>(); } else { META_LOCK_DEBUGGING = null; META_LOCK_DEBUGGING_DB = null; META_LOCK_DEBUGGING_STACK = null; } }
The default name of the system user. This name is only used as long as there is no administrator user registered.
/** * The default name of the system user. This name is only used as long as * there is no administrator user registered. */
private static final String SYSTEM_USER_NAME = "DBA"; private final boolean persistent; private final String databaseName; private final String databaseShortName; private final String databaseURL; private final String cipher; private final byte[] filePasswordHash; private final byte[] fileEncryptionKey; private final HashMap<String, Role> roles = new HashMap<>(); private final HashMap<String, User> users = new HashMap<>(); private final HashMap<String, Setting> settings = new HashMap<>(); private final HashMap<String, Schema> schemas = new HashMap<>(); private final HashMap<String, Right> rights = new HashMap<>(); private final HashMap<String, Domain> domains = new HashMap<>(); private final HashMap<String, UserAggregate> aggregates = new HashMap<>(); private final HashMap<String, Comment> comments = new HashMap<>(); private final HashMap<String, TableEngine> tableEngines = new HashMap<>(); private final Set<Session> userSessions = Collections.synchronizedSet(new HashSet<Session>()); private final AtomicReference<Session> exclusiveSession = new AtomicReference<>(); private final BitSet objectIds = new BitSet(); private final Object lobSyncObject = new Object(); private Schema mainSchema; private Schema infoSchema; private int nextSessionId; private int nextTempTableId; private User systemUser; private Session systemSession; private Session lobSession; private Table meta; private Index metaIdIndex; private FileLock lock; private WriterThread writer; private volatile boolean starting; private TraceSystem traceSystem; private Trace trace; private final FileLockMethod fileLockMethod; private Role publicRole; private final AtomicLong modificationDataId = new AtomicLong(); private final AtomicLong modificationMetaId = new AtomicLong(); private CompareMode compareMode; private String cluster = Constants.CLUSTERING_DISABLED; private boolean readOnly; private int writeDelay = Constants.DEFAULT_WRITE_DELAY; private DatabaseEventListener eventListener; private int maxMemoryRows = SysProperties.MAX_MEMORY_ROWS; private int maxMemoryUndo = Constants.DEFAULT_MAX_MEMORY_UNDO; private int lockMode = Constants.DEFAULT_LOCK_MODE; private int maxLengthInplaceLob; private int allowLiterals = Constants.ALLOW_LITERALS_ALL; private int powerOffCount = initialPowerOffCount; private volatile int closeDelay; private DelayedDatabaseCloser delayedCloser; private volatile boolean closing; private boolean ignoreCase; private boolean deleteFilesOnDisconnect; private String lobCompressionAlgorithm; private boolean optimizeReuseResults = true; private final String cacheType; private final String accessModeData; private boolean referentialIntegrity = true; private Mode mode = Mode.getRegular();
ie. the MULTI_THREADED setting
/** ie. the MULTI_THREADED setting */
private boolean multiThreaded; private int maxOperationMemory = Constants.DEFAULT_MAX_OPERATION_MEMORY; private SmallLRUCache<String, String[]> lobFileListCache; private final boolean autoServerMode; private final int autoServerPort; private Server server; private HashMap<TableLinkConnection, TableLinkConnection> linkConnections; private final TempFileDeleter tempFileDeleter = TempFileDeleter.getInstance(); private PageStore pageStore; private Properties reconnectLastLock; private volatile long reconnectCheckNext; private volatile boolean reconnectChangePending; private volatile int checkpointAllowed; private volatile boolean checkpointRunning; private final Object reconnectSync = new Object(); private int cacheSize; private int compactMode; private SourceCompiler compiler; private volatile boolean metaTablesInitialized; private boolean flushOnEachCommit; private LobStorageInterface lobStorage; private final int pageSize; private int defaultTableType = Table.TYPE_CACHED; private final DbSettings dbSettings; private final long reconnectCheckDelayNs; private int logMode; private MVTableEngine.Store store; private int retentionTime; private boolean allowBuiltinAliasOverride; private final AtomicReference<DbException> backgroundException = new AtomicReference<>(); private JavaObjectSerializer javaObjectSerializer; private String javaObjectSerializerName; private volatile boolean javaObjectSerializerInitialized; private boolean queryStatistics; private int queryStatisticsMaxEntries = Constants.QUERY_STATISTICS_MAX_ENTRIES; private QueryStatisticsData queryStatisticsData; private RowFactory rowFactory = RowFactory.DEFAULT; private LocalResultFactory resultFactory = LocalResultFactory.DEFAULT; private Authenticator authenticator; public Database(ConnectionInfo ci, String cipher) { if (ASSERT) { META_LOCK_DEBUGGING.set(null); META_LOCK_DEBUGGING_DB.set(null); META_LOCK_DEBUGGING_STACK.set(null); } String name = ci.getName(); this.dbSettings = ci.getDbSettings(); this.reconnectCheckDelayNs = TimeUnit.MILLISECONDS.toNanos(dbSettings.reconnectCheckDelay); this.compareMode = CompareMode.getInstance(null, 0); this.persistent = ci.isPersistent(); this.filePasswordHash = ci.getFilePasswordHash(); this.fileEncryptionKey = ci.getFileEncryptionKey(); this.databaseName = name; this.databaseShortName = parseDatabaseShortName(); this.maxLengthInplaceLob = Constants.DEFAULT_MAX_LENGTH_INPLACE_LOB; this.cipher = cipher; String lockMethodName = ci.getProperty("FILE_LOCK", null); this.accessModeData = StringUtils.toLowerEnglish( ci.getProperty("ACCESS_MODE_DATA", "rw")); this.autoServerMode = ci.getProperty("AUTO_SERVER", false); this.autoServerPort = ci.getProperty("AUTO_SERVER_PORT", 0); int defaultCacheSize = Utils.scaleForAvailableMemory( Constants.CACHE_SIZE_DEFAULT); this.cacheSize = ci.getProperty("CACHE_SIZE", defaultCacheSize); this.pageSize = ci.getProperty("PAGE_SIZE", Constants.DEFAULT_PAGE_SIZE); if ("r".equals(accessModeData)) { readOnly = true; } if (dbSettings.mvStore && lockMethodName == null) { if (autoServerMode) { fileLockMethod = FileLockMethod.FILE; } else { fileLockMethod = FileLockMethod.FS; } } else { fileLockMethod = FileLock.getFileLockMethod(lockMethodName); } if (dbSettings.mvStore && fileLockMethod == FileLockMethod.SERIALIZED) { throw DbException.getUnsupportedException( "MV_STORE combined with FILE_LOCK=SERIALIZED"); } this.databaseURL = ci.getURL(); String listener = ci.removeProperty("DATABASE_EVENT_LISTENER", null); if (listener != null) { listener = StringUtils.trim(listener, true, true, "'"); setEventListenerClass(listener); } String modeName = ci.removeProperty("MODE", null); if (modeName != null) { mode = Mode.getInstance(modeName); if (mode == null) { throw DbException.get(ErrorCode.UNKNOWN_MODE_1, modeName); } } this.logMode = ci.getProperty("LOG", PageStore.LOG_MODE_SYNC); this.javaObjectSerializerName = ci.getProperty("JAVA_OBJECT_SERIALIZER", null); this.multiThreaded = ci.getProperty("MULTI_THREADED", dbSettings.mvStore); this.allowBuiltinAliasOverride = ci.getProperty("BUILTIN_ALIAS_OVERRIDE", false); boolean closeAtVmShutdown = dbSettings.dbCloseOnExit; int traceLevelFile = ci.getIntProperty(SetTypes.TRACE_LEVEL_FILE, TraceSystem.DEFAULT_TRACE_LEVEL_FILE); int traceLevelSystemOut = ci.getIntProperty(SetTypes.TRACE_LEVEL_SYSTEM_OUT, TraceSystem.DEFAULT_TRACE_LEVEL_SYSTEM_OUT); this.cacheType = StringUtils.toUpperEnglish( ci.removeProperty("CACHE_TYPE", Constants.CACHE_TYPE_DEFAULT)); openDatabase(traceLevelFile, traceLevelSystemOut, closeAtVmShutdown, ci); } private void openDatabase(int traceLevelFile, int traceLevelSystemOut, boolean closeAtVmShutdown, ConnectionInfo ci) { try { open(traceLevelFile, traceLevelSystemOut, ci); if (closeAtVmShutdown) { OnExitDatabaseCloser.register(this); } } catch (Throwable e) { try { if (e instanceof OutOfMemoryError) { e.fillInStackTrace(); } boolean alreadyOpen = e instanceof DbException && ((DbException) e).getErrorCode() == ErrorCode.DATABASE_ALREADY_OPEN_1; if (alreadyOpen) { stopServer(); } if (traceSystem != null) { if (e instanceof DbException && !alreadyOpen) { // only write if the database is not already in use trace.error(e, "opening {0}", databaseName); } traceSystem.close(); } closeOpenFilesAndUnlock(false); } catch(Throwable ex) { e.addSuppressed(ex); } throw DbException.convert(e); } } public int getLockTimeout() { Setting setting = findSetting( SetTypes.getTypeName(SetTypes.DEFAULT_LOCK_TIMEOUT)); return setting == null ? Constants.INITIAL_LOCK_TIMEOUT : setting.getIntValue(); }
Create a new row for a table.
Params:
  • data – the values
  • memory – whether the row is in memory
Returns:the created row
/** * Create a new row for a table. * * @param data the values * @param memory whether the row is in memory * @return the created row */
public Row createRow(Value[] data, int memory) { return rowFactory.createRow(data, memory); } public RowFactory getRowFactory() { return rowFactory; } public void setRowFactory(RowFactory rowFactory) { this.rowFactory = rowFactory; } public LocalResultFactory getResultFactory() { return resultFactory; } public void setResultFactory(LocalResultFactory resultFactory) { this.resultFactory = resultFactory; } public static void setInitialPowerOffCount(int count) { initialPowerOffCount = count; } public void setPowerOffCount(int count) { if (powerOffCount == -1) { return; } powerOffCount = count; } public MVTableEngine.Store getStore() { return store; } public void setStore(MVTableEngine.Store store) { this.store = store; this.retentionTime = store.getMvStore().getRetentionTime(); }
Check if two values are equal with the current comparison mode.
Params:
  • a – the first value
  • b – the second value
Returns:true if both objects are equal
/** * Check if two values are equal with the current comparison mode. * * @param a the first value * @param b the second value * @return true if both objects are equal */
public boolean areEqual(Value a, Value b) { // can not use equals because ValueDecimal 0.0 is not equal to 0.00. return a.compareTo(b, mode, compareMode) == 0; }
Compare two values with the current comparison mode. The values may have different data types including NULL.
Params:
  • a – the first value
  • b – the second value
Returns:0 if both values are equal, -1 if the first value is smaller, and 1 otherwise
/** * Compare two values with the current comparison mode. The values may have * different data types including NULL. * * @param a the first value * @param b the second value * @return 0 if both values are equal, -1 if the first value is smaller, and * 1 otherwise */
public int compare(Value a, Value b) { return a.compareTo(b, mode, compareMode); }
Compare two values with the current comparison mode. The values may have different data types including NULL.
Params:
  • a – the first value
  • b – the second value
  • forEquality – perform only check for equality (= or <>)
Returns:0 if both values are equal, -1 if the first value is smaller, 1 if the second value is larger, Integer.MIN_VALUE if order is not defined due to NULL comparison
/** * Compare two values with the current comparison mode. The values may have * different data types including NULL. * * @param a the first value * @param b the second value * @param forEquality perform only check for equality (= or &lt;&gt;) * @return 0 if both values are equal, -1 if the first value is smaller, 1 * if the second value is larger, {@link Integer#MIN_VALUE} if order * is not defined due to NULL comparison */
public int compareWithNull(Value a, Value b, boolean forEquality) { return a.compareWithNull(b, forEquality, mode, compareMode); }
Compare two values with the current comparison mode. The values must be of the same type.
Params:
  • a – the first value
  • b – the second value
Returns:0 if both values are equal, -1 if the first value is smaller, and 1 otherwise
/** * Compare two values with the current comparison mode. The values must be * of the same type. * * @param a the first value * @param b the second value * @return 0 if both values are equal, -1 if the first value is smaller, and * 1 otherwise */
public int compareTypeSafe(Value a, Value b) { return a.compareTypeSafe(b, compareMode); } public long getModificationDataId() { return modificationDataId.get(); }
Set or reset the pending change flag in the .lock.db file.
Params:
  • pending – the new value of the flag
Returns:true if the call was successful, false if another connection was faster
/** * Set or reset the pending change flag in the .lock.db file. * * @param pending the new value of the flag * @return true if the call was successful, * false if another connection was faster */
private synchronized boolean reconnectModified(boolean pending) { if (readOnly || lock == null || fileLockMethod != FileLockMethod.SERIALIZED) { return true; } try { if (pending == reconnectChangePending) { long now = System.nanoTime(); if (now > reconnectCheckNext) { if (pending) { String pos = pageStore == null ? null : Long.toString(pageStore.getWriteCountTotal()); lock.setProperty("logPos", pos); lock.save(); } reconnectCheckNext = now + reconnectCheckDelayNs; } return true; } Properties old = lock.load(); if (pending) { if (old.getProperty("changePending") != null) { return false; } trace.debug("wait before writing"); Thread.sleep(TimeUnit.NANOSECONDS.toMillis((long) (reconnectCheckDelayNs * 1.1))); Properties now = lock.load(); if (!now.equals(old)) { // somebody else was faster return false; } } String pos = pageStore == null ? null : Long.toString(pageStore.getWriteCountTotal()); lock.setProperty("logPos", pos); if (pending) { lock.setProperty("changePending", "true-" + Math.random()); } else { lock.setProperty("changePending", null); } // ensure that the writer thread will // not reset the flag before we are done reconnectCheckNext = System.nanoTime() + 2 * reconnectCheckDelayNs; old = lock.save(); if (pending) { trace.debug("wait before writing again"); Thread.sleep(TimeUnit.NANOSECONDS.toMillis((long) (reconnectCheckDelayNs * 1.1))); Properties now = lock.load(); if (!now.equals(old)) { // somebody else was faster return false; } } else { Thread.sleep(1); } reconnectLastLock = old; reconnectChangePending = pending; reconnectCheckNext = System.nanoTime() + reconnectCheckDelayNs; return true; } catch (Exception e) { trace.error(e, "pending {0}", pending); return false; } } public long getNextModificationDataId() { return modificationDataId.incrementAndGet(); } public long getModificationMetaId() { return modificationMetaId.get(); } public long getNextModificationMetaId() { // if the meta data has been modified, the data is modified as well // (because MetaTable returns modificationDataId) modificationDataId.incrementAndGet(); return modificationMetaId.incrementAndGet() - 1; } public int getPowerOffCount() { return powerOffCount; } @Override public void checkPowerOff() { if (powerOffCount == 0) { return; } if (powerOffCount > 1) { powerOffCount--; return; } if (powerOffCount != -1) { try { powerOffCount = -1; stopWriter(); if (store != null) { store.closeImmediately(); } synchronized(this) { if (pageStore != null) { try { pageStore.close(); } catch (DbException e) { // ignore } pageStore = null; } } if (lock != null) { stopServer(); if (fileLockMethod != FileLockMethod.SERIALIZED) { // allow testing shutdown lock.unlock(); } lock = null; } if (traceSystem != null) { traceSystem.close(); } } catch (DbException e) { DbException.traceThrowable(e); } } Engine.getInstance().close(databaseName); throw DbException.get(ErrorCode.DATABASE_IS_CLOSED); }
Check if a database with the given name exists.
Params:
  • name – the name of the database (including path)
Returns:true if one exists
/** * Check if a database with the given name exists. * * @param name the name of the database (including path) * @return true if one exists */
static boolean exists(String name) { if (FileUtils.exists(name + Constants.SUFFIX_PAGE_FILE)) { return true; } return FileUtils.exists(name + Constants.SUFFIX_MV_FILE); }
Get the trace object for the given module id.
Params:
  • moduleId – the module id
Returns:the trace object
/** * Get the trace object for the given module id. * * @param moduleId the module id * @return the trace object */
public Trace getTrace(int moduleId) { return traceSystem.getTrace(moduleId); } @Override public FileStore openFile(String name, String openMode, boolean mustExist) { if (mustExist && !FileUtils.exists(name)) { throw DbException.get(ErrorCode.FILE_NOT_FOUND_1, name); } FileStore store = FileStore.open(this, name, openMode, cipher, filePasswordHash); try { store.init(); } catch (DbException e) { store.closeSilently(); throw e; } return store; }
Check if the file password hash is correct.
Params:
  • testCipher – the cipher algorithm
  • testHash – the hash code
Returns:true if the cipher algorithm and the password match
/** * Check if the file password hash is correct. * * @param testCipher the cipher algorithm * @param testHash the hash code * @return true if the cipher algorithm and the password match */
boolean validateFilePasswordHash(String testCipher, byte[] testHash) { if (!Objects.equals(testCipher, this.cipher)) { return false; } return Utils.compareSecure(testHash, filePasswordHash); } private String parseDatabaseShortName() { String n = databaseName; if (n.endsWith(":")) { n = null; } if (n != null) { StringTokenizer tokenizer = new StringTokenizer(n, "/\\:,;"); while (tokenizer.hasMoreTokens()) { n = tokenizer.nextToken(); } } if (n == null || n.isEmpty()) { n = "unnamed"; } return dbSettings.databaseToUpper ? StringUtils.toUpperEnglish(n) : dbSettings.databaseToLower ? StringUtils.toLowerEnglish(n) : n; } private synchronized void open(int traceLevelFile, int traceLevelSystemOut, ConnectionInfo ci) { if (persistent) { String dataFileName = databaseName + Constants.SUFFIX_OLD_DATABASE_FILE; boolean existsData = FileUtils.exists(dataFileName); String pageFileName = databaseName + Constants.SUFFIX_PAGE_FILE; String mvFileName = databaseName + Constants.SUFFIX_MV_FILE; boolean existsPage = FileUtils.exists(pageFileName); boolean existsMv = FileUtils.exists(mvFileName); if (existsData && (!existsPage && !existsMv)) { throw DbException.get( ErrorCode.FILE_VERSION_ERROR_1, "Old database: " + dataFileName + " - please convert the database " + "to a SQL script and re-create it."); } if (existsPage && !FileUtils.canWrite(pageFileName)) { readOnly = true; } if (existsMv && !FileUtils.canWrite(mvFileName)) { readOnly = true; } if (existsPage && !existsMv) { dbSettings.mvStore = false; // Need to re-init this because the first time we do it we don't // know if we have an mvstore or a pagestore. multiThreaded = ci.getProperty("MULTI_THREADED", false); } if (readOnly) { if (traceLevelFile >= TraceSystem.DEBUG) { String traceFile = Utils.getProperty("java.io.tmpdir", ".") + "/" + "h2_" + System.currentTimeMillis(); traceSystem = new TraceSystem(traceFile + Constants.SUFFIX_TRACE_FILE); } else { traceSystem = new TraceSystem(null); } } else { traceSystem = new TraceSystem(databaseName + Constants.SUFFIX_TRACE_FILE); } traceSystem.setLevelFile(traceLevelFile); traceSystem.setLevelSystemOut(traceLevelSystemOut); trace = traceSystem.getTrace(Trace.DATABASE); trace.info("opening {0} (build {1})", databaseName, Constants.BUILD_ID); if (autoServerMode) { if (readOnly || fileLockMethod == FileLockMethod.NO || fileLockMethod == FileLockMethod.SERIALIZED || fileLockMethod == FileLockMethod.FS) { throw DbException.getUnsupportedException( "autoServerMode && (readOnly || " + "fileLockMethod == NO || " + "fileLockMethod == SERIALIZED || " + "fileLockMethod == FS || " + "inMemory)"); } } String lockFileName = databaseName + Constants.SUFFIX_LOCK_FILE; if (readOnly) { if (FileUtils.exists(lockFileName)) { throw DbException.get(ErrorCode.DATABASE_ALREADY_OPEN_1, "Lock file exists: " + lockFileName); } } if (!readOnly && fileLockMethod != FileLockMethod.NO) { if (fileLockMethod != FileLockMethod.FS) { lock = new FileLock(traceSystem, lockFileName, Constants.LOCK_SLEEP); lock.lock(fileLockMethod); if (autoServerMode) { startServer(lock.getUniqueId()); } } } if (SysProperties.MODIFY_ON_WRITE) { while (isReconnectNeeded()) { // wait until others stopped writing } } else { while (isReconnectNeeded() && !beforeWriting()) { // wait until others stopped writing and // until we can write (the file is not yet open - // no need to re-connect) } } deleteOldTempFiles(); starting = true; if (SysProperties.MODIFY_ON_WRITE) { try { getPageStore(); } catch (DbException e) { if (e.getErrorCode() != ErrorCode.DATABASE_IS_READ_ONLY) { throw e; } pageStore = null; while (!beforeWriting()) { // wait until others stopped writing and // until we can write (the file is not yet open - // no need to re-connect) } getPageStore(); } } else { getPageStore(); } starting = false; if (store == null) { writer = WriterThread.create(this, writeDelay); } else { setWriteDelay(writeDelay); } } else { if (autoServerMode) { throw DbException.getUnsupportedException( "autoServerMode && inMemory"); } traceSystem = new TraceSystem(null); trace = traceSystem.getTrace(Trace.DATABASE); if (dbSettings.mvStore) { getPageStore(); } } if(store != null) { store.getTransactionStore().init(); } systemUser = new User(this, 0, SYSTEM_USER_NAME, true); mainSchema = new Schema(this, Constants.MAIN_SCHEMA_ID, sysIdentifier(Constants.SCHEMA_MAIN), systemUser, true); infoSchema = new Schema(this, Constants.INFORMATION_SCHEMA_ID, sysIdentifier("INFORMATION_SCHEMA"), systemUser, true); schemas.put(mainSchema.getName(), mainSchema); schemas.put(infoSchema.getName(), infoSchema); publicRole = new Role(this, 0, sysIdentifier(Constants.PUBLIC_ROLE_NAME), true); roles.put(publicRole.getName(), publicRole); systemUser.setAdmin(true); systemSession = new Session(this, systemUser, ++nextSessionId); lobSession = new Session(this, systemUser, ++nextSessionId); CreateTableData data = new CreateTableData(); ArrayList<Column> cols = data.columns; Column columnId = new Column("ID", Value.INT); columnId.setNullable(false); cols.add(columnId); cols.add(new Column("HEAD", Value.INT)); cols.add(new Column("TYPE", Value.INT)); cols.add(new Column("SQL", Value.STRING)); boolean create = true; if (pageStore != null) { create = pageStore.isNew(); } data.tableName = "SYS"; data.id = 0; data.temporary = false; data.persistData = persistent; data.persistIndexes = persistent; data.create = create; data.isHidden = true; data.session = systemSession; starting = true; meta = mainSchema.createTable(data); handleUpgradeIssues(); IndexColumn[] pkCols = IndexColumn.wrap(new Column[] { columnId }); metaIdIndex = meta.addIndex(systemSession, "SYS_ID", 0, pkCols, IndexType.createPrimaryKey( false, false), true, null); systemSession.commit(true); objectIds.set(0); Cursor cursor = metaIdIndex.find(systemSession, null, null); ArrayList<MetaRecord> records = new ArrayList<>((int) metaIdIndex.getRowCountApproximation()); while (cursor.next()) { MetaRecord rec = new MetaRecord(cursor.get()); objectIds.set(rec.getId()); records.add(rec); } Collections.sort(records); synchronized (systemSession) { for (MetaRecord rec : records) { rec.execute(this, systemSession, eventListener); } } systemSession.commit(true); if (store != null) { store.getTransactionStore().endLeftoverTransactions(); store.removeTemporaryMaps(objectIds); } recompileInvalidViews(systemSession); starting = false; if (!readOnly) { // set CREATE_BUILD in a new database String name = SetTypes.getTypeName(SetTypes.CREATE_BUILD); if (settings.get(name) == null) { Setting setting = new Setting(this, allocateObjectId(), name); setting.setIntValue(Constants.BUILD_ID); lockMeta(systemSession); addDatabaseObject(systemSession, setting); } setSortSetting(SetTypes.BINARY_COLLATION, SysProperties.SORT_BINARY_UNSIGNED, true); setSortSetting(SetTypes.UUID_COLLATION, SysProperties.SORT_UUID_UNSIGNED, false); // mark all ids used in the page store if (pageStore != null) { BitSet f = pageStore.getObjectIds(); for (int i = 0, len = f.length(); i < len; i++) { if (f.get(i) && !objectIds.get(i)) { trace.info("unused object id: " + i); objectIds.set(i); } } } } getLobStorage().init(); systemSession.commit(true); trace.info("opened {0}", databaseName); if (checkpointAllowed > 0) { afterWriting(); } }
Preserves a current default value of a sorting setting if it is not the same as default for older versions of H2 and if it was not modified by user.
Params:
  • type – setting type
  • defValue – current default value (may be modified via system properties)
  • oldDefault – default value for old versions
/** * Preserves a current default value of a sorting setting if it is not the * same as default for older versions of H2 and if it was not modified by * user. * * @param type * setting type * @param defValue * current default value (may be modified via system properties) * @param oldDefault * default value for old versions */
private void setSortSetting(int type, boolean defValue, boolean oldDefault) { if (defValue == oldDefault) { return; } String name = SetTypes.getTypeName(type); if (settings.get(name) == null) { Setting setting = new Setting(this, allocateObjectId(), name); setting.setStringValue(defValue ? CompareMode.UNSIGNED : CompareMode.SIGNED); lockMeta(systemSession); addDatabaseObject(systemSession, setting); } } private void handleUpgradeIssues() { if (store != null && !isReadOnly()) { MVStore mvStore = store.getMvStore(); // Version 1.4.197 erroneously handles index on SYS_ID.ID as secondary // and does not delegate to scan index as it should. // This code will try to fix that by converging ROW_ID and ID, // since they may have got out of sync, and by removing map "index.0", // which corresponds to a secondary index. if (mvStore.hasMap("index.0")) { Index scanIndex = meta.getScanIndex(systemSession); Cursor curs = scanIndex.find(systemSession, null, null); List<Row> allMetaRows = new ArrayList<>(); boolean needRepair = false; while (curs.next()) { Row row = curs.get(); allMetaRows.add(row); long rowId = row.getKey(); int id = row.getValue(0).getInt(); if (id != rowId) { needRepair = true; row.setKey(id); } } if (needRepair) { Row[] array = allMetaRows.toArray(new Row[0]); Arrays.sort(array, new Comparator<Row>() { @Override public int compare(Row o1, Row o2) { return Integer.compare(o1.getValue(0).getInt(), o2.getValue(0).getInt()); } }); meta.truncate(systemSession); for (Row row : array) { meta.addRow(systemSession, row); } systemSession.commit(true); } mvStore.removeMap("index.0"); mvStore.commit(); } } } private void startServer(String key) { try { server = Server.createTcpServer( "-tcpPort", Integer.toString(autoServerPort), "-tcpAllowOthers", "-tcpDaemon", "-key", key, databaseName); server.start(); } catch (SQLException e) { throw DbException.convert(e); } String localAddress = NetUtils.getLocalAddress(); String address = localAddress + ":" + server.getPort(); lock.setProperty("server", address); String hostName = NetUtils.getHostName(localAddress); lock.setProperty("hostName", hostName); lock.save(); } private void stopServer() { if (server != null) { Server s = server; // avoid calling stop recursively // because stopping the server will // try to close the database as well server = null; s.stop(); } } private void recompileInvalidViews(Session session) { boolean atLeastOneRecompiledSuccessfully; do { atLeastOneRecompiledSuccessfully = false; for (Table obj : getAllTablesAndViews(false)) { if (obj instanceof TableView) { TableView view = (TableView) obj; if (view.isInvalid()) { view.recompile(session, true, false); if (!view.isInvalid()) { atLeastOneRecompiledSuccessfully = true; } } } } } while (atLeastOneRecompiledSuccessfully); TableView.clearIndexCaches(session.getDatabase()); } private void initMetaTables() { if (metaTablesInitialized) { return; } synchronized (infoSchema) { if (!metaTablesInitialized) { for (int type = 0, count = MetaTable.getMetaTableTypeCount(); type < count; type++) { MetaTable m = new MetaTable(infoSchema, -1 - type, type); infoSchema.add(m); } metaTablesInitialized = true; } } } private void addMeta(Session session, DbObject obj) { assert Thread.holdsLock(this); int id = obj.getId(); if (id > 0 && !starting && !obj.isTemporary()) { Row r = meta.getTemplateRow(); MetaRecord.populateRowFromDBObject(obj, r); synchronized (objectIds) { objectIds.set(id); } if (SysProperties.CHECK) { verifyMetaLocked(session); } meta.addRow(session, r); } }
Verify the meta table is locked.
Params:
  • session – the session
/** * Verify the meta table is locked. * * @param session the session */
public void verifyMetaLocked(Session session) { if (meta != null && !meta.isLockedExclusivelyBy(session) && lockMode != Constants.LOCK_MODE_OFF) { throw DbException.throwInternalError(); } }
Lock the metadata table for updates.
Params:
  • session – the session
Returns:whether it was already locked before by this session
/** * Lock the metadata table for updates. * * @param session the session * @return whether it was already locked before by this session */
public boolean lockMeta(Session session) { // this method can not be synchronized on the database object, // as unlocking is also synchronized on the database object - // so if locking starts just before unlocking, locking could // never be successful if (meta == null) { return true; } if (ASSERT) { // If we are locking two different databases in the same stack, just ignore it. // This only happens in TestLinkedTable where we connect to another h2 DB in the // same process. if (META_LOCK_DEBUGGING_DB.get() != null && META_LOCK_DEBUGGING_DB.get() != this) { final Session prev = META_LOCK_DEBUGGING.get(); if (prev == null) { META_LOCK_DEBUGGING.set(session); META_LOCK_DEBUGGING_DB.set(this); META_LOCK_DEBUGGING_STACK.set(new Throwable("Last meta lock granted in this stack trace, "+ "this is debug information for following IllegalStateException")); } else if (prev != session) { META_LOCK_DEBUGGING_STACK.get().printStackTrace(); throw new IllegalStateException("meta currently locked by " + prev +", sessionid="+ prev.getId() + " and trying to be locked by different session, " + session +", sessionid="+ session.getId() + " on same thread"); } } } return meta.lock(session, true, true); }
Unlock the metadata table.
Params:
  • session – the session
/** * Unlock the metadata table. * * @param session the session */
public void unlockMeta(Session session) { if (meta != null) { unlockMetaDebug(session); meta.unlock(session); session.unlock(meta); } }
This method doesn't actually unlock the metadata table, all it does it reset the debugging flags.
Params:
  • session – the session
/** * This method doesn't actually unlock the metadata table, all it does it * reset the debugging flags. * * @param session the session */
public void unlockMetaDebug(Session session) { if (ASSERT) { if (META_LOCK_DEBUGGING.get() == session) { META_LOCK_DEBUGGING.set(null); META_LOCK_DEBUGGING_DB.set(null); META_LOCK_DEBUGGING_STACK.set(null); } } }
Remove the given object from the meta data.
Params:
  • session – the session
  • id – the id of the object to remove
/** * Remove the given object from the meta data. * * @param session the session * @param id the id of the object to remove */
public void removeMeta(Session session, int id) { if (id > 0 && !starting) { SearchRow r = meta.getTemplateSimpleRow(false); r.setValue(0, ValueInt.get(id)); boolean wasLocked = lockMeta(session); try { Cursor cursor = metaIdIndex.find(session, r, r); if (cursor.next()) { if (lockMode != Constants.LOCK_MODE_OFF && !wasLocked) { throw DbException.throwInternalError(); } Row found = cursor.get(); meta.removeRow(session, found); if (SysProperties.CHECK) { checkMetaFree(session, id); } } } finally { if (!wasLocked) { // must not keep the lock if it was not locked // otherwise updating sequences may cause a deadlock unlockMeta(session); } } if (isMVStore()) { // release of the object id has to be postponed until the end of the transaction, // otherwise it might be re-used prematurely, and it would make // rollback impossible or lead to MVMaps name collision, // so until then ids are accumulated within session session.scheduleDatabaseObjectIdForRelease(id); } else { // but PageStore, on the other hand, for reasons unknown to me, // requires immediate id release synchronized (this) { objectIds.clear(id); } } } }
Mark some database ids as unused.
Params:
  • idsToRelease – the ids to release
/** * Mark some database ids as unused. * @param idsToRelease the ids to release */
void releaseDatabaseObjectIds(BitSet idsToRelease) { synchronized (objectIds) { objectIds.andNot(idsToRelease); } } @SuppressWarnings("unchecked") private HashMap<String, DbObject> getMap(int type) { HashMap<String, ? extends DbObject> result; switch (type) { case DbObject.USER: result = users; break; case DbObject.SETTING: result = settings; break; case DbObject.ROLE: result = roles; break; case DbObject.RIGHT: result = rights; break; case DbObject.SCHEMA: result = schemas; break; case DbObject.DOMAIN: result = domains; break; case DbObject.COMMENT: result = comments; break; case DbObject.AGGREGATE: result = aggregates; break; default: throw DbException.throwInternalError("type=" + type); } return (HashMap<String, DbObject>) result; }
Add a schema object to the database.
Params:
  • session – the session
  • obj – the object to add
/** * Add a schema object to the database. * * @param session the session * @param obj the object to add */
public void addSchemaObject(Session session, SchemaObject obj) { int id = obj.getId(); if (id > 0 && !starting) { checkWritingAllowed(); } lockMeta(session); synchronized (this) { obj.getSchema().add(obj); addMeta(session, obj); } }
Add an object to the database.
Params:
  • session – the session
  • obj – the object to add
/** * Add an object to the database. * * @param session the session * @param obj the object to add */
public synchronized void addDatabaseObject(Session session, DbObject obj) { int id = obj.getId(); if (id > 0 && !starting) { checkWritingAllowed(); } HashMap<String, DbObject> map = getMap(obj.getType()); if (obj.getType() == DbObject.USER) { User user = (User) obj; if (user.isAdmin() && systemUser.getName().equals(SYSTEM_USER_NAME)) { systemUser.rename(user.getName()); } } String name = obj.getName(); if (SysProperties.CHECK && map.get(name) != null) { DbException.throwInternalError("object already exists"); } lockMeta(session); addMeta(session, obj); map.put(name, obj); }
Get the user defined aggregate function if it exists, or null if not.
Params:
  • name – the name of the user defined aggregate function
Returns:the aggregate function or null
/** * Get the user defined aggregate function if it exists, or null if not. * * @param name the name of the user defined aggregate function * @return the aggregate function or null */
public UserAggregate findAggregate(String name) { return aggregates.get(name); }
Get the comment for the given database object if one exists, or null if not.
Params:
  • object – the database object
Returns:the comment or null
/** * Get the comment for the given database object if one exists, or null if * not. * * @param object the database object * @return the comment or null */
public Comment findComment(DbObject object) { if (object.getType() == DbObject.COMMENT) { return null; } String key = Comment.getKey(object); return comments.get(key); }
Get the role if it exists, or null if not.
Params:
  • roleName – the name of the role
Returns:the role or null
/** * Get the role if it exists, or null if not. * * @param roleName the name of the role * @return the role or null */
public Role findRole(String roleName) { return roles.get(StringUtils.toUpperEnglish(roleName)); }
Get the schema if it exists, or null if not.
Params:
  • schemaName – the name of the schema
Returns:the schema or null
/** * Get the schema if it exists, or null if not. * * @param schemaName the name of the schema * @return the schema or null */
public Schema findSchema(String schemaName) { Schema schema = schemas.get(schemaName); if (schema == infoSchema) { initMetaTables(); } return schema; }
Get the setting if it exists, or null if not.
Params:
  • name – the name of the setting
Returns:the setting or null
/** * Get the setting if it exists, or null if not. * * @param name the name of the setting * @return the setting or null */
public Setting findSetting(String name) { return settings.get(name); }
Get the user if it exists, or null if not.
Params:
  • name – the name of the user
Returns:the user or null
/** * Get the user if it exists, or null if not. * * @param name the name of the user * @return the user or null */
public User findUser(String name) { return users.get(StringUtils.toUpperEnglish(name)); }
Get the domain if it exists, or null if not.
Params:
  • name – the name of the domain
Returns:the domain or null
/** * Get the domain if it exists, or null if not. * * @param name the name of the domain * @return the domain or null */
public Domain findDomain(String name) { return domains.get(name); }
Get user with the given name. This method throws an exception if the user does not exist.
Params:
  • name – the user name
Throws:
Returns:the user
/** * Get user with the given name. This method throws an exception if the user * does not exist. * * @param name the user name * @return the user * @throws DbException if the user does not exist */
public User getUser(String name) { User user = findUser(name); if (user == null) { throw DbException.get(ErrorCode.USER_NOT_FOUND_1, name); } return user; }
Create a session for the given user.
Params:
  • user – the user
Throws:
Returns:the session, or null if the database is currently closing
/** * Create a session for the given user. * * @param user the user * @return the session, or null if the database is currently closing * @throws DbException if the database is in exclusive mode */
synchronized Session createSession(User user) { if (closing) { return null; } if (exclusiveSession.get() != null) { throw DbException.get(ErrorCode.DATABASE_IS_IN_EXCLUSIVE_MODE); } Session session = new Session(this, user, ++nextSessionId); userSessions.add(session); trace.info("connecting session #{0} to {1}", session.getId(), databaseName); if (delayedCloser != null) { delayedCloser.reset(); delayedCloser = null; } return session; }
Remove a session. This method is called after the user has disconnected.
Params:
  • session – the session
/** * Remove a session. This method is called after the user has disconnected. * * @param session the session */
public synchronized void removeSession(Session session) { if (session != null) { exclusiveSession.compareAndSet(session, null); userSessions.remove(session); if (session != systemSession && session != lobSession) { trace.info("disconnecting session #{0}", session.getId()); } } if (userSessions.isEmpty() && session != systemSession && session != lobSession) { if (closeDelay == 0) { close(false); } else if (closeDelay < 0) { return; } else { delayedCloser = new DelayedDatabaseCloser(this, closeDelay * 1000); } } if (session != systemSession && session != lobSession && session != null) { trace.info("disconnected session #{0}", session.getId()); } } private synchronized void closeAllSessionsException(Session except) { Session[] all = userSessions.toArray(EMPTY_SESSION_ARRAY); for (Session s : all) { if (s != except) { try { // this will rollback outstanding transaction s.close(); } catch (DbException e) { trace.error(e, "disconnecting session #{0}", s.getId()); } } } }
Close the database.
Params:
  • fromShutdownHook – true if this method is called from the shutdown hook
/** * Close the database. * * @param fromShutdownHook true if this method is called from the shutdown * hook */
void close(boolean fromShutdownHook) { DbException b = backgroundException.getAndSet(null); try { closeImpl(fromShutdownHook); } catch (Throwable t) { if (b != null) { t.addSuppressed(b); } throw t; } if (b != null) { // wrap the exception, so we see it was thrown here throw DbException.get(b.getErrorCode(), b, b.getMessage()); } } private void closeImpl(boolean fromShutdownHook) { try { synchronized (this) { if (closing) { return; } if (fileLockMethod == FileLockMethod.SERIALIZED && !reconnectChangePending) { // another connection may have written something - don't write try { closeOpenFilesAndUnlock(false); } catch (DbException e) { // ignore } traceSystem.close(); return; } closing = true; stopServer(); if (!userSessions.isEmpty()) { if (!fromShutdownHook) { return; } trace.info("closing {0} from shutdown hook", databaseName); closeAllSessionsException(null); } trace.info("closing {0}", databaseName); if (eventListener != null) { // allow the event listener to connect to the database closing = false; DatabaseEventListener e = eventListener; // set it to null, to make sure it's called only once eventListener = null; e.closingDatabase(); if (!userSessions.isEmpty()) { // if a connection was opened, we can't close the database return; } closing = true; } if (!this.isReadOnly()) { removeOrphanedLobs(); } } try { if (systemSession != null) { if (powerOffCount != -1) { for (Table table : getAllTablesAndViews(false)) { if (table.isGlobalTemporary()) { table.removeChildrenAndResources(systemSession); } else { table.close(systemSession); } } for (SchemaObject obj : getAllSchemaObjects( DbObject.SEQUENCE)) { Sequence sequence = (Sequence) obj; sequence.close(); } } for (SchemaObject obj : getAllSchemaObjects( DbObject.TRIGGER)) { TriggerObject trigger = (TriggerObject) obj; try { trigger.close(); } catch (SQLException e) { trace.error(e, "close"); } } if (powerOffCount != -1) { meta.close(systemSession); systemSession.commit(true); } } } catch (DbException e) { trace.error(e, "close"); } tempFileDeleter.deleteAll(); try { closeOpenFilesAndUnlock(true); } catch (DbException e) { trace.error(e, "close"); } trace.info("closed"); traceSystem.close(); OnExitDatabaseCloser.unregister(this); if (deleteFilesOnDisconnect && persistent) { deleteFilesOnDisconnect = false; try { String directory = FileUtils.getParent(databaseName); String name = FileUtils.getName(databaseName); DeleteDbFiles.execute(directory, name, true); } catch (Exception e) { // ignore (the trace is closed already) } } } finally { Engine.getInstance().close(databaseName); } } private void removeOrphanedLobs() { // remove all session variables and temporary lobs if (!persistent) { return; } boolean lobStorageIsUsed = infoSchema.findTableOrView( systemSession, LobStorageBackend.LOB_DATA_TABLE) != null; lobStorageIsUsed |= store != null; if (!lobStorageIsUsed) { return; } try { getLobStorage(); lobStorage.removeAllForTable( LobStorageFrontend.TABLE_ID_SESSION_VARIABLE); } catch (DbException e) { trace.error(e, "close"); } } private void stopWriter() { if (writer != null) { writer.stopThread(); writer = null; } }
Close all open files and unlock the database.
Params:
  • flush – whether writing is allowed
/** * Close all open files and unlock the database. * * @param flush whether writing is allowed */
private synchronized void closeOpenFilesAndUnlock(boolean flush) { try { stopWriter(); if (pageStore != null) { if (flush) { try { pageStore.checkpoint(); if (!readOnly) { lockMeta(pageStore.getPageStoreSession()); pageStore.compact(compactMode); unlockMeta(pageStore.getPageStoreSession()); } } catch (DbException e) { if (ASSERT) { int code = e.getErrorCode(); if (code != ErrorCode.DATABASE_IS_CLOSED && code != ErrorCode.LOCK_TIMEOUT_1 && code != ErrorCode.IO_EXCEPTION_2) { e.printStackTrace(); } } trace.error(e, "close"); } catch (Throwable t) { if (ASSERT) { t.printStackTrace(); } trace.error(t, "close"); } } } reconnectModified(false); if (store != null) { MVStore mvStore = store.getMvStore(); if (mvStore != null && !mvStore.isClosed()) { boolean compactFully = compactMode == CommandInterface.SHUTDOWN_COMPACT || compactMode == CommandInterface.SHUTDOWN_DEFRAG || getSettings().defragAlways; if (!compactFully && !mvStore.isReadOnly()) { if (dbSettings.maxCompactTime > 0) { try { store.compactFile(dbSettings.maxCompactTime); } catch (Throwable t) { trace.error(t, "compactFile"); } } else { mvStore.commit(); } } store.close(compactFully); } } if (systemSession != null) { systemSession.close(); systemSession = null; } if (lobSession != null) { lobSession.close(); lobSession = null; } closeFiles(); if (persistent && lock == null && fileLockMethod != FileLockMethod.NO && fileLockMethod != FileLockMethod.FS) { // everything already closed (maybe in checkPowerOff) // don't delete temp files in this case because // the database could be open now (even from within another process) return; } if (persistent) { deleteOldTempFiles(); } } finally { if (lock != null) { if (fileLockMethod == FileLockMethod.SERIALIZED) { // wait before deleting the .lock file, // otherwise other connections can not detect that if (lock.load().containsKey("changePending")) { try { Thread.sleep(TimeUnit.NANOSECONDS .toMillis((long) (reconnectCheckDelayNs * 1.1))); } catch (InterruptedException e) { trace.error(e, "close"); } } } lock.unlock(); lock = null; } } } private synchronized void closeFiles() { try { if (store != null) { store.closeImmediately(); } if (pageStore != null) { pageStore.close(); pageStore = null; } } catch (DbException e) { trace.error(e, "close"); } } private void checkMetaFree(Session session, int id) { SearchRow r = meta.getTemplateSimpleRow(false); r.setValue(0, ValueInt.get(id)); Cursor cursor = metaIdIndex.find(session, r, r); if (cursor.next()) { DbException.throwInternalError(); } }
Allocate a new object id.
Returns:the id
/** * Allocate a new object id. * * @return the id */
public int allocateObjectId() { Object lock = isMVStore() ? objectIds : this; int i; synchronized (lock) { i = objectIds.nextClearBit(0); objectIds.set(i); } return i; }
Returns main schema (usually PUBLIC).
Returns:main schema (usually PUBLIC)
/** * Returns main schema (usually PUBLIC). * * @return main schema (usually PUBLIC) */
public Schema getMainSchema() { return mainSchema; } public ArrayList<UserAggregate> getAllAggregates() { return new ArrayList<>(aggregates.values()); } public ArrayList<Comment> getAllComments() { return new ArrayList<>(comments.values()); } public int getAllowLiterals() { if (starting) { return Constants.ALLOW_LITERALS_ALL; } return allowLiterals; } public ArrayList<Right> getAllRights() { return new ArrayList<>(rights.values()); } public ArrayList<Role> getAllRoles() { return new ArrayList<>(roles.values()); }
Get all schema objects.
Returns:all objects of all types
/** * Get all schema objects. * * @return all objects of all types */
public ArrayList<SchemaObject> getAllSchemaObjects() { initMetaTables(); ArrayList<SchemaObject> list = new ArrayList<>(); for (Schema schema : schemas.values()) { schema.getAll(list); } return list; }
Get all schema objects of the given type.
Params:
  • type – the object type
Returns:all objects of that type
/** * Get all schema objects of the given type. * * @param type the object type * @return all objects of that type */
public ArrayList<SchemaObject> getAllSchemaObjects(int type) { if (type == DbObject.TABLE_OR_VIEW) { initMetaTables(); } ArrayList<SchemaObject> list = new ArrayList<>(); for (Schema schema : schemas.values()) { schema.getAll(type, list); } return list; }
Get all tables and views.
Params:
  • includeMeta – whether to force including the meta data tables (if true, metadata tables are always included; if false, metadata tables are only included if they are already initialized)
Returns:all objects of that type
/** * Get all tables and views. * * @param includeMeta whether to force including the meta data tables (if * true, metadata tables are always included; if false, metadata * tables are only included if they are already initialized) * @return all objects of that type */
public ArrayList<Table> getAllTablesAndViews(boolean includeMeta) { if (includeMeta) { initMetaTables(); } ArrayList<Table> list = new ArrayList<>(); for (Schema schema : schemas.values()) { list.addAll(schema.getAllTablesAndViews()); } return list; }
Get all synonyms.
Returns:all objects of that type
/** * Get all synonyms. * * @return all objects of that type */
public ArrayList<TableSynonym> getAllSynonyms() { ArrayList<TableSynonym> list = new ArrayList<>(); for (Schema schema : schemas.values()) { list.addAll(schema.getAllSynonyms()); } return list; }
Get the tables with the given name, if any.
Params:
  • name – the table name
Returns:the list
/** * Get the tables with the given name, if any. * * @param name the table name * @return the list */
public ArrayList<Table> getTableOrViewByName(String name) { // we expect that at most one table matches, at least in most cases ArrayList<Table> list = new ArrayList<>(1); for (Schema schema : schemas.values()) { Table table = schema.getTableOrViewByName(name); if (table != null) { list.add(table); } } return list; } public ArrayList<Schema> getAllSchemas() { initMetaTables(); return new ArrayList<>(schemas.values()); } public ArrayList<Setting> getAllSettings() { return new ArrayList<>(settings.values()); } public ArrayList<Domain> getAllDomains() { return new ArrayList<>(domains.values()); } public ArrayList<User> getAllUsers() { return new ArrayList<>(users.values()); } public String getCacheType() { return cacheType; } public String getCluster() { return cluster; } @Override public CompareMode getCompareMode() { return compareMode; } @Override public String getDatabasePath() { if (persistent) { return FileUtils.toRealPath(databaseName); } return null; } public String getShortName() { return databaseShortName; } public String getName() { return databaseName; }
Get all sessions that are currently connected to the database.
Params:
  • includingSystemSession – if the system session should also be included
Returns:the list of sessions
/** * Get all sessions that are currently connected to the database. * * @param includingSystemSession if the system session should also be * included * @return the list of sessions */
public Session[] getSessions(boolean includingSystemSession) { ArrayList<Session> list; // need to synchronized on userSession, otherwise the list // may contain null elements synchronized (userSessions) { list = new ArrayList<>(userSessions); } // copy, to ensure the reference is stable Session sys = systemSession; Session lob = lobSession; if (includingSystemSession && sys != null) { list.add(sys); } if (includingSystemSession && lob != null) { list.add(lob); } return list.toArray(new Session[0]); }
Update an object in the system table.
Params:
  • session – the session
  • obj – the database object
/** * Update an object in the system table. * * @param session the session * @param obj the database object */
public void updateMeta(Session session, DbObject obj) { if (isMVStore()) { int id = obj.getId(); if (id > 0) { if (!starting && !obj.isTemporary()) { Row newRow = meta.getTemplateRow(); MetaRecord.populateRowFromDBObject(obj, newRow); Row oldRow = metaIdIndex.getRow(session, id); if (oldRow != null) { meta.updateRow(session, oldRow, newRow); } } // for temporary objects synchronized (objectIds) { objectIds.set(id); } } } else { boolean metaWasLocked = lockMeta(session); synchronized (this) { int id = obj.getId(); removeMeta(session, id); addMeta(session, obj); // for temporary objects if(id > 0) { objectIds.set(id); } } if (!metaWasLocked) { unlockMeta(session); } } }
Rename a schema object.
Params:
  • session – the session
  • obj – the object
  • newName – the new name
/** * Rename a schema object. * * @param session the session * @param obj the object * @param newName the new name */
public synchronized void renameSchemaObject(Session session, SchemaObject obj, String newName) { checkWritingAllowed(); obj.getSchema().rename(obj, newName); updateMetaAndFirstLevelChildren(session, obj); } private synchronized void updateMetaAndFirstLevelChildren(Session session, DbObject obj) { ArrayList<DbObject> list = obj.getChildren(); Comment comment = findComment(obj); if (comment != null) { DbException.throwInternalError(comment.toString()); } updateMeta(session, obj); // remember that this scans only one level deep! if (list != null) { for (DbObject o : list) { if (o.getCreateSQL() != null) { updateMeta(session, o); } } } }
Rename a database object.
Params:
  • session – the session
  • obj – the object
  • newName – the new name
/** * Rename a database object. * * @param session the session * @param obj the object * @param newName the new name */
public synchronized void renameDatabaseObject(Session session, DbObject obj, String newName) { checkWritingAllowed(); int type = obj.getType(); HashMap<String, DbObject> map = getMap(type); if (SysProperties.CHECK) { if (!map.containsKey(obj.getName())) { DbException.throwInternalError("not found: " + obj.getName()); } if (obj.getName().equals(newName) || map.containsKey(newName)) { DbException.throwInternalError("object already exists: " + newName); } } obj.checkRename(); map.remove(obj.getName()); obj.rename(newName); map.put(newName, obj); updateMetaAndFirstLevelChildren(session, obj); }
Create a temporary file in the database folder.
Returns:the file name
/** * Create a temporary file in the database folder. * * @return the file name */
public String createTempFile() { try { boolean inTempDir = readOnly; String name = databaseName; if (!persistent) { name = "memFS:" + name; } return FileUtils.createTempFile(name, Constants.SUFFIX_TEMP_FILE, inTempDir); } catch (IOException e) { throw DbException.convertIOException(e, databaseName); } } private void deleteOldTempFiles() { String path = FileUtils.getParent(databaseName); for (String name : FileUtils.newDirectoryStream(path)) { if (name.endsWith(Constants.SUFFIX_TEMP_FILE) && name.startsWith(databaseName)) { // can't always delete the files, they may still be open FileUtils.tryDelete(name); } } }
Get the schema. If the schema does not exist, an exception is thrown.
Params:
  • schemaName – the name of the schema
Throws:
Returns:the schema
/** * Get the schema. If the schema does not exist, an exception is thrown. * * @param schemaName the name of the schema * @return the schema * @throws DbException no schema with that name exists */
public Schema getSchema(String schemaName) { Schema schema = findSchema(schemaName); if (schema == null) { throw DbException.get(ErrorCode.SCHEMA_NOT_FOUND_1, schemaName); } return schema; }
Remove the object from the database.
Params:
  • session – the session
  • obj – the object to remove
/** * Remove the object from the database. * * @param session the session * @param obj the object to remove */
public synchronized void removeDatabaseObject(Session session, DbObject obj) { checkWritingAllowed(); String objName = obj.getName(); int type = obj.getType(); HashMap<String, DbObject> map = getMap(type); if (SysProperties.CHECK && !map.containsKey(objName)) { DbException.throwInternalError("not found: " + objName); } Comment comment = findComment(obj); lockMeta(session); if (comment != null) { removeDatabaseObject(session, comment); } int id = obj.getId(); obj.removeChildrenAndResources(session); map.remove(objName); removeMeta(session, id); }
Get the first table that depends on this object.
Params:
  • obj – the object to find
  • except – the table to exclude (or null)
Returns:the first dependent table, or null
/** * Get the first table that depends on this object. * * @param obj the object to find * @param except the table to exclude (or null) * @return the first dependent table, or null */
public Table getDependentTable(SchemaObject obj, Table except) { switch (obj.getType()) { case DbObject.COMMENT: case DbObject.CONSTRAINT: case DbObject.INDEX: case DbObject.RIGHT: case DbObject.TRIGGER: case DbObject.USER: return null; default: } HashSet<DbObject> set = new HashSet<>(); for (Table t : getAllTablesAndViews(false)) { if (except == t) { continue; } else if (TableType.VIEW == t.getTableType()) { continue; } set.clear(); t.addDependencies(set); if (set.contains(obj)) { return t; } } return null; }
Remove an object from the system table.
Params:
  • session – the session
  • obj – the object to be removed
/** * Remove an object from the system table. * * @param session the session * @param obj the object to be removed */
public void removeSchemaObject(Session session, SchemaObject obj) { int type = obj.getType(); if (type == DbObject.TABLE_OR_VIEW) { Table table = (Table) obj; if (table.isTemporary() && !table.isGlobalTemporary()) { session.removeLocalTempTable(table); return; } } else if (type == DbObject.INDEX) { Index index = (Index) obj; Table table = index.getTable(); if (table.isTemporary() && !table.isGlobalTemporary()) { session.removeLocalTempTableIndex(index); return; } } else if (type == DbObject.CONSTRAINT) { Constraint constraint = (Constraint) obj; Table table = constraint.getTable(); if (table.isTemporary() && !table.isGlobalTemporary()) { session.removeLocalTempTableConstraint(constraint); return; } } checkWritingAllowed(); lockMeta(session); synchronized (this) { Comment comment = findComment(obj); if (comment != null) { removeDatabaseObject(session, comment); } obj.getSchema().remove(obj); int id = obj.getId(); if (!starting) { Table t = getDependentTable(obj, null); if (t != null) { obj.getSchema().add(obj); throw DbException.get(ErrorCode.CANNOT_DROP_2, obj.getSQL(false), t.getSQL(false)); } obj.removeChildrenAndResources(session); } removeMeta(session, id); } }
Check if this database is disk-based.
Returns:true if it is disk-based, false if it is in-memory only.
/** * Check if this database is disk-based. * * @return true if it is disk-based, false if it is in-memory only. */
public boolean isPersistent() { return persistent; } public TraceSystem getTraceSystem() { return traceSystem; } public synchronized void setCacheSize(int kb) { if (starting) { int max = MathUtils.convertLongToInt(Utils.getMemoryMax()) / 2; kb = Math.min(kb, max); } cacheSize = kb; if (pageStore != null) { pageStore.setMaxCacheMemory(kb); } if (store != null) { store.setCacheSize(Math.max(1, kb)); } } public synchronized void setMasterUser(User user) { lockMeta(systemSession); addDatabaseObject(systemSession, user); systemSession.commit(true); } public Role getPublicRole() { return publicRole; }
Get a unique temporary table name.
Params:
  • baseName – the prefix of the returned name
  • session – the session
Returns:a unique name
/** * Get a unique temporary table name. * * @param baseName the prefix of the returned name * @param session the session * @return a unique name */
public synchronized String getTempTableName(String baseName, Session session) { String tempName; do { tempName = baseName + "_COPY_" + session.getId() + "_" + nextTempTableId++; } while (mainSchema.findTableOrView(session, tempName) != null); return tempName; } public void setCompareMode(CompareMode compareMode) { this.compareMode = compareMode; } public void setCluster(String cluster) { this.cluster = cluster; } @Override public void checkWritingAllowed() { if (readOnly) { throw DbException.get(ErrorCode.DATABASE_IS_READ_ONLY); } if (fileLockMethod == FileLockMethod.SERIALIZED) { if (!reconnectChangePending) { throw DbException.get(ErrorCode.DATABASE_IS_READ_ONLY); } } } public boolean isReadOnly() { return readOnly; } public void setWriteDelay(int value) { writeDelay = value; if (writer != null) { writer.setWriteDelay(value); // TODO check if MIN_WRITE_DELAY is a good value flushOnEachCommit = writeDelay < Constants.MIN_WRITE_DELAY; } if (store != null) { int millis = value < 0 ? 0 : value; store.getMvStore().setAutoCommitDelay(millis); } } public int getRetentionTime() { return retentionTime; } public void setRetentionTime(int value) { retentionTime = value; if (store != null) { store.getMvStore().setRetentionTime(value); } } public void setAllowBuiltinAliasOverride(boolean b) { allowBuiltinAliasOverride = b; } public boolean isAllowBuiltinAliasOverride() { return allowBuiltinAliasOverride; }
Check if flush-on-each-commit is enabled.
Returns:true if it is
/** * Check if flush-on-each-commit is enabled. * * @return true if it is */
public boolean getFlushOnEachCommit() { return flushOnEachCommit; }
Get the list of in-doubt transactions.
Returns:the list
/** * Get the list of in-doubt transactions. * * @return the list */
public ArrayList<InDoubtTransaction> getInDoubtTransactions() { if (store != null) { return store.getInDoubtTransactions(); } return pageStore == null ? null : pageStore.getInDoubtTransactions(); }
Prepare a transaction.
Params:
  • session – the session
  • transaction – the name of the transaction
/** * Prepare a transaction. * * @param session the session * @param transaction the name of the transaction */
synchronized void prepareCommit(Session session, String transaction) { if (readOnly) { return; } if (store != null) { store.prepareCommit(session, transaction); return; } if (pageStore != null) { pageStore.flushLog(); pageStore.prepareCommit(session, transaction); } }
Commit the current transaction of the given session.
Params:
  • session – the session
/** * Commit the current transaction of the given session. * * @param session the session */
synchronized void commit(Session session) { throwLastBackgroundException(); if (readOnly) { return; } if (pageStore != null) { pageStore.commit(session); } session.setAllCommitted(); } private void throwLastBackgroundException() { DbException b = backgroundException.getAndSet(null); if (b != null) { // wrap the exception, so we see it was thrown here throw DbException.get(b.getErrorCode(), b, b.getMessage()); } } public void setBackgroundException(DbException e) { if (backgroundException.compareAndSet(null, e)) { TraceSystem t = getTraceSystem(); if (t != null) { t.getTrace(Trace.DATABASE).error(e, "flush"); } } } public Throwable getBackgroundException() { IllegalStateException exception = store.getMvStore().getPanicException(); if(exception != null) { return exception; } return backgroundException.getAndSet(null); }
Flush all pending changes to the transaction log.
/** * Flush all pending changes to the transaction log. */
public synchronized void flush() { if (readOnly) { return; } if (pageStore != null) { pageStore.flushLog(); } if (store != null) { try { store.flush(); } catch (RuntimeException e) { backgroundException.compareAndSet(null, DbException.convert(e)); throw e; } } } public void setEventListener(DatabaseEventListener eventListener) { this.eventListener = eventListener; } public void setEventListenerClass(String className) { if (className == null || className.isEmpty()) { eventListener = null; } else { try { eventListener = (DatabaseEventListener) JdbcUtils.loadUserClass(className).getDeclaredConstructor().newInstance(); String url = databaseURL; if (cipher != null) { url += ";CIPHER=" + cipher; } eventListener.init(url); } catch (Throwable e) { throw DbException.get( ErrorCode.ERROR_SETTING_DATABASE_EVENT_LISTENER_2, e, className, e.toString()); } } }
Set the progress of a long running operation. This method calls the DatabaseEventListener if one is registered.
Params:
  • state – the DatabaseEventListener state
  • name – the object name
  • x – the current position
  • max – the highest value
/** * Set the progress of a long running operation. * This method calls the {@link DatabaseEventListener} if one is registered. * * @param state the {@link DatabaseEventListener} state * @param name the object name * @param x the current position * @param max the highest value */
public void setProgress(int state, String name, int x, int max) { if (eventListener != null) { try { eventListener.setProgress(state, name, x, max); } catch (Exception e2) { // ignore this (user made) exception } } }
This method is called after an exception occurred, to inform the database event listener (if one is set).
Params:
  • e – the exception
  • sql – the SQL statement
/** * This method is called after an exception occurred, to inform the database * event listener (if one is set). * * @param e the exception * @param sql the SQL statement */
public void exceptionThrown(SQLException e, String sql) { if (eventListener != null) { try { eventListener.exceptionThrown(e, sql); } catch (Exception e2) { // ignore this (user made) exception } } }
Synchronize the files with the file system. This method is called when executing the SQL statement CHECKPOINT SYNC.
/** * Synchronize the files with the file system. This method is called when * executing the SQL statement CHECKPOINT SYNC. */
public synchronized void sync() { if (readOnly) { return; } if (store != null) { store.sync(); } if (pageStore != null) { pageStore.sync(); } } public int getMaxMemoryRows() { return maxMemoryRows; } public void setMaxMemoryRows(int value) { this.maxMemoryRows = value; } public void setMaxMemoryUndo(int value) { this.maxMemoryUndo = value; } public int getMaxMemoryUndo() { return maxMemoryUndo; } public void setLockMode(int lockMode) { switch (lockMode) { case Constants.LOCK_MODE_OFF: if (multiThreaded && !isMVStore()) { // Currently the combination of MV_STORE=FALSE, LOCK_MODE=0 and // MULTI_THREADED=TRUE is not supported. Also see code in // JdbcDatabaseMetaData#supportsTransactionIsolationLevel(int) throw DbException.get( ErrorCode.UNSUPPORTED_SETTING_COMBINATION, "MV_STORE=FALSE & LOCK_MODE=0 & MULTI_THREADED=TRUE"); } break; case Constants.LOCK_MODE_READ_COMMITTED: case Constants.LOCK_MODE_TABLE: case Constants.LOCK_MODE_TABLE_GC: break; default: throw DbException.getInvalidValueException("lock mode", lockMode); } this.lockMode = lockMode; } public int getLockMode() { return lockMode; } public void setCloseDelay(int value) { this.closeDelay = value; } public Session getSystemSession() { return systemSession; }
Check if the database is in the process of closing.
Returns:true if the database is closing
/** * Check if the database is in the process of closing. * * @return true if the database is closing */
public boolean isClosing() { return closing; } public void setMaxLengthInplaceLob(int value) { this.maxLengthInplaceLob = value; } @Override public int getMaxLengthInplaceLob() { return maxLengthInplaceLob; } public void setIgnoreCase(boolean b) { ignoreCase = b; } public boolean getIgnoreCase() { if (starting) { // tables created at startup must not be converted to ignorecase return false; } return ignoreCase; } public synchronized void setDeleteFilesOnDisconnect(boolean b) { this.deleteFilesOnDisconnect = b; } @Override public String getLobCompressionAlgorithm(int type) { return lobCompressionAlgorithm; } public void setLobCompressionAlgorithm(String stringValue) { this.lobCompressionAlgorithm = stringValue; } public synchronized void setMaxLogSize(long value) { if (pageStore != null) { pageStore.setMaxLogSize(value); } } public void setAllowLiterals(int value) { this.allowLiterals = value; } public boolean getOptimizeReuseResults() { return optimizeReuseResults; } public void setOptimizeReuseResults(boolean b) { optimizeReuseResults = b; } @Override public Object getLobSyncObject() { return lobSyncObject; } public int getSessionCount() { return userSessions.size(); } public void setReferentialIntegrity(boolean b) { referentialIntegrity = b; } public boolean getReferentialIntegrity() { return referentialIntegrity; } public void setQueryStatistics(boolean b) { queryStatistics = b; synchronized (this) { if (!b) { queryStatisticsData = null; } } } public boolean getQueryStatistics() { return queryStatistics; } public void setQueryStatisticsMaxEntries(int n) { queryStatisticsMaxEntries = n; if (queryStatisticsData != null) { synchronized (this) { if (queryStatisticsData != null) { queryStatisticsData.setMaxQueryEntries(queryStatisticsMaxEntries); } } } } public QueryStatisticsData getQueryStatisticsData() { if (!queryStatistics) { return null; } if (queryStatisticsData == null) { synchronized (this) { if (queryStatisticsData == null) { queryStatisticsData = new QueryStatisticsData(queryStatisticsMaxEntries); } } } return queryStatisticsData; }
Check if the database is currently opening. This is true until all stored SQL statements have been executed.
Returns:true if the database is still starting
/** * Check if the database is currently opening. This is true until all stored * SQL statements have been executed. * * @return true if the database is still starting */
public boolean isStarting() { return starting; }
Check if MVStore backend is used for this database.
Returns:true for MVStore, false for PageStore
/** * Check if MVStore backend is used for this database. * * @return {@code true} for MVStore, {@code false} for PageStore */
public boolean isMVStore() { return dbSettings.mvStore; }
Called after the database has been opened and initialized. This method notifies the event listener if one has been set.
/** * Called after the database has been opened and initialized. This method * notifies the event listener if one has been set. */
void opened() { if (eventListener != null) { eventListener.opened(); } if (writer != null) { writer.startThread(); } } public void setMode(Mode mode) { this.mode = mode; } public Mode getMode() { return mode; } public boolean isMultiThreaded() { return multiThreaded; } public void setMultiThreaded(boolean multiThreaded) { if (multiThreaded && this.multiThreaded != multiThreaded) { if (lockMode == Constants.LOCK_MODE_OFF && !isMVStore()) { // Currently the combination of MV_STORE=FALSE, LOCK_MODE=0 and // MULTI_THREADED=TRUE is not supported. throw DbException.get( ErrorCode.UNSUPPORTED_SETTING_COMBINATION, "MV_STORE=FALSE & LOCK_MODE=0 & MULTI_THREADED=TRUE"); } } this.multiThreaded = multiThreaded; } public void setMaxOperationMemory(int maxOperationMemory) { this.maxOperationMemory = maxOperationMemory; } public int getMaxOperationMemory() { return maxOperationMemory; } public Session getExclusiveSession() { return exclusiveSession.get(); }
Set the session that can exclusively access the database.
Params:
  • session – the session
  • closeOthers – whether other sessions are closed
/** * Set the session that can exclusively access the database. * * @param session the session * @param closeOthers whether other sessions are closed */
public void setExclusiveSession(Session session, boolean closeOthers) { this.exclusiveSession.set(session); if (closeOthers) { closeAllSessionsException(session); } } @Override public SmallLRUCache<String, String[]> getLobFileListCache() { if (lobFileListCache == null) { lobFileListCache = SmallLRUCache.newInstance(128); } return lobFileListCache; }
Checks if the system table (containing the catalog) is locked.
Returns:true if it is currently locked
/** * Checks if the system table (containing the catalog) is locked. * * @return true if it is currently locked */
public boolean isSysTableLocked() { return meta == null || meta.isLockedExclusively(); }
Checks if the system table (containing the catalog) is locked by the given session.
Params:
  • session – the session
Returns:true if it is currently locked
/** * Checks if the system table (containing the catalog) is locked by the * given session. * * @param session the session * @return true if it is currently locked */
public boolean isSysTableLockedBy(Session session) { return meta == null || meta.isLockedExclusivelyBy(session); }
Open a new connection or get an existing connection to another database.
Params:
  • driver – the database driver or null
  • url – the database URL
  • user – the user name
  • password – the password
Returns:the connection
/** * Open a new connection or get an existing connection to another database. * * @param driver the database driver or null * @param url the database URL * @param user the user name * @param password the password * @return the connection */
public TableLinkConnection getLinkConnection(String driver, String url, String user, String password) { if (linkConnections == null) { linkConnections = new HashMap<>(); } return TableLinkConnection.open(linkConnections, driver, url, user, password, dbSettings.shareLinkedConnections); } @Override public String toString() { return databaseShortName + ":" + super.toString(); }
Immediately close the database.
/** * Immediately close the database. */
public void shutdownImmediately() { closing = true; setPowerOffCount(1); try { checkPowerOff(); } catch (DbException e) { // ignore } closeFiles(); } @Override public TempFileDeleter getTempFileDeleter() { return tempFileDeleter; } public PageStore getPageStore() { if (dbSettings.mvStore) { if (store == null) { store = MVTableEngine.init(this); } return null; } synchronized (this) { if (pageStore == null) { pageStore = new PageStore(this, databaseName + Constants.SUFFIX_PAGE_FILE, accessModeData, cacheSize); if (pageSize != Constants.DEFAULT_PAGE_SIZE) { pageStore.setPageSize(pageSize); } if (!readOnly && fileLockMethod == FileLockMethod.FS) { pageStore.setLockFile(true); } pageStore.setLogMode(logMode); pageStore.open(); } return pageStore; } }
Get the first user defined table, excluding the LOB_BLOCKS table that the Recover tool creates.
Returns:the table or null if no table is defined
/** * Get the first user defined table, excluding the LOB_BLOCKS table that the * Recover tool creates. * * @return the table or null if no table is defined */
public Table getFirstUserTable() { for (Table table : getAllTablesAndViews(false)) { if (table.getCreateSQL() != null) { if (table.isHidden()) { // LOB tables continue; } // exclude the LOB_MAP that the Recover tool creates if (table.getSchema().getId() == Constants.INFORMATION_SCHEMA_ID && table.getName().equalsIgnoreCase("LOB_BLOCKS")) { continue; } return table; } } return null; }
Check if the contents of the database was changed and therefore it is required to re-connect. This method waits until pending changes are completed. If a pending change takes too long (more than 2 seconds), the pending change is broken (removed from the properties file).
Returns:true if reconnecting is required
/** * Check if the contents of the database was changed and therefore it is * required to re-connect. This method waits until pending changes are * completed. If a pending change takes too long (more than 2 seconds), the * pending change is broken (removed from the properties file). * * @return true if reconnecting is required */
public boolean isReconnectNeeded() { if (fileLockMethod != FileLockMethod.SERIALIZED) { return false; } if (reconnectChangePending) { return false; } long now = System.nanoTime(); if (now < reconnectCheckNext) { return false; } reconnectCheckNext = now + reconnectCheckDelayNs; if (lock == null) { lock = new FileLock(traceSystem, databaseName + Constants.SUFFIX_LOCK_FILE, Constants.LOCK_SLEEP); } try { Properties prop = lock.load(), first = prop; while (true) { if (prop.equals(reconnectLastLock)) { return false; } if (prop.getProperty("changePending", null) == null) { break; } if (System.nanoTime() > now + reconnectCheckDelayNs * 10) { if (first.equals(prop)) { // the writing process didn't update the file - // it may have terminated lock.setProperty("changePending", null); lock.save(); break; } } trace.debug("delay (change pending)"); Thread.sleep(TimeUnit.NANOSECONDS.toMillis(reconnectCheckDelayNs)); prop = lock.load(); } reconnectLastLock = prop; } catch (Exception e) { // DbException, InterruptedException trace.error(e, "readOnly {0}", readOnly); // ignore } return true; }
Flush all changes when using the serialized mode, and if there are pending changes, and some time has passed. This switches to a new transaction log and resets the change pending flag in the .lock.db file.
/** * Flush all changes when using the serialized mode, and if there are * pending changes, and some time has passed. This switches to a new * transaction log and resets the change pending flag in * the .lock.db file. */
public void checkpointIfRequired() { if (fileLockMethod != FileLockMethod.SERIALIZED || readOnly || !reconnectChangePending || closing) { return; } long now = System.nanoTime(); if (now > reconnectCheckNext + reconnectCheckDelayNs) { if (checkpointAllowed < 0) { DbException.throwInternalError(Integer.toString(checkpointAllowed)); } synchronized (reconnectSync) { if (checkpointAllowed > 0) { return; } checkpointRunning = true; } synchronized (this) { trace.debug("checkpoint start"); flushSequences(); checkpoint(); reconnectModified(false); trace.debug("checkpoint end"); } synchronized (reconnectSync) { checkpointRunning = false; } } } public boolean isFileLockSerialized() { return fileLockMethod == FileLockMethod.SERIALIZED; } private void flushSequences() { for (SchemaObject obj : getAllSchemaObjects(DbObject.SEQUENCE)) { Sequence sequence = (Sequence) obj; sequence.flushWithoutMargin(); } }
Flush all changes and open a new transaction log.
/** * Flush all changes and open a new transaction log. */
public void checkpoint() { if (persistent) { synchronized (this) { if (pageStore != null) { pageStore.checkpoint(); } } if (store != null) { store.flush(); } } getTempFileDeleter().deleteUnused(); }
This method is called before writing to the transaction log.
Returns:true if the call was successful and writing is allowed, false if another connection was faster
/** * This method is called before writing to the transaction log. * * @return true if the call was successful and writing is allowed, * false if another connection was faster */
public boolean beforeWriting() { if (fileLockMethod != FileLockMethod.SERIALIZED) { return true; } while (checkpointRunning) { try { Thread.sleep(10 + (int) (Math.random() * 10)); } catch (Exception e) { // ignore InterruptedException } } synchronized (reconnectSync) { if (reconnectModified(true)) { if (++checkpointAllowed > 20) { throw DbException.throwInternalError(Integer.toString(checkpointAllowed)); } return true; } } // make sure the next call to isReconnectNeeded() returns true reconnectCheckNext = System.nanoTime() - 1; reconnectLastLock = null; return false; }
This method is called after updates are finished.
/** * This method is called after updates are finished. */
public void afterWriting() { if (fileLockMethod != FileLockMethod.SERIALIZED) { return; } synchronized (reconnectSync) { checkpointAllowed--; } if (checkpointAllowed < 0) { throw DbException.throwInternalError(Integer.toString(checkpointAllowed)); } }
Switch the database to read-only mode.
Params:
  • readOnly – the new value
/** * Switch the database to read-only mode. * * @param readOnly the new value */
public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; } public void setCompactMode(int compactMode) { this.compactMode = compactMode; } public SourceCompiler getCompiler() { if (compiler == null) { compiler = new SourceCompiler(); } return compiler; } @Override public LobStorageInterface getLobStorage() { if (lobStorage == null) { if (dbSettings.mvStore) { lobStorage = new LobStorageMap(this); } else { lobStorage = new LobStorageBackend(this); } } return lobStorage; } public JdbcConnection getLobConnectionForInit() { String url = Constants.CONN_URL_INTERNAL; JdbcConnection conn = new JdbcConnection( systemSession, systemUser.getName(), url); conn.setTraceLevel(TraceSystem.OFF); return conn; } public JdbcConnection getLobConnectionForRegularUse() { String url = Constants.CONN_URL_INTERNAL; JdbcConnection conn = new JdbcConnection( lobSession, systemUser.getName(), url); conn.setTraceLevel(TraceSystem.OFF); return conn; } public Session getLobSession() { return lobSession; } public void setLogMode(int log) { if (log < 0 || log > 2) { throw DbException.getInvalidValueException("LOG", log); } if (store != null) { this.logMode = log; return; } synchronized (this) { if (pageStore != null) { if (log != PageStore.LOG_MODE_SYNC || pageStore.getLogMode() != PageStore.LOG_MODE_SYNC) { // write the log mode in the trace file when enabling or // disabling a dangerous mode trace.error(null, "log {0}", log); } this.logMode = log; pageStore.setLogMode(log); } } } public int getLogMode() { if (store != null) { return logMode; } synchronized (this) { if (pageStore != null) { return pageStore.getLogMode(); } } return PageStore.LOG_MODE_OFF; } public int getDefaultTableType() { return defaultTableType; } public void setDefaultTableType(int defaultTableType) { this.defaultTableType = defaultTableType; } public DbSettings getSettings() { return dbSettings; }
Create a new hash map. Depending on the configuration, the key is case sensitive or case insensitive.
Type parameters:
  • <V> – the value type
Returns:the hash map
/** * Create a new hash map. Depending on the configuration, the key is case * sensitive or case insensitive. * * @param <V> the value type * @return the hash map */
public <V> HashMap<String, V> newStringMap() { return dbSettings.caseInsensitiveIdentifiers ? new CaseInsensitiveMap<V>() : new HashMap<String, V>(); }
Create a new hash map. Depending on the configuration, the key is case sensitive or case insensitive.
Type parameters:
  • <V> – the value type
Returns:the hash map
/** * Create a new hash map. Depending on the configuration, the key is case * sensitive or case insensitive. * * @param <V> the value type * @return the hash map */
public <V> ConcurrentHashMap<String, V> newConcurrentStringMap() { return dbSettings.caseInsensitiveIdentifiers ? new CaseInsensitiveConcurrentMap<V>() : new ConcurrentHashMap<String, V>(); }
Compare two identifiers (table names, column names,...) and verify they are equal. Case sensitivity depends on the configuration.
Params:
  • a – the first identifier
  • b – the second identifier
Returns:true if they match
/** * Compare two identifiers (table names, column names,...) and verify they * are equal. Case sensitivity depends on the configuration. * * @param a the first identifier * @param b the second identifier * @return true if they match */
public boolean equalsIdentifiers(String a, String b) { return a.equals(b) || dbSettings.caseInsensitiveIdentifiers && a.equalsIgnoreCase(b); }
Returns identifier in upper or lower case depending on database settings.
Params:
  • upperName – identifier in the upper case
Returns:identifier in upper or lower case
/** * Returns identifier in upper or lower case depending on database settings. * * @param upperName * identifier in the upper case * @return identifier in upper or lower case */
public String sysIdentifier(String upperName) { assert isUpperSysIdentifier(upperName); return dbSettings.databaseToLower ? StringUtils.toLowerEnglish(upperName) : upperName; } private static boolean isUpperSysIdentifier(String upperName) { int l = upperName.length(); if (l == 0) { return false; } for (int i = 0; i < l; i++) { int ch = upperName.charAt(i); if (ch < 'A' || ch > 'Z' && ch != '_') { return false; } } return true; } @Override public int readLob(long lobId, byte[] hmac, long offset, byte[] buff, int off, int length) { throw DbException.throwInternalError(); } public byte[] getFileEncryptionKey() { return fileEncryptionKey; } public int getPageSize() { return pageSize; } @Override public JavaObjectSerializer getJavaObjectSerializer() { initJavaObjectSerializer(); return javaObjectSerializer; } private void initJavaObjectSerializer() { if (javaObjectSerializerInitialized) { return; } synchronized (this) { if (javaObjectSerializerInitialized) { return; } String serializerName = javaObjectSerializerName; if (serializerName != null) { serializerName = serializerName.trim(); if (!serializerName.isEmpty() && !serializerName.equals("null")) { try { javaObjectSerializer = (JavaObjectSerializer) JdbcUtils.loadUserClass(serializerName).getDeclaredConstructor().newInstance(); } catch (Exception e) { throw DbException.convert(e); } } } javaObjectSerializerInitialized = true; } } public void setJavaObjectSerializerName(String serializerName) { synchronized (this) { javaObjectSerializerInitialized = false; javaObjectSerializerName = serializerName; } }
Get the table engine class, loading it if needed.
Params:
  • tableEngine – the table engine name
Returns:the class
/** * Get the table engine class, loading it if needed. * * @param tableEngine the table engine name * @return the class */
public TableEngine getTableEngine(String tableEngine) { assert Thread.holdsLock(this); TableEngine engine = tableEngines.get(tableEngine); if (engine == null) { try { engine = (TableEngine) JdbcUtils.loadUserClass(tableEngine).getDeclaredConstructor().newInstance(); } catch (Exception e) { throw DbException.convert(e); } tableEngines.put(tableEngine, engine); } return engine; }
get authenticator for database users
Returns:authenticator set for database
/** * get authenticator for database users * @return authenticator set for database */
public Authenticator getAuthenticator() { return authenticator; }
Set current database authenticator
Params:
  • authenticator – = authenticator to set, null to revert to the Internal authenticator
/** * Set current database authenticator * * @param authenticator = authenticator to set, null to revert to the Internal authenticator */
public void setAuthenticator(Authenticator authenticator) { if (authenticator!=null) { authenticator.init(this); } this.authenticator=authenticator; } }