From 6164cedbd13867a0a572ea4c847f54d23a41ed6a Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Fri, 21 Sep 2018 14:49:12 +0200 Subject: [PATCH] [#7839] Emulate { UPDATE | DELETE } ORDER BY and LIMIT --- .../main/java/org/jooq/DeleteLimitStep.java | 12 +-- .../main/java/org/jooq/DeleteOrderByStep.java | 16 ++-- jOOQ/src/main/java/org/jooq/DeleteQuery.java | 11 +-- .../main/java/org/jooq/UpdateLimitStep.java | 12 +-- .../main/java/org/jooq/UpdateOrderByStep.java | 16 ++-- jOOQ/src/main/java/org/jooq/UpdateQuery.java | 11 +-- .../java/org/jooq/impl/DeleteQueryImpl.java | 78 +++++++++++++++---- .../java/org/jooq/impl/UpdateQueryImpl.java | 63 +++++++++++---- 8 files changed, 138 insertions(+), 81 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/DeleteLimitStep.java b/jOOQ/src/main/java/org/jooq/DeleteLimitStep.java index b564480a7b..469495b794 100644 --- a/jOOQ/src/main/java/org/jooq/DeleteLimitStep.java +++ b/jOOQ/src/main/java/org/jooq/DeleteLimitStep.java @@ -37,10 +37,6 @@ */ package org.jooq; -// ... -import static org.jooq.SQLDialect.MARIADB; -import static org.jooq.SQLDialect.MYSQL; - /** * This type is used for the {@link Delete}'s DSL API. *

@@ -75,14 +71,14 @@ import static org.jooq.SQLDialect.MYSQL; public interface DeleteLimitStep extends DeleteReturningStep { /** - * Add a LIMIT clause to the query + * Add a LIMIT clause to the query. */ - @Support({ MARIADB, MYSQL }) + @Support DeleteReturningStep limit(Number numberOfRows); /** - * Add a LIMIT clause to the query using named parameters + * Add a LIMIT clause to the query using named parameters. */ - @Support({ MARIADB, MYSQL }) + @Support DeleteReturningStep limit(Param numberOfRows); } \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/DeleteOrderByStep.java b/jOOQ/src/main/java/org/jooq/DeleteOrderByStep.java index 550f14888d..8afc95fd55 100644 --- a/jOOQ/src/main/java/org/jooq/DeleteOrderByStep.java +++ b/jOOQ/src/main/java/org/jooq/DeleteOrderByStep.java @@ -37,10 +37,6 @@ */ package org.jooq; -// ... -import static org.jooq.SQLDialect.MARIADB; -import static org.jooq.SQLDialect.MYSQL; - import java.util.Collection; /** @@ -77,19 +73,19 @@ import java.util.Collection; public interface DeleteOrderByStep extends DeleteLimitStep { /** - * Add an ORDER BY clause to the query + * Add an ORDER BY clause to the query. */ - @Support({ MARIADB, MYSQL }) + @Support DeleteLimitStep orderBy(OrderField... fields); /** - * Add an ORDER BY clause to the query + * Add an ORDER BY clause to the query. */ - @Support({ MARIADB, MYSQL }) + @Support DeleteLimitStep orderBy(Collection> fields); /** - * Add an ORDER BY clause to the query + * Add an ORDER BY clause to the query. *

* Indexes start at 1 in SQL! *

@@ -97,6 +93,6 @@ public interface DeleteOrderByStep extends DeleteLimitStep * orderBy(DSL.literal(1).desc()) to apply descending * ordering */ - @Support({ MARIADB, MYSQL }) + @Support DeleteLimitStep orderBy(int... fieldIndexes); } \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/DeleteQuery.java b/jOOQ/src/main/java/org/jooq/DeleteQuery.java index b4d51b3a57..53482c7020 100644 --- a/jOOQ/src/main/java/org/jooq/DeleteQuery.java +++ b/jOOQ/src/main/java/org/jooq/DeleteQuery.java @@ -38,12 +38,9 @@ package org.jooq; -// ... // ... // ... import static org.jooq.SQLDialect.FIREBIRD; -import static org.jooq.SQLDialect.MARIADB; -import static org.jooq.SQLDialect.MYSQL; // ... import static org.jooq.SQLDialect.POSTGRES; @@ -109,7 +106,7 @@ public interface DeleteQuery extends ConditionProvider, Delete * * @param fields The ordering fields */ - @Support({ MARIADB, MYSQL }) + @Support void addOrderBy(OrderField... fields); /** @@ -117,7 +114,7 @@ public interface DeleteQuery extends ConditionProvider, Delete * * @param fields The ordering fields */ - @Support({ MARIADB, MYSQL }) + @Support void addOrderBy(Collection> fields); /** @@ -125,7 +122,7 @@ public interface DeleteQuery extends ConditionProvider, Delete * * @param numberOfRows The number of rows to return */ - @Support({ MARIADB, MYSQL }) + @Support void addLimit(Number numberOfRows); /** @@ -133,7 +130,7 @@ public interface DeleteQuery extends ConditionProvider, Delete * * @param numberOfRows The number of rows to return */ - @Support({ MARIADB, MYSQL }) + @Support void addLimit(Param numberOfRows); // ------------------------------------------------------------------------ diff --git a/jOOQ/src/main/java/org/jooq/UpdateLimitStep.java b/jOOQ/src/main/java/org/jooq/UpdateLimitStep.java index 629e0a2f49..871a9652b8 100644 --- a/jOOQ/src/main/java/org/jooq/UpdateLimitStep.java +++ b/jOOQ/src/main/java/org/jooq/UpdateLimitStep.java @@ -37,10 +37,6 @@ */ package org.jooq; -// ... -import static org.jooq.SQLDialect.MARIADB; -import static org.jooq.SQLDialect.MYSQL; - /** * This type is used for the {@link Update}'s DSL API. *

