提交 a263a7d0 authored 作者: Thomas Mueller's avatar Thomas Mueller

More bugs in the server-less multi-connection mode have been fixed.

上级 b4ee1336
......@@ -18,7 +18,11 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>User defined functions: the source code is now available using
<ul><li>More bugs in the server-less multi-connection mode have been fixed.
</li><li>When running against an old database, the SCRIPT statement could generate a
SQL script that contained duplicate indexes (PRIMARY_KEY_E).
</li><li>JdbcConnectionPool.getConnection() could throw a NullPointerException.
</li><li>User defined functions: the source code is now available using
SELECT SOURCE FROM INFORMATION_SCHEMA.FUNCTION_ALIASES.
</li><li>User defined functions with source code didn't work after re-opening the database.
</li><li>The newsfeeds are now Atom 1.0 standard compliant.
......
......@@ -169,8 +169,9 @@ public class Database implements DataHandler {
private Properties reconnectLastLock;
private volatile long reconnectCheckNext;
private volatile boolean reconnectChangePending;
private volatile boolean checkpointAllowed;
private volatile int checkpointAllowed;
private volatile boolean checkpointRunning;
private final Object reconnectSync = new Object();
private int cacheSize;
private boolean compactFully;
private SourceCompiler compiler;
......@@ -467,7 +468,7 @@ public class Database implements DataHandler {
}
}
Engine.getInstance().close(databaseName);
throw Message.getSQLException(ErrorCode.SIMULATED_POWER_OFF);
throw Message.getSQLException(ErrorCode.DATABASE_IS_CLOSED);
}
/**
......@@ -719,6 +720,7 @@ public class Database implements DataHandler {
}
systemSession.commit(true);
traceSystem.getTrace(Trace.DATABASE).info("opened " + databaseName);
afterWriting();
}
public Schema getMainSchema() {
......@@ -1289,7 +1291,7 @@ public class Database implements DataHandler {
pageStore.compact(compactFully);
}
} catch (SQLException e) {
if (e.getErrorCode() != ErrorCode.SIMULATED_POWER_OFF){
if (e.getErrorCode() != ErrorCode.DATABASE_IS_CLOSED){
// e.printStackTrace();
// TODO don't ignore exceptions
}
......@@ -2388,24 +2390,27 @@ public class Database implements DataHandler {
if (fileLockMethod != FileLock.LOCK_SERIALIZED || readOnly || !reconnectChangePending || closing) {
return;
}
// to avoid race conditions, we already set it here
// (overlap with checkpointAllowed)
checkpointRunning = true;
if (!checkpointAllowed) {
checkpointRunning = false;
return;
}
long now = System.currentTimeMillis();
if (now > reconnectCheckNext + SysProperties.RECONNECT_CHECK_DELAY) {
if (SysProperties.CHECK && checkpointAllowed < 0) {
Message.throwInternalError();
}
synchronized (reconnectSync) {
if (checkpointAllowed > 0) {
return;
}
checkpointRunning = true;
}
synchronized (this) {
getTrace().debug("checkpoint start");
flushIndexes(0);
checkpoint();
reconnectModified(false);
checkpointRunning = false;
getTrace().debug("checkpoint end");
}
synchronized (reconnectSync) {
checkpointRunning = false;
}
}
}
......@@ -2462,10 +2467,9 @@ public class Database implements DataHandler {
* false if another connection was faster
*/
public boolean beforeWriting() {
if (fileLockMethod == FileLock.LOCK_SERIALIZED) {
// to avoid race conditions, we already set it here
// (overlap with checkpointRunning)
checkpointAllowed = false;
if (fileLockMethod != FileLock.LOCK_SERIALIZED) {
return true;
}
while (checkpointRunning) {
try {
Thread.sleep(10 + (int) (Math.random() * 10));
......@@ -2473,22 +2477,34 @@ public class Database implements DataHandler {
// ignore
}
}
boolean writingAllowed = reconnectModified(true);
if (!writingAllowed) {
// make sure the next call to isReconnectNeeded() returns yes
reconnectCheckNext = System.currentTimeMillis() - 1;
reconnectLastLock = null;
}
return writingAllowed;
synchronized (reconnectSync) {
if (reconnectModified(true)) {
checkpointAllowed++;
if (SysProperties.CHECK && checkpointAllowed > 20) {
throw Message.throwInternalError();
}
return true;
}
}
// make sure the next call to isReconnectNeeded() returns true
reconnectCheckNext = System.currentTimeMillis() - 1;
reconnectLastLock = null;
return false;
}
/**
* This method is called after updates are finished.
*/
public void afterWriting() {
checkpointAllowed = true;
if (fileLockMethod != FileLock.LOCK_SERIALIZED) {
return;
}
synchronized (reconnectSync) {
checkpointAllowed--;
}
if (SysProperties.CHECK && checkpointAllowed < 0) {
throw Message.throwInternalError();
}
}
/**
......
......@@ -28,7 +28,7 @@ import org.h2.log.UndoLogRecord;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.message.TraceSystem;
import org.h2.result.LocalResult;
import org.h2.result.ResultInterface;
import org.h2.result.Row;
import org.h2.schema.Schema;
import org.h2.store.DataHandler;
......@@ -93,7 +93,7 @@ public class Session extends SessionWithState {
private long sessionStart = System.currentTimeMillis();
private long currentCommandStart;
private HashMap<String, Value> variables;
private HashSet<LocalResult> temporaryResults;
private HashSet<ResultInterface> temporaryResults;
private int queryTimeout = SysProperties.getMaxQueryTimeout();
private int lastUncommittedDelete;
private boolean commitOrRollbackDisabled;
......@@ -1059,7 +1059,7 @@ public class Session extends SessionWithState {
*
* @param result the temporary result set
*/
public void addTemporaryResult(LocalResult result) {
public void addTemporaryResult(ResultInterface result) {
if (!result.needToClose()) {
return;
}
......@@ -1078,7 +1078,7 @@ public class Session extends SessionWithState {
*/
public void closeTemporaryResults() {
if (temporaryResults != null) {
for (LocalResult result : temporaryResults) {
for (ResultInterface result : temporaryResults) {
result.close();
}
temporaryResults = null;
......@@ -1129,6 +1129,10 @@ public class Session extends SessionWithState {
}
}
public void afterWriting() {
database.afterWriting();
}
public SessionInterface reconnect(boolean write) throws SQLException {
readSessionState();
close();
......
......@@ -89,4 +89,10 @@ public interface SessionInterface {
*/
SessionInterface reconnect(boolean write) throws SQLException;
/**
* Called after writing has ended. It needs to be called after
* isReconnectNeeded(true) returned false.
*/
void afterWriting();
}
......@@ -661,4 +661,8 @@ public class SessionRemote extends SessionWithState implements SessionFactory, D
return this;
}
public void afterWriting() {
// nothing to do
}
}
......@@ -306,9 +306,10 @@ public class JdbcConnection extends TraceObject implements Connection {
if (!session.isClosed()) {
try {
// roll back unless that would require to re-connect
// (the transaction can't be rolled back when re-connecting)
// (the transaction can't be rolled back after re-connecting)
if (!session.isReconnectNeeded(true)) {
rollbackInternal();
session.afterWriting();
}
commit = closeAndSetNull(commit);
rollback = closeAndSetNull(rollback);
......@@ -405,8 +406,12 @@ public class JdbcConnection extends TraceObject implements Connection {
try {
debugCodeCall("commit");
checkClosedForWrite();
try {
commit = prepareCommand("COMMIT", commit);
commit.executeUpdate();
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -423,7 +428,11 @@ public class JdbcConnection extends TraceObject implements Connection {
try {
debugCodeCall("rollback");
checkClosedForWrite();
try {
rollbackInternal();
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -914,7 +923,11 @@ public class JdbcConnection extends TraceObject implements Connection {
JdbcSavepoint sp = convertSavepoint(savepoint);
debugCode("rollback(" + sp.getTraceObjectName() + ");");
checkClosedForWrite();
try {
sp.rollback();
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -1338,6 +1351,10 @@ public class JdbcConnection extends TraceObject implements Connection {
}
}
protected void afterWriting() {
session.afterWriting();
}
String getURL() throws SQLException {
checkClosed();
return url;
......@@ -1408,8 +1425,12 @@ public class JdbcConnection extends TraceObject implements Connection {
int id = getNextId(TraceObject.CLOB);
debugCodeAssign("Clob", TraceObject.CLOB, id, "createClob()");
checkClosedForWrite();
try {
ValueLob v = ValueLob.createSmallLob(Value.CLOB, MemoryUtils.EMPTY_BYTES);
return new JdbcClob(this, v, id);
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -1425,8 +1446,12 @@ public class JdbcConnection extends TraceObject implements Connection {
int id = getNextId(TraceObject.BLOB);
debugCodeAssign("Blob", TraceObject.BLOB, id, "createClob()");
checkClosedForWrite();
try {
ValueLob v = ValueLob.createSmallLob(Value.BLOB, MemoryUtils.EMPTY_BYTES);
return new JdbcBlob(this, v, id);
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......
......@@ -125,7 +125,11 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
try {
debugCodeCall("executeUpdate");
checkClosedForWrite();
try {
return executeUpdateInternal();
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -160,6 +164,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
debugCodeCall("execute");
}
checkClosedForWrite();
try {
closeOldResultSet();
boolean returnsResultSet;
synchronized (conn.getSession()) {
......@@ -180,6 +185,9 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
}
}
return returnsResultSet;
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -700,6 +708,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
debugCode("setBlob("+parameterIndex+", x);");
}
checkClosedForWrite();
try {
Value v;
if (x == null) {
v = ValueNull.INSTANCE;
......@@ -707,6 +716,9 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
v = conn.createBlob(x.getBinaryStream(), -1);
}
setParameter(parameterIndex, v);
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -727,8 +739,12 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
debugCode("setBlob("+parameterIndex+", x);");
}
checkClosedForWrite();
try {
Value v = conn.createBlob(x, -1);
setParameter(parameterIndex, v);
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -747,6 +763,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
debugCode("setClob("+parameterIndex+", x);");
}
checkClosedForWrite();
try {
Value v;
if (x == null) {
v = ValueNull.INSTANCE;
......@@ -754,6 +771,9 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
v = conn.createClob(x.getCharacterStream(), -1);
}
setParameter(parameterIndex, v);
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -774,6 +794,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
debugCode("setClob("+parameterIndex+", x);");
}
checkClosedForWrite();
try {
Value v;
if (x == null) {
v = ValueNull.INSTANCE;
......@@ -781,6 +802,9 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
v = conn.createClob(x, -1);
}
setParameter(parameterIndex, v);
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -833,8 +857,12 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
debugCode("setBinaryStream("+parameterIndex+", x, "+length+"L);");
}
checkClosedForWrite();
try {
Value v = conn.createBlob(x, length);
setParameter(parameterIndex, v);
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -897,8 +925,12 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
debugCode("setAsciiStream("+parameterIndex+", x, "+length+"L);");
}
checkClosedForWrite();
try {
Value v = conn.createClob(IOUtils.getAsciiReader(x), length);
setParameter(parameterIndex, v);
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -960,8 +992,12 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
debugCode("setCharacterStream("+parameterIndex+", x, "+length+"L);");
}
checkClosedForWrite();
try {
Value v = conn.createClob(x, length);
setParameter(parameterIndex, v);
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -1045,7 +1081,6 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
public int[] executeBatch() throws SQLException {
try {
debugCodeCall("executeBatch");
checkClosedForWrite();
if (batchParameters == null) {
// TODO batch: check what other database do if no parameters are set
batchParameters = ObjectArray.newInstance();
......@@ -1053,6 +1088,8 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
int[] result = new int[batchParameters.size()];
boolean error = false;
SQLException next = null;
checkClosedForWrite();
try {
for (int i = 0; i < batchParameters.size(); i++) {
Value[] set = batchParameters.get(i);
ObjectArray< ? extends ParameterInterface> parameters = command.getParameters();
......@@ -1084,6 +1121,9 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
throw e;
}
return result;
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -1096,6 +1136,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
try {
debugCodeCall("addBatch");
checkClosedForWrite();
try {
ObjectArray< ? extends ParameterInterface> parameters = command.getParameters();
Value[] set = new Value[parameters.size()];
for (int i = 0; i < parameters.size(); i++) {
......@@ -1107,6 +1148,9 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
batchParameters = ObjectArray.newInstance();
}
batchParameters.add(set);
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -1276,8 +1320,12 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
debugCode("setNCharacterStream("+parameterIndex+", x, "+length+"L);");
}
checkClosedForWrite();
try {
Value v = conn.createClob(x, length);
setParameter(parameterIndex, v);
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -1338,8 +1386,12 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
debugCode("setNClob("+parameterIndex+", x);");
}
checkClosedForWrite();
try {
Value v = conn.createClob(x, -1);
setParameter(parameterIndex, v);
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -1360,8 +1412,12 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
debugCode("setClob("+parameterIndex+", x, "+length+"L);");
}
checkClosedForWrite();
try {
Value v = conn.createClob(x, length);
setParameter(parameterIndex, v);
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -1382,8 +1438,12 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
debugCode("setBlob("+parameterIndex+", x, "+length+"L);");
}
checkClosedForWrite();
try {
Value v = conn.createBlob(x, length);
setParameter(parameterIndex, v);
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -1404,8 +1464,12 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
debugCode("setNClob("+parameterIndex+", x, "+length+"L);");
}
checkClosedForWrite();
try {
Value v = conn.createClob(x, length);
setParameter(parameterIndex, v);
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......
......@@ -114,6 +114,7 @@ public class JdbcStatement extends TraceObject implements Statement {
private int executeUpdateInternal(String sql) throws SQLException {
checkClosedForWrite();
try {
closeOldResultSet();
sql = conn.translateSQL(sql, escapeProcessing);
CommandInterface command = conn.prepareCommand(sql, fetchSize);
......@@ -127,6 +128,9 @@ public class JdbcStatement extends TraceObject implements Statement {
}
command.close();
return updateCount;
} finally {
afterWriting();
}
}
/**
......@@ -153,6 +157,7 @@ public class JdbcStatement extends TraceObject implements Statement {
private boolean executeInternal(String sql) throws SQLException {
int id = getNextId(TraceObject.RESULT_SET);
checkClosedForWrite();
try {
closeOldResultSet();
sql = conn.translateSQL(sql, escapeProcessing);
CommandInterface command = conn.prepareCommand(sql, fetchSize);
......@@ -176,6 +181,9 @@ public class JdbcStatement extends TraceObject implements Statement {
}
command.close();
return returnsResultSet;
} finally {
afterWriting();
}
}
/**
......@@ -607,6 +615,7 @@ public class JdbcStatement extends TraceObject implements Statement {
try {
debugCodeCall("executeBatch");
checkClosedForWrite();
try {
if (batchCommands == null) {
// TODO batch: check what other database do if no commands are set
batchCommands = ObjectArray.newInstance();
......@@ -630,6 +639,9 @@ public class JdbcStatement extends TraceObject implements Statement {
throw new BatchUpdateException(result);
}
return result;
} finally {
afterWriting();
}
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -883,6 +895,15 @@ public class JdbcStatement extends TraceObject implements Statement {
return false;
}
/**
* Called after each write operation.
*/
void afterWriting() {
if (conn != null) {
conn.afterWriting();
}
}
/**
* INTERNAL.
* Close and old result set if there is still one open.
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论