Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/fix-endless-chunk-rewriting-when…
Browse files Browse the repository at this point in the history
…-db-is-idle' into fix-endless-chunk-rewriting-when-db-is-idle
  • Loading branch information
vreuland committed Feb 22, 2024
2 parents cab7c06 + 2e860ac commit adac0f9
Show file tree
Hide file tree
Showing 15 changed files with 546 additions and 257 deletions.
4 changes: 4 additions & 0 deletions h2/src/docsrc/html/changelog.html
Expand Up @@ -21,6 +21,10 @@ <h1>Change Log</h1>

<h2>Next Version (unreleased)</h2>
<ul>
<li>Issue #3106: Trailing commas in SELECT are accepted by the parser
</li>
<li>PR #3992: Add IPv6 support to H2 Console
</li>
<li>Issue #3966: Unable to insert null into a JSON column
</li>
<li>Issue #3554: Regression in 2.1.214 when joining 2 recursive CTE containing bind values
Expand Down
161 changes: 103 additions & 58 deletions h2/src/main/org/h2/command/Command.java
Expand Up @@ -6,6 +6,7 @@
package org.h2.command;

import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Set;
import org.h2.api.ErrorCode;
Expand All @@ -18,10 +19,13 @@
import org.h2.expression.ParameterInterface;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.result.BatchResult;
import org.h2.result.MergedResult;
import org.h2.result.ResultInterface;
import org.h2.result.ResultWithGeneratedKeys;
import org.h2.result.ResultWithPaddedStrings;
import org.h2.util.Utils;
import org.h2.value.Value;

