From cf00bd9d6d71ec71f8a0912fc077766858971f68 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Wed, 15 Oct 2014 19:02:04 +0200 Subject: [PATCH] [#3579] Fixed UNION implementation for databases that support LIMIT / OFFSET --- .../java/org/jooq/impl/SelectQueryImpl.java | 255 +++++++++++------- 1 file changed, 156 insertions(+), 99 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java index 2ee1b7f3be..df5cd2c852 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java @@ -176,13 +176,18 @@ class SelectQueryImpl extends AbstractResultQuery implement private final QueryPartList groupBy; private final ConditionProviderImpl having; private final WindowList window; - private final List unionOp; - private final List>> union; private final SortFieldList orderBy; private boolean orderBySiblings; private final QueryPartList> seek; private boolean seekBefore; private final Limit limit; + private final List unionOp; + private final List>> union; + private final SortFieldList unionOrderBy; + private boolean unionOrderBySiblings; // [#3579] TODO + private final QueryPartList> unionSeek; + private boolean unionSeekBefore; // [#3579] TODO + private final Limit unionLimit; SelectQueryImpl(WithImpl with, Configuration configuration) { this(with, configuration, null); @@ -210,11 +215,14 @@ class SelectQueryImpl extends AbstractResultQuery implement this.groupBy = new QueryPartList(); this.having = new ConditionProviderImpl(); this.window = new WindowList(); - this.unionOp = new ArrayList(); - this.union = new ArrayList>>(); this.orderBy = new SortFieldList(); this.seek = new QueryPartList>(); this.limit = new Limit(); + this.unionOp = new ArrayList(); + this.union = new ArrayList>>(); + this.unionOrderBy = new SortFieldList(); + this.unionSeek = new QueryPartList>(); + this.unionLimit = new Limit(); if (from != null) { this.from.add(from.asTable()); @@ -337,91 +345,91 @@ class SelectQueryImpl extends AbstractResultQuery implement .data(DATA_WRAP_DERIVED_TABLES_IN_PARENTHESES, null); } - // If a limit applies - if (getLimit().isApplicable()) { - switch (dialect) { + switch (dialect) { - /* [pro] xx - xx xxxxxx xxxxx xxx xxxxxx xxxxxxxxxxxxxx xxxx xxxxx xxxxxx xxxxxx - xxxx xxxxxxx - xxxx xxxxxxxxxx - xxxx xxxxxxxxxx - xxxx xxxxxxxxxx + /* [pro] xx + xx xxxxxx xxxxx xxx xxxxxx xxxxxxxxxxxxxx xxxx xxxxx xxxxxx xxxxxx + xxxx xxxxxxx + xxxx xxxxxxxxxx + xxxx xxxxxxxxxx + xxxx xxxxxxxxxx + xx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - xxxxxx + xxxx + xxxxxxxxxxxxxxxxxxxxxxxxx - xx xxxx xxxx xxxxx xxx xxx xxxxxxxxxxxxx - xxxx xxxx - xxxx xxxxxx - xxxx xxxxxxx x + xxxxxx - xx xxx xxxxxxxx xxxxxxxx x xxxxxx xxxxx xxxxxxx xxxxxxx - xx xxxxxx xxx xxxxxxx xxxx xxxxxx - xx xxxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - x + xx xxxx xxxx xxxxx xxx xxx xxxxxxxxxxxxx + xxxx xxxx + xxxx xxxxxx + xxxx xxxxxxx x - xx xxxxxxxx xxx xx xx xxxxxxxxx - xxxx x - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - x + xx xxx xxxxxxxx xxxxxxxx x xxxxxx xxxxx xxxxxxx xxxxxxx + xx xxxxxx xxx xxxxxxx xxxx xxxxxx + xx xxxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - xxxxxx - x + xx xxxxxxxx xxx xx xx xxxxxxxxx + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - xx xxxxxx xxx xxx xxx xxxxxx xxxxxxx x xxx xxxxxx xxxxxxx xxxxxx - xx xxxxxx xxx xx xxxxxxxxx xx xxx xxxxxxx xxx xx xxx - xxxx xxxxxxx - xxxx xxxxxxxxxxx - xxxx xxxx - xxxx xxxxxxxxxxxxxx x + xxxx + xxxxxxxxxxxxxxxxxxxxxxxxx - xx xxxxxx xxx xxxxxxxx xxxxxxx xxxxxx xxx xxxxxxx xxxx xxxxxx - xx xxxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xxxxxxxxxxxxxxxxxxxxxxxxx - x + xxxxxx + x - xx xxxxxx xxxxxxxxxx - xxxx x - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - x + xx xxxxxx xxx xxx xxx xxxxxx xxxxxxx x xxx xxxxxx xxxxxxx xxxxxx + xx xxxxxx xxx xx xxxxxxxxx xx xxx xxxxxxx xxx xx xxx + xxxx xxxxxxx + xxxx xxxxxxxxxxx + xxxx xxxx + xxxx xxxxxxxxxxxxxx x - xxxxxx - x + xx xxxxxx xxx xxxxxxxx xxxxxxx xxxxxx xxx xxxxxxx xxxx xxxxxx + xx xxxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxx - xx xxxxxxxx xxx xxxx xx xxxxx xxxxxxx - xxxx xxxxxxxxx + xx xxxxxx xxxxxxxxxx + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - xx xxxxxx xxx xxx xx xxxxx xx xxxxxxx xxx xxxx xxxxxxx - xxxx xxxxxxx x + xxxx + xxxxxxxxxxxxxxxxxxxxxxxxx - xx xxxxxx xxx xxxxxxxx xxxxxxx xxxxxx xxx xxxxxxx xxxx xxxxxx - xx xxxxxxxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxx xx xxxxxxxxx x - xxxxxxxxxxxxxxxxxxxxxxxxx - x + xxxxxx + x - xx xxxxxx xxxxxxxxxx - xxxx x - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - x + xx xxxxxxxx xxx xxxx xx xxxxx xxxxxxx + xxxx xxxxxxxxx - xxxxxx - x + xx xxxxxx xxx xxx xx xxxxx xx xxxxxxx xxx xxxx xxxxxxx + xxxx xxxxxxx x - xx [/pro] */ - // By default, render the dialect's limit clause - default: { - toSQLReferenceLimitDefault(context); - break; - } + xx xxxxxx xxx xxxxxxxx xxxxxxx xxxxxx xxx xxxxxxx xxxx xxxxxx + xx xxxxxxxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxx + + xx xxxxxx xxxxxxxxxx + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + + xxxx + xxxxxxxxxxxxxxxxxxxxxxxxx + + xxxxxx + x + + xx [/pro] */ + // By default, render the dialect's limit clause + default: { + toSQLReferenceLimitDefault(context); + + break; } } - // If no limit applies, just render the rest of the query - else { - toSQLReference0(context); - } - // [#1296] FOR UPDATE is simulated in some dialects using ResultSet.CONCUR_UPDATABLE if (forUpdate && !asList(CUBRID).contains(family)) { context.formatSeparator() @@ -555,10 +563,19 @@ class SelectQueryImpl extends AbstractResultQuery implement * The default LIMIT / OFFSET clause in most dialects */ private void toSQLReferenceLimitDefault(Context context) { + Object data = context.data(DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE); + + context.data(DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE, true); toSQLReference0(context); - context.visit(getLimit()); + + if (data == null) + context.data().remove(DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE); + else + context.data(DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE, data); } + static final String DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE = "org.jooq.configuration.render-trailing-limit-if-applicable"; + /* [pro] xx xxx x xxxxxxxx xxx xxxxx x xxxxxx xxxxxx xx xxx xxxxxx xxxxxxxxxxxxxxxx @@ -750,8 +767,7 @@ class SelectQueryImpl extends AbstractResultQuery implement case UNION_ALL: context.start(SELECT_UNION_ALL); break; } - if (i > 0) - unionParenthesis(context, "("); + unionParenthesis(context, "("); } } @@ -1042,9 +1058,20 @@ class SelectQueryImpl extends AbstractResultQuery implement context.end(SELECT_WINDOW); + // ORDER BY clause for local subselect + // ----------------------------------- + toSQLOrderBy( + context, + originalFields, alternativeFields, + false, wrapQueryExpressionBodyInDerivedTable, + orderBy, limit + ); + // SET operations like UNION, EXCEPT, INTERSECT // -------------------------------------------- if (unionOpSize > 0) { + unionParenthesis(context, ")"); + for (int i = 0; i < unionOpSize; i++) { CombineOperator op = unionOp.get(i); @@ -1078,11 +1105,27 @@ class SelectQueryImpl extends AbstractResultQuery implement xxxxxxx xxxx xx [/pro] */ - // ORDER BY clause - // --------------- + // ORDER BY clause for UNION + // ------------------------- + toSQLOrderBy( + context, + originalFields, alternativeFields, + wrapQueryExpressionInDerivedTable, wrapQueryExpressionBodyInDerivedTable, + unionOrderBy, unionLimit + ); + } + + private final void toSQLOrderBy( + Context context, + Field[] originalFields, Field[] alternativeFields, + boolean wrapQueryExpressionInDerivedTable, boolean wrapQueryExpressionBodyInDerivedTable, + SortFieldList actualOrderBy, + Limit actualLimit + ) { + context.start(SELECT_ORDER_BY); - if (!getOrderBy().isEmpty()) { + if (!actualOrderBy.isEmpty()) { context.formatSeparator() .keyword("order") .sql(orderBySiblings ? " " : "") @@ -1103,7 +1146,7 @@ class SelectQueryImpl extends AbstractResultQuery implement xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxx - xxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxx @@ -1111,14 +1154,14 @@ class SelectQueryImpl extends AbstractResultQuery implement xxxx xx [/pro] */ { - context.visit(getOrderBy()); + context.visit(actualOrderBy); } } /* [pro] xx xx xxxxxxx xxx xxxxxx xxxx xxxxxxxx xx xxxxx xx xxxxxxx xxxxx xxxx xx xxxxxx xx xxxxx - xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxx xxxx xxxxxx xxx @@ -1135,6 +1178,9 @@ class SelectQueryImpl extends AbstractResultQuery implement xxxxxxxxxxxxxxxx xxxxxxxxxx xx [/pro] */ + + if (context.data().containsKey(DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE) && actualLimit.isApplicable()) + context.visit(actualLimit); } /* [pro] xx @@ -1288,36 +1334,36 @@ class SelectQueryImpl extends AbstractResultQuery implement @Override public final void addLimit(int numberOfRows) { - limit.setNumberOfRows(numberOfRows); + getLimit().setNumberOfRows(numberOfRows); } @Override public final void addLimit(Param numberOfRows) { - limit.setNumberOfRows(numberOfRows); + getLimit().setNumberOfRows(numberOfRows); } @Override public final void addLimit(int offset, int numberOfRows) { - limit.setOffset(offset); - limit.setNumberOfRows(numberOfRows); + getLimit().setOffset(offset); + getLimit().setNumberOfRows(numberOfRows); } @Override public final void addLimit(int offset, Param numberOfRows) { - limit.setOffset(offset); - limit.setNumberOfRows(numberOfRows); + getLimit().setOffset(offset); + getLimit().setNumberOfRows(numberOfRows); } @Override public final void addLimit(Param offset, int numberOfRows) { - limit.setOffset(offset); - limit.setNumberOfRows(numberOfRows); + getLimit().setOffset(offset); + getLimit().setNumberOfRows(numberOfRows); } @Override public final void addLimit(Param offset, Param numberOfRows) { - limit.setOffset(offset); - limit.setNumberOfRows(numberOfRows); + getLimit().setOffset(offset); + getLimit().setNumberOfRows(numberOfRows); } @Override @@ -1473,10 +1519,6 @@ class SelectQueryImpl extends AbstractResultQuery implement return groupBy; } - final Limit getLimit() { - return limit; - } - @SuppressWarnings({ "rawtypes", "unchecked" }) final ConditionProviderImpl getWhere() { if (getOrderBy().isEmpty() || getSeek().isEmpty()) { @@ -1553,11 +1595,15 @@ class SelectQueryImpl extends AbstractResultQuery implement } final SortFieldList getOrderBy() { - return orderBy; + return (unionOp.size() == 0) ? orderBy : unionOrderBy; } final QueryPartList> getSeek() { - return seek; + return (unionOp.size() == 0) ? seek : unionSeek; + } + + final Limit getLimit() { + return (unionOp.size() == 0) ? limit : unionLimit; } /* [pro] xx @@ -1624,7 +1670,10 @@ class SelectQueryImpl extends AbstractResultQuery implement @Override public final void setOrderBySiblings(boolean orderBySiblings) { - this.orderBySiblings = orderBySiblings; + if (unionOp.size() == 0) + this.orderBySiblings = orderBySiblings; + else + this.unionOrderBySiblings = orderBySiblings; } @Override @@ -1634,7 +1683,11 @@ class SelectQueryImpl extends AbstractResultQuery implement @Override public final void addSeekAfter(Collection> fields) { - seekBefore = false; + if (unionOp.size() == 0) + seekBefore = false; + else + unionSeekBefore = false; + getSeek().addAll(fields); } @@ -1645,7 +1698,11 @@ class SelectQueryImpl extends AbstractResultQuery implement @Override public final void addSeekBefore(Collection> fields) { - seekBefore = true; + if (unionOp.size() == 0) + seekBefore = true; + else + unionSeekBefore = true; + getSeek().addAll(fields); } @@ -1767,7 +1824,7 @@ class SelectQueryImpl extends AbstractResultQuery implement private final Select combine(CombineOperator op, Select other) { int index = unionOp.size() - 1; - if (index == -1 || unionOp.get(index) != op || op == INTERSECT || op == EXCEPT) { + if (index == -1 || unionOp.get(index) != op || op == EXCEPT) { unionOp.add(op); union.add(new QueryPartList>());