diff --git a/jOOQ-manual/src/main/resources/org/jooq/web/grammar-3.12.txt b/jOOQ-manual/src/main/resources/org/jooq/web/grammar-3.12.txt index f9908db7bc..14788a6cb2 100644 --- a/jOOQ-manual/src/main/resources/org/jooq/web/grammar-3.12.txt +++ b/jOOQ-manual/src/main/resources/org/jooq/web/grammar-3.12.txt @@ -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' diff --git a/jOOQ/src/main/java/org/jooq/SelectForUpdateStep.java b/jOOQ/src/main/java/org/jooq/SelectForUpdateStep.java index 45d314ec8e..42834af014 100644 --- a/jOOQ/src/main/java/org/jooq/SelectForUpdateStep.java +++ b/jOOQ/src/main/java/org/jooq/SelectForUpdateStep.java @@ -133,6 +133,15 @@ public interface SelectForUpdateStep extends SelectOptionStep< @Support({ CUBRID, DERBY, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) SelectForUpdateOfStep forUpdate(); + /** + * Add a FOR NO KEY UPDATE clause to the end of the query. + * + * @see SelectQuery#setForShare(boolean) see LockProvider for more + * details + */ + @Support({ POSTGRES }) + SelectForUpdateOfStep forNoKeyUpdate(); + /** * Add a FOR SHARE clause to the end of the query. * @@ -142,6 +151,15 @@ public interface SelectForUpdateStep extends SelectOptionStep< @Support({ MARIADB, MYSQL, POSTGRES }) SelectOptionStep forShare(); + /** + * Add a FOR KEY SHARE clause to the end of the query. + * + * @see SelectQuery#setForShare(boolean) see LockProvider for more + * details + */ + @Support({ POSTGRES }) + SelectForUpdateOfStep forKeyShare(); + diff --git a/jOOQ/src/main/java/org/jooq/SelectQuery.java b/jOOQ/src/main/java/org/jooq/SelectQuery.java index 5d3d1c925f..60085e9e2c 100644 --- a/jOOQ/src/main/java/org/jooq/SelectQuery.java +++ b/jOOQ/src/main/java/org/jooq/SelectQuery.java @@ -871,6 +871,12 @@ public interface SelectQuery extends Select, 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 * FOR UPDATE clause, instead of the full row. @@ -1014,6 +1020,12 @@ public interface SelectQuery extends Select, ConditionProvi @Support({ MARIADB, MYSQL, POSTGRES }) void setForShare(boolean forShare); + /** + * Sets the "FOR KEY SHARE" flag onto the query. + */ + @Support({ POSTGRES }) + void setForKeyShare(boolean forKeyShare); + diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index 813896a720..dcd8cc4eb0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -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", diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectImpl.java index 75070a1f14..8cb503eec8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectImpl.java @@ -1734,6 +1734,12 @@ final class SelectImpl... fields) { getQuery().setForUpdateOf(fields); @@ -1778,6 +1784,12 @@ final class SelectImpl extends AbstractResultQuery imp private String option; private boolean distinct; private QueryPartList distinctOn; - private boolean forUpdate; private QueryPartList> 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 extends AbstractResultQuery 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 extends AbstractResultQuery 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 extends AbstractResultQuery 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 extends AbstractResultQuery 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 extends AbstractResultQuery imp @Override final boolean isForUpdate() { - return forUpdate; + return forUpdateLockMode == ForUpdateLockMode.UPDATE; } @Override @@ -2590,7 +2612,29 @@ final class SelectQueryImpl extends AbstractResultQuery imp /** * The lock mode for the FOR UPDATE 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 FOR UPDATE clause, if set. + */ + private static enum ForUpdateWaitMode { WAIT("wait"), NOWAIT("nowait"), SKIP_LOCKED("skip locked"), @@ -2599,7 +2643,7 @@ final class SelectQueryImpl extends AbstractResultQuery imp private final Keyword keyword; - private ForUpdateMode(String sql) { + private ForUpdateWaitMode(String sql) { this.keyword = DSL.keyword(sql); }