Skip to content

Commit

Permalink
Merge pull request #3975 from andreitokar/issue-3960
Browse files Browse the repository at this point in the history
Fix NPE on page rewrite
  • Loading branch information
andreitokar committed Jan 22, 2024
2 parents 7e780f6 + 1b2a2da commit 1bb86bf
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 51 deletions.
2 changes: 1 addition & 1 deletion h2/src/main/org/h2/fulltext/FullTextLucene.java
Expand Up @@ -446,7 +446,7 @@ protected static ResultSet search(Connection conn, String text,
&& i + offset < totalHits
&& i + offset < len; i++) {
ScoreDoc sd = docs.scoreDocs[i + offset];
Document doc = searcher.doc(sd.doc);
Document doc = searcher.getIndexReader().storedFields().document(sd.doc);
float score = sd.score;
String q = doc.get(LUCENE_FIELD_QUERY);
if (data) {
Expand Down
8 changes: 8 additions & 0 deletions h2/src/main/org/h2/jmx/DocumentedMBean.java
Expand Up @@ -23,6 +23,14 @@ public class DocumentedMBean extends StandardMBean {
private final String interfaceName;
private Properties resources;

/**
* Constructor
* @param impl bean implementation
* @param mbeanInterface bean interface class
* @param <T> bean type
* @throws NotCompliantMBeanException if the mbeanInterface does not follow JMX design patterns
* for Management Interfaces, or if the given implementation does not implement the specified interface.
*/
public <T> DocumentedMBean(T impl, Class<T> mbeanInterface)
throws NotCompliantMBeanException {
super(impl, mbeanInterface);
Expand Down
43 changes: 23 additions & 20 deletions h2/src/main/org/h2/mvstore/FileStore.java
Expand Up @@ -1903,28 +1903,31 @@ private int rewriteChunks(Set<Integer> set, boolean secondPass) {
int rewrittenPageCount = 0;
for (int chunkId : set) {
C chunk = chunks.get(chunkId);
long[] toc = getToC(chunk);
if (toc != null) {
for (int pageNo = 0; (pageNo = chunk.occupancy.nextClearBit(pageNo)) < chunk.pageCount; ++pageNo) {
long tocElement = toc[pageNo];
int mapId = DataUtils.getPageMapId(tocElement);
MVMap<String, String> metaMap = mvStore.getMetaMap();
MVMap<?, ?> map = mapId == layout.getId() ? layout
: mapId == metaMap.getId() ? metaMap : mvStore.getMap(mapId);
if (map != null && !map.isClosed()) {
assert !map.isSingleWriter();
if (secondPass || DataUtils.isLeafPosition(tocElement)) {
long pagePos = DataUtils.composePagePos(chunkId, tocElement);
serializationLock.unlock();
try {
if (map.rewritePage(pagePos)) {
++rewrittenPageCount;
if (mapId == metaMap.getId()) {
mvStore.markMetaChanged();
// there is a chance for a chunk to be dropped after set of chunks to be rewritten has been determined
if (chunk != null) {
long[] toc = getToC(chunk);
if (toc != null) {
for (int pageNo = 0; (pageNo = chunk.occupancy.nextClearBit(pageNo)) < chunk.pageCount; ++pageNo) {
long tocElement = toc[pageNo];
int mapId = DataUtils.getPageMapId(tocElement);
MVMap<String, String> metaMap = mvStore.getMetaMap();
MVMap<?, ?> map = mapId == layout.getId() ? layout
: mapId == metaMap.getId() ? metaMap : mvStore.getMap(mapId);
if (map != null && !map.isClosed()) {
assert !map.isSingleWriter();
if (secondPass || DataUtils.isLeafPosition(tocElement)) {
long pagePos = DataUtils.composePagePos(chunkId, tocElement);
serializationLock.unlock();
try {
if (map.rewritePage(pagePos)) {
++rewrittenPageCount;
if (mapId == metaMap.getId()) {
mvStore.markMetaChanged();
}
}
} finally {
serializationLock.lock();
}
} finally {
serializationLock.lock();
}
}
}
Expand Down
5 changes: 0 additions & 5 deletions h2/src/main/org/h2/store/FileStoreInputStream.java
Expand Up @@ -140,11 +140,6 @@ public void close() {
}
}

@Override
protected void finalize() {
close();
}

@Override
public int read() throws IOException {
fillBuffer();
Expand Down
3 changes: 3 additions & 0 deletions h2/src/main/org/h2/tools/MultiDimension.java
Expand Up @@ -21,6 +21,9 @@ public class MultiDimension implements Comparator<long[]> {

private static final MultiDimension INSTANCE = new MultiDimension();

/**
* Protected constructor
*/
protected MultiDimension() {
// don't allow construction by normal code
// but allow tests
Expand Down
3 changes: 3 additions & 0 deletions h2/src/main/org/h2/tools/Server.java
Expand Up @@ -32,6 +32,9 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
private boolean fromCommandLine;
private boolean started;

/**
* Generic constructor
*/
public Server() {
// nothing to do
this.service = null;
Expand Down
9 changes: 7 additions & 2 deletions h2/src/main/org/h2/value/ValueRow.java
Expand Up @@ -164,10 +164,10 @@ public ValueRow cloneWithOrder(int[] newOrder) {
}

ExtTypeInfoRow typeInfoRow = (ExtTypeInfoRow) type.getExtTypeInfo();
Object[] fields = typeInfoRow.getFields().toArray();
Map.Entry<String, TypeInfo>[] fields = typeInfoRow.getFields().toArray(createEntriesArray(length));
LinkedHashMap<String, TypeInfo> newFields = new LinkedHashMap<>(length);
for (int i = 0; i < length; i++) {
Map.Entry<String, TypeInfo> field = (Map.Entry<String, TypeInfo>) fields[newOrder[i]];
Map.Entry<String, TypeInfo> field = fields[newOrder[i]];
newFields.put(field.getKey(), field.getValue());
}
ExtTypeInfoRow newTypeInfoRow = new ExtTypeInfoRow(newFields);
Expand All @@ -177,6 +177,11 @@ public ValueRow cloneWithOrder(int[] newOrder) {
return new ValueRow(newType, newValues);
}

@SuppressWarnings("unchecked")
private static <K,V> Map.Entry<K,V>[] createEntriesArray(int length) {
return (Map.Entry<K,V>[])new Map.Entry[length];
}

@Override
public boolean equals(Object other) {
if (!(other instanceof ValueRow)) {
Expand Down
34 changes: 11 additions & 23 deletions h2/src/test/org/h2/test/TestBase.java
Expand Up @@ -1201,11 +1201,13 @@ private static String[] getData(ResultSet rs, int len) throws SQLException {
}

private static String formatRow(String[] row) {
String sb = "";
StringBuilder sb = new StringBuilder();
sb.append("{");
for (String r : row) {
sb += "{" + r + "}";
sb.append("{").append(r).append("}");
}
return "{" + sb + "}";
sb.append("}");
return sb.toString();
}

/**
Expand Down Expand Up @@ -1460,14 +1462,12 @@ protected <T> T assertThrows(final Class<?> expectedExceptionClass,
" for " + formatMethodCall(m, args));
}
if (!expectedExceptionClass.isAssignableFrom(t.getClass())) {
AssertionError ae = new AssertionError("Expected an exception of type\n" +
throw new AssertionError("Expected an exception of type\n" +
expectedExceptionClass.getSimpleName() +
" to be thrown, but the method under test threw an exception of type\n" +
t.getClass().getSimpleName() +
" (see in the 'Caused by' for the exception that was thrown) for " +
formatMethodCall(m, args));
ae.initCause(t);
throw ae;
formatMethodCall(m, args), t);
}
return false;
}, obj);
Expand Down Expand Up @@ -1514,18 +1514,10 @@ protected <T> T assertThrows(int expectedErrorCode, T obj) {
protected <T> T assertThrows(final ResultVerifier verifier, final T obj) {
Class<?> c = obj.getClass();
InvocationHandler ih = new InvocationHandler() {
private Exception called = new Exception("No method called");
@Override
protected void finalize() {
if (called != null) {
called.printStackTrace(System.err);
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Exception {
try {
called = null;
Object ret = method.invoke(obj, args);
verifier.verify(ret, null, method, args);
return ret;
Expand Down Expand Up @@ -1643,11 +1635,9 @@ protected void assertThrows(int expectedErrorCode, VoidCallable c) {

private static void checkException(Class<?> expectedExceptionClass, Throwable t) throws AssertionError {
if (!expectedExceptionClass.isAssignableFrom(t.getClass())) {
AssertionError ae = new AssertionError("Expected an exception of type\n"
throw new AssertionError("Expected an exception of type\n"
+ expectedExceptionClass.getSimpleName() + " to be thrown, but an exception of type\n"
+ t.getClass().getSimpleName() + " was thrown");
ae.initCause(t);
throw ae;
+ t.getClass().getSimpleName() + " was thrown", t);
}
}

Expand All @@ -1669,11 +1659,9 @@ public static void checkErrorCode(int expectedErrorCode, Throwable t) throws Ass
errorCode = 0;
}
if (errorCode != expectedErrorCode) {
AssertionError ae = new AssertionError("Expected an SQLException or DbException with error code "
throw new AssertionError("Expected an SQLException or DbException with error code "
+ expectedErrorCode + ", but got a "
+ (t == null ? "null" : t.getClass().getName() + " exception " + " with error code " + errorCode));
ae.initCause(t);
throw ae;
+ (t == null ? "null" : t.getClass().getName() + " exception " + " with error code " + errorCode), t);
}
}

Expand Down

0 comments on commit 1bb86bf

Please sign in to comment.