package org.h2.engine;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import org.h2.api.DatabaseEventListener;
import org.h2.api.ErrorCode;
import org.h2.api.JavaObjectSerializer;
import org.h2.api.TableEngine;
import org.h2.command.CommandInterface;
import org.h2.command.ddl.CreateTableData;
import org.h2.command.dml.SetTypes;
import org.h2.constraint.Constraint;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.message.TraceSystem;
import org.h2.mvstore.MVStore;
import org.h2.mvstore.db.MVTableEngine;
import org.h2.result.LocalResultFactory;
import org.h2.result.Row;
import org.h2.result.RowFactory;
import org.h2.result.SearchRow;
import org.h2.schema.Schema;
import org.h2.schema.SchemaObject;
import org.h2.schema.Sequence;
import org.h2.schema.TriggerObject;
import org.h2.security.auth.Authenticator;
import org.h2.store.DataHandler;
import org.h2.store.FileLock;
import org.h2.store.FileLockMethod;
import org.h2.store.FileStore;
import org.h2.store.InDoubtTransaction;
import org.h2.store.LobStorageBackend;
import org.h2.store.LobStorageFrontend;
import org.h2.store.LobStorageInterface;
import org.h2.store.LobStorageMap;
import org.h2.store.PageStore;
import org.h2.store.WriterThread;
import org.h2.store.fs.FileUtils;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.MetaTable;
import org.h2.table.Table;
import org.h2.table.TableLinkConnection;
import org.h2.table.TableSynonym;
import org.h2.table.TableType;
import org.h2.table.TableView;
import org.h2.tools.DeleteDbFiles;
import org.h2.tools.Server;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
import org.h2.util.SmallLRUCache;
import org.h2.util.SourceCompiler;
import org.h2.util.StringUtils;
import org.h2.util.TempFileDeleter;
import org.h2.util.Utils;
import org.h2.value.CaseInsensitiveMap;
import org.h2.value.CompareMode;
import org.h2.value.CaseInsensitiveConcurrentMap;
import org.h2.value.Value;
import org.h2.value.ValueInt;
public class Database implements DataHandler {
private static int initialPowerOffCount;
private static final boolean ASSERT;
private static final ThreadLocal<Session> META_LOCK_DEBUGGING;
private static final ThreadLocal<Database> META_LOCK_DEBUGGING_DB;
private static final ThreadLocal<Throwable> META_LOCK_DEBUGGING_STACK;
private static final Session[] EMPTY_SESSION_ARRAY = new Session[0];
static {
boolean a = false;
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;
}
}
private static final String SYSTEM_USER_NAME = "DBA";
private final boolean persistent;
private final String databaseName;
private final String databaseShortName;
private final String databaseURL;
private final String cipher;
private final byte[] filePasswordHash;
private final byte[] fileEncryptionKey;
private final HashMap<String, Role> roles = new HashMap<>();
private final HashMap<String, User> users = new HashMap<>();
private final HashMap<String, Setting> settings = new HashMap<>();
private final HashMap<String, Schema> schemas = new HashMap<>();
private final HashMap<String, Right> rights = new HashMap<>();
private final HashMap<String, Domain> domains = new HashMap<>();
private final HashMap<String, UserAggregate> aggregates = new HashMap<>();
private final HashMap<String, Comment> = new HashMap<>();
private final HashMap<String, TableEngine> tableEngines = new HashMap<>();
private final Set<Session> userSessions =
Collections.synchronizedSet(new HashSet<Session>());
private final AtomicReference<Session> exclusiveSession = new AtomicReference<>();
private final BitSet objectIds = new BitSet();
private final Object lobSyncObject = new Object();
private Schema mainSchema;
private Schema infoSchema;
private int nextSessionId;
private int nextTempTableId;
private User systemUser;
private Session systemSession;
private Session lobSession;
private Table meta;
private Index metaIdIndex;
private FileLock lock;
private WriterThread writer;
private volatile boolean starting;
private TraceSystem traceSystem;
private Trace trace;
private final FileLockMethod fileLockMethod;
private Role publicRole;
private final AtomicLong modificationDataId = new AtomicLong();
private final AtomicLong modificationMetaId = new AtomicLong();
private CompareMode compareMode;
private String cluster = Constants.CLUSTERING_DISABLED;
private boolean readOnly;
private int writeDelay = Constants.DEFAULT_WRITE_DELAY;
private DatabaseEventListener eventListener;
private int maxMemoryRows = SysProperties.MAX_MEMORY_ROWS;
private int maxMemoryUndo = Constants.DEFAULT_MAX_MEMORY_UNDO;
private int lockMode = Constants.DEFAULT_LOCK_MODE;
private int maxLengthInplaceLob;
private int allowLiterals = Constants.ALLOW_LITERALS_ALL;
private int powerOffCount = initialPowerOffCount;
private volatile int closeDelay;
private DelayedDatabaseCloser delayedCloser;
private volatile boolean closing;
private boolean ignoreCase;
private boolean deleteFilesOnDisconnect;
private String lobCompressionAlgorithm;
private boolean optimizeReuseResults = true;
private final String cacheType;
private final String accessModeData;
private boolean referentialIntegrity = true;
private Mode mode = Mode.getRegular();
private boolean multiThreaded;
private int maxOperationMemory =
Constants.DEFAULT_MAX_OPERATION_MEMORY;
private SmallLRUCache<String, String[]> lobFileListCache;
private final boolean autoServerMode;
private final int autoServerPort;
private Server server;
private HashMap<TableLinkConnection, TableLinkConnection> linkConnections;
private final TempFileDeleter tempFileDeleter = TempFileDeleter.getInstance();
private PageStore pageStore;
private Properties reconnectLastLock;
private volatile long reconnectCheckNext;
private volatile boolean reconnectChangePending;
private volatile int checkpointAllowed;
private volatile boolean checkpointRunning;
private final Object reconnectSync = new Object();
private int cacheSize;
private int compactMode;
private SourceCompiler compiler;
private volatile boolean metaTablesInitialized;
private boolean flushOnEachCommit;
private LobStorageInterface lobStorage;
private final int pageSize;
private int defaultTableType = Table.TYPE_CACHED;
private final DbSettings dbSettings;
private final long reconnectCheckDelayNs;
private int logMode;
private MVTableEngine.Store store;
private int retentionTime;
private boolean allowBuiltinAliasOverride;
private final AtomicReference<DbException> backgroundException = new AtomicReference<>();
private JavaObjectSerializer javaObjectSerializer;
private String javaObjectSerializerName;
private volatile boolean javaObjectSerializerInitialized;
private boolean queryStatistics;
private int queryStatisticsMaxEntries = Constants.QUERY_STATISTICS_MAX_ENTRIES;
private QueryStatisticsData queryStatisticsData;
private RowFactory rowFactory = RowFactory.DEFAULT;
private LocalResultFactory resultFactory = LocalResultFactory.DEFAULT;
private Authenticator authenticator;
public Database(ConnectionInfo ci, String cipher) {
if (ASSERT) {
META_LOCK_DEBUGGING.set(null);
META_LOCK_DEBUGGING_DB.set(null);
META_LOCK_DEBUGGING_STACK.set(null);
}
String name = ci.getName();
this.dbSettings = ci.getDbSettings();
this.reconnectCheckDelayNs = TimeUnit.MILLISECONDS.toNanos(dbSettings.reconnectCheckDelay);
this.compareMode = CompareMode.getInstance(null, 0);
this.persistent = ci.isPersistent();
this.filePasswordHash = ci.getFilePasswordHash();
this.fileEncryptionKey = ci.getFileEncryptionKey();
this.databaseName = name;
this.databaseShortName = parseDatabaseShortName();
this.maxLengthInplaceLob = Constants.DEFAULT_MAX_LENGTH_INPLACE_LOB;
this.cipher = cipher;
String lockMethodName = ci.getProperty("FILE_LOCK", null);
this.accessModeData = StringUtils.toLowerEnglish(
ci.getProperty("ACCESS_MODE_DATA", "rw"));
this.autoServerMode = ci.getProperty("AUTO_SERVER", false);
this.autoServerPort = ci.getProperty("AUTO_SERVER_PORT", 0);
int defaultCacheSize = Utils.scaleForAvailableMemory(
Constants.CACHE_SIZE_DEFAULT);
this.cacheSize =
ci.getProperty("CACHE_SIZE", defaultCacheSize);
this.pageSize = ci.getProperty("PAGE_SIZE",
Constants.DEFAULT_PAGE_SIZE);
if ("r".equals(accessModeData)) {
readOnly = true;
}
if (dbSettings.mvStore && lockMethodName == null) {
if (autoServerMode) {
fileLockMethod = FileLockMethod.FILE;
} else {
fileLockMethod = FileLockMethod.FS;
}
} else {
fileLockMethod = FileLock.getFileLockMethod(lockMethodName);
}
if (dbSettings.mvStore && fileLockMethod == FileLockMethod.SERIALIZED) {
throw DbException.getUnsupportedException(
"MV_STORE combined with FILE_LOCK=SERIALIZED");
}
this.databaseURL = ci.getURL();
String listener = ci.removeProperty("DATABASE_EVENT_LISTENER", null);
if (listener != null) {
listener = StringUtils.trim(listener, true, true, "'");
setEventListenerClass(listener);
}
String modeName = ci.removeProperty("MODE", null);
if (modeName != null) {
mode = Mode.getInstance(modeName);
if (mode == null) {
throw DbException.get(ErrorCode.UNKNOWN_MODE_1, modeName);
}
}
this.logMode =
ci.getProperty("LOG", PageStore.LOG_MODE_SYNC);
this.javaObjectSerializerName =
ci.getProperty("JAVA_OBJECT_SERIALIZER", null);
this.multiThreaded =
ci.getProperty("MULTI_THREADED", dbSettings.mvStore);
this.allowBuiltinAliasOverride =
ci.getProperty("BUILTIN_ALIAS_OVERRIDE", false);
boolean closeAtVmShutdown =
dbSettings.dbCloseOnExit;
int traceLevelFile =
ci.getIntProperty(SetTypes.TRACE_LEVEL_FILE,
TraceSystem.DEFAULT_TRACE_LEVEL_FILE);
int traceLevelSystemOut =
ci.getIntProperty(SetTypes.TRACE_LEVEL_SYSTEM_OUT,
TraceSystem.DEFAULT_TRACE_LEVEL_SYSTEM_OUT);
this.cacheType = StringUtils.toUpperEnglish(
ci.removeProperty("CACHE_TYPE", Constants.CACHE_TYPE_DEFAULT));
openDatabase(traceLevelFile, traceLevelSystemOut, closeAtVmShutdown, ci);
}
private void openDatabase(int traceLevelFile, int traceLevelSystemOut,
boolean closeAtVmShutdown, ConnectionInfo ci) {
try {
open(traceLevelFile, traceLevelSystemOut, ci);
if (closeAtVmShutdown) {
OnExitDatabaseCloser.register(this);
}
} catch (Throwable e) {
try {
if (e instanceof OutOfMemoryError) {
e.fillInStackTrace();
}
boolean alreadyOpen = e instanceof DbException
&& ((DbException) e).getErrorCode() == ErrorCode.DATABASE_ALREADY_OPEN_1;
if (alreadyOpen) {
stopServer();
}
if (traceSystem != null) {
if (e instanceof DbException && !alreadyOpen) {
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();
}
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();
}
public boolean areEqual(Value a, Value b) {
return a.compareTo(b, mode, compareMode) == 0;
}
public int compare(Value a, Value b) {
return a.compareTo(b, mode, compareMode);
}
public int compareWithNull(Value a, Value b, boolean forEquality) {
return a.compareWithNull(b, forEquality, mode, compareMode);
}
public int compareTypeSafe(Value a, Value b) {
return a.compareTypeSafe(b, compareMode);
}
public long getModificationDataId() {
return modificationDataId.get();
}
private synchronized boolean reconnectModified(boolean pending) {
if (readOnly || lock == null ||
fileLockMethod != FileLockMethod.SERIALIZED) {
return true;
}
try {
if (pending == reconnectChangePending) {
long now = System.nanoTime();
if (now > reconnectCheckNext) {
if (pending) {
String pos = pageStore == null ?
null : Long.toString(pageStore.getWriteCountTotal());
lock.setProperty("logPos", pos);
lock.save();
}
reconnectCheckNext = now + reconnectCheckDelayNs;
}
return true;
}
Properties old = lock.load();
if (pending) {
if (old.getProperty("changePending") != null) {
return false;
}
trace.debug("wait before writing");
Thread.sleep(TimeUnit.NANOSECONDS.toMillis((long) (reconnectCheckDelayNs * 1.1)));
Properties now = lock.load();
if (!now.equals(old)) {
return false;
}
}
String pos = pageStore == null ?
null : Long.toString(pageStore.getWriteCountTotal());
lock.setProperty("logPos", pos);
if (pending) {
lock.setProperty("changePending", "true-" + Math.random());
} else {
lock.setProperty("changePending", null);
}
reconnectCheckNext = System.nanoTime() +
2 * reconnectCheckDelayNs;
old = lock.save();
if (pending) {
trace.debug("wait before writing again");
Thread.sleep(TimeUnit.NANOSECONDS.toMillis((long) (reconnectCheckDelayNs * 1.1)));
Properties now = lock.load();
if (!now.equals(old)) {
return false;
}
} else {
Thread.sleep(1);
}
reconnectLastLock = old;
reconnectChangePending = pending;
reconnectCheckNext = System.nanoTime() + reconnectCheckDelayNs;
return true;
} catch (Exception e) {
trace.error(e, "pending {0}", pending);
return false;
}
}
public long getNextModificationDataId() {
return modificationDataId.incrementAndGet();
}
public long getModificationMetaId() {
return modificationMetaId.get();
}
public long getNextModificationMetaId() {
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) {
}
pageStore = null;
}
}
if (lock != null) {
stopServer();
if (fileLockMethod != FileLockMethod.SERIALIZED) {
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);
}
static boolean exists(String name) {
if (FileUtils.exists(name + Constants.SUFFIX_PAGE_FILE)) {
return true;
}
return FileUtils.exists(name + Constants.SUFFIX_MV_FILE);
}
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;
}
boolean validateFilePasswordHash(String testCipher, byte[] testHash) {
if (!Objects.equals(testCipher, this.cipher)) {
return false;
}
return Utils.compareSecure(testHash, filePasswordHash);
}
private String parseDatabaseShortName() {
String n = databaseName;
if (n.endsWith(":")) {
n = null;
}
if (n != null) {
StringTokenizer tokenizer = new StringTokenizer(n, "/\\:,;");
while (tokenizer.hasMoreTokens()) {
n = tokenizer.nextToken();
}
}
if (n == null || n.isEmpty()) {
n = "unnamed";
}
return dbSettings.databaseToUpper ? StringUtils.toUpperEnglish(n)
: dbSettings.databaseToLower ? StringUtils.toLowerEnglish(n) : n;
}
private synchronized void open(int traceLevelFile, int traceLevelSystemOut, ConnectionInfo ci) {
if (persistent) {
String dataFileName = databaseName + Constants.SUFFIX_OLD_DATABASE_FILE;
boolean existsData = FileUtils.exists(dataFileName);
String pageFileName = databaseName + Constants.SUFFIX_PAGE_FILE;
String mvFileName = databaseName + Constants.SUFFIX_MV_FILE;
boolean existsPage = FileUtils.exists(pageFileName);
boolean existsMv = FileUtils.exists(mvFileName);
if (existsData && (!existsPage && !existsMv)) {
throw DbException.get(
ErrorCode.FILE_VERSION_ERROR_1, "Old database: " +
dataFileName +
" - please convert the database " +
"to a SQL script and re-create it.");
}
if (existsPage && !FileUtils.canWrite(pageFileName)) {
readOnly = true;
}
if (existsMv && !FileUtils.canWrite(mvFileName)) {
readOnly = true;
}
if (existsPage && !existsMv) {
dbSettings.mvStore = false;
multiThreaded = ci.getProperty("MULTI_THREADED", false);
}
if (readOnly) {
if (traceLevelFile >= TraceSystem.DEBUG) {
String traceFile = Utils.getProperty("java.io.tmpdir", ".") +
"/" + "h2_" + System.currentTimeMillis();
traceSystem = new TraceSystem(traceFile +
Constants.SUFFIX_TRACE_FILE);
} else {
traceSystem = new TraceSystem(null);
}
} else {
traceSystem = new TraceSystem(databaseName +
Constants.SUFFIX_TRACE_FILE);
}
traceSystem.setLevelFile(traceLevelFile);
traceSystem.setLevelSystemOut(traceLevelSystemOut);
trace = traceSystem.getTrace(Trace.DATABASE);
trace.info("opening {0} (build {1})", databaseName, Constants.BUILD_ID);
if (autoServerMode) {
if (readOnly ||
fileLockMethod == FileLockMethod.NO ||
fileLockMethod == FileLockMethod.SERIALIZED ||
fileLockMethod == FileLockMethod.FS) {
throw DbException.getUnsupportedException(
"autoServerMode && (readOnly || " +
"fileLockMethod == NO || " +
"fileLockMethod == SERIALIZED || " +
"fileLockMethod == FS || " +
"inMemory)");
}
}
String lockFileName = databaseName + Constants.SUFFIX_LOCK_FILE;
if (readOnly) {
if (FileUtils.exists(lockFileName)) {
throw DbException.get(ErrorCode.DATABASE_ALREADY_OPEN_1,
"Lock file exists: " + lockFileName);
}
}
if (!readOnly && fileLockMethod != FileLockMethod.NO) {
if (fileLockMethod != FileLockMethod.FS) {
lock = new FileLock(traceSystem, lockFileName, Constants.LOCK_SLEEP);
lock.lock(fileLockMethod);
if (autoServerMode) {
startServer(lock.getUniqueId());
}
}
}
if (SysProperties.MODIFY_ON_WRITE) {
while (isReconnectNeeded()) {
}
} else {
while (isReconnectNeeded() && !beforeWriting()) {
}
}
deleteOldTempFiles();
starting = true;
if (SysProperties.MODIFY_ON_WRITE) {
try {
getPageStore();
} catch (DbException e) {
if (e.getErrorCode() != ErrorCode.DATABASE_IS_READ_ONLY) {
throw e;
}
pageStore = null;
while (!beforeWriting()) {
}
getPageStore();
}
} else {
getPageStore();
}
starting = false;
if (store == null) {
writer = WriterThread.create(this, writeDelay);
} else {
setWriteDelay(writeDelay);
}
} else {
if (autoServerMode) {
throw DbException.getUnsupportedException(
"autoServerMode && inMemory");
}
traceSystem = new TraceSystem(null);
trace = traceSystem.getTrace(Trace.DATABASE);
if (dbSettings.mvStore) {
getPageStore();
}
}
if(store != null) {
store.getTransactionStore().init();
}
systemUser = new User(this, 0, SYSTEM_USER_NAME, true);
mainSchema = new Schema(this, Constants.MAIN_SCHEMA_ID, sysIdentifier(Constants.SCHEMA_MAIN), systemUser,
true);
infoSchema = new Schema(this, Constants.INFORMATION_SCHEMA_ID, sysIdentifier("INFORMATION_SCHEMA"), systemUser,
true);
schemas.put(mainSchema.getName(), mainSchema);
schemas.put(infoSchema.getName(), infoSchema);
publicRole = new Role(this, 0, sysIdentifier(Constants.PUBLIC_ROLE_NAME), true);
roles.put(publicRole.getName(), publicRole);
systemUser.setAdmin(true);
systemSession = new Session(this, systemUser, ++nextSessionId);
lobSession = new Session(this, systemUser, ++nextSessionId);
CreateTableData data = new CreateTableData();
ArrayList<Column> cols = data.columns;
Column columnId = new Column("ID", Value.INT);
columnId.setNullable(false);
cols.add(columnId);
cols.add(new Column("HEAD", Value.INT));
cols.add(new Column("TYPE", Value.INT));
cols.add(new Column("SQL", Value.STRING));
boolean create = true;
if (pageStore != null) {
create = pageStore.isNew();
}
data.tableName = "SYS";
data.id = 0;
data.temporary = false;
data.persistData = persistent;
data.persistIndexes = persistent;
data.create = create;
data.isHidden = true;
data.session = systemSession;
starting = true;
meta = mainSchema.createTable(data);
handleUpgradeIssues();
IndexColumn[] pkCols = IndexColumn.wrap(new Column[] { columnId });
metaIdIndex = meta.addIndex(systemSession, "SYS_ID",
0, pkCols, IndexType.createPrimaryKey(
false, false), true, null);
systemSession.commit(true);
objectIds.set(0);
Cursor cursor = metaIdIndex.find(systemSession, null, null);
ArrayList<MetaRecord> records = new ArrayList<>((int) metaIdIndex.getRowCountApproximation());
while (cursor.next()) {
MetaRecord rec = new MetaRecord(cursor.get());
objectIds.set(rec.getId());
records.add(rec);
}
Collections.sort(records);
synchronized (systemSession) {
for (MetaRecord rec : records) {
rec.execute(this, systemSession, eventListener);
}
}
systemSession.commit(true);
if (store != null) {
store.getTransactionStore().endLeftoverTransactions();
store.removeTemporaryMaps(objectIds);
}
recompileInvalidViews(systemSession);
starting = false;
if (!readOnly) {
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);
if (pageStore != null) {
BitSet f = pageStore.getObjectIds();
for (int i = 0, len = f.length(); i < len; i++) {
if (f.get(i) && !objectIds.get(i)) {
trace.info("unused object id: " + i);
objectIds.set(i);
}
}
}
}
getLobStorage().init();
systemSession.commit(true);
trace.info("opened {0}", databaseName);
if (checkpointAllowed > 0) {
afterWriting();
}
}
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();
if (mvStore.hasMap("index.0")) {
Index scanIndex = meta.getScanIndex(systemSession);
Cursor curs = scanIndex.find(systemSession, null, null);
List<Row> allMetaRows = new ArrayList<>();
boolean needRepair = false;
while (curs.next()) {
Row row = curs.get();
allMetaRows.add(row);
long rowId = row.getKey();
int id = row.getValue(0).getInt();
if (id != rowId) {
needRepair = true;
row.setKey(id);
}
}
if (needRepair) {
Row[] array = allMetaRows.toArray(new Row[0]);
Arrays.sort(array, new Comparator<Row>() {
@Override
public int compare(Row o1, Row o2) {
return Integer.compare(o1.getValue(0).getInt(), o2.getValue(0).getInt());
}
});
meta.truncate(systemSession);
for (Row row : array) {
meta.addRow(systemSession, row);
}
systemSession.commit(true);
}
mvStore.removeMap("index.0");
mvStore.commit();
}
}
}
private void startServer(String key) {
try {
server = Server.createTcpServer(
"-tcpPort", Integer.toString(autoServerPort),
"-tcpAllowOthers",
"-tcpDaemon",
"-key", key, databaseName);
server.start();
} catch (SQLException e) {
throw DbException.convert(e);
}
String localAddress = NetUtils.getLocalAddress();
String address = localAddress + ":" + server.getPort();
lock.setProperty("server", address);
String hostName = NetUtils.getHostName(localAddress);
lock.setProperty("hostName", hostName);
lock.save();
}
private void stopServer() {
if (server != null) {
Server s = server;
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);
}
}
public void verifyMetaLocked(Session session) {
if (meta != null && !meta.isLockedExclusivelyBy(session)
&& lockMode != Constants.LOCK_MODE_OFF) {
throw DbException.throwInternalError();
}
}
public boolean lockMeta(Session session) {
if (meta == null) {
return true;
}
if (ASSERT) {
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);
}
public void unlockMeta(Session session) {
if (meta != null) {
unlockMetaDebug(session);
meta.unlock(session);
session.unlock(meta);
}
}
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);
}
}
}
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) {
unlockMeta(session);
}
}
if (isMVStore()) {
session.scheduleDatabaseObjectIdForRelease(id);
} else {
synchronized (this) {
objectIds.clear(id);
}
}
}
}
void releaseDatabaseObjectIds(BitSet idsToRelease) {
synchronized (objectIds) {
objectIds.andNot(idsToRelease);
}
}
@SuppressWarnings("unchecked")
private HashMap<String, DbObject> getMap(int type) {
HashMap<String, ? extends DbObject> result;
switch (type) {
case DbObject.USER:
result = users;
break;
case DbObject.SETTING:
result = settings;
break;
case DbObject.ROLE:
result = roles;
break;
case DbObject.RIGHT:
result = rights;
break;
case DbObject.SCHEMA:
result = schemas;
break;
case DbObject.DOMAIN:
result = domains;
break;
case DbObject.COMMENT:
result = comments;
break;
case DbObject.AGGREGATE:
result = aggregates;
break;
default:
throw DbException.throwInternalError("type=" + type);
}
return (HashMap<String, DbObject>) result;
}
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);
}
}
public synchronized void addDatabaseObject(Session session, DbObject obj) {
int id = obj.getId();
if (id > 0 && !starting) {
checkWritingAllowed();
}
HashMap<String, DbObject> map = getMap(obj.getType());
if (obj.getType() == DbObject.USER) {
User user = (User) obj;
if (user.isAdmin() && systemUser.getName().equals(SYSTEM_USER_NAME)) {
systemUser.rename(user.getName());
}
}
String name = obj.getName();
if (SysProperties.CHECK && map.get(name) != null) {
DbException.throwInternalError("object already exists");
}
lockMeta(session);
addMeta(session, obj);
map.put(name, obj);
}
public UserAggregate findAggregate(String name) {
return aggregates.get(name);
}
public Comment (DbObject object) {
if (object.getType() == DbObject.COMMENT) {
return null;
}
String key = Comment.getKey(object);
return comments.get(key);
}
public Role findRole(String roleName) {
return roles.get(StringUtils.toUpperEnglish(roleName));
}
public Schema findSchema(String schemaName) {
Schema schema = schemas.get(schemaName);
if (schema == infoSchema) {
initMetaTables();
}
return schema;
}
public Setting findSetting(String name) {
return settings.get(name);
}
public User findUser(String name) {
return users.get(StringUtils.toUpperEnglish(name));
}
public Domain findDomain(String name) {
return domains.get(name);
}
public User getUser(String name) {
User user = findUser(name);
if (user == null) {
throw DbException.get(ErrorCode.USER_NOT_FOUND_1, name);
}
return user;
}
synchronized Session createSession(User user) {
if (closing) {
return null;
}
if (exclusiveSession.get() != null) {
throw DbException.get(ErrorCode.DATABASE_IS_IN_EXCLUSIVE_MODE);
}
Session session = new Session(this, user, ++nextSessionId);
userSessions.add(session);
trace.info("connecting session #{0} to {1}", session.getId(), databaseName);
if (delayedCloser != null) {
delayedCloser.reset();
delayedCloser = null;
}
return session;
}
public synchronized void removeSession(Session session) {
if (session != null) {
exclusiveSession.compareAndSet(session, null);
userSessions.remove(session);
if (session != systemSession && session != lobSession) {
trace.info("disconnecting session #{0}", session.getId());
}
}
if (userSessions.isEmpty() &&
session != systemSession && session != lobSession) {
if (closeDelay == 0) {
close(false);
} else if (closeDelay < 0) {
return;
} else {
delayedCloser = new DelayedDatabaseCloser(this, closeDelay * 1000);
}
}
if (session != systemSession &&
session != lobSession && session != null) {
trace.info("disconnected session #{0}", session.getId());
}
}
private synchronized void closeAllSessionsException(Session except) {
Session[] all = userSessions.toArray(EMPTY_SESSION_ARRAY);
for (Session s : all) {
if (s != except) {
try {
s.close();
} catch (DbException e) {
trace.error(e, "disconnecting session #{0}", s.getId());
}
}
}
}
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) {
throw DbException.get(b.getErrorCode(), b, b.getMessage());
}
}
private void closeImpl(boolean fromShutdownHook) {
try {
synchronized (this) {
if (closing) {
return;
}
if (fileLockMethod == FileLockMethod.SERIALIZED &&
!reconnectChangePending) {
try {
closeOpenFilesAndUnlock(false);
} catch (DbException e) {
}
traceSystem.close();
return;
}
closing = true;
stopServer();
if (!userSessions.isEmpty()) {
if (!fromShutdownHook) {
return;
}
trace.info("closing {0} from shutdown hook", databaseName);
closeAllSessionsException(null);
}
trace.info("closing {0}", databaseName);
if (eventListener != null) {
closing = false;
DatabaseEventListener e = eventListener;
eventListener = null;
e.closingDatabase();
if (!userSessions.isEmpty()) {
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) {
}
}
} finally {
Engine.getInstance().close(databaseName);
}
}
private void removeOrphanedLobs() {
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;
}
}
private synchronized void closeOpenFilesAndUnlock(boolean flush) {
try {
stopWriter();
if (pageStore != null) {
if (flush) {
try {
pageStore.checkpoint();
if (!readOnly) {
lockMeta(pageStore.getPageStoreSession());
pageStore.compact(compactMode);
unlockMeta(pageStore.getPageStoreSession());
}
} catch (DbException e) {
if (ASSERT) {
int code = e.getErrorCode();
if (code != ErrorCode.DATABASE_IS_CLOSED &&
code != ErrorCode.LOCK_TIMEOUT_1 &&
code != ErrorCode.IO_EXCEPTION_2) {
e.printStackTrace();
}
}
trace.error(e, "close");
} catch (Throwable t) {
if (ASSERT) {
t.printStackTrace();
}
trace.error(t, "close");
}
}
}
reconnectModified(false);
if (store != null) {
MVStore mvStore = store.getMvStore();
if (mvStore != null && !mvStore.isClosed()) {
boolean compactFully =
compactMode == CommandInterface.SHUTDOWN_COMPACT ||
compactMode == CommandInterface.SHUTDOWN_DEFRAG ||
getSettings().defragAlways;
if (!compactFully && !mvStore.isReadOnly()) {
if (dbSettings.maxCompactTime > 0) {
try {
store.compactFile(dbSettings.maxCompactTime);
} catch (Throwable t) {
trace.error(t, "compactFile");
}
} else {
mvStore.commit();
}
}
store.close(compactFully);
}
}
if (systemSession != null) {
systemSession.close();
systemSession = null;
}
if (lobSession != null) {
lobSession.close();
lobSession = null;
}
closeFiles();
if (persistent && lock == null &&
fileLockMethod != FileLockMethod.NO &&
fileLockMethod != FileLockMethod.FS) {
return;
}
if (persistent) {
deleteOldTempFiles();
}
} finally {
if (lock != null) {
if (fileLockMethod == FileLockMethod.SERIALIZED) {
if (lock.load().containsKey("changePending")) {
try {
Thread.sleep(TimeUnit.NANOSECONDS
.toMillis((long) (reconnectCheckDelayNs * 1.1)));
} catch (InterruptedException e) {
trace.error(e, "close");
}
}
}
lock.unlock();
lock = null;
}
}
}
private synchronized void closeFiles() {
try {
if (store != null) {
store.closeImmediately();
}
if (pageStore != null) {
pageStore.close();
pageStore = null;
}
} catch (DbException e) {
trace.error(e, "close");
}
}
private void checkMetaFree(Session session, int id) {
SearchRow r = meta.getTemplateSimpleRow(false);
r.setValue(0, ValueInt.get(id));
Cursor cursor = metaIdIndex.find(session, r, r);
if (cursor.next()) {
DbException.throwInternalError();
}
}
public int allocateObjectId() {
Object lock = isMVStore() ? objectIds : this;
int i;
synchronized (lock) {
i = objectIds.nextClearBit(0);
objectIds.set(i);
}
return i;
}
public Schema getMainSchema() {
return mainSchema;
}
public ArrayList<UserAggregate> getAllAggregates() {
return new ArrayList<>(aggregates.values());
}
public ArrayList<Comment> () {
return new ArrayList<>(comments.values());
}
public int getAllowLiterals() {
if (starting) {
return Constants.ALLOW_LITERALS_ALL;
}
return allowLiterals;
}
public ArrayList<Right> getAllRights() {
return new ArrayList<>(rights.values());
}
public ArrayList<Role> getAllRoles() {
return new ArrayList<>(roles.values());
}
public ArrayList<SchemaObject> getAllSchemaObjects() {
initMetaTables();
ArrayList<SchemaObject> list = new ArrayList<>();
for (Schema schema : schemas.values()) {
schema.getAll(list);
}
return list;
}
public ArrayList<SchemaObject> getAllSchemaObjects(int type) {
if (type == DbObject.TABLE_OR_VIEW) {
initMetaTables();
}
ArrayList<SchemaObject> list = new ArrayList<>();
for (Schema schema : schemas.values()) {
schema.getAll(type, list);
}
return list;
}
public ArrayList<Table> getAllTablesAndViews(boolean includeMeta) {
if (includeMeta) {
initMetaTables();
}
ArrayList<Table> list = new ArrayList<>();
for (Schema schema : schemas.values()) {
list.addAll(schema.getAllTablesAndViews());
}
return list;
}
public ArrayList<TableSynonym> getAllSynonyms() {
ArrayList<TableSynonym> list = new ArrayList<>();
for (Schema schema : schemas.values()) {
list.addAll(schema.getAllSynonyms());
}
return list;
}
public ArrayList<Table> getTableOrViewByName(String name) {
ArrayList<Table> list = new ArrayList<>(1);
for (Schema schema : schemas.values()) {
Table table = schema.getTableOrViewByName(name);
if (table != null) {
list.add(table);
}
}
return list;
}
public ArrayList<Schema> getAllSchemas() {
initMetaTables();
return new ArrayList<>(schemas.values());
}
public ArrayList<Setting> getAllSettings() {
return new ArrayList<>(settings.values());
}
public ArrayList<Domain> getAllDomains() {
return new ArrayList<>(domains.values());
}
public ArrayList<User> getAllUsers() {
return new ArrayList<>(users.values());
}
public String getCacheType() {
return cacheType;
}
public String getCluster() {
return cluster;
}
@Override
public CompareMode getCompareMode() {
return compareMode;
}
@Override
public String getDatabasePath() {
if (persistent) {
return FileUtils.toRealPath(databaseName);
}
return null;
}
public String getShortName() {
return databaseShortName;
}
public String getName() {
return databaseName;
}
public Session[] getSessions(boolean includingSystemSession) {
ArrayList<Session> list;
synchronized (userSessions) {
list = new ArrayList<>(userSessions);
}
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]);
}
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);
}
}
synchronized (objectIds) {
objectIds.set(id);
}
}
} else {
boolean metaWasLocked = lockMeta(session);
synchronized (this) {
int id = obj.getId();
removeMeta(session, id);
addMeta(session, obj);
if(id > 0) {
objectIds.set(id);
}
}
if (!metaWasLocked) {
unlockMeta(session);
}
}
}
public synchronized void renameSchemaObject(Session session,
SchemaObject obj, String newName) {
checkWritingAllowed();
obj.getSchema().rename(obj, newName);
updateMetaAndFirstLevelChildren(session, obj);
}
private synchronized void updateMetaAndFirstLevelChildren(Session session, DbObject obj) {
ArrayList<DbObject> list = obj.getChildren();
Comment comment = findComment(obj);
if (comment != null) {
DbException.throwInternalError(comment.toString());
}
updateMeta(session, obj);
if (list != null) {
for (DbObject o : list) {
if (o.getCreateSQL() != null) {
updateMeta(session, o);
}
}
}
}
public synchronized void renameDatabaseObject(Session session,
DbObject obj, String newName) {
checkWritingAllowed();
int type = obj.getType();
HashMap<String, DbObject> map = getMap(type);
if (SysProperties.CHECK) {
if (!map.containsKey(obj.getName())) {
DbException.throwInternalError("not found: " + obj.getName());
}
if (obj.getName().equals(newName) || map.containsKey(newName)) {
DbException.throwInternalError("object already exists: " + newName);
}
}
obj.checkRename();
map.remove(obj.getName());
obj.rename(newName);
map.put(newName, obj);
updateMetaAndFirstLevelChildren(session, obj);
}
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)) {
FileUtils.tryDelete(name);
}
}
}
public Schema getSchema(String schemaName) {
Schema schema = findSchema(schemaName);
if (schema == null) {
throw DbException.get(ErrorCode.SCHEMA_NOT_FOUND_1, schemaName);
}
return schema;
}
public synchronized void removeDatabaseObject(Session session, DbObject obj) {
checkWritingAllowed();
String objName = obj.getName();
int type = obj.getType();
HashMap<String, DbObject> map = getMap(type);
if (SysProperties.CHECK && !map.containsKey(objName)) {
DbException.throwInternalError("not found: " + objName);
}
Comment comment = findComment(obj);
lockMeta(session);
if (comment != null) {
removeDatabaseObject(session, comment);
}
int id = obj.getId();
obj.removeChildrenAndResources(session);
map.remove(objName);
removeMeta(session, id);
}
public Table getDependentTable(SchemaObject obj, Table except) {
switch (obj.getType()) {
case DbObject.COMMENT:
case DbObject.CONSTRAINT:
case DbObject.INDEX:
case DbObject.RIGHT:
case DbObject.TRIGGER:
case DbObject.USER:
return null;
default:
}
HashSet<DbObject> set = new HashSet<>();
for (Table t : getAllTablesAndViews(false)) {
if (except == t) {
continue;
} else if (TableType.VIEW == t.getTableType()) {
continue;
}
set.clear();
t.addDependencies(set);
if (set.contains(obj)) {
return t;
}
}
return null;
}
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);
}
}
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;
}
public synchronized String getTempTableName(String baseName, Session session) {
String tempName;
do {
tempName = baseName + "_COPY_" + session.getId() +
"_" + nextTempTableId++;
} while (mainSchema.findTableOrView(session, tempName) != null);
return tempName;
}
public void setCompareMode(CompareMode compareMode) {
this.compareMode = compareMode;
}
public void setCluster(String cluster) {
this.cluster = cluster;
}
@Override
public void checkWritingAllowed() {
if (readOnly) {
throw DbException.get(ErrorCode.DATABASE_IS_READ_ONLY);
}
if (fileLockMethod == FileLockMethod.SERIALIZED) {
if (!reconnectChangePending) {
throw DbException.get(ErrorCode.DATABASE_IS_READ_ONLY);
}
}
}
public boolean isReadOnly() {
return readOnly;
}
public void setWriteDelay(int value) {
writeDelay = value;
if (writer != null) {
writer.setWriteDelay(value);
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;
}
public boolean getFlushOnEachCommit() {
return flushOnEachCommit;
}
public ArrayList<InDoubtTransaction> getInDoubtTransactions() {
if (store != null) {
return store.getInDoubtTransactions();
}
return pageStore == null ? null : pageStore.getInDoubtTransactions();
}
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);
}
}
synchronized void commit(Session session) {
throwLastBackgroundException();
if (readOnly) {
return;
}
if (pageStore != null) {
pageStore.commit(session);
}
session.setAllCommitted();
}
private void throwLastBackgroundException() {
DbException b = backgroundException.getAndSet(null);
if (b != null) {
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);
}
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());
}
}
}
public void setProgress(int state, String name, int x, int max) {
if (eventListener != null) {
try {
eventListener.setProgress(state, name, x, max);
} catch (Exception e2) {
}
}
}
public void exceptionThrown(SQLException e, String sql) {
if (eventListener != null) {
try {
eventListener.exceptionThrown(e, sql);
} catch (Exception e2) {
}
}
}
public synchronized void sync() {
if (readOnly) {
return;
}
if (store != null) {
store.sync();
}
if (pageStore != null) {
pageStore.sync();
}
}
public int getMaxMemoryRows() {
return maxMemoryRows;
}
public void setMaxMemoryRows(int value) {
this.maxMemoryRows = value;
}
public void setMaxMemoryUndo(int value) {
this.maxMemoryUndo = value;
}
public int getMaxMemoryUndo() {
return maxMemoryUndo;
}
public void setLockMode(int lockMode) {
switch (lockMode) {
case Constants.LOCK_MODE_OFF:
if (multiThreaded && !isMVStore()) {
throw DbException.get(
ErrorCode.UNSUPPORTED_SETTING_COMBINATION,
"MV_STORE=FALSE & LOCK_MODE=0 & MULTI_THREADED=TRUE");
}
break;
case Constants.LOCK_MODE_READ_COMMITTED:
case Constants.LOCK_MODE_TABLE:
case Constants.LOCK_MODE_TABLE_GC:
break;
default:
throw DbException.getInvalidValueException("lock mode", lockMode);
}
this.lockMode = lockMode;
}
public int getLockMode() {
return lockMode;
}
public void setCloseDelay(int value) {
this.closeDelay = value;
}
public Session getSystemSession() {
return systemSession;
}
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) {
return false;
}
return ignoreCase;
}
public synchronized void setDeleteFilesOnDisconnect(boolean b) {
this.deleteFilesOnDisconnect = b;
}
@Override
public String getLobCompressionAlgorithm(int type) {
return lobCompressionAlgorithm;
}
public void setLobCompressionAlgorithm(String stringValue) {
this.lobCompressionAlgorithm = stringValue;
}
public synchronized void setMaxLogSize(long value) {
if (pageStore != null) {
pageStore.setMaxLogSize(value);
}
}
public void setAllowLiterals(int value) {
this.allowLiterals = value;
}
public boolean getOptimizeReuseResults() {
return optimizeReuseResults;
}
public void setOptimizeReuseResults(boolean b) {
optimizeReuseResults = b;
}
@Override
public Object getLobSyncObject() {
return lobSyncObject;
}
public int getSessionCount() {
return userSessions.size();
}
public void setReferentialIntegrity(boolean b) {
referentialIntegrity = b;
}
public boolean getReferentialIntegrity() {
return referentialIntegrity;
}
public void setQueryStatistics(boolean b) {
queryStatistics = b;
synchronized (this) {
if (!b) {
queryStatisticsData = null;
}
}
}
public boolean getQueryStatistics() {
return queryStatistics;
}
public void setQueryStatisticsMaxEntries(int n) {
queryStatisticsMaxEntries = n;
if (queryStatisticsData != null) {
synchronized (this) {
if (queryStatisticsData != null) {
queryStatisticsData.setMaxQueryEntries(queryStatisticsMaxEntries);
}
}
}
}
public QueryStatisticsData getQueryStatisticsData() {
if (!queryStatistics) {
return null;
}
if (queryStatisticsData == null) {
synchronized (this) {
if (queryStatisticsData == null) {
queryStatisticsData = new QueryStatisticsData(queryStatisticsMaxEntries);
}
}
}
return queryStatisticsData;
}
public boolean isStarting() {
return starting;
}
public boolean isMVStore() {
return dbSettings.mvStore;
}
void opened() {
if (eventListener != null) {
eventListener.opened();
}
if (writer != null) {
writer.startThread();
}
}
public void setMode(Mode mode) {
this.mode = mode;
}
public Mode getMode() {
return mode;
}
public boolean isMultiThreaded() {
return multiThreaded;
}
public void setMultiThreaded(boolean multiThreaded) {
if (multiThreaded && this.multiThreaded != multiThreaded) {
if (lockMode == Constants.LOCK_MODE_OFF && !isMVStore()) {
throw DbException.get(
ErrorCode.UNSUPPORTED_SETTING_COMBINATION,
"MV_STORE=FALSE & LOCK_MODE=0 & MULTI_THREADED=TRUE");
}
}
this.multiThreaded = multiThreaded;
}
public void setMaxOperationMemory(int maxOperationMemory) {
this.maxOperationMemory = maxOperationMemory;
}
public int getMaxOperationMemory() {
return maxOperationMemory;
}
public Session getExclusiveSession() {
return exclusiveSession.get();
}
public void setExclusiveSession(Session session, boolean closeOthers) {
this.exclusiveSession.set(session);
if (closeOthers) {
closeAllSessionsException(session);
}
}
@Override
public SmallLRUCache<String, String[]> getLobFileListCache() {
if (lobFileListCache == null) {
lobFileListCache = SmallLRUCache.newInstance(128);
}
return lobFileListCache;
}
public boolean isSysTableLocked() {
return meta == null || meta.isLockedExclusively();
}
public boolean isSysTableLockedBy(Session session) {
return meta == null || meta.isLockedExclusivelyBy(session);
}
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();
}
public void shutdownImmediately() {
closing = true;
setPowerOffCount(1);
try {
checkPowerOff();
} catch (DbException e) {
}
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;
}
}
public Table getFirstUserTable() {
for (Table table : getAllTablesAndViews(false)) {
if (table.getCreateSQL() != null) {
if (table.isHidden()) {
continue;
}
if (table.getSchema().getId() == Constants.INFORMATION_SCHEMA_ID
&& table.getName().equalsIgnoreCase("LOB_BLOCKS")) {
continue;
}
return table;
}
}
return null;
}
public boolean isReconnectNeeded() {
if (fileLockMethod != FileLockMethod.SERIALIZED) {
return false;
}
if (reconnectChangePending) {
return false;
}
long now = System.nanoTime();
if (now < reconnectCheckNext) {
return false;
}
reconnectCheckNext = now + reconnectCheckDelayNs;
if (lock == null) {
lock = new FileLock(traceSystem, databaseName +
Constants.SUFFIX_LOCK_FILE, Constants.LOCK_SLEEP);
}
try {
Properties prop = lock.load(), first = prop;
while (true) {
if (prop.equals(reconnectLastLock)) {
return false;
}
if (prop.getProperty("changePending", null) == null) {
break;
}
if (System.nanoTime() >
now + reconnectCheckDelayNs * 10) {
if (first.equals(prop)) {
lock.setProperty("changePending", null);
lock.save();
break;
}
}
trace.debug("delay (change pending)");
Thread.sleep(TimeUnit.NANOSECONDS.toMillis(reconnectCheckDelayNs));
prop = lock.load();
}
reconnectLastLock = prop;
} catch (Exception e) {
trace.error(e, "readOnly {0}", readOnly);
}
return true;
}
public void checkpointIfRequired() {
if (fileLockMethod != FileLockMethod.SERIALIZED ||
readOnly || !reconnectChangePending || closing) {
return;
}
long now = System.nanoTime();
if (now > reconnectCheckNext + reconnectCheckDelayNs) {
if (checkpointAllowed < 0) {
DbException.throwInternalError(Integer.toString(checkpointAllowed));
}
synchronized (reconnectSync) {
if (checkpointAllowed > 0) {
return;
}
checkpointRunning = true;
}
synchronized (this) {
trace.debug("checkpoint start");
flushSequences();
checkpoint();
reconnectModified(false);
trace.debug("checkpoint end");
}
synchronized (reconnectSync) {
checkpointRunning = false;
}
}
}
public boolean isFileLockSerialized() {
return fileLockMethod == FileLockMethod.SERIALIZED;
}
private void flushSequences() {
for (SchemaObject obj : getAllSchemaObjects(DbObject.SEQUENCE)) {
Sequence sequence = (Sequence) obj;
sequence.flushWithoutMargin();
}
}
public void checkpoint() {
if (persistent) {
synchronized (this) {
if (pageStore != null) {
pageStore.checkpoint();
}
}
if (store != null) {
store.flush();
}
}
getTempFileDeleter().deleteUnused();
}
public boolean beforeWriting() {
if (fileLockMethod != FileLockMethod.SERIALIZED) {
return true;
}
while (checkpointRunning) {
try {
Thread.sleep(10 + (int) (Math.random() * 10));
} catch (Exception e) {
}
}
synchronized (reconnectSync) {
if (reconnectModified(true)) {
if (++checkpointAllowed > 20) {
throw DbException.throwInternalError(Integer.toString(checkpointAllowed));
}
return true;
}
}
reconnectCheckNext = System.nanoTime() - 1;
reconnectLastLock = null;
return false;
}
public void afterWriting() {
if (fileLockMethod != FileLockMethod.SERIALIZED) {
return;
}
synchronized (reconnectSync) {
checkpointAllowed--;
}
if (checkpointAllowed < 0) {
throw DbException.throwInternalError(Integer.toString(checkpointAllowed));
}
}
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) {
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;
}
public <V> HashMap<String, V> newStringMap() {
return dbSettings.caseInsensitiveIdentifiers ? new CaseInsensitiveMap<V>() : new HashMap<String, V>();
}
public <V> ConcurrentHashMap<String, V> newConcurrentStringMap() {
return dbSettings.caseInsensitiveIdentifiers ? new CaseInsensitiveConcurrentMap<V>()
: new ConcurrentHashMap<String, V>();
}
public boolean equalsIdentifiers(String a, String b) {
return a.equals(b) || dbSettings.caseInsensitiveIdentifiers && a.equalsIgnoreCase(b);
}
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;
}
}
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;
}
public Authenticator getAuthenticator() {
return authenticator;
}
public void setAuthenticator(Authenticator authenticator) {
if (authenticator!=null) {
authenticator.init(this);
}
this.authenticator=authenticator;
}
}