@@ -77,15 +73,15 @@ import static org.jooq.SQLDialect.MYSQL; public interface UpdateLimitStep extends UpdateReturningStep { /** - * Add a LIMIT clause to the query + * Add a LIMIT clause to the query. */ - @Support({ MARIADB, MYSQL }) + @Support UpdateReturningStep limit(Number numberOfRows); /** - * Add a LIMIT clause to the query using named parameters + * Add a LIMIT clause to the query using named parameters. */ - @Support({ MARIADB, MYSQL }) + @Support UpdateReturningStep limit(Param numberOfRows); diff --git a/jOOQ/src/main/java/org/jooq/UpdateOrderByStep.java b/jOOQ/src/main/java/org/jooq/UpdateOrderByStep.java index 5d80207df3..5e31a593ae 100644 --- a/jOOQ/src/main/java/org/jooq/UpdateOrderByStep.java +++ b/jOOQ/src/main/java/org/jooq/UpdateOrderByStep.java @@ -37,10 +37,6 @@ */ package org.jooq; -// ... -import static org.jooq.SQLDialect.MARIADB; -import static org.jooq.SQLDialect.MYSQL; - import java.util.Collection; /** @@ -79,19 +75,19 @@ import java.util.Collection; public interface UpdateOrderByStep extends UpdateLimitStep { /** - * Add an ORDER BY clause to the query + * Add an ORDER BY clause to the query. */ - @Support({ MARIADB, MYSQL }) + @Support UpdateLimitStep orderBy(OrderField... fields); /** - * Add an ORDER BY clause to the query + * Add an ORDER BY clause to the query. */ - @Support({ MARIADB, MYSQL }) + @Support UpdateLimitStep orderBy(Collection> fields); /** - * Add an ORDER BY clause to the query + * Add an ORDER BY clause to the query. *

* Indexes start at 1 in SQL! *

@@ -99,7 +95,7 @@ public interface UpdateOrderByStep extends UpdateLimitStep * orderBy(DSL.literal(1).desc()) to apply descending * ordering */ - @Support({ MARIADB, MYSQL }) + @Support UpdateLimitStep orderBy(int... fieldIndexes); } diff --git a/jOOQ/src/main/java/org/jooq/UpdateQuery.java b/jOOQ/src/main/java/org/jooq/UpdateQuery.java index 4be025942a..d78a9a3119 100644 --- a/jOOQ/src/main/java/org/jooq/UpdateQuery.java +++ b/jOOQ/src/main/java/org/jooq/UpdateQuery.java @@ -38,7 +38,6 @@ package org.jooq; -// ... // ... // ... import static org.jooq.SQLDialect.FIREBIRD; @@ -46,8 +45,6 @@ import static org.jooq.SQLDialect.H2; // ... import static org.jooq.SQLDialect.HSQLDB; // ... -import static org.jooq.SQLDialect.MARIADB; -import static org.jooq.SQLDialect.MYSQL; // ... import static org.jooq.SQLDialect.POSTGRES; // ... @@ -419,7 +416,7 @@ public interface UpdateQuery extends StoreQuery, ConditionP * * @param fields The ordering fields */ - @Support({ MARIADB, MYSQL }) + @Support void addOrderBy(OrderField... fields); /** @@ -427,7 +424,7 @@ public interface UpdateQuery extends StoreQuery, ConditionP * * @param fields The ordering fields */ - @Support({ MARIADB, MYSQL }) + @Support void addOrderBy(Collection> fields); /** @@ -435,7 +432,7 @@ public interface UpdateQuery extends StoreQuery, ConditionP * * @param numberOfRows The number of rows to return */ - @Support({ MARIADB, MYSQL }) + @Support void addLimit(Number numberOfRows); /** @@ -443,7 +440,7 @@ public interface UpdateQuery extends StoreQuery, ConditionP * * @param numberOfRows The number of rows to return */ - @Support({ MARIADB, MYSQL }) + @Support void addLimit(Param numberOfRows); // ------------------------------------------------------------------------ diff --git a/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java index 10d73e961e..7e5cbcd17f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java @@ -43,9 +43,32 @@ import static org.jooq.Clause.DELETE_DELETE; import static org.jooq.Clause.DELETE_RETURNING; import static org.jooq.Clause.DELETE_WHERE; // ... +// ... +// ... +// ... +import static org.jooq.SQLDialect.CUBRID; +// ... +import static org.jooq.SQLDialect.DERBY; +import static org.jooq.SQLDialect.FIREBIRD; +import static org.jooq.SQLDialect.H2; +// ... +import static org.jooq.SQLDialect.HSQLDB; +// ... +// ... import static org.jooq.SQLDialect.MARIADB; import static org.jooq.SQLDialect.MYSQL; +// ... +import static org.jooq.SQLDialect.POSTGRES; +// ... +// ... +import static org.jooq.SQLDialect.SQLITE; +// ... +// ... +// ... +// ... import static org.jooq.conf.SettingsTools.getExecuteDeleteWithoutWhere; +import static org.jooq.impl.DSL.row; +import static org.jooq.impl.DSL.select; import static org.jooq.impl.Keywords.K_DELETE; import static org.jooq.impl.Keywords.K_FROM; import static org.jooq.impl.Keywords.K_LIMIT; @@ -67,6 +90,8 @@ import org.jooq.Param; import org.jooq.Record; import org.jooq.SQLDialect; import org.jooq.Table; +import org.jooq.TableField; +import org.jooq.UniqueKey; /** * @author Lukas Eder @@ -76,10 +101,11 @@ final class DeleteQueryImpl extends AbstractDMLQuery implem private static final long serialVersionUID = -1943687511774150929L; private static final Clause[] CLAUSES = { DELETE }; private static final EnumSet SPECIAL_DELETE_AS_SYNTAX = EnumSet.of(MARIADB, MYSQL); + private static final EnumSet NO_SUPPORT_LIMIT = EnumSet.of(CUBRID, DERBY, FIREBIRD, H2, HSQLDB, POSTGRES, SQLITE); private final ConditionProviderImpl condition; private final SortFieldList orderBy; - private Param limit; + private Param limit; DeleteQueryImpl(Configuration configuration, WithImpl with, Table table) { super(configuration, with, table); @@ -167,25 +193,45 @@ final class DeleteQueryImpl extends AbstractDMLQuery implem .declareTables(true) .visit(table) .declareTables(declare) - .end(DELETE_DELETE) - .start(DELETE_WHERE); + .end(DELETE_DELETE); - if (hasWhere()) - ctx.formatSeparator() - .visit(K_WHERE).sql(' ') - .visit(getWhere()); + if (limit != null && NO_SUPPORT_LIMIT.contains(ctx.family()) && !table.getKeys().isEmpty()) { + UniqueKey key = table.getPrimaryKey() != null ? table.getPrimaryKey() : table.getKeys().get(0); - ctx.end(DELETE_WHERE); + @SuppressWarnings("unchecked") + TableField[] keyFields = (TableField[]) key.getFieldsArray(); - if (!orderBy.isEmpty()) - ctx.formatSeparator() - .visit(K_ORDER_BY).sql(' ') - .visit(orderBy); + ctx.start(DELETE_WHERE) + .formatSeparator() + .visit(K_WHERE).sql(' '); - if (limit != null) - ctx.formatSeparator() - .visit(K_LIMIT).sql(' ') - .visit(limit); + if (keyFields.length == 1) + ctx.visit(keyFields[0].in(select(keyFields[0]).from(table).where(getWhere()).orderBy(orderBy).limit(limit))); + else + ctx.visit(row(keyFields).in(select(keyFields).from(table).where(getWhere()).orderBy(orderBy).limit(limit))); + + ctx.end(DELETE_WHERE); + } + else { + ctx.start(DELETE_WHERE); + + if (hasWhere()) + ctx.formatSeparator() + .visit(K_WHERE).sql(' ') + .visit(getWhere()); + + ctx.end(DELETE_WHERE); + + if (!orderBy.isEmpty()) + ctx.formatSeparator() + .visit(K_ORDER_BY).sql(' ') + .visit(orderBy); + + if (limit != null) + ctx.formatSeparator() + .visit(K_LIMIT).sql(' ') + .visit(limit); + } ctx.start(DELETE_RETURNING); toSQLReturning(ctx); diff --git a/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java index 7831099176..af3c525cbd 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java @@ -47,17 +47,28 @@ import static org.jooq.Clause.UPDATE_UPDATE; import static org.jooq.Clause.UPDATE_WHERE; // ... // ... +// ... +import static org.jooq.SQLDialect.CUBRID; +// ... +import static org.jooq.SQLDialect.DERBY; +import static org.jooq.SQLDialect.FIREBIRD; import static org.jooq.SQLDialect.H2; // ... import static org.jooq.SQLDialect.HSQLDB; // ... // ... +// ... import static org.jooq.SQLDialect.POSTGRES; import static org.jooq.SQLDialect.POSTGRES_10; // ... // ... +import static org.jooq.SQLDialect.SQLITE; +// ... +// ... +// ... // ... import static org.jooq.conf.SettingsTools.getExecuteUpdateWithoutWhere; +import static org.jooq.impl.DSL.row; import static org.jooq.impl.DSL.select; import static org.jooq.impl.Keywords.K_FROM; import static org.jooq.impl.Keywords.K_LIMIT; @@ -130,7 +141,9 @@ import org.jooq.RowN; import org.jooq.SQLDialect; import org.jooq.Select; import org.jooq.Table; +import org.jooq.TableField; import org.jooq.TableLike; +import org.jooq.UniqueKey; import org.jooq.UpdateQuery; /** @@ -144,6 +157,7 @@ final class UpdateQueryImpl extends AbstractStoreQuery impl private static final EnumSet SUPPORT_RVE_SET = EnumSet.of(H2, HSQLDB, POSTGRES); + private static final EnumSet NO_SUPPORT_LIMIT = EnumSet.of(CUBRID, DERBY, FIREBIRD, H2, HSQLDB, POSTGRES, SQLITE); private final FieldMapForUpdate updateMap; private final TableList from; @@ -152,7 +166,7 @@ final class UpdateQueryImpl extends AbstractStoreQuery impl private Row multiValue; private Select multiSelect; private final SortFieldList orderBy; - private Param limit; + private Param limit; UpdateQueryImpl(Configuration configuration, WithImpl with, Table table) { super(configuration, with, table); @@ -629,24 +643,43 @@ final class UpdateQueryImpl extends AbstractStoreQuery impl break; } - ctx.start(UPDATE_WHERE); + if (limit != null && NO_SUPPORT_LIMIT.contains(ctx.family()) && !table.getKeys().isEmpty()) { + UniqueKey key = table.getPrimaryKey() != null ? table.getPrimaryKey() : table.getKeys().get(0); - if (hasWhere()) - ctx.formatSeparator() - .visit(K_WHERE).sql(' ') - .visit(getWhere()); + @SuppressWarnings("unchecked") + TableField[] keyFields = (TableField[]) key.getFieldsArray(); - ctx.end(UPDATE_WHERE); + ctx.start(UPDATE_WHERE) + .formatSeparator() + .visit(K_WHERE).sql(' '); - if (!orderBy.isEmpty()) - ctx.formatSeparator() - .visit(K_ORDER_BY).sql(' ') - .visit(orderBy); + if (keyFields.length == 1) + ctx.visit(keyFields[0].in(select(keyFields[0]).from(table).where(getWhere()).orderBy(orderBy).limit(limit))); + else + ctx.visit(row(keyFields).in(select(keyFields).from(table).where(getWhere()).orderBy(orderBy).limit(limit))); - if (limit != null) - ctx.formatSeparator() - .visit(K_LIMIT).sql(' ') - .visit(limit); + ctx.end(UPDATE_WHERE); + } + else { + ctx.start(UPDATE_WHERE); + + if (hasWhere()) + ctx.formatSeparator() + .visit(K_WHERE).sql(' ') + .visit(getWhere()); + + ctx.end(UPDATE_WHERE); + + if (!orderBy.isEmpty()) + ctx.formatSeparator() + .visit(K_ORDER_BY).sql(' ') + .visit(orderBy); + + if (limit != null) + ctx.formatSeparator() + .visit(K_LIMIT).sql(' ') + .visit(limit); + } ctx.start(UPDATE_RETURNING); toSQLReturning(ctx);