/**
* Represents a SQL statement. This object is only used on the server side.
Expand Down Expand Up @@ -151,10 +155,10 @@ protected void checkCanceled() {
}

@Override
public void stop() {
public void stop(boolean commitIfAutoCommit) {
if (session.isOpen()) {
commitIfNonTransactional();
if (isTransactional() && session.getAutoCommit()) {
if (commitIfAutoCommit && isTransactional() && session.getAutoCommit()) {
session.commit(false);
}
}
Expand Down Expand Up @@ -228,7 +232,7 @@ public ResultInterface executeQuery(long maxrows, boolean scrollable) {
session.resetThreadLocalSession(oldSession);
session.endStatement();
if (callStop) {
stop();
stop(true);
}
}
} finally {
Expand All @@ -238,74 +242,115 @@ public ResultInterface executeQuery(long maxrows, boolean scrollable) {

@Override
public ResultWithGeneratedKeys executeUpdate(Object generatedKeysRequest) {
long start = 0;
boolean callStop = true;
session.lock();
try {
Database database = getDatabase();
session.waitIfExclusiveModeEnabled();
commitIfNonTransactional();
SessionLocal.Savepoint rollback = session.setSavepoint();
session.startStatementWithinTransaction(this);
DbException ex = null;
Session oldSession = session.setThreadLocalSession();
try {
while (true) {
database.checkPowerOff();
try {
return update(generatedKeysRequest);
} catch (DbException e) {
// cannot retry some commands
if (!isRetryable()) {
throw e;
return executeUpdate(generatedKeysRequest, true);
} finally {
session.unlock();
}
}

@Override
public BatchResult executeBatchUpdate(ArrayList<Value[]> batchParameters, Object generatedKeysRequest) {
session.lock();
try {
session.waitIfExclusiveModeEnabled();
int size = batchParameters.size();
long[] updateCounts = new long[size];
MergedResult generatedKeys = generatedKeysRequest != null ? new MergedResult() : null;
ArrayList<SQLException> exceptions = new ArrayList<>();
for (int i = 0; i < size; i++) {
Value[] set = batchParameters.get(i);
ArrayList<? extends ParameterInterface> parameters = getParameters();
for (int j = 0, l = set.length; j < l; j++) {
parameters.get(j).setValue(set[j], true);
}
long updateCount;
try {
ResultWithGeneratedKeys result = executeUpdate(generatedKeysRequest, i + 1 == size);
updateCount = result.getUpdateCount();
if (generatedKeys != null) {
ResultInterface keys = result.getGeneratedKeys();
if (keys != null) {
generatedKeys.add(keys);
}
start = filterConcurrentUpdate(e, start);
} catch (OutOfMemoryError e) {
callStop = false;
database.shutdownImmediately();
throw DbException.convert(e);
} catch (Throwable e) {
throw DbException.convert(e);
}
} catch (Exception e) {
exceptions.add(DbException.toSQLException(e));
updateCount = Statement.EXECUTE_FAILED;
}
} catch (DbException e) {
e = e.addSQL(sql);
SQLException s = e.getSQLException();
database.exceptionThrown(s, sql);
if (s.getErrorCode() == ErrorCode.OUT_OF_MEMORY) {
callStop = false;
database.shutdownImmediately();
throw e;
}
updateCounts[i] = updateCount;
}
return new BatchResult(updateCounts, generatedKeys != null ? generatedKeys.getResult() : null, exceptions);
} finally {
session.unlock();
}
}

private ResultWithGeneratedKeys executeUpdate(Object generatedKeysRequest, boolean commitIfAutoCommit) {
long start = 0;
boolean callStop = true;
Database database = getDatabase();
commitIfNonTransactional();
SessionLocal.Savepoint rollback = session.setSavepoint();
session.startStatementWithinTransaction(this);
DbException ex = null;
Session oldSession = session.setThreadLocalSession();
try {
while (true) {
database.checkPowerOff();
try {
database.checkPowerOff();
if (s.getErrorCode() == ErrorCode.DEADLOCK_1) {
session.rollback();
} else {
session.rollbackTo(rollback);
return update(generatedKeysRequest);
} catch (DbException e) {
// cannot retry some commands
if (!isRetryable()) {
throw e;
}
} catch (Throwable nested) {
e.addSuppressed(nested);
start = filterConcurrentUpdate(e, start);
} catch (OutOfMemoryError e) {
callStop = false;
database.shutdownImmediately();
throw DbException.convert(e);
} catch (Throwable e) {
throw DbException.convert(e);
}
ex = e;
}
} catch (DbException e) {
e = e.addSQL(sql);
SQLException s = e.getSQLException();
database.exceptionThrown(s, sql);
if (s.getErrorCode() == ErrorCode.OUT_OF_MEMORY) {
callStop = false;
database.shutdownImmediately();
throw e;
} finally {
session.resetThreadLocalSession(oldSession);
try {
session.endStatement();
if (callStop) {
stop();
}
} catch (Throwable nested) {
if (ex == null) {
throw nested;
} else {
ex.addSuppressed(nested);
}
}
try {
database.checkPowerOff();
if (s.getErrorCode() == ErrorCode.DEADLOCK_1) {
session.rollback();
} else {
session.rollbackTo(rollback);
}
} catch (Throwable nested) {
e.addSuppressed(nested);
}
ex = e;
throw e;
} finally {
session.unlock();
session.resetThreadLocalSession(oldSession);
try {
session.endStatement();
if (callStop) {
stop(commitIfAutoCommit);
}
} catch (Throwable nested) {
if (ex == null) {
throw nested;
} else {
ex.addSuppressed(nested);
}
}
}
}

Expand Down
25 changes: 23 additions & 2 deletions h2/src/main/org/h2/command/CommandInterface.java
Expand Up @@ -7,8 +7,10 @@

import java.util.ArrayList;
import org.h2.expression.ParameterInterface;
import org.h2.result.BatchResult;
import org.h2.result.ResultInterface;
import org.h2.result.ResultWithGeneratedKeys;
import org.h2.value.Value;

/**
* Represents a SQL statement.
Expand Down Expand Up @@ -600,10 +602,29 @@ public interface CommandInterface extends AutoCloseable {
*/
ResultWithGeneratedKeys executeUpdate(Object generatedKeysRequest);


/**
* Executes the statement with multiple sets of parameters.
*
* @param batchParameters
* batch parameters
* @param generatedKeysRequest
* {@code null} or {@code false} if generated keys are not needed,
* {@code true} if generated keys should be configured
* automatically, {@code int[]} to specify column indices to
* return generated keys from, or {@code String[]} to specify
* column names to return generated keys from
* @return result of batch execution
*/
BatchResult executeBatchUpdate(ArrayList<Value[]> batchParameters, Object generatedKeysRequest);

/**
* Stop the command execution, release all locks and resources
* Stop the command execution, release all locks and resources.
*
* @param commitIfAutoCommit
* commit the session if auto-commit is enabled
*/
void stop();
void stop(boolean commitIfAutoCommit);

/**
* Close the statement.
Expand Down
6 changes: 3 additions & 3 deletions h2/src/main/org/h2/command/CommandList.java
Expand Up @@ -75,10 +75,10 @@ public ResultInterface query(long maxrows) {
}

@Override
public void stop() {
command.stop();
public void stop(boolean commitIfAutoCommit) {
command.stop(commitIfAutoCommit);
if (remainingCommand != null) {
remainingCommand.stop();
remainingCommand.stop(commitIfAutoCommit);
}
}

Expand Down

0 comments on commit adac0f9

Please sign in to comment.