[#7705] Add support for PostgreSQL FOR NO KEY UPDATE and FOR KEY SHARE
This commit is contained in:
parent
245a4f19c9
commit
5c77c2c031
@ -435,6 +435,8 @@ offsetFetch =
|
||||
|
||||
forUpdate =
|
||||
'FOR SHARE'
|
||||
| 'FOR KEY SHARE'
|
||||
| 'FOR NO KEY UPDATE'
|
||||
| 'FOR UPDATE' [ 'OF' fields ] [ 'NOWAIT' | 'WAIT' unsignedInteger | 'SKIP LOCKED' ]
|
||||
;
|
||||
|
||||
@ -806,7 +808,8 @@ castDataType =
|
||||
|
||||
dataType =
|
||||
(
|
||||
'BIGINT' [ 'UNSIGNED' ]
|
||||
'ARRAY'
|
||||
| 'BIGINT' [ 'UNSIGNED' ]
|
||||
| 'BINARY' [ '(' unsignedInteger ')' ]
|
||||
| 'BIT' [ '(' unsignedInteger ')' ]
|
||||
| 'BLOB' [ '(' unsignedInteger ')' ]
|
||||
@ -834,6 +837,7 @@ dataType =
|
||||
| 'NUMBER' [ '(' unsignedInteger [ ',' unsignedInteger ] ')' ]
|
||||
| 'NUMERIC' [ '(' unsignedInteger [ ',' unsignedInteger ] ')' ]
|
||||
| 'NVARCHAR' [ '(' unsignedInteger ')' ] [ 'COLLATE' collationName ]
|
||||
| 'OTHER'
|
||||
| 'REAL' [ '(' unsignedInteger [ ',' unsignedInteger ] ')' ]
|
||||
| 'SERIAL'
|
||||
| 'SERIAL4'
|
||||
|
||||
@ -133,6 +133,15 @@ public interface SelectForUpdateStep<R extends Record> extends SelectOptionStep<
|
||||
@Support({ CUBRID, DERBY, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES })
|
||||
SelectForUpdateOfStep<R> forUpdate();
|
||||
|
||||
/**
|
||||
* Add a <code>FOR NO KEY UPDATE</code> clause to the end of the query.
|
||||
*
|
||||
* @see SelectQuery#setForShare(boolean) see LockProvider for more
|
||||
* details
|
||||
*/
|
||||
@Support({ POSTGRES })
|
||||
SelectForUpdateOfStep<R> forNoKeyUpdate();
|
||||
|
||||
/**
|
||||
* Add a <code>FOR SHARE</code> clause to the end of the query.
|
||||
*
|
||||
@ -142,6 +151,15 @@ public interface SelectForUpdateStep<R extends Record> extends SelectOptionStep<
|
||||
@Support({ MARIADB, MYSQL, POSTGRES })
|
||||
SelectOptionStep<R> forShare();
|
||||
|
||||
/**
|
||||
* Add a <code>FOR KEY SHARE</code> clause to the end of the query.
|
||||
*
|
||||
* @see SelectQuery#setForShare(boolean) see LockProvider for more
|
||||
* details
|
||||
*/
|
||||
@Support({ POSTGRES })
|
||||
SelectForUpdateOfStep<R> forKeyShare();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -871,6 +871,12 @@ public interface SelectQuery<R extends Record> extends Select<R>, ConditionProvi
|
||||
@Support({ CUBRID, DERBY, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES })
|
||||
void setForUpdate(boolean forUpdate);
|
||||
|
||||
/**
|
||||
* Sets the "FOR NO KEY UPDATE" flag onto the query.
|
||||
*/
|
||||
@Support({ POSTGRES })
|
||||
void setForNoKeyUpdate(boolean forNoKeyUpdate);
|
||||
|
||||
/**
|
||||
* Some RDBMS allow for specifying the fields that should be locked by the
|
||||
* <code>FOR UPDATE</code> clause, instead of the full row.
|
||||
@ -1014,6 +1020,12 @@ public interface SelectQuery<R extends Record> extends Select<R>, ConditionProvi
|
||||
@Support({ MARIADB, MYSQL, POSTGRES })
|
||||
void setForShare(boolean forShare);
|
||||
|
||||
/**
|
||||
* Sets the "FOR KEY SHARE" flag onto the query.
|
||||
*/
|
||||
@Support({ POSTGRES })
|
||||
void setForKeyShare(boolean forKeyShare);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -891,27 +891,29 @@ final class ParserImpl implements Parser {
|
||||
}
|
||||
|
||||
if (parseKeywordIf(ctx, "FOR")) {
|
||||
if (parseKeywordIf(ctx, "SHARE")) {
|
||||
if (parseKeywordIf(ctx, "KEY SHARE"))
|
||||
result.setForKeyShare(true);
|
||||
else if (parseKeywordIf(ctx, "NO KEY UPDATE"))
|
||||
result.setForNoKeyUpdate(true);
|
||||
else if (parseKeywordIf(ctx, "SHARE"))
|
||||
result.setForShare(true);
|
||||
}
|
||||
else if (parseKeywordIf(ctx, "UPDATE")) {
|
||||
else if (parseKeywordIf(ctx, "UPDATE"))
|
||||
result.setForUpdate(true);
|
||||
|
||||
if (parseKeywordIf(ctx, "OF"))
|
||||
result.setForUpdateOf(parseFields(ctx));
|
||||
|
||||
if (parseKeywordIf(ctx, "NOWAIT"))
|
||||
result.setForUpdateNoWait();
|
||||
else if (parseKeywordIf(ctx, "WAIT") && ctx.requireProEdition())
|
||||
|
||||
|
||||
|
||||
;
|
||||
else if (parseKeywordIf(ctx, "SKIP LOCKED"))
|
||||
result.setForUpdateSkipLocked();
|
||||
}
|
||||
else
|
||||
throw ctx.expected("SHARE", "UPDATE");
|
||||
throw ctx.expected("UPDATE", "NO KEY UPDATE", "SHARE", "KEY SHARE");
|
||||
|
||||
if (parseKeywordIf(ctx, "OF"))
|
||||
result.setForUpdateOf(parseFields(ctx));
|
||||
|
||||
if (parseKeywordIf(ctx, "NOWAIT"))
|
||||
result.setForUpdateNoWait();
|
||||
else if (parseKeywordIf(ctx, "WAIT") && ctx.requireProEdition())
|
||||
|
||||
|
||||
|
||||
;
|
||||
else if (parseKeywordIf(ctx, "SKIP LOCKED"))
|
||||
result.setForUpdateSkipLocked();
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -8394,6 +8396,9 @@ final class ParserImpl implements Parser {
|
||||
"EXCEPT",
|
||||
"FETCH FIRST",
|
||||
"FETCH NEXT",
|
||||
"FOR KEY SHARE",
|
||||
"FOR NO KEY UPDATE",
|
||||
"FOR SHARE",
|
||||
"FOR UPDATE",
|
||||
"FROM",
|
||||
"GO", // The T-SQL statement batch delimiter, not a SELECT keyword
|
||||
@ -8421,6 +8426,8 @@ final class ParserImpl implements Parser {
|
||||
"EXCEPT",
|
||||
"FETCH FIRST",
|
||||
"FETCH NEXT",
|
||||
"FOR KEY SHARE",
|
||||
"FOR NO KEY UPDATE",
|
||||
"FOR SHARE",
|
||||
"FOR UPDATE",
|
||||
"FULL JOIN",
|
||||
|
||||
@ -1734,6 +1734,12 @@ final class SelectImpl<R extends Record, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final SelectImpl forNoKeyUpdate() {
|
||||
getQuery().setForNoKeyUpdate(true);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final SelectImpl of(Field<?>... fields) {
|
||||
getQuery().setForUpdateOf(fields);
|
||||
@ -1778,6 +1784,12 @@ final class SelectImpl<R extends Record, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final SelectImpl forKeyShare() {
|
||||
getQuery().setForKeyShare(true);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -103,8 +103,7 @@ import static org.jooq.impl.Keywords.K_BY;
|
||||
import static org.jooq.impl.Keywords.K_CONNECT_BY;
|
||||
import static org.jooq.impl.Keywords.K_DISTINCT;
|
||||
import static org.jooq.impl.Keywords.K_DISTINCT_ON;
|
||||
import static org.jooq.impl.Keywords.K_FOR_SHARE;
|
||||
import static org.jooq.impl.Keywords.K_FOR_UPDATE;
|
||||
import static org.jooq.impl.Keywords.K_FOR;
|
||||
import static org.jooq.impl.Keywords.K_FROM;
|
||||
import static org.jooq.impl.Keywords.K_GROUP_BY;
|
||||
import static org.jooq.impl.Keywords.K_HAVING;
|
||||
@ -226,12 +225,11 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
private String option;
|
||||
private boolean distinct;
|
||||
private QueryPartList<SelectFieldOrAsterisk> distinctOn;
|
||||
private boolean forUpdate;
|
||||
private QueryPartList<Field<?>> forUpdateOf;
|
||||
private TableList forUpdateOfTables;
|
||||
private ForUpdateMode forUpdateMode;
|
||||
private ForUpdateLockMode forUpdateLockMode;
|
||||
private ForUpdateWaitMode forUpdateWaitMode;
|
||||
private int forUpdateWait;
|
||||
private boolean forShare;
|
||||
|
||||
|
||||
|
||||
@ -653,9 +651,47 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
}
|
||||
|
||||
// [#1296] FOR UPDATE is emulated in some dialects using ResultSet.CONCUR_UPDATABLE
|
||||
if (forUpdate && !NO_SUPPORT_FOR_UPDATE.contains(family)) {
|
||||
context.formatSeparator()
|
||||
.visit(K_FOR_UPDATE);
|
||||
if (forUpdateLockMode != null && !NO_SUPPORT_FOR_UPDATE.contains(family)) {
|
||||
switch (forUpdateLockMode) {
|
||||
case UPDATE:
|
||||
context.formatSeparator()
|
||||
.visit(K_FOR)
|
||||
.sql(' ')
|
||||
.visit(forUpdateLockMode.toKeyword());
|
||||
break;
|
||||
|
||||
case SHARE:
|
||||
switch (family) {
|
||||
|
||||
// MySQL has a non-standard implementation for the "FOR SHARE" clause
|
||||
|
||||
|
||||
|
||||
|
||||
case MARIADB:
|
||||
case MYSQL:
|
||||
context.formatSeparator()
|
||||
.visit(K_LOCK_IN_SHARE_MODE);
|
||||
break;
|
||||
|
||||
// Postgres is known to implement the "FOR SHARE" clause like this
|
||||
default:
|
||||
context.visit(K_FOR)
|
||||
.sql(' ')
|
||||
.visit(forUpdateLockMode.toKeyword());
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case KEY_SHARE:
|
||||
case NO_KEY_UPDATE:
|
||||
default:
|
||||
context.visit(K_FOR)
|
||||
.sql(' ')
|
||||
.visit(forUpdateLockMode.toKeyword());
|
||||
break;
|
||||
}
|
||||
|
||||
if (Tools.isNotEmpty(forUpdateOf)) {
|
||||
|
||||
@ -701,41 +737,19 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
// [#3186] Firebird's FOR UPDATE clause has a different semantics. To achieve "regular"
|
||||
// FOR UPDATE semantics, we should use FOR UPDATE WITH LOCK
|
||||
if (family == FIREBIRD) {
|
||||
if (family == FIREBIRD)
|
||||
context.sql(' ').visit(K_WITH_LOCK);
|
||||
}
|
||||
|
||||
if (forUpdateMode != null) {
|
||||
if (forUpdateWaitMode != null) {
|
||||
context.sql(' ');
|
||||
context.visit(forUpdateMode.toKeyword());
|
||||
context.visit(forUpdateWaitMode.toKeyword());
|
||||
|
||||
if (forUpdateMode == ForUpdateMode.WAIT) {
|
||||
if (forUpdateWaitMode == ForUpdateWaitMode.WAIT) {
|
||||
context.sql(' ');
|
||||
context.sql(forUpdateWait);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (forShare) {
|
||||
switch (family) {
|
||||
|
||||
// MySQL has a non-standard implementation for the "FOR SHARE" clause
|
||||
|
||||
|
||||
|
||||
|
||||
case MARIADB:
|
||||
case MYSQL:
|
||||
context.formatSeparator()
|
||||
.visit(K_LOCK_IN_SHARE_MODE);
|
||||
break;
|
||||
|
||||
// Postgres is known to implement the "FOR SHARE" clause like this
|
||||
default:
|
||||
context.formatSeparator()
|
||||
.visit(K_FOR_SHARE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1857,8 +1871,17 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
@Override
|
||||
public final void setForUpdate(boolean forUpdate) {
|
||||
this.forUpdate = forUpdate;
|
||||
this.forShare = false;
|
||||
this.forUpdateLockMode = ForUpdateLockMode.UPDATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setForNoKeyUpdate(boolean forNoKeyUpdate) {
|
||||
this.forUpdateLockMode = ForUpdateLockMode.NO_KEY_UPDATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setForKeyShare(boolean forKeyShare) {
|
||||
this.forUpdateLockMode = ForUpdateLockMode.KEY_SHARE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1892,24 +1915,23 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
@Override
|
||||
public final void setForUpdateNoWait() {
|
||||
setForUpdate(true);
|
||||
forUpdateMode = ForUpdateMode.NOWAIT;
|
||||
forUpdateWaitMode = ForUpdateWaitMode.NOWAIT;
|
||||
forUpdateWait = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setForUpdateSkipLocked() {
|
||||
setForUpdate(true);
|
||||
forUpdateMode = ForUpdateMode.SKIP_LOCKED;
|
||||
forUpdateWaitMode = ForUpdateWaitMode.SKIP_LOCKED;
|
||||
forUpdateWait = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setForShare(boolean forShare) {
|
||||
this.forUpdate = false;
|
||||
this.forShare = forShare;
|
||||
this.forUpdateLockMode = ForUpdateLockMode.SHARE;
|
||||
this.forUpdateOf = null;
|
||||
this.forUpdateOfTables = null;
|
||||
this.forUpdateMode = null;
|
||||
this.forUpdateWaitMode = null;
|
||||
this.forUpdateWait = 0;
|
||||
}
|
||||
|
||||
@ -2246,7 +2268,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
@Override
|
||||
final boolean isForUpdate() {
|
||||
return forUpdate;
|
||||
return forUpdateLockMode == ForUpdateLockMode.UPDATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -2590,7 +2612,29 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
/**
|
||||
* The lock mode for the <code>FOR UPDATE</code> clause, if set.
|
||||
*/
|
||||
private static enum ForUpdateMode {
|
||||
private static enum ForUpdateLockMode {
|
||||
UPDATE("update"),
|
||||
NO_KEY_UPDATE("no key update"),
|
||||
SHARE("share"),
|
||||
KEY_SHARE("key share"),
|
||||
|
||||
;
|
||||
|
||||
private final Keyword keyword;
|
||||
|
||||
private ForUpdateLockMode(String sql) {
|
||||
this.keyword = DSL.keyword(sql);
|
||||
}
|
||||
|
||||
public final Keyword toKeyword() {
|
||||
return keyword;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The wait mode for the <code>FOR UPDATE</code> clause, if set.
|
||||
*/
|
||||
private static enum ForUpdateWaitMode {
|
||||
WAIT("wait"),
|
||||
NOWAIT("nowait"),
|
||||
SKIP_LOCKED("skip locked"),
|
||||
@ -2599,7 +2643,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
private final Keyword keyword;
|
||||
|
||||
private ForUpdateMode(String sql) {
|
||||
private ForUpdateWaitMode(String sql) {
|
||||
this.keyword = DSL.keyword(sql);
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user