Skip to content

Commit 578cb6d

Browse files
authored
Merge pull request #3831 from katzyn/for_update
Add Oracle-style NOWAIT, WAIT n, and SKIP LOCKED to FOR UPDATE clause
2 parents e9807ac + 484eec4 commit 578cb6d

23 files changed

+360
-73
lines changed

h2/src/docsrc/html/changelog.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ <h1>Change Log</h1>
2121

2222
<h2>Next Version (unreleased)</h2>
2323
<ul>
24-
<li>Issue #2671: ANY | SOME with array
24+
<li>PR #3831: Add Oracle-style NOWAIT, WAIT n, and SKIP LOCKED to FOR UPDATE clause
25+
</li>
26+
<li>RP #3830: Fix time zone of time/timestamp with time zone AT LOCAL
27+
</li>
28+
<li>PR #3822 / Issue #2671: Add quantified comparison predicates with array
2529
</li>
2630
<li>PR #3820: Add partial implementation of JSON simplified accessor
2731
</li>

h2/src/main/org/h2/command/Command.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,8 @@ public ResultInterface executeQuery(long maxrows, boolean scrollable) {
195195
}
196196
return result;
197197
} catch (DbException e) {
198-
// cannot retry DDL
199-
if (isCurrentCommandADefineCommand()) {
198+
// cannot retry some commands
199+
if (!isRetryable()) {
200200
throw e;
201201
}
202202
start = filterConcurrentUpdate(e, start);
@@ -251,8 +251,8 @@ public ResultWithGeneratedKeys executeUpdate(Object generatedKeysRequest) {
251251
try {
252252
return update(generatedKeysRequest);
253253
} catch (DbException e) {
254-
// cannot retry DDL
255-
if (isCurrentCommandADefineCommand()) {
254+
// cannot retry some commands
255+
if (!isRetryable()) {
256256
throw e;
257257
}
258258
start = filterConcurrentUpdate(e, start);
@@ -373,11 +373,11 @@ public void setCanReuse(boolean canReuse) {
373373
public abstract Set<DbObject> getDependencies();
374374

375375
/**
376-
* Is the command we just tried to execute a DefineCommand (i.e. DDL).
376+
* Returns is this command can be repeated again on locking failure.
377377
*
378-
* @return true if yes
378+
* @return is this command can be repeated again on locking failure
379379
*/
380-
protected abstract boolean isCurrentCommandADefineCommand();
380+
protected abstract boolean isRetryable();
381381

382382
protected final Database getDatabase() {
383383
return session.getDatabase();

h2/src/main/org/h2/command/CommandContainer.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import java.util.Set;
1212
import org.h2.api.DatabaseEventListener;
1313
import org.h2.api.ErrorCode;
14-
import org.h2.command.ddl.DefineCommand;
1514
import org.h2.command.dml.DataChangeStatement;
1615
import org.h2.engine.Database;
1716
import org.h2.engine.DbObject;
@@ -308,7 +307,8 @@ public Set<DbObject> getDependencies() {
308307
}
309308

310309
@Override
311-
protected boolean isCurrentCommandADefineCommand() {
312-
return prepared instanceof DefineCommand;
310+
protected boolean isRetryable() {
311+
return prepared.isRetryable();
313312
}
313+
314314
}

h2/src/main/org/h2/command/CommandList.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,16 @@ public Set<DbObject> getDependencies() {
120120
}
121121

122122
@Override
123-
protected boolean isCurrentCommandADefineCommand() {
124-
return command.isCurrentCommandADefineCommand();
123+
protected boolean isRetryable() {
124+
if (!command.isRetryable()) {
125+
return false;
126+
}
127+
for (Prepared prepared : commands) {
128+
if (!prepared.isRetryable()) {
129+
return false;
130+
}
131+
}
132+
return remainingCommand == null || remainingCommand.isRetryable();
125133
}
134+
126135
}

h2/src/main/org/h2/command/Parser.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
import static org.h2.util.ParserUtil.YEAR;
133133
import static org.h2.util.ParserUtil._ROWID_;
134134

135+
import java.math.BigDecimal;
135136
import java.nio.charset.Charset;
136137
import java.text.Collator;
137138
import java.util.ArrayList;
@@ -227,6 +228,7 @@
227228
import org.h2.command.dml.SetTypes;
228229
import org.h2.command.dml.TransactionCommand;
229230
import org.h2.command.dml.Update;
231+
import org.h2.command.query.ForUpdate;
230232
import org.h2.command.query.Query;
231233
import org.h2.command.query.QueryOrderBy;
232234
import org.h2.command.query.Select;
@@ -2773,10 +2775,25 @@ private void parseEndOfQuery(Query command) {
27732775
do {
27742776
readIdentifierWithSchema();
27752777
} while (readIf(COMMA));
2776-
} else if (readIf("NOWAIT")) {
2777-
// TODO parser: select for update nowait: should not wait
27782778
}
2779-
command.setForUpdate(true);
2779+
ForUpdate forUpdate;
2780+
if (readIf("NOWAIT")) {
2781+
forUpdate = ForUpdate.NOWAIT;
2782+
} else if (readIf("WAIT")) {
2783+
BigDecimal timeout;
2784+
if (currentTokenType != LITERAL || (timeout = token.value(session).getBigDecimal()) == null
2785+
|| timeout.signum() < 0
2786+
|| timeout.compareTo(BigDecimal.valueOf(Integer.MAX_VALUE, 3)) > 0) {
2787+
throw DbException.getSyntaxError(sqlCommand, token.start(), "timeout (0..2147483.647)");
2788+
}
2789+
read();
2790+
forUpdate = ForUpdate.wait(timeout.movePointRight(3).intValue());
2791+
} else if (readIf("SKIP", "LOCKED")) {
2792+
forUpdate = ForUpdate.SKIP_LOCKED;
2793+
} else {
2794+
forUpdate = ForUpdate.DEFAULT;
2795+
}
2796+
command.setForUpdate(forUpdate);
27802797
} else if (readIf("READ") || readIf(FETCH)) {
27812798
read("ONLY");
27822799
}

h2/src/main/org/h2/command/Prepared.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,4 +495,14 @@ public void collectDependencies(HashSet<DbObject> dependencies) {}
495495
protected final Database getDatabase() {
496496
return session.getDatabase();
497497
}
498+
499+
/**
500+
* Returns is this command can be repeated again on locking failure.
501+
*
502+
* @return is this command can be repeated again on locking failure
503+
*/
504+
public boolean isRetryable() {
505+
return true;
506+
}
507+
498508
}

h2/src/main/org/h2/command/ddl/DefineCommand.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,9 @@ public boolean isTransactional() {
4949
return transactional;
5050
}
5151

52+
@Override
53+
public boolean isRetryable() {
54+
return false;
55+
}
56+
5257
}

h2/src/main/org/h2/command/dml/Delete.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public long update(ResultTarget deltaChangeCollector, ResultOption deltaChangeCo
5858
while (nextRow(limitRows, count)) {
5959
Row row = targetTableFilter.get();
6060
if (table.isRowLockable()) {
61-
Row lockedRow = table.lockRow(session, row);
61+
Row lockedRow = table.lockRow(session, row, -1);
6262
if (lockedRow == null) {
6363
continue;
6464
}

h2/src/main/org/h2/command/dml/MergeUsing.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public long update(ResultTarget deltaChangeCollector, ResultOption deltaChangeCo
103103
if (!nullRow) {
104104
Row targetRow = targetTableFilter.get();
105105
if (table.isRowLockable()) {
106-
Row lockedRow = table.lockRow(session, targetRow);
106+
Row lockedRow = table.lockRow(session, targetRow, -1);
107107
if (lockedRow == null) {
108108
if (previousSource != source) {
109109
missedSource = source;

h2/src/main/org/h2/command/dml/Update.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public long update(ResultTarget deltaChangeCollector, ResultOption deltaChangeCo
6767
while (nextRow(limitRows, count)) {
6868
Row oldRow = targetTableFilter.get();
6969
if (table.isRowLockable()) {
70-
Row lockedRow = table.lockRow(session, oldRow);
70+
Row lockedRow = table.lockRow(session, oldRow, -1);
7171
if (lockedRow == null) {
7272
continue;
7373
}

0 commit comments

Comments
 (0)