提交 b3e571af authored 作者: Sergi Vladykin's avatar Sergi Vladykin

Merge branch 'master' of https://github.com/h2database/h2database

...@@ -33,6 +33,14 @@ Change Log ...@@ -33,6 +33,14 @@ Change Log
</li> </li>
<li>Issue #295: JdbcResultSet.getObject(int, Class) returns null instead of throwing. <li>Issue #295: JdbcResultSet.getObject(int, Class) returns null instead of throwing.
</li> </li>
<li>Mac OS X: Console tool process did not stop on exit.
</li>
<li>MVStoreTool: add "repair" feature.
</li>
<li>Garbage collection of unused chunks should be faster still.
</li>
<li>MVStore / transaction store: opening a store in read-only mode does no longer loop.
</li>
</ul> </ul>
<h2>Version 1.4.192 Beta (2016-05-26)</h2> <h2>Version 1.4.192 Beta (2016-05-26)</h2>
......
...@@ -171,11 +171,6 @@ public class DataUtils { ...@@ -171,11 +171,6 @@ public class DataUtils {
*/ */
private static final byte[] EMPTY_BYTES = {}; private static final byte[] EMPTY_BYTES = {};
/**
* 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. * Get the length of the variable size int.
* *
...@@ -300,14 +295,12 @@ public class DataUtils { ...@@ -300,14 +295,12 @@ public class DataUtils {
/** /**
* Write characters from a string (without the length). * Write characters from a string (without the length).
* *
* @param buff the target buffer * @param buff the target buffer (must be large enough)
* @param s the string * @param s the string
* @param len the number of characters * @param len the number of characters
* @return the byte buffer
*/ */
public static ByteBuffer writeStringData(ByteBuffer buff, public static void writeStringData(ByteBuffer buff,
String s, int len) { String s, int len) {
buff = DataUtils.ensureCapacity(buff, 3 * len);
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
int c = s.charAt(i); int c = s.charAt(i);
if (c < 0x80) { if (c < 0x80) {
...@@ -321,7 +314,6 @@ public class DataUtils { ...@@ -321,7 +314,6 @@ public class DataUtils {
buff.put((byte) (c & 0x3f)); buff.put((byte) (c & 0x3f));
} }
} }
return buff;
} }
/** /**
...@@ -860,32 +852,6 @@ public class DataUtils { ...@@ -860,32 +852,6 @@ public class DataUtils {
} }
} }
/**
* Ensure the byte buffer has the given capacity, plus 1 KB. If not, a new,
* larger byte buffer is created and the data is copied.
*
* @param buff the byte buffer
* @param len the minimum remaining capacity
* @return the byte buffer (possibly a new one)
*/
public static ByteBuffer ensureCapacity(ByteBuffer buff, int len) {
len += 1024;
if (buff.remaining() > len) {
return buff;
}
return grow(buff, len);
}
private static ByteBuffer grow(ByteBuffer buff, int len) {
len = buff.remaining() + len;
int capacity = buff.capacity();
len = Math.max(len, Math.min(capacity + MAX_GROW, capacity * 2));
ByteBuffer temp = ByteBuffer.allocate(len);
buff.flip();
temp.put(buff);
return temp;
}
/** /**
* Read a hex long value from a map. * Read a hex long value from a map.
* *
......
...@@ -1056,8 +1056,11 @@ public class MVStore { ...@@ -1056,8 +1056,11 @@ public class MVStore {
long time = getTimeSinceCreation(); long time = getTimeSinceCreation();
int freeDelay = retentionTime / 10; int freeDelay = retentionTime / 10;
if (time >= lastFreeUnusedChunks + freeDelay) { if (time >= lastFreeUnusedChunks + freeDelay) {
// set early in case it fails (out of memory or so)
lastFreeUnusedChunks = time; lastFreeUnusedChunks = time;
freeUnusedChunks(); freeUnusedChunks();
// set it here as well, to avoid calling it often if it was slow
lastFreeUnusedChunks = getTimeSinceCreation();
} }
int currentUnsavedPageCount = unsavedMemory; int currentUnsavedPageCount = unsavedMemory;
long storeVersion = currentStoreVersion; long storeVersion = currentStoreVersion;
...@@ -1514,6 +1517,9 @@ public class MVStore { ...@@ -1514,6 +1517,9 @@ public class MVStore {
* @param minPercent the minimum percentage to save * @param minPercent the minimum percentage to save
*/ */
private void shrinkFileIfPossible(int minPercent) { private void shrinkFileIfPossible(int minPercent) {
if (fileStore.isReadOnly()) {
return;
}
long end = getFileLengthInUse(); long end = getFileLengthInUse();
long fileSize = fileStore.size(); long fileSize = fileStore.size();
if (end >= fileSize) { if (end >= fileSize) {
...@@ -2660,6 +2666,15 @@ public class MVStore { ...@@ -2660,6 +2666,15 @@ public class MVStore {
return cache; return cache;
} }
/**
* Whether the store is read-only.
*
* @return true if it is
*/
public boolean isReadOnly() {
return fileStore == null ? false : fileStore.isReadOnly();
}
/** /**
* A background writer thread to automatically store changes from time to * A background writer thread to automatically store changes from time to
* time. * time.
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
package org.h2.mvstore; package org.h2.mvstore;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.Writer; import java.io.Writer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
...@@ -60,6 +61,9 @@ public class MVStoreTool { ...@@ -60,6 +61,9 @@ public class MVStoreTool {
} else if ("-compress".equals(args[i])) { } else if ("-compress".equals(args[i])) {
String fileName = args[++i]; String fileName = args[++i];
compact(fileName, true); compact(fileName, true);
} else if ("-repair".equals(args[i])) {
String fileName = args[++i];
repair(fileName);
} }
} }
} }
...@@ -325,12 +329,13 @@ public class MVStoreTool { ...@@ -325,12 +329,13 @@ public class MVStoreTool {
* *
* @param fileName the name of the file * @param fileName the name of the file
* @param writer the print writer * @param writer the print writer
* @return null if successful (if there was no error), otherwise the error message
*/ */
public static void info(String fileName, Writer writer) { public static String info(String fileName, Writer writer) {
PrintWriter pw = new PrintWriter(writer, true); PrintWriter pw = new PrintWriter(writer, true);
if (!FilePath.get(fileName).exists()) { if (!FilePath.get(fileName).exists()) {
pw.println("File not found: " + fileName); pw.println("File not found: " + fileName);
return; return "File not found: " + fileName;
} }
long fileLength = FileUtils.size(fileName); long fileLength = FileUtils.size(fileName);
MVStore store = new MVStore.Builder(). MVStore store = new MVStore.Builder().
...@@ -389,10 +394,12 @@ public class MVStoreTool { ...@@ -389,10 +394,12 @@ public class MVStoreTool {
} catch (Exception e) { } catch (Exception e) {
pw.println("ERROR: " + e); pw.println("ERROR: " + e);
e.printStackTrace(pw); e.printStackTrace(pw);
return e.getMessage();
} finally { } finally {
store.close(); store.close();
} }
pw.flush(); pw.flush();
return null;
} }
private static String formatTimestamp(long t, long start) { private static String formatTimestamp(long t, long start) {
...@@ -518,6 +525,137 @@ public class MVStoreTool { ...@@ -518,6 +525,137 @@ public class MVStoreTool {
} }
} }
/**
* Repair a store by rolling back to the newest good version.
*
* @param fileName the file name
*/
public static void repair(String fileName) {
PrintWriter pw = new PrintWriter(System.out);
long version = Long.MAX_VALUE;
OutputStream ignore = new OutputStream() {
@Override
public void write(int b) throws IOException {
// ignore
}
};
while (version >= 0) {
pw.println(version == Long.MAX_VALUE ? "Trying latest version" : ("Trying version " + version));
pw.flush();
version = rollback(fileName, version, new PrintWriter(ignore));
try {
String error = info(fileName + ".temp", new PrintWriter(ignore));
if (error == null) {
FilePath.get(fileName).moveTo(FilePath.get(fileName + ".back"), true);
FilePath.get(fileName + ".temp").moveTo(FilePath.get(fileName), true);
pw.println("Success");
break;
}
pw.println(" ... failed: " + error);
} catch (Exception e) {
pw.println("Fail: " + e.getMessage());
pw.flush();
}
version--;
}
pw.flush();
}
/**
* Roll back to a given revision into a a file called *.temp.
*
* @param fileName the file name
* @param targetVersion the version to roll back to (Long.MAX_VALUE for the latest version)
* @return the version rolled back to (-1 if no version)
*/
public static long rollback(String fileName, long targetVersion, Writer writer) {
long newestVersion = -1;
PrintWriter pw = new PrintWriter(writer, true);
if (!FilePath.get(fileName).exists()) {
pw.println("File not found: " + fileName);
return newestVersion;
}
FileChannel file = null;
FileChannel target = null;
int blockSize = MVStore.BLOCK_SIZE;
try {
file = FilePath.get(fileName).open("r");
FilePath.get(fileName + ".temp").delete();
target = FilePath.get(fileName + ".temp").open("rw");
long fileSize = file.size();
ByteBuffer block = ByteBuffer.allocate(4096);
Chunk newestChunk = null;
for (long pos = 0; pos < fileSize;) {
block.rewind();
DataUtils.readFully(file, pos, block);
block.rewind();
int headerType = block.get();
if (headerType == 'H') {
block.rewind();
target.write(block, pos);
pos += blockSize;
continue;
}
if (headerType != 'c') {
pos += blockSize;
continue;
}
Chunk c = null;
try {
c = Chunk.readChunkHeader(block, pos);
} catch (IllegalStateException e) {
pos += blockSize;
continue;
}
if (c.len <= 0) {
// not a chunk
pos += blockSize;
continue;
}
int length = c.len * MVStore.BLOCK_SIZE;
ByteBuffer chunk = ByteBuffer.allocate(length);
DataUtils.readFully(file, pos, chunk);
if (c.version > targetVersion) {
// newer than the requested version
pos += length;
continue;
}
chunk.rewind();
target.write(chunk, pos);
if (newestChunk == null || c.version > newestChunk.version) {
newestChunk = c;
newestVersion = c.version;
}
pos += length;
}
int length = newestChunk.len * MVStore.BLOCK_SIZE;
ByteBuffer chunk = ByteBuffer.allocate(length);
DataUtils.readFully(file, newestChunk.block * MVStore.BLOCK_SIZE, chunk);
chunk.rewind();
target.write(chunk, fileSize);
} catch (IOException e) {
pw.println("ERROR: " + e);
e.printStackTrace(pw);
} finally {
if (file != null) {
try {
file.close();
} catch (IOException e) {
// ignore
}
}
if (target != null) {
try {
target.close();
} catch (IOException e) {
// ignore
}
}
}
pw.flush();
return newestVersion;
}
/** /**
* A data type that can read any data that is persisted, and converts it to * A data type that can read any data that is persisted, and converts it to
* a byte array. * a byte array.
......
...@@ -73,19 +73,7 @@ public class WriteBuffer { ...@@ -73,19 +73,7 @@ public class WriteBuffer {
*/ */
public WriteBuffer putStringData(String s, int len) { public WriteBuffer putStringData(String s, int len) {
ByteBuffer b = ensureCapacity(3 * len); ByteBuffer b = ensureCapacity(3 * len);
for (int i = 0; i < len; i++) { DataUtils.writeStringData(b, s, len);
int c = s.charAt(i);
if (c < 0x80) {
b.put((byte) c);
} else if (c >= 0x800) {
b.put((byte) (0xe0 | (c >> 12)));
b.put((byte) (((c >> 6) & 0x3f)));
b.put((byte) (c & 0x3f));
} else {
b.put((byte) (0xc0 | (c >> 6)));
b.put((byte) (c & 0x3f));
}
}
return this; return this;
} }
......
...@@ -1246,6 +1246,10 @@ public class TransactionStore { ...@@ -1246,6 +1246,10 @@ public class TransactionStore {
Object[] d; Object[] d;
d = transaction.store.undoLog.get(id); d = transaction.store.undoLog.get(id);
if (d == null) { if (d == null) {
if (transaction.store.store.isReadOnly()) {
// uncommitted transaction for a read-only store
return null;
}
// this entry should be committed or rolled back // this entry should be committed or rolled back
// in the meantime (the transaction might still be open) // in the meantime (the transaction might still be open)
// or it might be changed again in a different // or it might be changed again in a different
......
...@@ -17,7 +17,8 @@ import org.h2.mvstore.type.ObjectDataType; ...@@ -17,7 +17,8 @@ import org.h2.mvstore.type.ObjectDataType;
import org.h2.util.New; import org.h2.util.New;
/** /**
* An r-tree implementation. It uses the quadratic split algorithm. * An r-tree implementation. It supports both the linear and the quadratic split
* algorithm.
* *
* @param <V> the value class * @param <V> the value class
*/ */
......
...@@ -351,9 +351,10 @@ ShutdownHandler { ...@@ -351,9 +351,10 @@ ShutdownHandler {
trayIconUsed = false; trayIconUsed = false;
} }
System.gc(); System.gc();
// Mac OS X: Console tool process did not stop on exit
System.exit(0);
} }
//*/ //*/
// System.exit(0);
} }
//## AWT ## //## AWT ##
......
...@@ -156,7 +156,7 @@ public class Profiler implements Runnable { ...@@ -156,7 +156,7 @@ public class Profiler implements Runnable {
sumClasses = true; sumClasses = true;
} else if ("-methods".equals(arg)) { } else if ("-methods".equals(arg)) {
sumMethods = true; sumMethods = true;
} else if ("-packages".equals(args)) { } else if ("-packages".equals(arg)) {
sumClasses = false; sumClasses = false;
sumMethods = false; sumMethods = false;
} else { } else {
......
...@@ -18,8 +18,8 @@ import java.util.concurrent.atomic.AtomicInteger; ...@@ -18,8 +18,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.h2.security.AES; import org.h2.security.AES;
/** /**
* Calculate the constant for the secondary hash function, so that the hash * Calculate the constant for the secondary / supplemental hash function, so
* function mixes the input bits as much as possible. * that the hash function mixes the input bits as much as possible.
*/ */
public class CalculateHashConstant implements Runnable { public class CalculateHashConstant implements Runnable {
...@@ -227,6 +227,30 @@ public class CalculateHashConstant implements Runnable { ...@@ -227,6 +227,30 @@ public class CalculateHashConstant implements Runnable {
System.out.println("Collisions: " + collisions); System.out.println("Collisions: " + collisions);
} }
/**
* Calculate the multiplicative inverse of a value (int).
*
* @param a the value
* @return the multiplicative inverse
*/
static long calcMultiplicativeInverse(long a) {
return BigInteger.valueOf(a).modPow(
BigInteger.valueOf((1 << 31) - 1), BigInteger.valueOf(1L << 32)).longValue();
}
/**
* Calculate the multiplicative inverse of a value (long).
*
* @param a the value
* @return the multiplicative inverse
*/
static long calcMultiplicativeInverseLong(long a) {
BigInteger oneShift64 = BigInteger.valueOf(1).shiftLeft(64);
BigInteger oneShift63 = BigInteger.valueOf(1).shiftLeft(63);
return BigInteger.valueOf(a).modPow(
oneShift63.subtract(BigInteger.ONE),
oneShift64).longValue();
}
/** /**
* Store a random file to be analyzed by the Diehard test. * Store a random file to be analyzed by the Diehard test.
*/ */
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论