Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2004-2019 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.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.StringTokenizer;
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.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.LobStorageMap;
import org.h2.mvstore.db.MVTableEngine;
import org.h2.pagestore.PageStore;
import org.h2.pagestore.WriterThread;
import org.h2.pagestore.db.LobStorageBackend;
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.LobStorageFrontend;
import org.h2.store.LobStorageInterface;
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.CurrentTimestamp;
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.Utils;
import org.h2.value.CaseInsensitiveConcurrentMap;
import org.h2.value.CaseInsensitiveMap;
import org.h2.value.CompareMode;
import org.h2.value.Value;
import org.h2.value.ValueInt;
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 class Database implements DataHandler, CastDataProvider {
private static int initialPowerOffCount;
private static final boolean ASSERT;
private static final ThreadLocal META_LOCK_DEBUGGING;
private static final ThreadLocal META_LOCK_DEBUGGING_DB;
private static final ThreadLocal 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.
*/
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 ConcurrentHashMap roles = new ConcurrentHashMap<>();
private final ConcurrentHashMap users = new ConcurrentHashMap<>();
private final ConcurrentHashMap settings = new ConcurrentHashMap<>();
private final ConcurrentHashMap schemas = new ConcurrentHashMap<>();
private final ConcurrentHashMap rights = new ConcurrentHashMap<>();
private final ConcurrentHashMap domains = new ConcurrentHashMap<>();
private final ConcurrentHashMap aggregates = new ConcurrentHashMap<>();
private final ConcurrentHashMap comments = new ConcurrentHashMap<>();
private final HashMap tableEngines = new HashMap<>();
private final Set userSessions =
Collections.synchronizedSet(new HashSet());
private final AtomicReference 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();
private int maxOperationMemory =
Constants.DEFAULT_MAX_OPERATION_MEMORY;
private SmallLRUCache lobFileListCache;
private final boolean autoServerMode;
private final int autoServerPort;
private Server server;
private HashMap linkConnections;
private final TempFileDeleter tempFileDeleter = TempFileDeleter.getInstance();
private PageStore pageStore;
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 int logMode;
private MVTableEngine.Store store;
private int retentionTime;
private boolean allowBuiltinAliasOverride;
private final AtomicReference 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 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 name = ci.getName();
this.dbSettings = ci.getDbSettings();
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;
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;
}
String lockMethodName = ci.getProperty("FILE_LOCK", null);
if (dbSettings.mvStore && lockMethodName == null) {
fileLockMethod = autoServerMode ? FileLockMethod.FILE : FileLockMethod.FS;
} else {
fileLockMethod = FileLock.getFileLockMethod(lockMethodName);
}
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.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));
this.ignoreCatalogs = ci.getProperty("IGNORE_CATALOGS",
dbSettings.ignoreCatalogs);
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.
*
* @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.
*
* @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, this, compareMode) == 0;
}
/**
* 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, this, compareMode);
}
/**
* 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 <>)
* @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, this, compareMode);
}
/**
* 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, this);
}
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 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();
// 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.
*
* @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);
}
/**
* Check if a database with the given name exists.
*
* @param name
* the name of the database (including path)
* @param mvStore
* {@code true} to check MVStore file only, {@code false} to
* check PageStore file only
* @return true if one exists
*/
static boolean exists(String name, boolean mvStore) {
return FileUtils.exists(name + (mvStore ? Constants.SUFFIX_MV_FILE : Constants.SUFFIX_PAGE_FILE));
}
/**
* 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;
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.setMvStore(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.FS) {
throw DbException.getUnsupportedException(
"autoServerMode && (readOnly || " +
"fileLockMethod == NO || " +
"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());
}
}
}
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;
getPageStore();
}
} else {
getPageStore();
}
starting = false;
} 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();
}
if (dbSettings.mvStore) {
// MVStore
for (Iterator i = dbSettings.getSettings().keySet().iterator(); i.hasNext();) {
if (i.next().startsWith("PAGE_STORE_")) {
i.remove();
}
}
} else if (store == null) {
// PageStore without additional MVStore for spatial features
for (Iterator i = dbSettings.getSettings().keySet().iterator(); i.hasNext();) {
String name = i.next();
if ("COMPRESS".equals(name) || "REUSE_SPACE".equals(name)) {
i.remove();
}
}
}
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 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 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 (persistent) {
if (store == null) {
writer = WriterThread.create(this, writeDelay);
} else {
setWriteDelay(writeDelay);
}
}
}
/**
* 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 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() {
@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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
* @param idsToRelease the ids to release
*/
void releaseDatabaseObjectIds(BitSet idsToRelease) {
synchronized (objectIds) {
objectIds.andNot(idsToRelease);
}
}
@SuppressWarnings("unchecked")
private Map getMap(int type) {
Map 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 (Map) result;
}
/**
* 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.
*
* @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();
}
Map 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.
*
* @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.
*
* @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) {
return roles.get(StringUtils.toUpperEnglish(roleName));
}
/**
* 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;
}
Schema schema = schemas.get(schemaName);
if (schema == infoSchema) {
initMetaTables();
}
return schema;
}
/**
* 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) {
return users.get(StringUtils.toUpperEnglish(name));
}
/**
* 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.
*
* @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.
*
* @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 Session createSession(User user, NetworkConnectionInfo networkConnectionInfo) {
if (closing) {
return null;
}
if (exclusiveSession.get() != null) {
throw DbException.get(ErrorCode.DATABASE_IS_IN_EXCLUSIVE_MODE);
}
Session session = new Session(this, user, ++nextSessionId);
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;
}
/**
* 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 closeAllSessionsExcept(Session except) {
Session[] all = userSessions.toArray(EMPTY_SESSION_ARRAY);
for (Session s : all) {
if (s != except) {
// indicate that session need to be closed ASAP
s.suspend();
}
}
int timeout = 2 * getLockTimeout();
long start = System.currentTimeMillis();
boolean done = false;
while (!done) {
long sleep = timeout / 20;
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 (Session 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 (Session 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) {
try {
synchronized (this) {
if (closing) {
return;
}
closing = true;
stopServer();
if (!userSessions.isEmpty()) {
if (!fromShutdownHook) {
return;
}
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();
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.
*
* @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");
}
}
}
if (store != null) {
MVStore mvStore = store.getMvStore();
if (mvStore != null && !mvStore.isClosed()) {
boolean compactFully =
compactMode == CommandInterface.SHUTDOWN_COMPACT ||
compactMode == CommandInterface.SHUTDOWN_DEFRAG ||
getSettings().defragAlways;
store.close(compactFully ? -1 : dbSettings.maxCompactTime);
}
}
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) {
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.
*
* @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).
*
* @return main schema (usually PUBLIC)
*/
public Schema getMainSchema() {
return mainSchema;
}
public ArrayList getAllAggregates() {
return new ArrayList<>(aggregates.values());
}
public ArrayList getAllComments() {
return new ArrayList<>(comments.values());
}
public int getAllowLiterals() {
if (starting) {
return Constants.ALLOW_LITERALS_ALL;
}
return allowLiterals;
}
public ArrayList getAllRights() {
return new ArrayList<>(rights.values());
}
public ArrayList getAllRoles() {
return new ArrayList<>(roles.values());
}
/**
* Get all schema objects.
*
* @return all objects of all types
*/
public ArrayList getAllSchemaObjects() {
initMetaTables();
ArrayList list = new ArrayList<>();
for (Schema schema : schemas.values()) {
schema.getAll(list);
}
return list;
}
/**
* Get all schema objects of the given type.
*
* @param type the object type
* @return all objects of that type
*/
public ArrayList getAllSchemaObjects(int type) {
if (type == DbObject.TABLE_OR_VIEW) {
initMetaTables();
}
ArrayList list = new ArrayList<>();
for (Schema schema : schemas.values()) {
schema.getAll(type, list);
}
return list;
}
/**
* 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
getAllTablesAndViews(boolean includeMeta) {
if (includeMeta) {
initMetaTables();
}
ArrayList
list = new ArrayList<>();
for (Schema schema : schemas.values()) {
list.addAll(schema.getAllTablesAndViews());
}
return list;
}
/**
* Get all synonyms.
*
* @return all objects of that type
*/
public ArrayList getAllSynonyms() {
ArrayList list = new ArrayList<>();
for (Schema schema : schemas.values()) {
list.addAll(schema.getAllSynonyms());
}
return list;
}
/**
* Get the tables with the given name, if any.
*
* @param name the table name
* @return the list
*/
public ArrayList
getTableOrViewByName(String name) {
// we expect that at most one table matches, at least in most cases
ArrayList
list = new ArrayList<>(1);
for (Schema schema : schemas.values()) {
Table table = schema.getTableOrViewByName(name);
if (table != null) {
list.add(table);
}
}
return list;
}
public Collection getAllSchemas() {
initMetaTables();
return schemas.values();
}
public Collection getAllSettings() {
return settings.values();
}
public Collection getAllDomains() {
return domains.values();
}
public Collection getAllUsers() {
return 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.
*
* @param includingSystemSession if the system session should also be
* included
* @return the list of sessions
*/
public Session[] getSessions(boolean includingSystemSession) {
ArrayList 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.
*
* @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.
*
* @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 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.
*
* @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();
Map 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.
*
* @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.
*
* @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(Session session, DbObject obj) {
checkWritingAllowed();
String objName = obj.getName();
int type = obj.getType();
Map 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.
*
* @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 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.
*
* @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.
*
* @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.
*
* @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);
}
}
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.
*
* @return true if it is
*/
public boolean getFlushOnEachCommit() {
return flushOnEachCommit;
}
/**
* Get the list of in-doubt transactions.
*
* @return the list
*/
public ArrayList getInDoubtTransactions() {
if (store != null) {
return store.getInDoubtTransactions();
}
return pageStore == null ? null : pageStore.getInDoubtTransactions();
}
/**
* 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.
*
* @param session the session
*/
synchronized void commit(Session session) {
throwLastBackgroundException();
if (readOnly) {
return;
}
if (pageStore != null) {
pageStore.commit(session);
}
session.setAllCommitted();
}
/**
* If there is a background store thread, and if there wasn an exception in
* that thread, throw it now.
*/
void throwLastBackgroundException() {
if (store == null || !store.getMvStore().isBackgroundThread()) {
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.
*/
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 {@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).
*
* @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;
}
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:
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.
*
* @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;
}
@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.
*
* @return true if the database is still starting
*/
public boolean isStarting() {
return starting;
}
/**
* 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.
*/
void opened() {
if (eventListener != null) {
eventListener.opened();
}
if (writer != null) {
writer.startThread();
}
}
public void setMode(Mode mode) {
this.mode = mode;
}
@Override
public Mode getMode() {
return mode;
}
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.
*
* @param session the session
* @param closeOthers whether other sessions are closed
* @return true if success, false otherwise
*/
public boolean setExclusiveSession(Session session, boolean closeOthers) {
if (!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, false otherwise
*/
public boolean unsetExclusiveSession(Session session) {
return exclusiveSession.compareAndSet(session, null);
}
@Override
public SmallLRUCache 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(Session 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();
}
@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.
*
* @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;
}
/**
* 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();
}
/**
* 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.
*
* @param the value type
* @return the hash map
*/
public HashMap 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 the value type
* @return the hash map
*/
public ConcurrentHashMap 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;
}
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.
*
* @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() {
/*
* This method shouldn't be used in this class, but return a value for
* safety.
*/
return CurrentTimestamp.get();
}
}