📄 database.java
字号:
/*
* Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.engine;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;
import org.h2.api.DatabaseEventListener;
import org.h2.command.dml.SetTypes;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.log.LogSystem;
import org.h2.log.UndoLogRecord;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.message.TraceSystem;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.schema.Schema;
import org.h2.schema.SchemaObject;
import org.h2.schema.Sequence;
import org.h2.store.DataHandler;
import org.h2.store.DataPage;
import org.h2.store.DiskFile;
import org.h2.store.FileLock;
import org.h2.store.FileStore;
import org.h2.store.RecordReader;
import org.h2.store.Storage;
import org.h2.store.WriterThread;
import org.h2.store.fs.FileSystem;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.MetaTable;
import org.h2.table.Table;
import org.h2.table.TableData;
import org.h2.table.TableView;
import org.h2.tools.DeleteDbFiles;
import org.h2.util.BitField;
import org.h2.util.ByteUtils;
import org.h2.util.CacheLRU;
import org.h2.util.ClassUtils;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.IntHashMap;
import org.h2.util.ObjectArray;
import org.h2.util.SmallLRUCache;
import org.h2.util.StringUtils;
import org.h2.value.CompareMode;
import org.h2.value.Value;
import org.h2.value.ValueInt;
import org.h2.value.ValueLob;
/**
* There is one database object per open database.
*
* The format of the meta data table is:
* id int, headPos int (for indexes), objectType int, sql varchar
*
* @since 2004-04-15 22:49
*/
public class Database implements DataHandler {
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 HashMap roles = new HashMap();
private final HashMap users = new HashMap();
private final HashMap settings = new HashMap();
private final HashMap schemas = new HashMap();
private final HashMap rights = new HashMap();
private final HashMap functionAliases = new HashMap();
private final HashMap userDataTypes = new HashMap();
private final HashMap aggregates = new HashMap();
private final HashMap comments = new HashMap();
private final Set sessions = Collections.synchronizedSet(new HashSet());
private Session exclusiveSession;
private final BitField objectIds = new BitField();
private final Object lobSyncObject = new Object();
private boolean textStorage;
private Schema mainSchema;
private Schema infoSchema;
private int nextSessionId;
private User systemUser;
private Session systemSession;
private TableData meta;
private Index metaIdIndex;
private FileLock lock;
private LogSystem log;
private WriterThread writer;
private IntHashMap storageMap = new IntHashMap();
private boolean starting;
private DiskFile fileData, fileIndex;
private TraceSystem traceSystem;
private DataPage dummy;
private int fileLockMethod;
private Role publicRole;
private long modificationDataId;
private long modificationMetaId;
private CompareMode compareMode;
private String cluster = Constants.CLUSTERING_DISABLED;
private boolean readOnly;
private boolean noDiskSpace;
private int writeDelay = Constants.DEFAULT_WRITE_DELAY;
private DatabaseEventListener eventListener;
private FileStore emergencyReserve;
private int maxMemoryRows = Constants.DEFAULT_MAX_MEMORY_ROWS;
private int maxMemoryUndo = SysProperties.DEFAULT_MAX_MEMORY_UNDO;
private int lockMode = SysProperties.DEFAULT_LOCK_MODE;
private boolean logIndexChanges;
private int logLevel = 1;
private int cacheSize;
private int maxLengthInplaceLob = Constants.DEFAULT_MAX_LENGTH_INPLACE_LOB;
private long biggestFileSize;
private int allowLiterals = Constants.DEFAULT_ALLOW_LITERALS;
private static int initialPowerOffCount;
private int powerOffCount = initialPowerOffCount;
private int closeDelay;
private DatabaseCloser delayedCloser;
private boolean recovery;
private volatile boolean closing;
private boolean ignoreCase;
private boolean deleteFilesOnDisconnect;
private String lobCompressionAlgorithm;
private boolean optimizeReuseResults = true;
private String cacheType;
private boolean indexSummaryValid = true;
private String accessModeLog, accessModeData;
private boolean referentialIntegrity = true;
private boolean multiVersion;
private DatabaseCloser closeOnExit;
private Mode mode = Mode.getInstance(Mode.REGULAR);
// TODO change in version 1.1
private boolean multiThreaded;
private int maxOperationMemory = SysProperties.DEFAULT_MAX_OPERATION_MEMORY;
private boolean lobFilesInDirectories = SysProperties.LOB_FILES_IN_DIRECTORIES;
private SmallLRUCache lobFileListCache = new SmallLRUCache(128);
public Database(String name, ConnectionInfo ci, String cipher) throws SQLException {
this.compareMode = new CompareMode(null, null, 0);
this.persistent = ci.isPersistent();
this.filePasswordHash = ci.getFilePasswordHash();
this.databaseName = name;
this.databaseShortName = parseDatabaseShortName();
this.cipher = cipher;
String lockMethodName = ci.removeProperty("FILE_LOCK", null);
this.accessModeLog = ci.removeProperty("ACCESS_MODE_LOG", "rw").toLowerCase();
this.accessModeData = ci.removeProperty("ACCESS_MODE_DATA", "rw").toLowerCase();
if ("r".equals(accessModeData)) {
readOnly = true;
accessModeLog = "r";
}
this.fileLockMethod = FileLock.getFileLockMethod(lockMethodName);
this.textStorage = ci.getTextStorage();
this.databaseURL = ci.getURL();
this.eventListener = ci.removeDatabaseEventListenerObject();
if (eventListener == null) {
String listener = ci.removeProperty("DATABASE_EVENT_LISTENER", null);
if (listener != null) {
if (listener.startsWith("'")) {
listener = listener.substring(1);
}
if (listener.endsWith("'")) {
listener = listener.substring(0, listener.length() - 1);
}
setEventListener(listener);
}
}
String log = ci.getProperty(SetTypes.LOG, null);
if (log != null) {
this.logIndexChanges = "2".equals(log);
}
String ignoreSummary = ci.getProperty("RECOVER", null);
if (ignoreSummary != null) {
this.recovery = true;
}
this.multiVersion = ci.removeProperty("MVCC", false);
boolean closeAtVmShutdown = ci.removeProperty("DB_CLOSE_ON_EXIT", true);
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", CacheLRU.TYPE_NAME));
try {
open(traceLevelFile, traceLevelSystemOut);
if (closeAtVmShutdown) {
closeOnExit = new DatabaseCloser(this, 0, true);
try {
Runtime.getRuntime().addShutdownHook(closeOnExit);
} catch (IllegalStateException e) {
// shutdown in progress - just don't register the handler
// (maybe an application wants to write something into a
// database at shutdown time)
} catch (SecurityException e) {
// applets may not do that - ignore
}
}
} catch (Throwable e) {
if (traceSystem != null) {
traceSystem.getTrace(Trace.DATABASE).error("opening " + databaseName, e);
traceSystem.close();
}
closeOpenFilesAndUnlock();
throw Message.convert(e);
}
}
public static void setInitialPowerOffCount(int count) {
initialPowerOffCount = count;
}
public void setPowerOffCount(int count) {
if (powerOffCount == -1) {
return;
}
powerOffCount = count;
}
public boolean getTextStorage() {
return textStorage;
}
public static boolean isTextStorage(String fileName, boolean defaultValue) throws SQLException {
byte[] magicText = Constants.MAGIC_FILE_HEADER_TEXT.getBytes();
byte[] magicBinary = Constants.MAGIC_FILE_HEADER.getBytes();
try {
InputStream fin = FileUtils.openFileInputStream(fileName);
byte[] magic = IOUtils.readBytesAndClose(fin, magicBinary.length);
if (ByteUtils.compareNotNull(magic, magicText) == 0) {
return true;
} else if (ByteUtils.compareNotNull(magic, magicBinary) == 0) {
return false;
} else if (magic.length < magicText.length) {
// file size is 0 or too small
return defaultValue;
}
throw Message.getSQLException(ErrorCode.FILE_VERSION_ERROR_1, fileName);
} catch (IOException e) {
throw Message.convertIOException(e, fileName);
}
}
public static byte[] getMagic(boolean textStorage) {
if (textStorage) {
return Constants.MAGIC_FILE_HEADER_TEXT.getBytes();
} else {
return Constants.MAGIC_FILE_HEADER.getBytes();
}
}
public byte[] getMagic() {
return getMagic(textStorage);
}
public boolean areEqual(Value a, Value b) throws SQLException {
// TODO optimization possible
// boolean is = a.compareEqual(b);
// boolean is2 = a.compareTo(b, compareMode) == 0;
// if(is != is2) {
// is = a.compareEqual(b);
// System.out.println("hey!");
// }
// return a.compareEqual(b);
return a.compareTo(b, compareMode) == 0;
}
public int compare(Value a, Value b) throws SQLException {
return a.compareTo(b, compareMode);
}
public int compareTypeSave(Value a, Value b) throws SQLException {
return a.compareTypeSave(b, compareMode);
}
public long getModificationDataId() {
return modificationDataId;
}
public long getNextModificationDataId() {
return ++modificationDataId;
}
public long getModificationMetaId() {
return modificationMetaId;
}
public long getNextModificationMetaId() {
// if the meta data has been modified, the data is modified as well
// (because MetaTable returns modificationDataId)
modificationDataId++;
return modificationMetaId++;
}
public int getPowerOffCount() {
return powerOffCount;
}
public void checkPowerOff() throws SQLException {
if (powerOffCount == 0) {
return;
}
if (powerOffCount > 1) {
powerOffCount--;
return;
}
if (powerOffCount != -1) {
try {
powerOffCount = -1;
if (log != null) {
try {
stopWriter();
log.close();
} catch (SQLException e) {
// ignore
}
log = null;
}
if (fileData != null) {
try {
fileData.close();
} catch (SQLException e) {
// ignore
}
fileData = null;
}
if (fileIndex != null) {
try {
fileIndex.close();
} catch (SQLException e) {
// ignore
}
fileIndex = null;
}
if (lock != null) {
lock.unlock();
lock = null;
}
if (emergencyReserve != null) {
emergencyReserve.closeAndDeleteSilently();
emergencyReserve = null;
}
} catch (Exception e) {
TraceSystem.traceThrowable(e);
}
}
Engine.getInstance().close(databaseName);
throw Message.getSQLException(ErrorCode.SIMULATED_POWER_OFF);
}
public static boolean exists(String name) {
return FileUtils.exists(name + Constants.SUFFIX_DATA_FILE);
}
public Trace getTrace(String module) {
return traceSystem.getTrace(module);
}
public FileStore openFile(String name, String mode, boolean mustExist) throws SQLException {
if (mustExist && !FileUtils.exists(name)) {
throw Message.getSQLException(ErrorCode.FILE_NOT_FOUND_1, name);
}
FileStore store = FileStore.open(this, name, mode, getMagic(), cipher, filePasswordHash);
try {
store.init();
} catch (SQLException e) {
store.closeSilently();
throw e;
}
return store;
}
public void checkFilePasswordHash(String c, byte[] hash) throws SQLException {
if (!ByteUtils.compareSecure(hash, filePasswordHash) || !StringUtils.equals(c, cipher)) {
try {
Thread.sleep(Constants.DELAY_WRONG_PASSWORD);
} catch (InterruptedException e) {
// ignore
}
throw Message.getSQLException(ErrorCode.WRONG_USER_OR_PASSWORD);
}
}
private void openFileData() throws SQLException {
fileData = new DiskFile(this, databaseName + Constants.SUFFIX_DATA_FILE, accessModeData, true, true,
SysProperties.CACHE_SIZE_DEFAULT);
}
private void openFileIndex() throws SQLException {
fileIndex = new DiskFile(this, databaseName + Constants.SUFFIX_INDEX_FILE, accessModeData, false,
logIndexChanges, SysProperties.CACHE_SIZE_INDEX_DEFAULT);
}
public DataPage getDataPage() {
return dummy;
}
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.length() == 0) {
n = "UNNAMED";
}
return StringUtils.toUpperEnglish(n);
}
private synchronized void open(int traceLevelFile, int traceLevelSystemOut) throws SQLException {
if (persistent) {
String dataFileName = databaseName + Constants.SUFFIX_DATA_FILE;
if (FileUtils.exists(dataFileName)) {
// if it is already read-only because ACCESS_MODE_DATA=r
readOnly = readOnly | FileUtils.isReadOnly(dataFileName);
textStorage = isTextStorage(dataFileName, textStorage);
lobFilesInDirectories |= FileUtils.exists(databaseName + Constants.SUFFIX_LOBS_DIRECTORY);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -