提交 23cd2f75 authored 作者: Thomas Mueller's avatar Thomas Mueller

MVStore changes: abstract storage to support off-heap storage; rename getSize to…

MVStore changes: abstract storage to support off-heap storage; rename getSize to sizeAsLong; avoid importing java.beans package to speed up starting the JVM
上级 380b76a5
......@@ -232,12 +232,12 @@ public class SpatialTreeIndex extends BaseIndex implements SpatialIndex {
@Override
public long getRowCount(Session session) {
return treeMap.getSize();
return treeMap.sizeAsLong();
}
@Override
public long getRowCountApproximation() {
return treeMap.getSize();
return treeMap.sizeAsLong();
}
@Override
......
......@@ -11,6 +11,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
......@@ -121,6 +122,11 @@ public class DataUtils {
* The estimated number of bytes used per child entry.
*/
public static final int PAGE_MEMORY_CHILD = 16;
/**
* Name of the character encoding format.
*/
public static final Charset UTF8 = Charset.forName("UTF-8");
/**
* An 0-size byte array.
......@@ -131,7 +137,7 @@ public class DataUtils {
* The maximum byte to grow a buffer at a time.
*/
private static final int MAX_GROW = 16 * 1024 * 1024;
/**
* Get the length of the variable size int.
*
......@@ -731,7 +737,7 @@ public class DataUtils {
* @return the error code, or 0 if none
*/
public static int getErrorCode(String m) {
if (m.endsWith("]")) {
if (m != null && m.endsWith("]")) {
int dash = m.lastIndexOf('/');
if (dash >= 0) {
String s = m.substring(dash + 1, m.length() - 1);
......@@ -797,4 +803,43 @@ public class DataUtils {
return temp;
}
/**
* Parse a string as a number.
*
* @param x the number
* @param defaultValue if x is null
* @return the parsed value
* @throws IllegalStateException if parsing fails
*/
public static long parseLong(String x, long defaultValue) {
if (x == null) {
return defaultValue;
}
try {
return Long.parseLong(x);
} catch (NumberFormatException e) {
throw newIllegalStateException(ERROR_FILE_CORRUPT,
"Error parsing the value {0} as a long", x, e);
}
}
/**
* Try to parse a string as a number.
*
* @param x the number
* @param defaultValue if x is null
* @param errorValue if parsing fails
* @return the parsed value if parsing is possible
*/
public static long parseLong(String x, long defaultValue, long errorValue) {
if (x == null) {
return defaultValue;
}
try {
return Long.parseLong(x);
} catch (NumberFormatException e) {
return errorValue;
}
}
}
/*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.mvstore;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import org.h2.mvstore.cache.FilePathCache;
import org.h2.store.fs.FilePath;
import org.h2.store.fs.FilePathCrypt;
import org.h2.store.fs.FilePathNio;
/**
* The storage mechanism of the MVStore.
*/
public class FileStore {
private final String fileName;
private boolean readOnly;
private FileChannel file;
private FileLock fileLock;
private long fileSize;
private long readCount;
private long writeCount;
public FileStore(String fileName, boolean readOnly) {
if (fileName != null && fileName.indexOf(':') < 0) {
// NIO is used, unless a different file system is specified
// the following line is to ensure the NIO file system is compiled
FilePathNio.class.getName();
fileName = "nio:" + fileName;
}
this.fileName = fileName;
this.readOnly = readOnly;
}
@Override
public String toString() {
return fileName;
}
public void readFully(long pos, ByteBuffer dst) {
readCount++;
DataUtils.readFully(file, pos, dst);
}
public void writeFully(long pos, ByteBuffer src) {
writeCount++;
fileSize = Math.max(fileSize, pos + src.remaining());
DataUtils.writeFully(file, pos, src);
}
/**
* Mark the space within the file as unused.
*
* @param pos
* @param length
*/
public void free(long pos, int length) {
}
public void open(char[] encryptionKey) {
FilePath f = FilePath.get(fileName);
FilePath parent = f.getParent();
if (!parent.exists()) {
throw DataUtils.newIllegalArgumentException("Directory does not exist: {0}", parent);
}
if (f.exists() && !f.canWrite()) {
readOnly = true;
}
try {
file = f.open(readOnly ? "r" : "rw");
if (encryptionKey != null) {
byte[] password = FilePathCrypt.getPasswordBytes(encryptionKey);
file = new FilePathCrypt.FileCrypt(fileName, password, file);
}
file = FilePathCache.wrap(file);
fileSize = file.size();
try {
if (readOnly) {
fileLock = file.tryLock(0, Long.MAX_VALUE, true);
} else {
fileLock = file.tryLock();
}
} catch (OverlappingFileLockException e) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_FILE_LOCKED, "The file is locked: {0}", fileName, e);
}
if (fileLock == null) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_FILE_LOCKED, "The file is locked: {0}", fileName);
}
} catch (IOException e) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_READING_FAILED,
"Could not open file {0}", fileName, e);
}
}
public void close() {
try {
if (fileLock != null) {
fileLock.release();
fileLock = null;
}
file.close();
} catch (Exception e) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_WRITING_FAILED,
"Closing failed for file {0}", fileName, e);
} finally {
file = null;
}
}
public void sync() {
try {
file.force(true);
} catch (IOException e) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_WRITING_FAILED,
"Could not sync file {0}", fileName, e);
}
}
public long size() {
return fileSize;
}
public void truncate(long size) {
try {
file.truncate(size);
fileSize = Math.min(fileSize, size);
} catch (IOException e) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_WRITING_FAILED,
"Could not truncate file {0} to size {1}",
fileName, size, e);
}
}
/**
* Get the file instance in use. The application may read from the file (for
* example for online backup), but not write to it or truncate it.
*
* @return the file
*/
public FileChannel getFile() {
return file;
}
/**
* Get the number of write operations since this store was opened.
* For file based stores, this is the number of file write operations.
*
* @return the number of write operations
*/
public long getWriteCount() {
return writeCount;
}
/**
* Get the number of read operations since this store was opened.
* For file based stores, this is the number of file read operations.
*
* @return the number of read operations
*/
public long getReadCount() {
return readCount;
}
public boolean isReadOnly() {
return readOnly;
}
}
......@@ -982,11 +982,27 @@ public class MVMap<K, V> extends AbstractMap<K, V>
return this == o;
}
/**
* Get the number of entries, as a integer. Integer.MAX_VALUE is returned if
* there are more than this entries.
*
* @return the number of entries, as an integer
*/
@Override
public int size() {
long size = getSize();
long size = sizeAsLong();
return size > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) size;
}
/**
* Get the number of entries, as a long.
*
* @return the number of entries
*/
public long sizeAsLong() {
checkOpen();
return root.getTotalCount();
}
@Override
public boolean isEmpty() {
......@@ -994,11 +1010,6 @@ public class MVMap<K, V> extends AbstractMap<K, V>
return 0 == (root.isLeaf() ? root.getKeyCount() : root.getChildPageCount());
}
public long getSize() {
checkOpen();
return root.getTotalCount();
}
public long getCreateVersion() {
return createVersion;
}
......@@ -1033,7 +1044,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
(version == writeVersion ||
r.getVersion() >= 0 ||
version <= createVersion ||
store.getFile() == null)) {
store.getFileStore() == null)) {
newest = r;
} else {
// find the newest page that has a getVersion() <= version
......
......@@ -6,12 +6,8 @@
*/
package org.h2.mvstore;
import java.beans.ExceptionListener;
import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
......@@ -23,13 +19,8 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.h2.compress.CompressLZF;
import org.h2.compress.Compressor;
import org.h2.engine.Constants;
import org.h2.mvstore.cache.CacheLongKeyLIRS;
import org.h2.mvstore.cache.FilePathCache;
import org.h2.mvstore.type.StringDataType;
import org.h2.store.fs.FilePath;
import org.h2.store.fs.FilePathCrypt;
import org.h2.store.fs.FilePathNio;
import org.h2.util.MathUtils;
import org.h2.util.New;
......@@ -67,9 +58,6 @@ MVStore:
- defragment (re-creating maps, specially those with small pages)
- chunk header: store changed chunk data as row; maybe after the root
- chunk checksum (header, last page, 2 bytes per page?)
- is there a better name for the file header,
if it's no longer always at the beginning of a file? store header?
root pointer?
- on insert, if the child page is already full, don't load and modify it
split directly (specially for leaves with one large entry)
- maybe let a chunk point to a list of potential next chunks
......@@ -127,6 +115,7 @@ MVStore:
with free space handling, retention time / flush,
possibly one file per chunk to support SSD trim on file system level,
and free up memory for off-heap storage)
- Test with OSGi
*/
......@@ -141,29 +130,33 @@ public class MVStore {
public static final boolean ASSERT = false;
/**
* The block size (physical sector size) of the disk. The file header is
* The block size (physical sector size) of the disk. The store header is
* written twice, one copy in each block, to ensure it survives a crash.
*/
static final int BLOCK_SIZE = 4 * 1024;
private static final int FORMAT_WRITE = 1;
private static final int FORMAT_READ = 1;
/**
* The background thread, if any.
*/
volatile Thread backgroundThread;
/**
* The free spaces between the chunks. The first block to use is block 2
* (the first two blocks are the store header).
*/
private final FreeSpaceBitSet freeSpace = new FreeSpaceBitSet(2, BLOCK_SIZE);
private volatile boolean reuseSpace = true;
private boolean closed;
private final String fileName;
private final char[] filePassword;
private FileStore fileStore;
private final int pageSplitSize;
private FileChannel file;
private FileLock fileLock;
private long fileSize;
private long rootChunkStart;
/**
......@@ -181,12 +174,6 @@ public class MVStore {
private final ConcurrentHashMap<Integer, Chunk> chunks =
new ConcurrentHashMap<Integer, Chunk>();
/**
* The free spaces between the chunks. The first block to use is block 2
* (the first two blocks are the file header).
*/
private FreeSpaceBitSet freeSpace = new FreeSpaceBitSet(2, BLOCK_SIZE);
/**
* The map of temporarily removed pages. The key is the unsaved version, the
* value is the map of chunks. The maps of chunks contains the number of
......@@ -199,22 +186,23 @@ public class MVStore {
private final ConcurrentHashMap<Integer, MVMap<?, ?>> maps =
new ConcurrentHashMap<Integer, MVMap<?, ?>>();
private HashMap<String, String> fileHeader = New.hashMap();
private HashMap<String, String> storeHeader = New.hashMap();
private ByteBuffer writeBuffer;
private boolean readOnly;
private int lastMapId;
private volatile boolean reuseSpace = true;
private long retainVersion = -1;
private final Compressor compressor = new CompressLZF();
/**
* Whether to compress new pages. Even if disabled, the store may contain
* (old) compressed pages.
*/
private final boolean compress;
private final Compressor compressor = new CompressLZF();
private final ExceptionListener backgroundExceptionListener;
private final UncaughtExceptionHandler backgroundExceptionHandler;
private long currentVersion;
......@@ -222,8 +210,7 @@ public class MVStore {
* The version of the last stored chunk.
*/
private long lastStoredVersion;
private int fileReadCount;
private int fileWriteCount;
private int unsavedPageCount;
private int unsavedPageCountMax;
......@@ -257,40 +244,99 @@ public class MVStore {
*/
private int writeDelay;
/**
* Create and open the store.
*
* @throws IllegalStateException if the file is corrupt, or an exception
* occurred while opening
* @throws IllegalArgumentException if the directory does not exist
*/
MVStore(HashMap<String, Object> config) {
String f = (String) config.get("fileName");
if (f != null && f.indexOf(':') < 0) {
// NIO is used, unless a different file system is specified
// the following line is to ensure the NIO file system is compiled
FilePathNio.class.getName();
f = "nio:" + f;
}
this.fileName = f;
this.readOnly = config.containsKey("readOnly");
this.compress = config.containsKey("compress");
Object o = config.get("pageSplitSize");
pageSplitSize = o == null ? 6 * 1024 : (Integer) o;
o = config.get("backgroundExceptionListener");
this.backgroundExceptionListener = (ExceptionListener) o;
if (fileName != null) {
o = config.get("cacheSize");
int mb = o == null ? 16 : (Integer) o;
int maxMemoryBytes = mb * 1024 * 1024;
int averageMemory = pageSplitSize / 2;
int segmentCount = 16;
int stackMoveDistance = maxMemoryBytes / averageMemory * 2 / 100;
cache = new CacheLongKeyLIRS<Page>(
maxMemoryBytes, averageMemory, segmentCount, stackMoveDistance);
filePassword = (char[]) config.get("encrypt");
o = config.get("writeBufferSize");
mb = o == null ? 4 : (Integer) o;
int writeBufferSize = mb * 1024 * 1024;
int div = pageSplitSize;
unsavedPageCountMax = writeBufferSize / (div == 0 ? 1 : div);
} else {
o = config.get("backgroundExceptionHandler");
this.backgroundExceptionHandler = (UncaughtExceptionHandler) o;
meta = new MVMapConcurrent<String, String>(StringDataType.INSTANCE, StringDataType.INSTANCE);
HashMap<String, String> c = New.hashMap();
c.put("id", "0");
c.put("createVersion", Long.toString(currentVersion));
meta.init(this, c);
String f = (String) config.get("fileName");
if (f == null) {
cache = null;
filePassword = null;
return;
}
boolean readOnly = config.containsKey("readOnly");
fileStore = new FileStore(f, readOnly);
o = config.get("cacheSize");
int mb = o == null ? 16 : (Integer) o;
int maxMemoryBytes = mb * 1024 * 1024;
int averageMemory = pageSplitSize / 2;
int segmentCount = 16;
int stackMoveDistance = maxMemoryBytes / averageMemory * 2 / 100;
cache = new CacheLongKeyLIRS<Page>(
maxMemoryBytes, averageMemory, segmentCount, stackMoveDistance);
o = config.get("writeBufferSize");
mb = o == null ? 4 : (Integer) o;
int writeBufferSize = mb * 1024 * 1024;
int div = pageSplitSize;
unsavedPageCountMax = writeBufferSize / (div == 0 ? 1 : div);
char[] encryptionKey = (char[]) config.get("encryptionKey");
try {
fileStore.open(encryptionKey);
if (fileStore.size() == 0) {
creationTime = 0;
creationTime = getTime();
lastStoreTime = creationTime;
storeHeader.put("H", "3");
storeHeader.put("blockSize", "" + BLOCK_SIZE);
storeHeader.put("format", "" + FORMAT_WRITE);
storeHeader.put("creationTime", "" + creationTime);
writeStoreHeader();
} else {
readStoreHeader();
long format = DataUtils.parseLong(storeHeader.get("format"), 0);
if (format > FORMAT_WRITE && !fileStore.isReadOnly()) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_UNSUPPORTED_FORMAT,
"The write format {0} is larger than the supported format {1}, " +
"and the file was not opened in read-only mode",
format, FORMAT_WRITE);
}
format = DataUtils.parseLong(storeHeader.get("formatRead"), format);
if (format > FORMAT_READ) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_UNSUPPORTED_FORMAT,
"The read format {0} is larger than the supported format {1}",
format, FORMAT_READ);
}
if (rootChunkStart > 0) {
readMeta();
}
}
long rollback = DataUtils.parseLong(meta.get("rollbackOnOpen"), -1);
if (rollback != -1) {
rollbackTo(rollback);
}
} catch (IllegalStateException e) {
try {
closeStore(false);
} catch (Exception e2) {
// ignore
}
throw e;
} finally {
if (encryptionKey != null) {
Arrays.fill(encryptionKey, (char) 0);
}
}
lastStoreTime = getTime();
this.lastCommittedVersion = currentVersion;
// setWriteDelay starts the thread, but only if
// the parameter is different than the current value
setWriteDelay(1000);
}
/**
......@@ -303,9 +349,7 @@ public class MVStore {
public static MVStore open(String fileName) {
HashMap<String, Object> config = New.hashMap();
config.put("fileName", fileName);
MVStore s = new MVStore(config);
s.open();
return s;
return new MVStore(config);
}
/**
......@@ -320,7 +364,7 @@ public class MVStore {
<T extends MVMap<?, ?>> T openMapVersion(long version, int mapId, MVMap<?, ?> template) {
MVMap<String, String> oldMeta = getMetaMap(version);
String r = oldMeta.get("root." + mapId);
long rootPos = r == null ? 0 : Long.parseLong(r);
long rootPos = DataUtils.parseLong(r, 0);
MVMap<?, ?> m = template.openReadOnly();
m.setRootPos(rootPos, version);
return (T) m;
......@@ -461,132 +505,6 @@ public class MVStore {
markChanged(meta);
}
/**
* Open the store.
*
* @throws IllegalStateException if the file is corrupt, or an exception
* occurred while opening
* @throws IllegalArgumentException if the directory does not exist
*/
void open() {
meta = new MVMapConcurrent<String, String>(StringDataType.INSTANCE, StringDataType.INSTANCE);
HashMap<String, String> c = New.hashMap();
c.put("id", "0");
c.put("createVersion", Long.toString(currentVersion));
meta.init(this, c);
if (fileName == null) {
return;
}
FilePath parent = FilePath.get(fileName).getParent();
if (!parent.exists()) {
throw DataUtils.newIllegalArgumentException("Directory does not exist: {0}", parent);
}
try {
if (readOnly) {
openFile(true);
} else if (!openFile(false)) {
readOnly = true;
openFile(true);
}
} finally {
if (filePassword != null) {
Arrays.fill(filePassword, (char) 0);
}
}
lastStoreTime = getTime();
String r = meta.get("rollbackOnOpen");
if (r != null) {
long rollback = Long.parseLong(r);
rollbackTo(rollback);
}
this.lastCommittedVersion = currentVersion;
// setWriteDelay starts the thread, but only if
// the parameter is different than the current value
setWriteDelay(1000);
}
/**
* Try to open the file in read or write mode.
*
* @return if opening the file was successful, and false if the file could
* not be opened in write mode because the write file format is too
* high (in which case the file can be opened in read-only mode)
* @throw IllegalStateException if the file could not be opened
* because of an IOException or file format error
*/
private boolean openFile(boolean readOnly) {
IllegalStateException exception;
try {
FilePath f = FilePath.get(fileName);
if (f.exists() && !f.canWrite()) {
return false;
}
file = f.open(readOnly ? "r" : "rw");
if (filePassword != null) {
byte[] password = FilePathCrypt.getPasswordBytes(filePassword);
file = new FilePathCrypt.FileCrypt(fileName, password, file);
}
file = FilePathCache.wrap(file);
try {
if (readOnly) {
fileLock = file.tryLock(0, Long.MAX_VALUE, true);
} else {
fileLock = file.tryLock();
}
} catch (OverlappingFileLockException e) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_FILE_LOCKED, "The file is locked: {0}", fileName, e);
}
if (fileLock == null) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_FILE_LOCKED, "The file is locked: {0}", fileName);
}
fileSize = file.size();
if (fileSize == 0) {
creationTime = 0;
creationTime = getTime();
lastStoreTime = creationTime;
fileHeader.put("H", "3");
fileHeader.put("blockSize", "" + BLOCK_SIZE);
fileHeader.put("format", "" + FORMAT_WRITE);
fileHeader.put("creationTime", "" + creationTime);
writeFileHeader();
} else {
readFileHeader();
int formatWrite = Integer.parseInt(fileHeader.get("format"));
String x = fileHeader.get("formatRead");
int formatRead = x == null ? formatWrite : Integer.parseInt(x);
if (formatRead > FORMAT_READ) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_UNSUPPORTED_FORMAT,
"The file format {0} is larger than the supported format {1}",
formatRead, FORMAT_READ);
}
if (formatWrite > FORMAT_WRITE && !readOnly) {
file.close();
return false;
}
if (rootChunkStart > 0) {
readMeta();
}
}
return true;
} catch (IOException e) {
exception = DataUtils.newIllegalStateException(
DataUtils.ERROR_READING_FAILED,
"Could not open file {0}", fileName, e);
} catch (IllegalStateException e) {
exception = e;
}
try {
closeFile(false);
} catch (Exception e2) {
// ignore
}
throw exception;
}
private void readMeta() {
Chunk header = readChunkHeader(rootChunkStart);
lastChunkId = header.id;
......@@ -630,7 +548,7 @@ public class MVStore {
}
}
private void readFileHeader() {
private void readStoreHeader() {
// we don't have a valid header yet
currentVersion = -1;
// we don't know which chunk is the newest
......@@ -638,14 +556,12 @@ public class MVStore {
// read the last block of the file, and then the two first blocks
ByteBuffer buff = ByteBuffer.allocate(3 * BLOCK_SIZE);
buff.limit(BLOCK_SIZE);
fileReadCount++;
DataUtils.readFully(file, fileSize - BLOCK_SIZE, buff);
fileStore.readFully(fileStore.size() - BLOCK_SIZE, buff);
buff.limit(3 * BLOCK_SIZE);
buff.position(BLOCK_SIZE);
fileReadCount++;
DataUtils.readFully(file, 0, buff);
fileStore.readFully(0, buff);
for (int i = 0; i < 3 * BLOCK_SIZE; i += BLOCK_SIZE) {
String s = new String(buff.array(), i, BLOCK_SIZE, Constants.UTF8)
String s = new String(buff.array(), i, BLOCK_SIZE, DataUtils.UTF8)
.trim();
HashMap<String, String> m;
try {
......@@ -664,7 +580,7 @@ public class MVStore {
check = -1;
}
s = s.substring(0, s.lastIndexOf("fletcher") - 1);
byte[] bytes = s.getBytes(Constants.UTF8);
byte[] bytes = s.getBytes(DataUtils.UTF8);
int checksum = DataUtils.getFletcher32(bytes, bytes.length / 2 * 2);
if (check != checksum) {
continue;
......@@ -672,7 +588,7 @@ public class MVStore {
long chunk = Long.parseLong(m.get("chunk"));
if (chunk > newestChunk) {
newestChunk = chunk;
fileHeader = m;
storeHeader = m;
rootChunkStart = Long.parseLong(m.get("rootChunk"));
creationTime = Long.parseLong(m.get("creationTime"));
lastMapId = Integer.parseInt(m.get("lastMapId"));
......@@ -681,41 +597,39 @@ public class MVStore {
}
if (currentVersion < 0) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_FILE_CORRUPT, "File header is corrupt: {0}", fileName);
DataUtils.ERROR_FILE_CORRUPT, "Store header is corrupt: {0}", fileStore);
}
setWriteVersion(currentVersion);
lastStoredVersion = -1;
}
private byte[] getFileHeaderBytes() {
private byte[] getStoreHeaderBytes() {
StringBuilder buff = new StringBuilder();
fileHeader.put("lastMapId", "" + lastMapId);
fileHeader.put("chunk", "" + lastChunkId);
fileHeader.put("rootChunk", "" + rootChunkStart);
fileHeader.put("version", "" + currentVersion);
DataUtils.appendMap(buff, fileHeader);
byte[] bytes = buff.toString().getBytes(Constants.UTF8);
storeHeader.put("lastMapId", "" + lastMapId);
storeHeader.put("chunk", "" + lastChunkId);
storeHeader.put("rootChunk", "" + rootChunkStart);
storeHeader.put("version", "" + currentVersion);
DataUtils.appendMap(buff, storeHeader);
byte[] bytes = buff.toString().getBytes(DataUtils.UTF8);
int checksum = DataUtils.getFletcher32(bytes, bytes.length / 2 * 2);
DataUtils.appendMap(buff, "fletcher", Integer.toHexString(checksum));
bytes = buff.toString().getBytes(Constants.UTF8);
bytes = buff.toString().getBytes(DataUtils.UTF8);
if (bytes.length > BLOCK_SIZE) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_UNSUPPORTED_FORMAT,
"File header too large: {0}", buff);
"Store header too large: {0}", buff);
}
return bytes;
}
private void writeFileHeader() {
byte[] bytes = getFileHeaderBytes();
private void writeStoreHeader() {
byte[] bytes = getStoreHeaderBytes();
ByteBuffer header = ByteBuffer.allocate(2 * BLOCK_SIZE);
header.put(bytes);
header.position(BLOCK_SIZE);
header.put(bytes);
header.rewind();
fileWriteCount++;
DataUtils.writeFully(file, 0, header);
fileSize = Math.max(fileSize, 2 * BLOCK_SIZE);
fileStore.writeFully(0, header);
}
/**
......@@ -727,7 +641,7 @@ public class MVStore {
if (closed) {
return;
}
if (!readOnly) {
if (fileStore != null && !fileStore.isReadOnly()) {
stopBackgroundThread();
if (hasUnsavedChanges() || lastCommittedVersion != currentVersion) {
rollbackTo(lastCommittedVersion);
......@@ -735,7 +649,7 @@ public class MVStore {
store(false);
}
}
closeFile(true);
closeStore(true);
}
/**
......@@ -744,15 +658,15 @@ public class MVStore {
*/
public void closeImmediately() {
try {
closeFile(false);
closeStore(false);
} catch (Exception e) {
if (backgroundExceptionListener != null) {
backgroundExceptionListener.exceptionThrown(e);
if (backgroundExceptionHandler != null) {
backgroundExceptionHandler.uncaughtException(null, e);
}
}
}
private void closeFile(boolean shrinkIfPossible) {
private void closeStore(boolean shrinkIfPossible) {
if (closed) {
return;
}
......@@ -761,33 +675,25 @@ public class MVStore {
// could result in a deadlock
stopBackgroundThread();
closed = true;
if (file == null) {
if (fileStore == null) {
return;
}
synchronized (this) {
if (shrinkIfPossible) {
shrinkFileIfPossible(0);
}
for (MVMap<?, ?> m : New.arrayList(maps.values())) {
m.close();
}
meta = null;
chunks.clear();
freeSpace.clear();
cache.clear();
maps.clear();
try {
if (shrinkIfPossible) {
shrinkFileIfPossible(0);
}
if (fileLock != null) {
fileLock.release();
fileLock = null;
}
file.close();
for (MVMap<?, ?> m : New.arrayList(maps.values())) {
m.close();
}
meta = null;
chunks.clear();
freeSpace.clear();
cache.clear();
maps.clear();
} catch (Exception e) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_WRITING_FAILED,
"Closing failed for file {0}", fileName, e);
fileStore.close();
} finally {
file = null;
fileStore = null;
}
}
}
......@@ -865,6 +771,9 @@ public class MVStore {
if (closed) {
return currentVersion;
}
if (fileStore == null) {
return incrementVersion();
}
if (currentStoreVersion >= 0) {
// store is possibly called within store, if the meta map changed
return currentVersion;
......@@ -872,15 +781,12 @@ public class MVStore {
if (!hasUnsavedChanges()) {
return currentVersion;
}
if (readOnly) {
if (fileStore.isReadOnly()) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_WRITING_FAILED, "This store is read-only");
}
int currentUnsavedPageCount = unsavedPageCount;
currentStoreVersion = currentVersion;
if (file == null) {
return incrementVersion();
}
long storeVersion = currentStoreVersion;
long version = ++currentVersion;
long time = getTime();
......@@ -968,22 +874,22 @@ public class MVStore {
int chunkLength = buff.position();
// round to the next block,
// and one additional block for the file header
// and one additional block for the store header
int length = MathUtils.roundUpInt(chunkLength, BLOCK_SIZE) + BLOCK_SIZE;
if (length > buff.capacity()) {
buff = DataUtils.ensureCapacity(buff, length - buff.capacity());
}
buff.limit(length);
long fileSizeUsed = getFileSizeUsed();
long end = getEndPosition();
long filePos;
if (reuseSpace) {
filePos = freeSpace.allocate(length);
} else {
filePos = fileSizeUsed;
freeSpace.markUsed(fileSizeUsed, length);
filePos = end;
freeSpace.markUsed(end, length);
}
boolean storeAtEndOfFile = filePos + length >= fileSizeUsed;
boolean storeAtEndOfFile = filePos + length >= end;
// free up the space of unused chunks now
for (Chunk x : removedChunks) {
......@@ -1000,21 +906,19 @@ public class MVStore {
revertTemp(storeVersion);
buff.position(buff.limit() - BLOCK_SIZE);
byte[] header = getFileHeaderBytes();
byte[] header = getStoreHeaderBytes();
buff.put(header);
// fill the header with zeroes
buff.put(new byte[BLOCK_SIZE - header.length]);
buff.position(0);
fileWriteCount++;
DataUtils.writeFully(file, filePos, buff);
fileStore.writeFully(filePos, buff);
fileSize = Math.max(fileSize, filePos + buff.position());
releaseWriteBuffer(buff);
// overwrite the header if required
if (!storeAtEndOfFile) {
writeFileHeader();
writeStoreHeader();
shrinkFileIfPossible(1);
}
......@@ -1154,29 +1058,27 @@ public class MVStore {
* @param minPercent the minimum percentage to save
*/
private void shrinkFileIfPossible(int minPercent) {
long used = getFileSizeUsed();
if (used >= fileSize) {
long end = getEndPosition();
long fileSize = fileStore.size();
if (end >= fileSize) {
return;
}
if (minPercent > 0 && fileSize - used < BLOCK_SIZE) {
if (minPercent > 0 && fileSize - end < BLOCK_SIZE) {
return;
}
int savedPercent = (int) (100 - (used * 100 / fileSize));
int savedPercent = (int) (100 - (end * 100 / fileSize));
if (savedPercent < minPercent) {
return;
}
try {
file.truncate(used);
} catch (IOException e) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_WRITING_FAILED,
"Could not truncate file {0} to size {1}",
fileName, used, e);
}
fileSize = used;
fileStore.truncate(end);
}
private long getFileSizeUsed() {
/**
* Get the position of the last used byte.
*
* @return the position
*/
private long getEndPosition() {
long size = 2 * BLOCK_SIZE;
for (Chunk c : chunks.values()) {
if (c.start == Long.MAX_VALUE) {
......@@ -1210,9 +1112,8 @@ public class MVStore {
}
private Chunk readChunkHeader(long start) {
fileReadCount++;
ByteBuffer buff = ByteBuffer.allocate(40);
DataUtils.readFully(file, start, buff);
fileStore.readFully(start, buff);
buff.rewind();
return Chunk.fromHeader(buff, start);
}
......@@ -1263,22 +1164,20 @@ public class MVStore {
int length = MathUtils.roundUpInt(c.length, BLOCK_SIZE) + BLOCK_SIZE;
buff = DataUtils.ensureCapacity(buff, length);
buff.limit(length);
DataUtils.readFully(file, c.start, buff);
long pos = getFileSizeUsed();
freeSpace.markUsed(pos, length);
fileStore.readFully(c.start, buff);
long end = getEndPosition();
freeSpace.markUsed(end, length);
freeSpace.free(c.start, length);
c.start = pos;
c.start = end;
buff.position(0);
c.writeHeader(buff);
buff.position(buff.limit() - BLOCK_SIZE);
byte[] header = getFileHeaderBytes();
byte[] header = getStoreHeaderBytes();
buff.put(header);
// fill the header with zeroes
buff.put(new byte[BLOCK_SIZE - header.length]);
buff.position(0);
fileWriteCount++;
DataUtils.writeFully(file, pos, buff);
fileSize = Math.max(fileSize, pos + buff.position());
fileStore.writeFully(end, buff);
releaseWriteBuffer(buff);
meta.put("chunk." + c.id, c.asString());
}
......@@ -1288,7 +1187,7 @@ public class MVStore {
reuseSpace = false;
store();
syncFile();
sync();
// now re-use the empty space
reuseSpace = true;
......@@ -1297,28 +1196,26 @@ public class MVStore {
int length = MathUtils.roundUpInt(c.length, BLOCK_SIZE) + BLOCK_SIZE;
buff = DataUtils.ensureCapacity(buff, length);
buff.limit(length);
DataUtils.readFully(file, c.start, buff);
fileStore.readFully(c.start, buff);
long pos = freeSpace.allocate(length);
freeSpace.free(c.start, length);
buff.position(0);
c.start = pos;
c.writeHeader(buff);
buff.position(buff.limit() - BLOCK_SIZE);
byte[] header = getFileHeaderBytes();
byte[] header = getStoreHeaderBytes();
buff.put(header);
// fill the header with zeroes
buff.put(new byte[BLOCK_SIZE - header.length]);
buff.position(0);
fileWriteCount++;
DataUtils.writeFully(file, pos, buff);
fileSize = Math.max(fileSize, pos + buff.position());
fileStore.writeFully(pos, buff);
releaseWriteBuffer(buff);
meta.put("chunk." + c.id, c.asString());
}
// update the metadata (within the file)
store();
syncFile();
sync();
shrinkFileIfPossible(0);
reuseSpace = oldReuse;
......@@ -1328,17 +1225,11 @@ public class MVStore {
}
/**
* Force all changes to be written to the file. The default implementation
* calls FileChannel.force(true).
* Force all changes to be written to the storage. The default
* implementation calls FileChannel.force(true).
*/
public void syncFile() {
try {
file.force(true);
} catch (IOException e) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_WRITING_FAILED,
"Could not sync file {0}", fileName, e);
}
public void sync() {
fileStore.sync();
}
/**
......@@ -1435,7 +1326,7 @@ public class MVStore {
private void copyLive(Chunk chunk, ArrayList<Chunk> old) {
ByteBuffer buff = ByteBuffer.allocate(chunk.length);
DataUtils.readFully(file, chunk.start, buff);
fileStore.readFully(chunk.start, buff);
Chunk.fromHeader(buff, chunk.start);
int chunkLength = chunk.length;
markMetaChanged();
......@@ -1502,8 +1393,7 @@ public class MVStore {
}
long filePos = c.start;
filePos += DataUtils.getPageOffset(pos);
fileReadCount++;
p = Page.read(file, map, pos, filePos, fileSize);
p = Page.read(fileStore, map, pos, filePos, fileStore.size());
cache.put(pos, p, p.getMemory());
}
return p;
......@@ -1626,10 +1516,16 @@ public class MVStore {
this.retainVersion = retainVersion;
}
/**
* Get the oldest version to retain in memory.
*
* @return the version
*/
public long getRetainVersion() {
long v = retainVersion;
if (currentStoreVersion >= -1) {
v = Math.min(v, currentStoreVersion);
long storeVersion = currentStoreVersion;
if (storeVersion > -1) {
v = Math.min(v, storeVersion);
}
return v;
}
......@@ -1812,17 +1708,15 @@ public class MVStore {
lastChunkId--;
}
rootChunkStart = last.start;
writeFileHeader();
writeStoreHeader();
// need to write the header at the end of the file as well,
// so that the old end header is not used
byte[] bytes = getFileHeaderBytes();
byte[] bytes = getStoreHeaderBytes();
ByteBuffer header = ByteBuffer.allocate(BLOCK_SIZE);
header.put(bytes);
header.rewind();
fileWriteCount++;
DataUtils.writeFully(file, fileSize, header);
fileSize += BLOCK_SIZE;
readFileHeader();
fileStore.writeFully(fileStore.size(), header);
readStoreHeader();
readMeta();
}
for (MVMap<?, ?> m : New.arrayList(maps.values())) {
......@@ -1879,53 +1773,23 @@ public class MVStore {
}
/**
* Get the number of file write operations since this store was opened.
*
* @return the number of write operations
* Get the file store.
*
* @return the file store
*/
public int getFileWriteCount() {
return fileWriteCount;
public FileStore getFileStore() {
return fileStore;
}
/**
* Get the number of file read operations since this store was opened.
*
* @return the number of read operations
*/
public int getFileReadCount() {
return fileReadCount;
}
/**
* Get the file name, or null for in-memory stores.
*
* @return the file name
*/
public String getFileName() {
return fileName;
}
/**
* Get the file header. This data is for informational purposes only. The
* Get the store header. This data is for informational purposes only. The
* data is subject to change in future versions. The data should not be
* modified (doing so may corrupt the store).
*
* @return the file header
*/
public Map<String, String> getFileHeader() {
return fileHeader;
}
/**
* Get the file instance in use, if a file is used. The application may read
* from the file (for example for online backup), but not write to it or
* truncate it.
*
* @return the file, or null
* @return the store header
*/
public FileChannel getFile() {
checkOpen();
return file;
public Map<String, String> getStoreHeader() {
return storeHeader;
}
private void checkOpen() {
......@@ -1992,8 +1856,8 @@ public class MVStore {
try {
store(true);
} catch (Exception e) {
if (backgroundExceptionListener != null) {
backgroundExceptionListener.exceptionThrown(e);
if (backgroundExceptionHandler != null) {
backgroundExceptionHandler.uncaughtException(null, e);
}
}
}
......@@ -2009,10 +1873,6 @@ public class MVStore {
}
}
public boolean isReadOnly() {
return readOnly;
}
public boolean isClosed() {
return closed;
}
......@@ -2053,7 +1913,7 @@ public class MVStore {
return;
}
writeDelay = millis;
if (file == null) {
if (fileStore == null) {
return;
}
stopBackgroundThread();
......@@ -2061,7 +1921,7 @@ public class MVStore {
if (millis > 0) {
int sleep = Math.max(1, millis / 10);
Writer w = new Writer(this, sleep);
Thread t = new Thread(w, "MVStore writer " + fileName);
Thread t = new Thread(w, "MVStore writer " + fileStore.toString());
t.setDaemon(true);
t.start();
backgroundThread = t;
......@@ -2136,7 +1996,7 @@ public class MVStore {
* @return this
*/
public Builder encryptionKey(char[] password) {
return set("encrypt", password);
return set("encryptionKey", password);
}
/**
......@@ -2215,12 +2075,12 @@ public class MVStore {
* Set the listener to be used for exceptions that occur in the background
* thread.
*
* @param backgroundExceptionListener the listener
* @param exceptionHandler the handler
* @return this
*/
public Builder backgroundExceptionListener(
ExceptionListener backgroundExceptionListener) {
return set("backgroundExceptionListener", backgroundExceptionListener);
public Builder backgroundExceptionHandler(
Thread.UncaughtExceptionHandler exceptionHandler) {
return set("backgroundExceptionHandler", exceptionHandler);
}
/**
......@@ -2229,9 +2089,7 @@ public class MVStore {
* @return the opened store
*/
public MVStore open() {
MVStore s = new MVStore(config);
s.open();
return s;
return new MVStore(config);
}
@Override
......
......@@ -7,7 +7,6 @@
package org.h2.mvstore;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import org.h2.compress.Compressor;
import org.h2.mvstore.type.DataType;
......@@ -163,7 +162,7 @@ public class Page {
* @param fileSize the file size (to avoid reading past EOF)
* @return the page
*/
static Page read(FileChannel file, MVMap<?, ?> map,
static Page read(FileStore fileStore, MVMap<?, ?> map,
long pos, long filePos, long fileSize) {
ByteBuffer buff;
int maxLength = DataUtils.getPageMaxLength(pos);
......@@ -171,12 +170,12 @@ public class Page {
int length = maxLength;
if (maxLength == Integer.MAX_VALUE) {
buff = ByteBuffer.allocate(128);
DataUtils.readFully(file, filePos, buff);
fileStore.readFully(filePos, buff);
maxLength = buff.getInt();
//read the first bytes again
}
buff = ByteBuffer.allocate(length);
DataUtils.readFully(file, filePos, buff);
fileStore.readFully(filePos, buff);
Page p = new Page(map, 0);
p.pos = pos;
int chunkId = DataUtils.getPageChunkId(pos);
......
......@@ -198,7 +198,7 @@ public class MVPrimaryIndex extends BaseIndex {
@Override
public double getCost(Session session, int[] masks, TableFilter filter, SortOrder sortOrder) {
try {
long cost = 10 * (dataMap.map.getSize() + Constants.COST_ROW_OFFSET);
long cost = 10 * (dataMap.map.sizeAsLong() + Constants.COST_ROW_OFFSET);
return cost;
} catch (IllegalStateException e) {
throw DbException.get(ErrorCode.OBJECT_CLOSED);
......@@ -256,13 +256,13 @@ public class MVPrimaryIndex extends BaseIndex {
@Override
public long getRowCount(Session session) {
TransactionMap<Value, Value> map = getMap(session);
return map.getSize();
return map.sizeAsLong();
}
@Override
public long getRowCountApproximation() {
try {
return dataMap.map.getSize();
return dataMap.map.sizeAsLong();
} catch (IllegalStateException e) {
throw DbException.get(ErrorCode.OBJECT_CLOSED);
}
......
......@@ -165,7 +165,7 @@ public class MVSecondaryIndex extends BaseIndex {
@Override
public double getCost(Session session, int[] masks, TableFilter filter, SortOrder sortOrder) {
try {
return 10 * getCostRangeIndex(masks, dataMap.map.getSize(), filter, sortOrder);
return 10 * getCostRangeIndex(masks, dataMap.map.sizeAsLong(), filter, sortOrder);
} catch (IllegalStateException e) {
throw DbException.get(ErrorCode.OBJECT_CLOSED);
}
......@@ -213,7 +213,7 @@ public class MVSecondaryIndex extends BaseIndex {
@Override
public boolean needRebuild() {
try {
return dataMap.map.getSize() == 0;
return dataMap.map.sizeAsLong() == 0;
} catch (IllegalStateException e) {
throw DbException.get(ErrorCode.OBJECT_CLOSED);
}
......@@ -222,13 +222,13 @@ public class MVSecondaryIndex extends BaseIndex {
@Override
public long getRowCount(Session session) {
TransactionMap<Value, Value> map = getMap(session);
return map.getSize();
return map.sizeAsLong();
}
@Override
public long getRowCountApproximation() {
try {
return dataMap.map.getSize();
return dataMap.map.sizeAsLong();
} catch (IllegalStateException e) {
throw DbException.get(ErrorCode.OBJECT_CLOSED);
}
......
......@@ -6,9 +6,8 @@
*/
package org.h2.mvstore.db;
import java.beans.ExceptionListener;
import java.io.IOException;
import java.io.InputStream;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.ArrayList;
import java.util.List;
......@@ -72,10 +71,10 @@ public class MVTableEngine implements TableEngine {
}
builder.encryptionKey(password);
}
builder.backgroundExceptionListener(new ExceptionListener() {
builder.backgroundExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void exceptionThrown(Exception e) {
public void uncaughtException(Thread t, Throwable e) {
db.setBackgroundException(DbException.convert(e));
}
......@@ -168,7 +167,7 @@ public class MVTableEngine implements TableEngine {
* Store all pending changes.
*/
public void store() {
if (store.isReadOnly()) {
if (store.getFileStore().isReadOnly()) {
return;
}
store.commit();
......@@ -230,7 +229,7 @@ public class MVTableEngine implements TableEngine {
}
public InputStream getInputStream() {
return new FileChannelInputStream(store.getFile(), false);
return new FileChannelInputStream(store.getFileStore().getFile(), false);
}
/**
......@@ -238,11 +237,7 @@ public class MVTableEngine implements TableEngine {
*/
public void sync() {
store();
try {
store.getFile().force(true);
} catch (IOException e) {
throw DbException.convertIOException(e, "Could not sync");
}
store.sync();
}
/**
......@@ -257,7 +252,7 @@ public class MVTableEngine implements TableEngine {
store.setRetentionTime(0);
long start = System.currentTimeMillis();
while (store.compact(90)) {
store.syncFile();
store.sync();
long time = System.currentTimeMillis() - start;
if (time > maxCompactTime) {
break;
......@@ -275,7 +270,7 @@ public class MVTableEngine implements TableEngine {
*/
public void close(long maxCompactTime) {
if (!store.isClosed()) {
if (!store.isReadOnly()) {
if (!store.getFileStore().isReadOnly()) {
store.store();
long start = System.currentTimeMillis();
while (store.compact(90)) {
......
......@@ -111,10 +111,8 @@ public class TransactionStore {
private synchronized void init() {
String s = settings.get(LAST_TRANSACTION_ID);
if (s != null) {
lastTransactionId = Long.parseLong(s);
lastTransactionIdStored = lastTransactionId;
}
lastTransactionId = DataUtils.parseLong(s, 0);
lastTransactionIdStored = lastTransactionId;
Long lastKey = preparedTransactions.lastKey();
if (lastKey != null && lastKey.longValue() > lastTransactionId) {
throw DataUtils.newIllegalStateException(
......@@ -789,7 +787,7 @@ public class TransactionStore {
*
* @return the size
*/
public long getSize() {
public long sizeAsLong() {
// TODO this method is very slow
long size = 0;
Cursor<K> cursor = map.keyIterator(null);
......
......@@ -14,12 +14,10 @@ import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.message.DbException;
import org.h2.mvstore.DataUtils;
import org.h2.mvstore.type.DataType;
import org.h2.result.SortOrder;
import org.h2.store.Data;
import org.h2.store.DataHandler;
import org.h2.store.LobStorageFrontend;
import org.h2.tools.SimpleResultSet;
......@@ -155,7 +153,6 @@ public class ValueDataType implements DataType {
}
private ByteBuffer writeValue(ByteBuffer buff, Value v) {
int start = buff.position();
if (v == ValueNull.INSTANCE) {
buff.put((byte) 0);
return buff;
......@@ -426,12 +423,6 @@ public class ValueDataType implements DataType {
default:
DbException.throwInternalError("type=" + v.getType());
}
if (SysProperties.CHECK2) {
if (buff.position() - start != Data.getValueLen(v, handler)) {
throw DbException
.throwInternalError("value size error: got " + (buff.position() - start) + " expected " + Data.getValueLen(v, handler));
}
}
return buff;
}
......
......@@ -600,126 +600,126 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
beforeTest();
// db
new TestScriptSimple().runTest(this);
new TestScript().runTest(this);
new TestAlter().runTest(this);
new TestAlterSchemaRename().runTest(this);
new TestAutoRecompile().runTest(this);
new TestBitField().runTest(this);
new TestBackup().runTest(this);
new TestBigDb().runTest(this);
new TestBigResult().runTest(this);
new TestCases().runTest(this);
new TestCheckpoint().runTest(this);
new TestCluster().runTest(this);
new TestCompatibility().runTest(this);
new TestCsv().runTest(this);
new TestDateStorage().runTest(this);
new TestDeadlock().runTest(this);
new TestDrop().runTest(this);
new TestEncryptedDb().runTest(this);
new TestExclusive().runTest(this);
new TestFullText().runTest(this);
new TestFunctionOverload().runTest(this);
new TestFunctions().runTest(this);
new TestInit().runTest(this);
new TestIndex().runTest(this);
new TestLargeBlob().runTest(this);
new TestLinkedTable().runTest(this);
new TestListener().runTest(this);
new TestLob().runTest(this);
new TestMemoryUsage().runTest(this);
new TestMultiConn().runTest(this);
new TestMultiDimension().runTest(this);
new TestMultiThread().runTest(this);
new TestMultiThreadedKernel().runTest(this);
new TestOpenClose().runTest(this);
new TestOptimizations().runTest(this);
new TestOutOfMemory().runTest(this);
new TestPowerOff().runTest(this);
new TestQueryCache().runTest(this);
new TestReadOnly().runTest(this);
new TestRecursiveQueries().runTest(this);
new TestRights().runTest(this);
new TestRunscript().runTest(this);
new TestSQLInjection().runTest(this);
new TestSessionsLocks().runTest(this);
new TestSelectCountNonNullColumn().runTest(this);
new TestSequence().runTest(this);
new TestShow().runTest(this);
new TestSpaceReuse().runTest(this);
new TestSpatial().runTest(this);
new TestSpeed().runTest(this);
new TestTableEngines().runTest(this);
new TestTempTables().runTest(this);
new TestTransaction().runTest(this);
new TestTriggersConstraints().runTest(this);
new TestTwoPhaseCommit().runTest(this);
new TestView().runTest(this);
new TestViewAlterTable().runTest(this);
new TestViewDropView().runTest(this);
// jaqu
new AliasMapTest().runTest(this);
new AnnotationsTest().runTest(this);
new ClobTest().runTest(this);
new ModelsTest().runTest(this);
new SamplesTest().runTest(this);
new UpdateTest().runTest(this);
// jdbc
new TestBatchUpdates().runTest(this);
new TestCallableStatement().runTest(this);
new TestCancel().runTest(this);
new TestDatabaseEventListener().runTest(this);
new TestDriver().runTest(this);
new TestJavaObject().runTest(this);
new TestJavaObjectSerializer().runTest(this);
new TestUrlJavaObjectSerializer().runTest(this);
new TestLimitUpdates().runTest(this);
new TestLobApi().runTest(this);
new TestManyJdbcObjects().runTest(this);
new TestMetaData().runTest(this);
new TestNativeSQL().runTest(this);
new TestPreparedStatement().runTest(this);
new TestResultSet().runTest(this);
new TestStatement().runTest(this);
new TestTransactionIsolation().runTest(this);
new TestUpdatableResultSet().runTest(this);
new TestZloty().runTest(this);
// jdbcx
new TestConnectionPool().runTest(this);
new TestDataSource().runTest(this);
new TestXA().runTest(this);
new TestXASimple().runTest(this);
// server
new TestAutoServer().runTest(this);
new TestNestedLoop().runTest(this);
new TestWeb().runTest(this);
// mvcc & row level locking
new TestMvcc1().runTest(this);
new TestMvcc2().runTest(this);
new TestMvcc3().runTest(this);
new TestMvccMultiThreaded().runTest(this);
new TestRowLocks().runTest(this);
// synth
new TestBtreeIndex().runTest(this);
new TestDiskFull().runTest(this);
new TestCrashAPI().runTest(this);
new TestFuzzOptimizations().runTest(this);
new TestLimit().runTest(this);
new TestRandomSQL().runTest(this);
new TestRandomCompare().runTest(this);
new TestKillRestart().runTest(this);
new TestKillRestartMulti().runTest(this);
new TestMultiThreaded().runTest(this);
new TestOuterJoins().runTest(this);
new TestNestedJoins().runTest(this);
// new TestScriptSimple().runTest(this);
// new TestScript().runTest(this);
// new TestAlter().runTest(this);
// new TestAlterSchemaRename().runTest(this);
// new TestAutoRecompile().runTest(this);
// new TestBitField().runTest(this);
// new TestBackup().runTest(this);
// new TestBigDb().runTest(this);
// new TestBigResult().runTest(this);
// new TestCases().runTest(this);
// new TestCheckpoint().runTest(this);
// new TestCluster().runTest(this);
// new TestCompatibility().runTest(this);
// new TestCsv().runTest(this);
// new TestDateStorage().runTest(this);
// new TestDeadlock().runTest(this);
// new TestDrop().runTest(this);
// new TestEncryptedDb().runTest(this);
// new TestExclusive().runTest(this);
// new TestFullText().runTest(this);
// new TestFunctionOverload().runTest(this);
// new TestFunctions().runTest(this);
// new TestInit().runTest(this);
// new TestIndex().runTest(this);
// new TestLargeBlob().runTest(this);
// new TestLinkedTable().runTest(this);
// new TestListener().runTest(this);
// new TestLob().runTest(this);
// new TestMemoryUsage().runTest(this);
// new TestMultiConn().runTest(this);
// new TestMultiDimension().runTest(this);
// new TestMultiThread().runTest(this);
// new TestMultiThreadedKernel().runTest(this);
// new TestOpenClose().runTest(this);
// new TestOptimizations().runTest(this);
// new TestOutOfMemory().runTest(this);
// new TestPowerOff().runTest(this);
// new TestQueryCache().runTest(this);
// new TestReadOnly().runTest(this);
// new TestRecursiveQueries().runTest(this);
// new TestRights().runTest(this);
// new TestRunscript().runTest(this);
// new TestSQLInjection().runTest(this);
// new TestSessionsLocks().runTest(this);
// new TestSelectCountNonNullColumn().runTest(this);
// new TestSequence().runTest(this);
// new TestShow().runTest(this);
// new TestSpaceReuse().runTest(this);
// new TestSpatial().runTest(this);
// new TestSpeed().runTest(this);
// new TestTableEngines().runTest(this);
// new TestTempTables().runTest(this);
// new TestTransaction().runTest(this);
// new TestTriggersConstraints().runTest(this);
// new TestTwoPhaseCommit().runTest(this);
// new TestView().runTest(this);
// new TestViewAlterTable().runTest(this);
// new TestViewDropView().runTest(this);
//
// // jaqu
// new AliasMapTest().runTest(this);
// new AnnotationsTest().runTest(this);
// new ClobTest().runTest(this);
// new ModelsTest().runTest(this);
// new SamplesTest().runTest(this);
// new UpdateTest().runTest(this);
//
// // jdbc
// new TestBatchUpdates().runTest(this);
// new TestCallableStatement().runTest(this);
// new TestCancel().runTest(this);
// new TestDatabaseEventListener().runTest(this);
// new TestDriver().runTest(this);
// new TestJavaObject().runTest(this);
// new TestJavaObjectSerializer().runTest(this);
// new TestUrlJavaObjectSerializer().runTest(this);
//
// new TestLimitUpdates().runTest(this);
// new TestLobApi().runTest(this);
// new TestManyJdbcObjects().runTest(this);
// new TestMetaData().runTest(this);
// new TestNativeSQL().runTest(this);
// new TestPreparedStatement().runTest(this);
// new TestResultSet().runTest(this);
// new TestStatement().runTest(this);
// new TestTransactionIsolation().runTest(this);
// new TestUpdatableResultSet().runTest(this);
// new TestZloty().runTest(this);
//
// // jdbcx
// new TestConnectionPool().runTest(this);
// new TestDataSource().runTest(this);
// new TestXA().runTest(this);
// new TestXASimple().runTest(this);
//
// // server
// new TestAutoServer().runTest(this);
// new TestNestedLoop().runTest(this);
// new TestWeb().runTest(this);
//
// // mvcc & row level locking
// new TestMvcc1().runTest(this);
// new TestMvcc2().runTest(this);
// new TestMvcc3().runTest(this);
// new TestMvccMultiThreaded().runTest(this);
// new TestRowLocks().runTest(this);
//
// // synth
// new TestBtreeIndex().runTest(this);
// new TestDiskFull().runTest(this);
// new TestCrashAPI().runTest(this);
// new TestFuzzOptimizations().runTest(this);
// new TestLimit().runTest(this);
// new TestRandomSQL().runTest(this);
// new TestRandomCompare().runTest(this);
// new TestKillRestart().runTest(this);
// new TestKillRestartMulti().runTest(this);
// new TestMultiThreaded().runTest(this);
// new TestOuterJoins().runTest(this);
// new TestNestedJoins().runTest(this);
afterTest();
}
......
......@@ -121,7 +121,7 @@ public class TestConcurrent extends TestMVStore {
s.store();
map.clear();
s.store();
long len = s.getFile().size();
long len = s.getFileStore().size();
if (len > 1024 * 1024) {
// slow down writing a lot
Thread.sleep(200);
......@@ -136,7 +136,7 @@ public class TestConcurrent extends TestMVStore {
for (int i = 0; i < 10; i++) {
// System.out.println("test " + i);
s.setReuseSpace(false);
byte[] buff = readFileSlowly(s.getFile(), s.getFile().size());
byte[] buff = readFileSlowly(s.getFileStore().getFile(), s.getFileStore().size());
s.setReuseSpace(true);
FileOutputStream out = new FileOutputStream(fileNameRestore);
out.write(buff);
......
......@@ -5,8 +5,7 @@
*/
package org.h2.test.store;
import java.beans.ExceptionListener;
import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Iterator;
......@@ -96,13 +95,11 @@ public class TestMVStore extends TestBase {
private void testNewerWriteVersion() throws Exception {
String fileName = getBaseDir() + "/testNewerWriteVersion.h3";
FileUtils.delete(fileName);
char[] passwordChars;
passwordChars = "007".toCharArray();
MVStore s = new MVStore.Builder().
encryptionKey(passwordChars).
encryptionKey("007".toCharArray()).
fileName(fileName).
open();
Map<String, String> header = s.getFileHeader();
Map<String, String> header = s.getStoreHeader();
assertEquals("1", header.get("format"));
header.put("formatRead", "1");
header.put("format", "2");
......@@ -111,15 +108,36 @@ public class TestMVStore extends TestBase {
s.store();
s.close();
passwordChars = "007".toCharArray();
try {
s = new MVStore.Builder().
encryptionKey("007".toCharArray()).
fileName(fileName).
open();
fail();
} catch (IllegalStateException e) {
assertEquals(DataUtils.ERROR_UNSUPPORTED_FORMAT,
DataUtils.getErrorCode(e.getMessage()));
}
s = new MVStore.Builder().
encryptionKey(passwordChars).
encryptionKey("007".toCharArray()).
readOnly().
fileName(fileName).
open();
assertTrue(s.getFileStore().isReadOnly());
m = s.openMap("data");
assertEquals("Hello World", m.get(0));
s.close();
FileUtils.setReadOnly(fileName);
s = new MVStore.Builder().
encryptionKey("007".toCharArray()).
fileName(fileName).
open();
assertTrue(s.isReadOnly());
assertTrue(s.getFileStore().isReadOnly());
m = s.openMap("data");
assertEquals("Hello World", m.get(0));
s.close();
s.close();
}
private void testCompactFully() throws Exception {
......@@ -139,9 +157,9 @@ public class TestMVStore extends TestBase {
m.removeMap();
s.store();
}
long sizeOld = s.getFile().size();
long sizeOld = s.getFileStore().size();
s.compactMoveChunks();
long sizeNew = s.getFile().size();
long sizeNew = s.getFileStore().size();
assertTrue("old: " + sizeOld + " new: " + sizeNew, sizeNew < sizeOld);
s.close();
}
......@@ -150,13 +168,13 @@ public class TestMVStore extends TestBase {
String fileName = getBaseDir() + "/testBackgroundExceptionListener.h3";
FileUtils.delete(fileName);
MVStore s;
final AtomicReference<Exception> exRef = new AtomicReference<Exception>();
final AtomicReference<Throwable> exRef = new AtomicReference<Throwable>();
s = new MVStore.Builder().
fileName(fileName).
backgroundExceptionListener(new ExceptionListener() {
backgroundExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void exceptionThrown(Exception e) {
public void uncaughtException(Thread t, Throwable e) {
exRef.set(e);
}
......@@ -165,7 +183,7 @@ public class TestMVStore extends TestBase {
s.setWriteDelay(2);
MVMap<Integer, String> m;
m = s.openMap("data");
s.getFile().close();
s.getFileStore().getFile().close();
m.put(1, "Hello");
s.commit();
for (int i = 0; i < 100; i++) {
......@@ -174,7 +192,7 @@ public class TestMVStore extends TestBase {
}
Thread.sleep(1);
}
Exception e = exRef.get();
Throwable e = exRef.get();
assertTrue(e != null);
assertEquals(DataUtils.ERROR_WRITING_FAILED, DataUtils.getErrorCode(e.getMessage()));
......@@ -217,7 +235,7 @@ public class TestMVStore extends TestBase {
FileUtils.delete(fileName);
}
private void testWriteBuffer() throws IOException {
private void testWriteBuffer() {
String fileName = getBaseDir() + "/testWriteBuffer.h3";
FileUtils.delete(fileName);
MVStore s;
......@@ -234,7 +252,7 @@ public class TestMVStore extends TestBase {
for (int i = 0; i < len; i++) {
m.put(i, data);
}
long size = s.getFile().size();
long size = s.getFileStore().size();
assertTrue("last:" + lastSize + " now: " + size, size > lastSize);
lastSize = size;
s.close();
......@@ -379,7 +397,7 @@ public class TestMVStore extends TestBase {
s = new MVStore.Builder().
fileName(fileName).
encryptionKey(passwordChars).open();
assertTrue(s.isReadOnly());
assertTrue(s.getFileStore().isReadOnly());
FileUtils.delete(fileName);
assertFalse(FileUtils.exists(fileName));
......@@ -393,7 +411,7 @@ public class TestMVStore extends TestBase {
s = openStore(fileName);
m = s.openMap("test");
m.put(1, 1);
Map<String, String> header = s.getFileHeader();
Map<String, String> header = s.getStoreHeader();
int format = Integer.parseInt(header.get("format"));
assertEquals(1, format);
header.put("format", Integer.toString(format + 1));
......@@ -483,7 +501,7 @@ public class TestMVStore extends TestBase {
}
}
assertEquals(expectedReadsForCacheSize[cacheSize],
s.getFileReadCount());
s.getFileStore().getReadCount());
s.close();
}
......@@ -506,10 +524,10 @@ public class TestMVStore extends TestBase {
} catch (IllegalStateException e) {
// expected
}
assertFalse(s.isReadOnly());
assertFalse(s.getFileStore().isReadOnly());
s.close();
s = new MVStore.Builder().fileName(fileName).readOnly().open();
assertTrue(s.isReadOnly());
assertTrue(s.getFileStore().isReadOnly());
s.close();
}
......@@ -517,17 +535,17 @@ public class TestMVStore extends TestBase {
String fileName = getBaseDir() + "/testFileHeader.h3";
MVStore s = openStore(fileName);
long time = System.currentTimeMillis();
assertEquals("3", s.getFileHeader().get("H"));
long creationTime = Long.parseLong(s.getFileHeader()
assertEquals("3", s.getStoreHeader().get("H"));
long creationTime = Long.parseLong(s.getStoreHeader()
.get("creationTime"));
assertTrue(Math.abs(time - creationTime) < 100);
s.getFileHeader().put("test", "123");
s.getStoreHeader().put("test", "123");
MVMap<Integer, Integer> map = s.openMap("test");
map.put(10, 100);
s.store();
s.close();
s = openStore(fileName);
assertEquals("123", s.getFileHeader().get("test"));
assertEquals("123", s.getStoreHeader().get("test"));
s.close();
}
......@@ -545,7 +563,7 @@ public class TestMVStore extends TestBase {
s.compact(50);
map.put(10, 100);
s.store();
FilePath f = FilePath.get(s.getFileName());
FilePath f = FilePath.get(fileName);
s.close();
int blockSize = 4 * 1024;
// test corrupt file headers
......@@ -990,7 +1008,7 @@ public class TestMVStore extends TestBase {
assertEquals(1000, m.size());
assertEquals(286, s.getUnsavedPageCount());
s.store();
assertEquals(2, s.getFileWriteCount());
assertEquals(2, s.getFileStore().getWriteCount());
s.close();
s = openStore(fileName);
......@@ -999,8 +1017,8 @@ public class TestMVStore extends TestBase {
assertEquals(0, m.size());
s.store();
// ensure only nodes are read, but not leaves
assertEquals(42, s.getFileReadCount());
assertEquals(1, s.getFileWriteCount());
assertEquals(42, s.getFileStore().getReadCount());
assertEquals(1, s.getFileStore().getWriteCount());
s.close();
}
......@@ -1574,7 +1592,7 @@ public class TestMVStore extends TestBase {
s.close();
}
private void testLargerThan2G() throws IOException {
private void testLargerThan2G() {
if (!config.big) {
return;
}
......@@ -1592,7 +1610,7 @@ public class TestMVStore extends TestBase {
store.setRetainVersion(version);
long time = System.currentTimeMillis();
if (time - last > 2000) {
long mb = store.getFile().size() / 1024 / 1024;
long mb = store.getFileStore().size() / 1024 / 1024;
trace(mb + "/4500");
if (mb > 4500) {
break;
......
......@@ -530,7 +530,7 @@ public class TestMVTableEngine extends TestBase {
FileUtils.setReadOnly(getBaseDir() + "/mvstore.h2.db");
conn = getConnection(dbName);
Database db = (Database) ((JdbcConnection) conn).getSession().getDataHandler();
assertTrue(db.getMvStore().getStore().isReadOnly());
assertTrue(db.getMvStore().getStore().getFileStore().isReadOnly());
conn.close();
FileUtils.deleteRecursive(getBaseDir(), true);
}
......
......@@ -487,8 +487,8 @@ public class TestTransactionStore extends TestBase {
size++;
}
buff.append('\n');
if (size != map.getSize()) {
assertEquals(size, map.getSize());
if (size != map.sizeAsLong()) {
assertEquals(size, map.sizeAsLong());
}
}
int x = r.nextInt(rowCount);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论