From 5bf3d9f57f31537fb10ba5a78bf92fb86bf16d71 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Fri, 7 Feb 2020 14:20:27 +0100 Subject: [PATCH] [jOOQ/jOOQ#4245] Add support for PostgreSQL's DELETE .. USING syntax --- jOOQ/src/main/java/org/jooq/DSLContext.java | 4 +- jOOQ/src/main/java/org/jooq/DeleteQuery.java | 18 +++++++ jOOQ/src/main/java/org/jooq/WithStep.java | 2 +- .../main/java/org/jooq/impl/CustomRecord.java | 4 +- jOOQ/src/main/java/org/jooq/impl/DSL.java | 6 +-- .../java/org/jooq/impl/DefaultDSLContext.java | 6 +-- .../main/java/org/jooq/impl/DeleteImpl.java | 50 ++++++++++++++++++- .../java/org/jooq/impl/DeleteQueryImpl.java | 32 +++++++++++- .../main/java/org/jooq/impl/ParserImpl.java | 18 ++++--- 9 files changed, 118 insertions(+), 22 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/DSLContext.java b/jOOQ/src/main/java/org/jooq/DSLContext.java index 4fc7a7dfc5..5590eef7ed 100644 --- a/jOOQ/src/main/java/org/jooq/DSLContext.java +++ b/jOOQ/src/main/java/org/jooq/DSLContext.java @@ -7998,7 +7998,7 @@ public interface DSLContext extends Scope , AutoCloseable { * Some but not all databases support aliased tables in delete statements. */ @Support - DeleteWhereStep deleteFrom(Table table); + DeleteUsingStep deleteFrom(Table table); /** * Create a new DSL delete statement. @@ -8006,7 +8006,7 @@ public interface DSLContext extends Scope , AutoCloseable { * This is an alias for {@link #deleteFrom(Table)} */ @Support - DeleteWhereStep delete(Table table); + DeleteUsingStep delete(Table table); // ------------------------------------------------------------------------- // XXX Batch query execution diff --git a/jOOQ/src/main/java/org/jooq/DeleteQuery.java b/jOOQ/src/main/java/org/jooq/DeleteQuery.java index fb57843766..5d3c21696a 100644 --- a/jOOQ/src/main/java/org/jooq/DeleteQuery.java +++ b/jOOQ/src/main/java/org/jooq/DeleteQuery.java @@ -62,6 +62,24 @@ import java.util.Collection; @SuppressWarnings("deprecation") public interface DeleteQuery extends ConditionProvider, Delete { + /** + * Add tables to the USING clause. + */ + @Support({ POSTGRES }) + void addUsing(TableLike table); + + /** + * Add tables to the USING clause. + */ + @Support({ POSTGRES }) + void addUsing(TableLike... tables); + + /** + * Add tables to the USING clause. + */ + @Support({ POSTGRES }) + void addUsing(Collection> tables); + // ------------------------------------------------------------------------ // Methods from ConditionProvider, OrderProvider, LockProvider // ------------------------------------------------------------------------ diff --git a/jOOQ/src/main/java/org/jooq/WithStep.java b/jOOQ/src/main/java/org/jooq/WithStep.java index 16ad61d73c..bc93a1ce7c 100644 --- a/jOOQ/src/main/java/org/jooq/WithStep.java +++ b/jOOQ/src/main/java/org/jooq/WithStep.java @@ -3179,5 +3179,5 @@ public interface WithStep extends QueryPart { * Some but not all databases support aliased tables in delete statements. */ @Support({ POSTGRES, SQLITE }) - DeleteWhereStep delete(Table table); + DeleteUsingStep delete(Table table); } diff --git a/jOOQ/src/main/java/org/jooq/impl/CustomRecord.java b/jOOQ/src/main/java/org/jooq/impl/CustomRecord.java index 75ddfc6fda..f30dfe7a0d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CustomRecord.java +++ b/jOOQ/src/main/java/org/jooq/impl/CustomRecord.java @@ -49,7 +49,9 @@ import org.jooq.TableRecord; *

* Client code may provide proper {@link TableRecord} implementations extending * this useful base class. All necessary parts of the {@link TableRecord} - * interface are already implemented. No methods need further implementation. + * interface are already implemented. No methods need further implementation, + * but implementations have to provide a no-arg constructor that can be called + * reflectively. *

* Use this base class when providing custom tables to any of the following * methods: diff --git a/jOOQ/src/main/java/org/jooq/impl/DSL.java b/jOOQ/src/main/java/org/jooq/impl/DSL.java index 6bca73a5f9..0879883843 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DSL.java +++ b/jOOQ/src/main/java/org/jooq/impl/DSL.java @@ -188,7 +188,7 @@ import org.jooq.DataType; import org.jooq.DatePart; // ... import org.jooq.Delete; -import org.jooq.DeleteWhereStep; +import org.jooq.DeleteUsingStep; import org.jooq.DerivedColumnList; import org.jooq.DropIndexOnStep; import org.jooq.DropSchemaStep; @@ -5775,7 +5775,7 @@ public class DSL { * @see DSLContext#deleteFrom(Table) */ @Support - public static DeleteWhereStep deleteFrom(Table table) { + public static DeleteUsingStep deleteFrom(Table table) { return dsl().deleteFrom(table); } @@ -5785,7 +5785,7 @@ public class DSL { * This is an alias for {@link #deleteFrom(Table)} */ @Support - public static DeleteWhereStep delete(Table table) { + public static DeleteUsingStep delete(Table table) { return dsl().deleteFrom(table); } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java index 647a1509fd..d4a58c53ca 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java @@ -127,7 +127,7 @@ import org.jooq.DDLFlag; import org.jooq.DSLContext; import org.jooq.DataType; import org.jooq.DeleteQuery; -import org.jooq.DeleteWhereStep; +import org.jooq.DeleteUsingStep; import org.jooq.DropIndexOnStep; import org.jooq.DropSchemaStep; import org.jooq.DropSequenceFinalStep; @@ -2686,12 +2686,12 @@ public class DefaultDSLContext extends AbstractScope implements DSLContext, Seri } @Override - public DeleteWhereStep delete(Table table) { + public DeleteUsingStep delete(Table table) { return deleteFrom(table); } @Override - public DeleteWhereStep deleteFrom(Table table) { + public DeleteUsingStep deleteFrom(Table table) { return new DeleteImpl<>(configuration(), null, table); } diff --git a/jOOQ/src/main/java/org/jooq/impl/DeleteImpl.java b/jOOQ/src/main/java/org/jooq/impl/DeleteImpl.java index 0ead7126c0..7fdb4fd3f2 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DeleteImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/DeleteImpl.java @@ -40,6 +40,7 @@ package org.jooq.impl; import static org.jooq.impl.DSL.condition; import static org.jooq.impl.DSL.exists; import static org.jooq.impl.DSL.notExists; +import static org.jooq.impl.DSL.table; import static org.jooq.impl.Tools.filterOne; import java.util.Collection; @@ -49,8 +50,9 @@ import org.jooq.Condition; import org.jooq.Configuration; import org.jooq.DeleteConditionStep; import org.jooq.DeleteResultStep; -import org.jooq.DeleteWhereStep; +import org.jooq.DeleteUsingStep; import org.jooq.Field; +import org.jooq.Name; import org.jooq.Operator; import org.jooq.OrderField; import org.jooq.Param; @@ -84,6 +86,7 @@ import org.jooq.Select; import org.jooq.SelectField; import org.jooq.SelectFieldOrAsterisk; import org.jooq.Table; +import org.jooq.TableLike; /** * @author Lukas Eder @@ -94,7 +97,7 @@ final class DeleteImpl implements // Cascading interface implementations for Delete behaviour - DeleteWhereStep, + DeleteUsingStep, DeleteConditionStep, DeleteResultStep { @@ -108,6 +111,49 @@ final class DeleteImpl super(new DeleteQueryImpl<>(configuration, with, table)); } + @Override + public final DeleteImpl using(TableLike table) { + getDelegate().addUsing(table); + return this; + } + + @Override + public final DeleteImpl using(TableLike... tables) { + getDelegate().addUsing(tables); + return this; + } + + @Override + public final DeleteImpl using(Collection> tables) { + getDelegate().addUsing(tables); + return this; + } + + @Override + public final DeleteImpl using(SQL sql) { + return using(table(sql)); + } + + @Override + public final DeleteImpl using(String sql) { + return using(table(sql)); + } + + @Override + public final DeleteImpl using(String sql, Object... bindings) { + return using(table(sql, bindings)); + } + + @Override + public final DeleteImpl using(String sql, QueryPart... parts) { + return using(table(sql, parts)); + } + + @Override + public final DeleteImpl using(Name name) { + return using(table(name)); + } + @Override public final DeleteImpl where(Condition conditions) { getDelegate().addConditions(conditions); diff --git a/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java index 194bf7919c..ecff4218e3 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java @@ -75,6 +75,7 @@ import static org.jooq.impl.Keywords.K_DELETE; import static org.jooq.impl.Keywords.K_FROM; import static org.jooq.impl.Keywords.K_LIMIT; import static org.jooq.impl.Keywords.K_ORDER_BY; +import static org.jooq.impl.Keywords.K_USING; import static org.jooq.impl.Keywords.K_WHERE; import java.util.Arrays; @@ -94,6 +95,7 @@ import org.jooq.Param; import org.jooq.Record; import org.jooq.SQLDialect; import org.jooq.Table; +import org.jooq.TableLike; /** * @author Lukas Eder @@ -105,6 +107,7 @@ final class DeleteQueryImpl extends AbstractDMLQuery implem private static final Set SPECIAL_DELETE_AS_SYNTAX = SQLDialect.supportedBy(MARIADB, MYSQL); private static final Set NO_SUPPORT_LIMIT = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, H2, HSQLDB, POSTGRES, SQLITE); + private final TableList using; private final ConditionProviderImpl condition; private final SortFieldList orderBy; private Param limit; @@ -112,6 +115,7 @@ final class DeleteQueryImpl extends AbstractDMLQuery implem DeleteQueryImpl(Configuration configuration, WithImpl with, Table table) { super(configuration, with, table); + this.using = new TableList(); this.condition = new ConditionProviderImpl(); this.orderBy = new SortFieldList(); } @@ -124,6 +128,23 @@ final class DeleteQueryImpl extends AbstractDMLQuery implem return condition.hasWhere(); } + @Override + public final void addUsing(Collection> f) { + for (TableLike provider : f) + using.add(provider.asTable()); + } + + @Override + public final void addUsing(TableLike f) { + using.add(f.asTable()); + } + + @Override + public final void addUsing(TableLike... f) { + for (TableLike provider : f) + using.add(provider.asTable()); + } + @Override public final void addConditions(Collection conditions) { condition.addConditions(conditions); @@ -194,8 +215,15 @@ final class DeleteQueryImpl extends AbstractDMLQuery implem ctx.visit(K_FROM).sql(' ') .declareTables(true) - .visit(table(ctx)) - .declareTables(declare) + .visit(table(ctx)); + + if (!using.isEmpty()) + ctx.formatSeparator() + .visit(K_USING) + .sql(' ') + .visit(using); + + ctx.declareTables(declare) .end(DELETE_DELETE); diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index 2780bca664..9112304e52 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -384,6 +384,7 @@ import org.jooq.Delete; import org.jooq.DeleteLimitStep; import org.jooq.DeleteOrderByStep; import org.jooq.DeleteReturningStep; +import org.jooq.DeleteUsingStep; import org.jooq.DeleteWhereStep; import org.jooq.DerivedColumnList; import org.jooq.DropIndexCascadeStep; @@ -1696,15 +1697,16 @@ final class ParserImpl implements Parser { table = table.as(parseIdentifierIf(ctx)); } - DeleteWhereStep s1 = with == null ? ctx.dsl.delete(table) : with.delete(table); - DeleteOrderByStep s2 = parseKeywordIf(ctx, "WHERE") ? s1.where(parseCondition(ctx)) : s1; - DeleteLimitStep s3 = parseKeywordIf(ctx, "ORDER BY") ? s2.orderBy(parseSortSpecification(ctx)) : s2; - DeleteReturningStep s4 = (limit != null || parseKeywordIf(ctx, "LIMIT")) - ? s3.limit(limit != null ? limit : parseParenthesisedUnsignedIntegerOrBindVariable(ctx)) - : s3; - Delete s5 = parseKeywordIf(ctx, "RETURNING") ? s4.returning(parseSelectList(ctx)) : s4; + DeleteUsingStep s1 = with == null ? ctx.dsl.delete(table) : with.delete(table); + DeleteWhereStep s2 = parseKeywordIf(ctx, "USING") ? s1.using(parseTables(ctx)) : s1; + DeleteOrderByStep s3 = parseKeywordIf(ctx, "WHERE") ? s2.where(parseCondition(ctx)) : s2; + DeleteLimitStep s4 = parseKeywordIf(ctx, "ORDER BY") ? s3.orderBy(parseSortSpecification(ctx)) : s3; + DeleteReturningStep s5 = (limit != null || parseKeywordIf(ctx, "LIMIT")) + ? s4.limit(limit != null ? limit : parseParenthesisedUnsignedIntegerOrBindVariable(ctx)) + : s4; + Delete s6 = parseKeywordIf(ctx, "RETURNING") ? s5.returning(parseSelectList(ctx)) : s5; - return s5; + return s6; } private static final Insert parseInsert(ParserContext ctx, WithImpl with) {