ttomcat-1778514358873.zip-extract/_dependencies/maven/com.h2database_h2-2.2.220/org/h2/engine/Database.java

Path
ttomcat-1778514358873.zip-extract/_dependencies/maven/com.h2database_h2-2.2.220/org/h2/engine/Database.java
Status
scanned
Type
file
Name
Database.java
Extension
.java
Programming language
Java
Mime type
text/x-java
File type
Java source, ASCII text
Tag

      
    
Rootfs path

      
    
Size
82118 (80.2 KB)
MD5
a12831390e71b6c0ee5ed28b96ccf9ea
SHA1
f5e49d82ba8af6276234f6a16193f5a462e55dfb
SHA256
3d2d5a823f5d3b941a558192f24b59601f5e51e5ca8e416675bc88725833186a
SHA512

      
    
SHA1_git
4a25c77927bfa4f48233787b9499169f2ec9257b
Is binary

      
    
Is text
True
Is archive

      
    
Is media

      
    
Is legal

      
    
Is manifest

      
    
Is readme

      
    
Is top level

      
    
Is key file

      
    
Database.java | 80.2 KB |

/* * Copyright 2004-2023 H2 Group. Multiple-Licensed under the MPL 2.0, * and the EPL 1.0 (https://h2database.com/html/license.html). * Initial Developer: H2 Group */ package org.h2.engine; import java.sql.SQLException; import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; 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.Prepared; import org.h2.command.ddl.CreateTableData; import org.h2.command.dml.SetTypes; import org.h2.constraint.Constraint; import org.h2.constraint.Constraint.Type; import org.h2.engine.Mode.ModeEnum; import org.h2.index.Cursor; import org.h2.index.Index; import org.h2.index.IndexType; import org.h2.message.DbException; import org.h2.message.Trace; import org.h2.message.TraceSystem; import org.h2.mode.DefaultNullOrdering; import org.h2.mode.PgCatalogSchema; import org.h2.mvstore.MVStoreException; import org.h2.mvstore.db.LobStorageMap; import org.h2.mvstore.db.Store; import org.h2.result.Row; import org.h2.result.RowFactory; import org.h2.result.SearchRow; import org.h2.schema.InformationSchema; 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.LobStorageInterface; import org.h2.store.fs.FileUtils; import org.h2.store.fs.encrypt.FileEncrypt; import org.h2.table.Column; import org.h2.table.IndexColumn; 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.NetworkConnectionInfo; import org.h2.util.SmallLRUCache; import org.h2.util.SourceCompiler; import org.h2.util.StringUtils; import org.h2.util.TempFileDeleter; import org.h2.util.TimeZoneProvider; import org.h2.util.Utils; import org.h2.value.CaseInsensitiveConcurrentMap; import org.h2.value.CaseInsensitiveMap; import org.h2.value.CompareMode; import org.h2.value.TypeInfo; import org.h2.value.ValueInteger; import org.h2.value.ValueTimestampTimeZone; /** * 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 final class Database implements DataHandler, CastDataProvider { private static int initialPowerOffCount; private static final boolean ASSERT; private static final ThreadLocal<SessionLocal> META_LOCK_DEBUGGING; private static final ThreadLocal<Database> META_LOCK_DEBUGGING_DB; private static final ThreadLocal<Throwable> META_LOCK_DEBUGGING_STACK; private static final SessionLocal[] EMPTY_SESSION_ARRAY = new SessionLocal[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. */ 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 ConcurrentHashMap<String, RightOwner> usersAndRoles = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, Setting> settings = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, Schema> schemas = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, Right> rights = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, Comment> comments = new ConcurrentHashMap<>(); private final HashMap<String, TableEngine> tableEngines = new HashMap<>(); private final Set<SessionLocal> userSessions = Collections.synchronizedSet(new HashSet<>()); private final AtomicReference<SessionLocal> exclusiveSession = new AtomicReference<>(); private final BitSet objectIds = new BitSet(); private final Object lobSyncObject = new Object(); private final Schema mainSchema; private final Schema infoSchema; private final Schema pgCatalogSchema; private int nextSessionId; private int nextTempTableId; private final User systemUser; private SessionLocal systemSession; private SessionLocal lobSession; private final Table meta; private final Index metaIdIndex; private FileLock lock; private volatile boolean starting; private final TraceSystem traceSystem; private final Trace trace; private final FileLockMethod fileLockMethod; private final Role publicRole; private final AtomicLong modificationDataId = new AtomicLong(); private final AtomicLong modificationMetaId = new AtomicLong(); /** * Used to trigger the client side to reload some of the settings. */ private final AtomicLong remoteSettingsId = new AtomicLong(); private CompareMode compareMode; private String cluster = Constants.CLUSTERING_DISABLED; private boolean readOnly; private DatabaseEventListener eventListener; private int maxMemoryRows = SysProperties.MAX_MEMORY_ROWS; private int lockMode; 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 boolean optimizeReuseResults = true; private final String cacheType; private boolean referentialIntegrity = true; private Mode mode = Mode.getRegular(); private DefaultNullOrdering defaultNullOrdering = DefaultNullOrdering.LOW; 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 int compactMode; private SourceCompiler compiler; private final LobStorageInterface lobStorage; private final int pageSize; private int defaultTableType = Table.TYPE_CACHED; private final DbSettings dbSettings; private final Store store; private boolean allowBuiltinAliasOverride; private final AtomicReference<DbException> backgroundException = new AtomicReference<>(); private JavaObjectSerializer javaObjectSerializer; private String javaObjectSerializerName; private volatile boolean javaObjectSerializerInitialized; private volatile boolean queryStatistics; private int queryStatisticsMaxEntries = Constants.QUERY_STATISTICS_MAX_ENTRIES; private QueryStatisticsData queryStatisticsData; private RowFactory rowFactory = RowFactory.getRowFactory(); private boolean ignoreCatalogs; 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 databaseName = ci.getName(); this.dbSettings = ci.getDbSettings(); this.compareMode = CompareMode.getInstance(null, 0); this.persistent = ci.isPersistent(); this.filePasswordHash = ci.getFilePasswordHash(); this.databaseName = databaseName; this.databaseShortName = parseDatabaseShortName(); this.maxLengthInplaceLob = persistent ? Constants.DEFAULT_MAX_LENGTH_INPLACE_LOB : Integer.MAX_VALUE - 8; this.cipher = cipher; this.autoServerMode = ci.getProperty("AUTO_SERVER", false); this.autoServerPort = ci.getProperty("AUTO_SERVER_PORT", 0); pageSize = ci.getProperty("PAGE_SIZE", Constants.DEFAULT_PAGE_SIZE); if (cipher != null && pageSize % FileEncrypt.BLOCK_SIZE != 0) { throw DbException.getUnsupportedException("CIPHER && PAGE_SIZE=" + pageSize); } String accessModeData = StringUtils.toLowerEnglish(ci.getProperty("ACCESS_MODE_DATA", "rw")); if ("r".equals(accessModeData)) { readOnly = true; } String lockMethodName = ci.getProperty("FILE_LOCK", null); fileLockMethod = lockMethodName != null ? FileLock.getFileLockMethod(lockMethodName) : autoServerMode ? FileLockMethod.FILE : FileLockMethod.FS; this.databaseURL = ci.getURL(); String s = ci.removeProperty("DATABASE_EVENT_LISTENER", null); if (s != null) { setEventListenerClass(StringUtils.trim(s, true, true, '\'')); } s = ci.removeProperty("MODE", null); if (s != null) { mode = Mode.getInstance(s); if (mode == null) { throw DbException.get(ErrorCode.UNKNOWN_MODE_1, s); } } s = ci.removeProperty("DEFAULT_NULL_ORDERING", null); if (s != null) { try { defaultNullOrdering = DefaultNullOrdering.valueOf(StringUtils.toUpperEnglish(s)); } catch (RuntimeException e) { throw DbException.getInvalidValueException("DEFAULT_NULL_ORDERING", s); } } s = ci.getProperty("JAVA_OBJECT_SERIALIZER", null); if (s != null) { s = StringUtils.trim(s, true, true, '\''); javaObjectSerializerName = s; } this.allowBuiltinAliasOverride = ci.getProperty("BUILTIN_ALIAS_OVERRIDE", false); boolean closeAtVmShutdown = dbSettings.dbCloseOnExit; if (autoServerMode && !closeAtVmShutdown) { throw DbException.getUnsupportedException("AUTO_SERVER=TRUE && DB_CLOSE_ON_EXIT=FALSE"); } 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)); this.ignoreCatalogs = ci.getProperty("IGNORE_CATALOGS", dbSettings.ignoreCatalogs); this.lockMode = ci.getProperty("LOCK_MODE", Constants.DEFAULT_LOCK_MODE); String traceFile; if (persistent) { if (readOnly) { if (traceLevelFile >= TraceSystem.DEBUG) { traceFile = Utils.getProperty("java.io.tmpdir", ".") + "/h2_" + System.currentTimeMillis() + Constants.SUFFIX_TRACE_FILE; } else { traceFile = null; } } else { traceFile = databaseName + Constants.SUFFIX_TRACE_FILE; } } else { traceFile = null; } traceSystem = new TraceSystem(traceFile); traceSystem.setLevelFile(traceLevelFile); traceSystem.setLevelSystemOut(traceLevelSystemOut); trace = traceSystem.getTrace(Trace.DATABASE); trace.info("opening {0} (build {1})", databaseName, Constants.BUILD_ID); try { if (autoServerMode && (readOnly || !persistent || fileLockMethod == FileLockMethod.NO || fileLockMethod == FileLockMethod.FS)) { throw DbException.getUnsupportedException( "AUTO_SERVER=TRUE && (readOnly || inMemory || FILE_LOCK=NO || FILE_LOCK=FS)"); } if (persistent) { 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); } } else if (fileLockMethod != FileLockMethod.NO && fileLockMethod != FileLockMethod.FS) { lock = new FileLock(traceSystem, lockFileName, Constants.LOCK_SLEEP); lock.lock(fileLockMethod); if (autoServerMode) { startServer(lock.getUniqueId()); } } deleteOldTempFiles(); } starting = true; if (dbSettings.mvStore) { store = new Store(this, ci.getFileEncryptionKey()); } else { throw new UnsupportedOperationException(); } starting = false; systemUser = new User(this, 0, SYSTEM_USER_NAME, true); systemUser.setAdmin(true); mainSchema = new Schema(this, Constants.MAIN_SCHEMA_ID, sysIdentifier(Constants.SCHEMA_MAIN), systemUser, true); infoSchema = new InformationSchema(this, systemUser); schemas.put(mainSchema.getName(), mainSchema); schemas.put(infoSchema.getName(), infoSchema); if (mode.getEnum() == ModeEnum.PostgreSQL) { pgCatalogSchema = new PgCatalogSchema(this, systemUser); schemas.put(pgCatalogSchema.getName(), pgCatalogSchema); } else { pgCatalogSchema = null; } publicRole = new Role(this, 0, sysIdentifier(Constants.PUBLIC_ROLE_NAME), true); usersAndRoles.put(publicRole.getName(), publicRole); systemSession = createSession(systemUser); lobSession = createSession(systemUser); Set<String> settingKeys = dbSettings.getSettings().keySet(); store.getTransactionStore().init(lobSession); settingKeys.removeIf(name -> name.startsWith("PAGE_STORE_")); CreateTableData data = createSysTableData(); starting = true; meta = mainSchema.createTable(data); IndexColumn[] pkCols = IndexColumn.wrap(new Column[] { data.columns.get(0) }); metaIdIndex = meta.addIndex(systemSession, "SYS_ID", 0, pkCols, 1, IndexType.createPrimaryKey(false, false), true, null); systemSession.commit(true); objectIds.set(0); executeMeta(); systemSession.commit(true); store.getTransactionStore().endLeftoverTransactions(); store.removeTemporaryMaps(objectIds); recompileInvalidViews(); starting = false; if (!readOnly) { // set CREATE_BUILD in a new database String settingName = SetTypes.getTypeName(SetTypes.CREATE_BUILD); Setting setting = settings.get(settingName); if (setting == null) { setting = new Setting(this, allocateObjectId(), settingName); setting.setIntValue(Constants.BUILD_ID); lockMeta(systemSession); addDatabaseObject(systemSession, setting); } } lobStorage = new LobStorageMap(this); lobSession.commit(true); systemSession.commit(true); trace.info("opened {0}", databaseName); if (persistent) { int writeDelay = ci.getProperty("WRITE_DELAY", Constants.DEFAULT_WRITE_DELAY); setWriteDelay(writeDelay); } if (closeAtVmShutdown) { OnExitDatabaseCloser.register(this); } } catch (Throwable e) { try { if (e instanceof OutOfMemoryError) { e.fillInStackTrace(); } if (e instanceof DbException) { if (((DbException) e).getErrorCode() == ErrorCode.DATABASE_ALREADY_OPEN_1) { stopServer(); } else { // only write if the database is not already in use trace.error(e, "opening {0}", databaseName); } } traceSystem.close(); closeOpenFilesAndUnlock(); } 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(); } public RowFactory getRowFactory() { return rowFactory; } public void setRowFactory(RowFactory rowFactory) { this.rowFactory = rowFactory; } public static void setInitialPowerOffCount(int count) { initialPowerOffCount = count; } public void setPowerOffCount(int count) { if (powerOffCount == -1) { return; } powerOffCount = count; } public Store getStore() { return store; } public long getModificationDataId() { return modificationDataId.get(); } 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 long getRemoteSettingsId() { return remoteSettingsId.get(); } public long getNextRemoteSettingsId() { return remoteSettingsId.incrementAndGet(); } public int getPowerOffCount() { return powerOffCount; } @Override public void checkPowerOff() { if (powerOffCount != 0) { checkPowerOff2(); } } private void checkPowerOff2() { if (powerOffCount > 1) { powerOffCount--; return; } if (powerOffCount != -1) { try { powerOffCount = -1; store.closeImmediately(); if (lock != null) { stopServer(); // allow testing shutdown lock.unlock(); lock = null; } if (traceSystem != null) { traceSystem.close(); } } catch (DbException e) { DbException.traceThrowable(e); } } Engine.close(databaseName); throw DbException.get(ErrorCode.DATABASE_IS_CLOSED); } /** * 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. * * @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; int l = n.length(), i = l; loop: while (--i >= 0) { char ch = n.charAt(i); switch (ch) { case '/': case ':': case '\\': break loop; } } n = ++i == l ? "UNNAMED" : n.substring(i); return StringUtils.truncateString( dbSettings.databaseToUpper ? StringUtils.toUpperEnglish(n) : dbSettings.databaseToLower ? StringUtils.toLowerEnglish(n) : n, Constants.MAX_IDENTIFIER_LENGTH); } private CreateTableData createSysTableData() { CreateTableData data = new CreateTableData(); ArrayList<Column> cols = data.columns; Column columnId = new Column("ID", TypeInfo.TYPE_INTEGER); columnId.setNullable(false); cols.add(columnId); cols.add(new Column("HEAD", TypeInfo.TYPE_INTEGER)); cols.add(new Column("TYPE", TypeInfo.TYPE_INTEGER)); cols.add(new Column("SQL", TypeInfo.TYPE_VARCHAR)); data.tableName = "SYS"; data.id = 0; data.temporary = false; data.persistData = persistent; data.persistIndexes = persistent; data.isHidden = true; data.session = systemSession; return data; } private void executeMeta() { Cursor cursor = metaIdIndex.find(systemSession, null, null); ArrayList<MetaRecord> firstRecords = new ArrayList<>(), domainRecords = new ArrayList<>(), middleRecords = new ArrayList<>(), constraintRecords = new ArrayList<>(), lastRecords = new ArrayList<>(); while (cursor.next()) { MetaRecord rec = new MetaRecord(cursor.get()); objectIds.set(rec.getId()); switch (rec.getObjectType()) { case DbObject.SETTING: case DbObject.USER: case DbObject.SCHEMA: case DbObject.FUNCTION_ALIAS: firstRecords.add(rec); break; case DbObject.DOMAIN: domainRecords.add(rec); break; case DbObject.SEQUENCE: case DbObject.CONSTANT: case DbObject.TABLE_OR_VIEW: case DbObject.INDEX: middleRecords.add(rec); break; case DbObject.CONSTRAINT: constraintRecords.add(rec); break; default: lastRecords.add(rec); } } synchronized (systemSession) { executeMeta(firstRecords); // Domains may depend on other domains int count = domainRecords.size(); if (count > 0) { for (int j = 0;; count = j) { DbException exception = null; for (int i = 0; i < count; i++) { MetaRecord rec = domainRecords.get(i); try { rec.prepareAndExecute(this, systemSession, eventListener); } catch (DbException ex) { if (exception == null) { exception = ex; } domainRecords.set(j++, rec); } } if (exception == null) { break; } if (count == j) { throw exception; } } } executeMeta(middleRecords); // Prepare, but don't create all constraints and sort them count = constraintRecords.size(); if (count > 0) { ArrayList<Prepared> constraints = new ArrayList<>(count); for (int i = 0; i < count; i++) { Prepared prepared = constraintRecords.get(i).prepare(this, systemSession, eventListener); if (prepared != null) { constraints.add(prepared); } } constraints.sort(MetaRecord.CONSTRAINTS_COMPARATOR); // Create constraints in order (unique and primary key before // all others) for (Prepared constraint : constraints) { MetaRecord.execute(this, constraint, eventListener, constraint.getSQL()); } } executeMeta(lastRecords); } } private void executeMeta(ArrayList<MetaRecord> records) { if (!records.isEmpty()) { records.sort(null); for (MetaRecord rec : records) { rec.prepareAndExecute(this, systemSession, eventListener); } } } 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() { boolean atLeastOneRecompiledSuccessfully; do { atLeastOneRecompiledSuccessfully = false; for (Schema schema : schemas.values()) { for (Table obj : schema.getAllTablesAndViews(null)) { if (obj instanceof TableView) { TableView view = (TableView) obj; if (view.isInvalid()) { view.recompile(systemSession, true, false); if (!view.isInvalid()) { atLeastOneRecompiledSuccessfully = true; } } } } } } while (atLeastOneRecompiledSuccessfully); TableView.clearIndexCaches(this); } private void addMeta(SessionLocal session, DbObject obj) { assert Thread.holdsLock(this); int id = obj.getId(); if (id > 0 && !obj.isTemporary()) { if (!isReadOnly()) { Row r = meta.getTemplateRow(); MetaRecord.populateRowFromDBObject(obj, r); assert objectIds.get(id); if (SysProperties.CHECK) { verifyMetaLocked(session); } Cursor cursor = metaIdIndex.find(session, r, r); if (!cursor.next()) { meta.addRow(session, r); } else { assert starting; Row oldRow = cursor.get(); MetaRecord rec = new MetaRecord(oldRow); assert rec.getId() == obj.getId(); assert rec.getObjectType() == obj.getType(); if (!rec.getSQL().equals(obj.getCreateSQLForMeta())) { meta.updateRow(session, oldRow, r); } } } } } /** * Verify the meta table is locked. * * @param session the session */ public void verifyMetaLocked(SessionLocal session) { if (lockMode != Constants.LOCK_MODE_OFF && meta != null && !meta.isLockedExclusivelyBy(session)) { throw DbException.getInternalError(); } } /** * Lock the metadata table for updates. * * @param session the session * @return whether it was already locked before by this session */ public boolean lockMeta(SessionLocal 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) { lockMetaAssertion(session); } return meta.lock(session, Table.EXCLUSIVE_LOCK); } private void lockMetaAssertion(SessionLocal session) { // 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 SessionLocal 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"); } } } /** * Unlock the metadata table. * * @param session the session */ public void unlockMeta(SessionLocal 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. * * @param session the session */ static void unlockMetaDebug(SessionLocal 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. * * @param session the session * @param id the id of the object to remove */ public void removeMeta(SessionLocal session, int id) { if (id > 0 && !starting) { SearchRow r = meta.getRowFactory().createRow(); r.setValue(0, ValueInteger.get(id)); boolean wasLocked = lockMeta(session); try { Cursor cursor = metaIdIndex.find(session, r, r); if (cursor.next()) { 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); } } // 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); } } /** * Mark some database ids as unused. * @param idsToRelease the ids to release */ public void releaseDatabaseObjectIds(BitSet idsToRelease) { synchronized (objectIds) { objectIds.andNot(idsToRelease); } } @SuppressWarnings("unchecked") private Map<String, DbObject> getMap(int type) { Map<String, ? extends DbObject> result; switch (type) { case DbObject.USER: case DbObject.ROLE: result = usersAndRoles; break; case DbObject.SETTING: result = settings; break; case DbObject.RIGHT: result = rights; break; case DbObject.SCHEMA: result = schemas; break; case DbObject.COMMENT: result = comments; break; default: throw DbException.getInternalError("type=" + type); } return (Map<String, DbObject>) result; } /** * Add a schema object to the database. * * @param session the session * @param obj the object to add */ public void addSchemaObject(SessionLocal 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. * * @param session the session * @param obj the object to add */ public synchronized void addDatabaseObject(SessionLocal session, DbObject obj) { int id = obj.getId(); if (id > 0 && !starting) { checkWritingAllowed(); } Map<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) { throw DbException.getInternalError("object already exists"); } lockMeta(session); addMeta(session, obj); map.put(name, obj); } /** * 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. * * @param roleName the name of the role * @return the role or null */ public Role findRole(String roleName) { RightOwner rightOwner = findUserOrRole(roleName); return rightOwner instanceof Role ? (Role) rightOwner : 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) { if (schemaName == null) { return null; } return schemas.get(schemaName); } /** * 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. * * @param name the name of the user * @return the user or null */ public User findUser(String name) { RightOwner rightOwner = findUserOrRole(name); return rightOwner instanceof User ? (User) rightOwner : null; } /** * 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; } /** * Get the user or role if it exists, or {@code null} if not. * * @param name the name of the user or role * @return the user, the role, or {@code null} */ public RightOwner findUserOrRole(String name) { return usersAndRoles.get(StringUtils.toUpperEnglish(name)); } /** * Create a session for the given user. * * @param user the user * @param networkConnectionInfo the network connection information, or {@code null} * @return the session, or null if the database is currently closing * @throws DbException if the database is in exclusive mode */ synchronized SessionLocal createSession(User user, NetworkConnectionInfo networkConnectionInfo) { if (closing) { return null; } if (exclusiveSession.get() != null) { throw DbException.get(ErrorCode.DATABASE_IS_IN_EXCLUSIVE_MODE); } SessionLocal session = createSession(user); session.setNetworkConnectionInfo(networkConnectionInfo); userSessions.add(session); trace.info("connecting session #{0} to {1}", session.getId(), databaseName); if (delayedCloser != null) { delayedCloser.reset(); delayedCloser = null; } return session; } private SessionLocal createSession(User user) { int id = ++nextSessionId; return new SessionLocal(this, user, id); } /** * Remove a session. This method is called after the user has disconnected. * * @param session the session */ public synchronized void removeSession(SessionLocal session) { if (session != null) { exclusiveSession.compareAndSet(session, null); if (userSessions.remove(session)) { trace.info("disconnecting session #{0}", session.getId()); } } if (isUserSession(session)) { if (userSessions.isEmpty()) { if (closeDelay == 0) { close(false); } else if (closeDelay < 0) { return; } else { delayedCloser = new DelayedDatabaseCloser(this, closeDelay * 1000); } } if (session != null) { trace.info("disconnected session #{0}", session.getId()); } } } boolean isUserSession(SessionLocal session) { return session != systemSession && session != lobSession; } private synchronized void closeAllSessionsExcept(SessionLocal except) { SessionLocal[] all = userSessions.toArray(EMPTY_SESSION_ARRAY); for (SessionLocal s : all) { if (s != except) { // indicate that session need to be closed ASAP s.suspend(); } } int timeout = 2 * getLockTimeout(); long start = System.currentTimeMillis(); // 'sleep' should be strictly greater than zero, otherwise real time is not taken into consideration // and the thread simply waits until notified long sleep = Math.max(timeout / 20, 1); boolean done = false; while (!done) { try { // although nobody going to notify us // it is vital to give up lock on a database wait(sleep); } catch (InterruptedException e1) { // ignore } if (System.currentTimeMillis() - start > timeout) { for (SessionLocal s : all) { if (s != except && !s.isClosed()) { try { // this will rollback outstanding transaction s.close(); } catch (Throwable e) { trace.error(e, "disconnecting session #{0}", s.getId()); } } } break; } done = true; for (SessionLocal s : all) { if (s != except && !s.isClosed()) { done = false; break; } } } } /** * 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) { synchronized (this) { if (closing || !fromShutdownHook && !userSessions.isEmpty()) { return; } closing = true; stopServer(); if (!userSessions.isEmpty()) { assert fromShutdownHook; trace.info("closing {0} from shutdown hook", databaseName); closeAllSessionsExcept(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(); closing = true; if (!userSessions.isEmpty()) { trace.info("event listener {0} left connection open", e.getClass().getName()); // if listener left an open connection closeAllSessionsExcept(null); } } } try { try { if (systemSession != null) { if (powerOffCount != -1) { for (Schema schema : schemas.values()) { for (Table table : schema.getAllTablesAndViews(null)) { if (table.isGlobalTemporary()) { removeSchemaObject(systemSession, table); } else { table.close(systemSession); } } } for (Schema schema : schemas.values()) { for (Sequence sequence : schema.getAllSequences()) { sequence.close(); } } } for (Schema schema : schemas.values()) { for (TriggerObject trigger : schema.getAllTriggers()) { try { trigger.close(); } catch (SQLException e) { trace.error(e, "close"); } } } if (powerOffCount != -1) { meta.close(systemSession); systemSession.commit(true); } if (lobSession != null) { lobSession.close(); lobSession = null; } systemSession.close(); systemSession = null; } tempFileDeleter.deleteAll(); closeOpenFilesAndUnlock(); } catch (DbException | MVStoreException 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.close(databaseName); } } /** * Close all open files and unlock the database. */ private synchronized void closeOpenFilesAndUnlock() { try { if (lobStorage != null) { lobStorage.close(); } if (store != null && !store.getMvStore().isClosed()) { if (compactMode == CommandInterface.SHUTDOWN_IMMEDIATELY) { store.closeImmediately(); } else { int allowedCompactionTime = compactMode == CommandInterface.SHUTDOWN_COMPACT || compactMode == CommandInterface.SHUTDOWN_DEFRAG || dbSettings.defragAlways ? -1 : dbSettings.maxCompactTime; store.close(allowedCompactionTime); } if (persistent) { // Don't delete temp files if everything is already closed // (maybe in checkPowerOff), the database could be open now // (even from within another process). if (lock != null || fileLockMethod == FileLockMethod.NO || fileLockMethod == FileLockMethod.FS) { deleteOldTempFiles(); } } } } finally { if (lock != null) { lock.unlock(); lock = null; } } } private synchronized void closeFiles() { try { store.closeImmediately(); } catch (DbException e) { trace.error(e, "close"); } } private void checkMetaFree(SessionLocal session, int id) { SearchRow r = meta.getRowFactory().createRow(); r.setValue(0, ValueInteger.get(id)); Cursor cursor = metaIdIndex.find(session, r, r); if (cursor.next()) { throw DbException.getInternalError(); } } /** * Allocate a new object id. * * @return the id */ public int allocateObjectId() { int i; synchronized (objectIds) { i = objectIds.nextClearBit(0); objectIds.set(i); } return i; } /** * Returns system user. * * @return system user */ public User getSystemUser() { return systemUser; } /** * Returns main schema (usually PUBLIC). * * @return main schema (usually PUBLIC) */ public Schema getMainSchema() { return mainSchema; } 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()); } /** * Get all tables and views. Meta data tables may be excluded. * * @return all objects of that type */ public ArrayList<Table> getAllTablesAndViews() { ArrayList<Table> list = new ArrayList<>(); for (Schema schema : schemas.values()) { list.addAll(schema.getAllTablesAndViews(null)); } return list; } /** * 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; } public Collection<Schema> getAllSchemas() { return schemas.values(); } public Collection<Schema> getAllSchemasNoMeta() { return schemas.values(); } public Collection<Setting> getAllSettings() { return settings.values(); } public Collection<RightOwner> getAllUsersAndRoles() { return usersAndRoles.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. * * @param includingSystemSession if the system session should also be * included * @return the list of sessions */ public SessionLocal[] getSessions(boolean includingSystemSession) { ArrayList<SessionLocal> list; // need to synchronized on this database, // otherwise the list may contain null elements synchronized (this) { list = new ArrayList<>(userSessions); } if (includingSystemSession) { // copy, to ensure the reference is stable SessionLocal s = systemSession; if (s != null) { list.add(s); } s = lobSession; if (s != null) { list.add(s); } } return list.toArray(new SessionLocal[0]); } /** * Update an object in the system table. * * @param session the session * @param obj the database object */ public void updateMeta(SessionLocal session, DbObject obj) { 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); } } } /** * Rename a schema object. * * @param session the session * @param obj the object * @param newName the new name */ public synchronized void renameSchemaObject(SessionLocal session, SchemaObject obj, String newName) { checkWritingAllowed(); obj.getSchema().rename(obj, newName); updateMetaAndFirstLevelChildren(session, obj); } private synchronized void updateMetaAndFirstLevelChildren(SessionLocal session, DbObject obj) { ArrayList<DbObject> list = obj.getChildren(); Comment comment = findComment(obj); if (comment != null) { throw DbException.getInternalError(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. * * @param session the session * @param obj the object * @param newName the new name */ public synchronized void renameDatabaseObject(SessionLocal session, DbObject obj, String newName) { checkWritingAllowed(); int type = obj.getType(); Map<String, DbObject> map = getMap(type); if (SysProperties.CHECK) { if (!map.containsKey(obj.getName())) { throw DbException.getInternalError("not found: " + obj.getName()); } if (obj.getName().equals(newName) || map.containsKey(newName)) { throw DbException.getInternalError("object already exists: " + newName); } } obj.checkRename(); map.remove(obj.getName()); obj.rename(newName); map.put(newName, obj); updateMetaAndFirstLevelChildren(session, obj); } 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. * * @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. * * @param session the session * @param obj the object to remove */ public synchronized void removeDatabaseObject(SessionLocal session, DbObject obj) { checkWritingAllowed(); String objName = obj.getName(); int type = obj.getType(); Map<String, DbObject> map = getMap(type); if (SysProperties.CHECK && !map.containsKey(objName)) { throw DbException.getInternalError("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. * * @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 (Schema schema : schemas.values()) { for (Table t : schema.getAllTablesAndViews(null)) { if (except == t || 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. * * @param session the session * @param obj the object to be removed */ public void removeSchemaObject(SessionLocal 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; if (constraint.getConstraintType() != Type.DOMAIN) { 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.getTraceSQL(), t.getTraceSQL()); } obj.removeChildrenAndResources(session); } removeMeta(session, id); } } /** * 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); } 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. * * @param baseName the prefix of the returned name * @param session the session * @return a unique name */ public synchronized String getTempTableName(String baseName, SessionLocal session) { int maxBaseLength = Constants.MAX_IDENTIFIER_LENGTH - (7 + ValueInteger.DISPLAY_SIZE * 2); if (baseName.length() > maxBaseLength) { baseName = baseName.substring(0, maxBaseLength); } 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); } } public boolean isReadOnly() { return readOnly; } public void setWriteDelay(int value) { store.getMvStore().setAutoCommitDelay(value < 0 ? 0 : value); } public int getRetentionTime() { return store.getMvStore().getRetentionTime(); } public void setRetentionTime(int value) { store.getMvStore().setRetentionTime(value); } public void setAllowBuiltinAliasOverride(boolean b) { allowBuiltinAliasOverride = b; } public boolean isAllowBuiltinAliasOverride() { return allowBuiltinAliasOverride; } /** * Get the list of in-doubt transactions. * * @return the list */ public ArrayList<InDoubtTransaction> getInDoubtTransactions() { return store.getInDoubtTransactions(); } /** * Prepare a transaction. * * @param session the session * @param transaction the name of the transaction */ synchronized void prepareCommit(SessionLocal session, String transaction) { if (!readOnly) { store.prepareCommit(session, transaction); } } /** * If there is a background store thread, and if there wasn an exception in * that thread, throw it now. */ 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() { MVStoreException exception = store.getMvStore().getPanicException(); if(exception != null) { return exception; } return backgroundException.getAndSet(null); } /** * Flush all pending changes to the transaction log. */ public synchronized void flush() { if (!readOnly) { 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 {@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 or 0 if unknown */ public void setProgress(int state, String name, long x, long 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). * * @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. */ public synchronized void sync() { if (readOnly) { return; } store.sync(); } public int getMaxMemoryRows() { return maxMemoryRows; } public void setMaxMemoryRows(int value) { this.maxMemoryRows = value; } public void setLockMode(int lockMode) { switch (lockMode) { case Constants.LOCK_MODE_OFF: case Constants.LOCK_MODE_READ_COMMITTED: break; case Constants.LOCK_MODE_TABLE: case Constants.LOCK_MODE_TABLE_GC: lockMode = Constants.LOCK_MODE_READ_COMMITTED; 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 SessionLocal getSystemSession() { return systemSession; } /** * 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 void setIgnoreCatalogs(boolean b) { ignoreCatalogs = b; } public boolean getIgnoreCatalogs() { return ignoreCatalogs; } public synchronized void setDeleteFilesOnDisconnect(boolean b) { this.deleteFilesOnDisconnect = b; } 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. * * @return true if the database is still starting */ public boolean isStarting() { return starting; } /** * 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(); } } public void setMode(Mode mode) { this.mode = mode; getNextRemoteSettingsId(); } @Override public Mode getMode() { return mode; } public void setDefaultNullOrdering(DefaultNullOrdering defaultNullOrdering) { this.defaultNullOrdering = defaultNullOrdering; } public DefaultNullOrdering getDefaultNullOrdering() { return defaultNullOrdering; } public void setMaxOperationMemory(int maxOperationMemory) { this.maxOperationMemory = maxOperationMemory; } public int getMaxOperationMemory() { return maxOperationMemory; } public SessionLocal getExclusiveSession() { return exclusiveSession.get(); } /** * Set the session that can exclusively access the database. * * @param session the session * @param closeOthers whether other sessions are closed * @return true if success or if database is in exclusive mode * set by this session already, false otherwise */ public boolean setExclusiveSession(SessionLocal session, boolean closeOthers) { if (exclusiveSession.get() != session && !exclusiveSession.compareAndSet(null, session)) { return false; } if (closeOthers) { closeAllSessionsExcept(session); } return true; } /** * Stop exclusive access the database by provided session. * * @param session the session * @return true if success or if database is in non-exclusive mode already, * false otherwise */ public boolean unsetExclusiveSession(SessionLocal session) { return exclusiveSession.get() == null || exclusiveSession.compareAndSet(session, null); } @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. * * @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. * * @param session the session * @return true if it is currently locked */ public boolean isSysTableLockedBy(SessionLocal session) { return meta == null || meta.isLockedExclusivelyBy(session); } /** * 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. */ public void shutdownImmediately() { closing = true; setPowerOffCount(1); try { checkPowerOff(); } catch (DbException e) { // ignore } closeFiles(); powerOffCount = 0; } @Override public TempFileDeleter getTempFileDeleter() { return tempFileDeleter; } /** * 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 (Schema schema : schemas.values()) { for (Table table : schema.getAllTablesAndViews(null)) { if (table.getCreateSQL() == null || table.isHidden()) { continue; } // exclude the LOB_MAP that the Recover tool creates if (schema.getId() == Constants.INFORMATION_SCHEMA_ID && table.getName().equalsIgnoreCase("LOB_BLOCKS")) { continue; } return table; } } return null; } /** * Flush all changes and open a new transaction log. */ public void checkpoint() { if (persistent) { store.flush(); } getTempFileDeleter().deleteUnused(); } /** * 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() { return lobStorage; } public SessionLocal getLobSession() { return lobSession; } 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. * * @param <V> the value type * @return the hash map */ public <V> HashMap<String, V> newStringMap() { return dbSettings.caseInsensitiveIdentifiers ? new CaseInsensitiveMap<>() : new HashMap<>(); } /** * Create a new hash map. Depending on the configuration, the key is case * sensitive or case insensitive. * * @param <V> the value type * @param initialCapacity the initial capacity * @return the hash map */ public <V> HashMap<String, V> newStringMap(int initialCapacity) { return dbSettings.caseInsensitiveIdentifiers ? new CaseInsensitiveMap<>(initialCapacity) : new HashMap<>(initialCapacity); } /** * 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<>() : new ConcurrentHashMap<>(); } /** * 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. * * @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; } char c = upperName.charAt(0); if (c < 'A' || c > 'Z') { return false; } l--; for (int i = 1; i < l; i++) { c = upperName.charAt(i); if ((c < 'A' || c > 'Z') && c != '_') { return false; } } if (l > 0) { c = upperName.charAt(l); if (c < 'A' || c > 'Z') { return false; } } return true; } @Override public int readLob(long lobId, byte[] hmac, long offset, byte[] buff, int off, int length) { throw DbException.getInternalError(); } 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; getNextRemoteSettingsId(); } } /** * 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 * @return authenticator set for database */ public Authenticator getAuthenticator() { return 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; } @Override public ValueTimestampTimeZone currentTimestamp() { Session session = SessionLocal.getThreadLocalSession(); if (session != null) { return session.currentTimestamp(); } throw DbException.getUnsupportedException("Unsafe comparison or cast"); } @Override public TimeZoneProvider currentTimeZone() { Session session = SessionLocal.getThreadLocalSession(); if (session != null) { return session.currentTimeZone(); } throw DbException.getUnsupportedException("Unsafe comparison or cast"); } @Override public boolean zeroBasedEnums() { return dbSettings.zeroBasedEnums; } }
Detected license expression

      
    
Detected license expression (SPDX)

      
    
Percentage of license text
0.16
Copyrights
- end_line: 2
  copyright: Copyright 2004-2023 H2 Group. Multiple-Licensed
  start_line: 2
Holders
- holder: H2 Group. Multiple-Licensed
  end_line: 2
  start_line: 2
Authors

      
    
License expression License clue details
(mpl-2.0 OR epl-1.0) AND proprietary-license {'score': 20.37, 'matcher': '3-seq', 'end_line': 3, 'rule_url': 'https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/rules/mpl-2.0_or_epl-1.0_and_proprietary-license_2.RULE', 'from_file': None, 'start_line': 2, 'matched_text': ' * Copyright 2004-2023 H2 Group. Multiple-Licensed under the MPL 2.0,\n * and the EPL 1.0 (https://h2database.com/html/license.html).', 'match_coverage': 20.37, 'matched_length': 11, 'rule_relevance': 100, 'rule_identifier': 'mpl-2.0_or_epl-1.0_and_proprietary-license_2.RULE', 'license_expression': '(mpl-2.0 OR epl-1.0) AND proprietary-license', 'license_expression_spdx': '(MPL-2.0 OR EPL-1.0) AND LicenseRef-scancode-proprietary-license'}
URL Start line End line
https://h2database.com/html/license.html 3 3
Package URL License Primary language
pkg:osgi/com.h2database.source@2.2.220