Unverified 提交 b03f027c authored 作者: Andrei Tokar's avatar Andrei Tokar 提交者: GitHub

Merge pull request #1217 from h2database/exception-handling

Postpone session.endStatement() until after commit.
...@@ -151,20 +151,15 @@ public abstract class Command implements CommandInterface { ...@@ -151,20 +151,15 @@ public abstract class Command implements CommandInterface {
@Override @Override
public void stop() { public void stop() {
session.endStatement();
session.setCurrentCommand(null, false); session.setCurrentCommand(null, false);
if (!isTransactional()) { if (!isTransactional()) {
session.commit(true); session.commit(true);
} else if (session.getAutoCommit()) { } else if (session.getAutoCommit()) {
session.commit(false); session.commit(false);
} else if (session.getDatabase().isMultiThreaded()) { } else {
Database db = session.getDatabase();
if (db != null) {
if (db.getLockMode() == Constants.LOCK_MODE_READ_COMMITTED) {
session.unlockReadLocks(); session.unlockReadLocks();
} }
} session.endStatement();
}
if (trace.isInfoEnabled() && startTimeNanos > 0) { if (trace.isInfoEnabled() && startTimeNanos > 0) {
long timeMillis = (System.nanoTime() - startTimeNanos) / 1000 / 1000; long timeMillis = (System.nanoTime() - startTimeNanos) / 1000 / 1000;
if (timeMillis > Constants.SLOW_QUERY_LIMIT_MS) { if (timeMillis > Constants.SLOW_QUERY_LIMIT_MS) {
...@@ -260,6 +255,7 @@ public abstract class Command implements CommandInterface { ...@@ -260,6 +255,7 @@ public abstract class Command implements CommandInterface {
Session.Savepoint rollback = session.setSavepoint(); Session.Savepoint rollback = session.setSavepoint();
session.startStatementWithinTransaction(); session.startStatementWithinTransaction();
session.setCurrentCommand(this, generatedKeysRequest); session.setCurrentCommand(this, generatedKeysRequest);
DbException ex = null;
try { try {
while (true) { while (true) {
database.checkPowerOff(); database.checkPowerOff();
...@@ -289,18 +285,29 @@ public abstract class Command implements CommandInterface { ...@@ -289,18 +285,29 @@ public abstract class Command implements CommandInterface {
database.shutdownImmediately(); database.shutdownImmediately();
throw e; throw e;
} }
try {
database.checkPowerOff(); database.checkPowerOff();
if (s.getErrorCode() == ErrorCode.DEADLOCK_1) { if (s.getErrorCode() == ErrorCode.DEADLOCK_1) {
session.rollback(); session.rollback();
} else { } else {
session.rollbackTo(rollback, false); session.rollbackTo(rollback, false);
} }
} catch (Throwable nested) {
e.addSuppressed(nested);
}
ex = e;
throw e; throw e;
} finally { } finally {
try { try {
if (callStop) { if (callStop) {
stop(); stop();
} }
} catch (Throwable nested) {
if (ex == null) {
throw nested;
} else {
ex.addSuppressed(nested);
}
} finally { } finally {
if (writing) { if (writing) {
database.afterWriting(); database.afterWriting();
......
...@@ -943,19 +943,14 @@ public class Session extends SessionWithState implements TransactionStore.Rollba ...@@ -943,19 +943,14 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
* READ_COMMITTED. * READ_COMMITTED.
*/ */
public void unlockReadLocks() { public void unlockReadLocks() {
if (database.isMVStore()) { if (!database.isMVStore() && database.isMultiThreaded() &&
// MVCC: keep shared locks (insert / update / delete) database.getLockMode() == Constants.LOCK_MODE_READ_COMMITTED) {
return; for (Iterator<Table> iter = locks.iterator(); iter.hasNext(); ) {
} Table t = iter.next();
// locks is modified in the loop
for (int i = 0; i < locks.size(); i++) {
Table t = locks.get(i);
if (!t.isLockedExclusively()) { if (!t.isLockedExclusively()) {
synchronized (database) {
t.unlock(this); t.unlock(this);
locks.remove(i); iter.remove();
} }
i--;
} }
} }
} }
......
...@@ -33,6 +33,10 @@ public class DbException extends RuntimeException { ...@@ -33,6 +33,10 @@ public class DbException extends RuntimeException {
private static final Properties MESSAGES = new Properties(); private static final Properties MESSAGES = new Properties();
public static final SQLException SQL_OOME =
new SQLException("OutOfMemoryError", "HY000", ErrorCode.OUT_OF_MEMORY, new OutOfMemoryError());
private static final DbException OOME = new DbException(SQL_OOME);
private Object source; private Object source;
static { static {
...@@ -288,6 +292,7 @@ public class DbException extends RuntimeException { ...@@ -288,6 +292,7 @@ public class DbException extends RuntimeException {
* @return the exception object * @return the exception object
*/ */
public static DbException convert(Throwable e) { public static DbException convert(Throwable e) {
try {
if (e instanceof DbException) { if (e instanceof DbException) {
return (DbException) e; return (DbException) e;
} else if (e instanceof SQLException) { } else if (e instanceof SQLException) {
...@@ -304,6 +309,15 @@ public class DbException extends RuntimeException { ...@@ -304,6 +309,15 @@ public class DbException extends RuntimeException {
throw (Error) e; throw (Error) e;
} }
return get(ErrorCode.GENERAL_ERROR_1, e, e.toString()); return get(ErrorCode.GENERAL_ERROR_1, e, e.toString());
} catch (Throwable ex) {
try {
DbException dbException = new DbException(new SQLException("GeneralError", "HY000", ErrorCode.GENERAL_ERROR_1, e));
dbException.addSuppressed(ex);
return dbException;
} catch (OutOfMemoryError ignore) {
return OOME;
}
}
} }
/** /**
......
...@@ -10,6 +10,7 @@ import java.sql.SQLException; ...@@ -10,6 +10,7 @@ import java.sql.SQLException;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicIntegerArray; import java.util.concurrent.atomic.AtomicIntegerArray;
import org.h2.api.ErrorCode;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
/** /**
...@@ -367,7 +368,11 @@ public class TraceObject { ...@@ -367,7 +368,11 @@ public class TraceObject {
} }
} catch(Throwable ignore) { } catch(Throwable ignore) {
if (e == null) { if (e == null) {
e = new SQLException("", "HY000", ex); try {
e = new SQLException("GeneralError", "HY000", ErrorCode.GENERAL_ERROR_1, ex);
} catch (OutOfMemoryError ignored) {
return DbException.SQL_OOME;
}
} }
e.addSuppressed(ignore); e.addSuppressed(ignore);
} }
......
...@@ -58,7 +58,7 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -58,7 +58,7 @@ public class MVPrimaryIndex extends BaseIndex {
Transaction t = mvTable.getTransactionBegin(); Transaction t = mvTable.getTransactionBegin();
dataMap = t.openMap(mapName, keyType, valueType); dataMap = t.openMap(mapName, keyType, valueType);
t.commit(); t.commit();
if (!table.isPersistData()) { if (!table.isPersistData() || !indexType.isPersistent()) {
dataMap.map.setVolatile(true); dataMap.map.setVolatile(true);
} }
Value k = dataMap.map.lastKey(); // include uncommitted keys as well Value k = dataMap.map.lastKey(); // include uncommitted keys as well
...@@ -113,7 +113,8 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -113,7 +113,8 @@ public class MVPrimaryIndex extends BaseIndex {
} }
TransactionMap<Value, Value> map = getMap(session); TransactionMap<Value, Value> map = getMap(session);
Value key = ValueLong.get(row.getKey()); long rowKey = row.getKey();
Value key = ValueLong.get(rowKey);
try { try {
Value oldValue = map.putIfAbsent(key, ValueArray.get(row.getValueList())); Value oldValue = map.putIfAbsent(key, ValueArray.get(row.getValueList()));
if (oldValue != null) { if (oldValue != null) {
...@@ -135,8 +136,9 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -135,8 +136,9 @@ public class MVPrimaryIndex extends BaseIndex {
} }
// because it's possible to directly update the key using the _rowid_ // because it's possible to directly update the key using the _rowid_
// syntax // syntax
if (row.getKey() > lastKey.get()) { long last;
lastKey.set(row.getKey()); while (rowKey > (last = lastKey.get())) {
if(lastKey.compareAndSet(last, rowKey)) break;
} }
} }
...@@ -223,33 +225,27 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -223,33 +225,27 @@ public class MVPrimaryIndex extends BaseIndex {
@Override @Override
public Cursor find(Session session, SearchRow first, SearchRow last) { public Cursor find(Session session, SearchRow first, SearchRow last) {
ValueLong min, max; ValueLong min = extractPKFromRow(first, ValueLong.MIN);
if (first == null) { ValueLong max = extractPKFromRow(last, ValueLong.MAX);
min = ValueLong.MIN; TransactionMap<Value, Value> map = getMap(session);
} else if (mainIndexColumn < 0) { return new MVStoreCursor(session, map.entryIterator(min, max));
min = ValueLong.get(first.getKey());
} else {
ValueLong v = (ValueLong) first.getValue(mainIndexColumn);
if (v == null) {
min = ValueLong.get(first.getKey());
} else {
min = v;
}
} }
if (last == null) {
max = ValueLong.MAX; private ValueLong extractPKFromRow(SearchRow row, ValueLong defaultValue) {
} else if (mainIndexColumn < 0) { ValueLong result;
max = ValueLong.get(last.getKey()); if (row == null) {
result = defaultValue;
} else if (mainIndexColumn == SearchRow.ROWID_INDEX) {
result = ValueLong.get(row.getKey());
} else { } else {
ValueLong v = (ValueLong) last.getValue(mainIndexColumn); ValueLong v = (ValueLong) row.getValue(mainIndexColumn);
if (v == null) { if (v == null) {
max = ValueLong.get(last.getKey()); result = ValueLong.get(row.getKey());
} else { } else {
max = v; result = v;
} }
} }
TransactionMap<Value, Value> map = getMap(session); return result;
return new MVStoreCursor(session, map.entryIterator(min, max));
} }
@Override @Override
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
*/ */
package org.h2.mvstore.db; package org.h2.mvstore.db;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
...@@ -421,9 +420,8 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex { ...@@ -421,9 +420,8 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
} }
key = first ? map.higherKey(key) : map.lowerKey(key); key = first ? map.higherKey(key) : map.lowerKey(key);
} }
ArrayList<Value> list = new ArrayList<>(1); MVStoreCursor cursor = new MVStoreCursor(session,
list.add(key); Collections.singletonList(key).iterator(), null);
MVStoreCursor cursor = new MVStoreCursor(session, list.iterator(), null);
cursor.next(); cursor.next();
return cursor; return cursor;
} }
...@@ -544,7 +542,6 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex { ...@@ -544,7 +542,6 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
public boolean previous() { public boolean previous() {
throw DbException.getUnsupportedException("previous"); throw DbException.getUnsupportedException("previous");
} }
} }
} }
...@@ -427,8 +427,9 @@ public class MVTable extends TableBase { ...@@ -427,8 +427,9 @@ public class MVTable extends TableBase {
@Override @Override
public void unlock(Session s) { public void unlock(Session s) {
if (database != null) { if (database != null) {
traceLock(s, lockExclusiveSession == s, TraceLockEvent.TRACE_LOCK_UNLOCK, NO_EXTRA_INFO); boolean wasLocked = lockExclusiveSession == s;
if (lockExclusiveSession == s) { traceLock(s, wasLocked, TraceLockEvent.TRACE_LOCK_UNLOCK, NO_EXTRA_INFO);
if (wasLocked) {
lockSharedSessions.remove(s); lockSharedSessions.remove(s);
lockExclusiveSession = null; lockExclusiveSession = null;
if (SysProperties.THREAD_DEADLOCK_DETECTOR) { if (SysProperties.THREAD_DEADLOCK_DETECTOR) {
...@@ -436,18 +437,18 @@ public class MVTable extends TableBase { ...@@ -436,18 +437,18 @@ public class MVTable extends TableBase {
EXCLUSIVE_LOCKS.get().remove(getName()); EXCLUSIVE_LOCKS.get().remove(getName());
} }
} }
} } else {
synchronized (getLockSyncObject()) { wasLocked = lockSharedSessions.remove(s) != null;
if (!lockSharedSessions.isEmpty()) {
lockSharedSessions.remove(s);
if (SysProperties.THREAD_DEADLOCK_DETECTOR) { if (SysProperties.THREAD_DEADLOCK_DETECTOR) {
if (SHARED_LOCKS.get() != null) { if (SHARED_LOCKS.get() != null) {
SHARED_LOCKS.get().remove(getName()); SHARED_LOCKS.get().remove(getName());
} }
} }
} }
if (!waitingSessions.isEmpty()) { if (wasLocked && !waitingSessions.isEmpty()) {
getLockSyncObject().notifyAll(); Object lockSyncObject = getLockSyncObject();
synchronized (lockSyncObject) {
lockSyncObject.notifyAll();
} }
} }
} }
......
...@@ -63,15 +63,18 @@ public class TestMemoryUsage extends TestDb { ...@@ -63,15 +63,18 @@ public class TestMemoryUsage extends TestDb {
} }
deleteDb("memoryUsage"); deleteDb("memoryUsage");
conn = getConnection("memoryUsage"); conn = getConnection("memoryUsage");
try {
eatMemory(4000); eatMemory(4000);
for (int i = 0; i < 4000; i++) { for (int i = 0; i < 4000; i++) {
Connection c2 = getConnection("memoryUsage"); Connection c2 = getConnection("memoryUsage");
c2.createStatement(); c2.createStatement();
c2.close(); c2.close();
} }
} finally {
freeMemory(); freeMemory();
conn.close(); conn.close();
} }
}
private void testCreateDropLoop() throws SQLException { private void testCreateDropLoop() throws SQLException {
deleteDb("memoryUsageCreateDropLoop"); deleteDb("memoryUsageCreateDropLoop");
...@@ -141,8 +144,8 @@ public class TestMemoryUsage extends TestDb { ...@@ -141,8 +144,8 @@ public class TestMemoryUsage extends TestDb {
} }
} }
} finally { } finally {
conn.close();
freeMemory(); freeMemory();
conn.close();
} }
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论