diff --git a/jOOQ/src/main/java/org/jooq/Comparator.java b/jOOQ/src/main/java/org/jooq/Comparator.java
index 615cf590b2..691f8511bc 100644
--- a/jOOQ/src/main/java/org/jooq/Comparator.java
+++ b/jOOQ/src/main/java/org/jooq/Comparator.java
@@ -66,82 +66,66 @@ import org.jetbrains.annotations.NotNull;
public enum Comparator {
@NotNull
-
@Support
IN("in", false, true),
@NotNull
-
@Support
NOT_IN("not in", false, true),
@NotNull
-
@Support
EQUALS("=", true, true),
@NotNull
-
@Support
NOT_EQUALS("<>", true, true),
@NotNull
-
@Support
LESS("<", true, true),
@NotNull
-
@Support
LESS_OR_EQUAL("<=", true, true),
@NotNull
-
@Support
GREATER(">", true, true),
@NotNull
-
@Support
GREATER_OR_EQUAL(">=", true, true),
@NotNull
-
@Support
IS_DISTINCT_FROM("is distinct from", false, false),
@NotNull
-
@Support
IS_NOT_DISTINCT_FROM("is not distinct from", false, false),
@NotNull
-
@Support
LIKE("like", false, false),
@NotNull
-
@Support
NOT_LIKE("not like", false, false),
@NotNull
-
@Support({ FIREBIRD, POSTGRES })
SIMILAR_TO("similar to", false, false),
@NotNull
-
@Support({ FIREBIRD, POSTGRES })
NOT_SIMILAR_TO("not similar to", false, false),
@NotNull
-
@Support
LIKE_IGNORE_CASE("ilike", false, false),
@NotNull
-
@Support
NOT_LIKE_IGNORE_CASE("not ilike", false, false),
@@ -173,6 +157,10 @@ public enum Comparator {
return keyword;
}
+ /**
+ * Get the inverse comparator such that A [op] B and
+ * NOT(A [inverse op] B).
+ */
public Comparator inverse() {
switch (this) {
case EQUALS: return NOT_EQUALS;
@@ -195,6 +183,25 @@ public enum Comparator {
}
}
+ /**
+ * Get the mirrored comparator such that A [op] B and
+ * B [mirrored op] A, or null if the comparator
+ * cannot be mirrored.
+ */
+ public Comparator mirror() {
+ switch (this) {
+ case EQUALS: return EQUALS;
+ case GREATER: return LESS;
+ case GREATER_OR_EQUAL: return LESS_OR_EQUAL;
+ case IS_DISTINCT_FROM: return IS_DISTINCT_FROM;
+ case IS_NOT_DISTINCT_FROM: return IS_NOT_DISTINCT_FROM;
+ case LESS: return GREATER;
+ case LESS_OR_EQUAL: return GREATER_OR_EQUAL;
+ case NOT_EQUALS: return NOT_EQUALS;
+ default: return null;
+ }
+ }
+
/**
* Whether this comparator supports quantifiers on the right-hand side.
*
diff --git a/jOOQ/src/main/java/org/jooq/conf/Settings.java b/jOOQ/src/main/java/org/jooq/conf/Settings.java
index 8cff54b40b..b142df9b86 100644
--- a/jOOQ/src/main/java/org/jooq/conf/Settings.java
+++ b/jOOQ/src/main/java/org/jooq/conf/Settings.java
@@ -94,6 +94,8 @@ public class Settings
protected Boolean transformAnsiJoinToTableLists = false;
@XmlElement(defaultValue = "false")
protected Boolean transformTableListsToAnsiJoin = false;
+ @XmlElement(defaultValue = "false")
+ protected Boolean transformRownumToLimit = false;
@XmlElement(defaultValue = "NEVER")
@XmlSchemaType(name = "string")
protected TransformUnneededArithmeticExpressions transformUnneededArithmeticExpressions = TransformUnneededArithmeticExpressions.NEVER;
@@ -847,6 +849,36 @@ public class Settings
this.transformTableListsToAnsiJoin = value;
}
+ /**
+ * Transform ROWNUM expressions to corresponding LIMIT clauses or ROW_NUMBER() expressions.
+ *
+ * In Oracle 11g and less, ROWNUM filtering was the most popular way to paginate. This pseudo
+ * column is not supported in other RDBMS, and should be replaced in Oracle 12c by the FETCH clause. This
+ * transformation allows for replacing such a filter by equivalent SQL, if possible.
+ *
+ * This feature is available in the commercial distribution only.
+ *
+ * @return
+ * possible object is
+ * {@link Boolean }
+ *
+ */
+ public Boolean isTransformRownumToLimit() {
+ return transformRownumToLimit;
+ }
+
+ /**
+ * Sets the value of the transformRownumToLimit property.
+ *
+ * @param value
+ * allowed object is
+ * {@link Boolean }
+ *
+ */
+ public void setTransformRownumToLimit(Boolean value) {
+ this.transformRownumToLimit = value;
+ }
+
/**
* Transform arithmetic expressions on literals and bind variables.
*
@@ -2432,6 +2464,11 @@ public class Settings
return this;
}
+ public Settings withTransformRownumToLimit(Boolean value) {
+ setTransformRownumToLimit(value);
+ return this;
+ }
+
/**
* Transform arithmetic expressions on literals and bind variables.
*
@@ -3018,6 +3055,7 @@ public class Settings
builder.append("fetchTriggerValuesAfterSQLServerOutput", fetchTriggerValuesAfterSQLServerOutput);
builder.append("transformAnsiJoinToTableLists", transformAnsiJoinToTableLists);
builder.append("transformTableListsToAnsiJoin", transformTableListsToAnsiJoin);
+ builder.append("transformRownumToLimit", transformRownumToLimit);
builder.append("transformUnneededArithmeticExpressions", transformUnneededArithmeticExpressions);
builder.append("backslashEscaping", backslashEscaping);
builder.append("paramType", paramType);
@@ -3324,6 +3362,15 @@ public class Settings
return false;
}
}
+ if (transformRownumToLimit == null) {
+ if (other.transformRownumToLimit!= null) {
+ return false;
+ }
+ } else {
+ if (!transformRownumToLimit.equals(other.transformRownumToLimit)) {
+ return false;
+ }
+ }
if (transformUnneededArithmeticExpressions == null) {
if (other.transformUnneededArithmeticExpressions!= null) {
return false;
@@ -3976,6 +4023,7 @@ public class Settings
result = ((prime*result)+((fetchTriggerValuesAfterSQLServerOutput == null)? 0 :fetchTriggerValuesAfterSQLServerOutput.hashCode()));
result = ((prime*result)+((transformAnsiJoinToTableLists == null)? 0 :transformAnsiJoinToTableLists.hashCode()));
result = ((prime*result)+((transformTableListsToAnsiJoin == null)? 0 :transformTableListsToAnsiJoin.hashCode()));
+ result = ((prime*result)+((transformRownumToLimit == null)? 0 :transformRownumToLimit.hashCode()));
result = ((prime*result)+((transformUnneededArithmeticExpressions == null)? 0 :transformUnneededArithmeticExpressions.hashCode()));
result = ((prime*result)+((backslashEscaping == null)? 0 :backslashEscaping.hashCode()));
result = ((prime*result)+((paramType == null)? 0 :paramType.hashCode()));
diff --git a/jOOQ/src/main/java/org/jooq/impl/CombinedCondition.java b/jOOQ/src/main/java/org/jooq/impl/CombinedCondition.java
index d558662168..1c0b15258b 100644
--- a/jOOQ/src/main/java/org/jooq/impl/CombinedCondition.java
+++ b/jOOQ/src/main/java/org/jooq/impl/CombinedCondition.java
@@ -104,10 +104,8 @@ final class CombinedCondition extends AbstractCondition {
return noCondition();
// [#9998] Otherwise, return the identity for the operator
- else if (operator == AND)
- return trueCondition();
else
- return falseCondition();
+ return identity(operator);
}
@Override
@@ -119,6 +117,36 @@ final class CombinedCondition extends AbstractCondition {
return false;
}
+ static final Condition identity(Operator operator) {
+ return operator == AND ? trueCondition() : falseCondition();
+ }
+
+ final Condition transform(F1 super Condition, ? extends Condition> function) {
+ List newList = null;
+
+ for (int i = 0; i < conditions.size(); i++) {
+ Condition oldC = conditions.get(i);
+ Condition newC = oldC instanceof CombinedCondition
+ ? ((CombinedCondition) oldC).transform(function)
+ : function.apply(oldC);
+
+ if (newC != oldC) {
+ if (newList == null)
+ newList = new ArrayList<>(conditions.subList(0, i));
+
+ if (newC != null)
+ newList.add(newC);
+ }
+ else if (newList != null)
+ newList.add(newC);
+ }
+
+ if (newList == null)
+ return this;
+ else
+ return of(operator, newList);
+ }
+
private CombinedCondition(Operator operator, int size) {
if (operator == null)
throw new IllegalArgumentException("The argument 'operator' must not be null");
diff --git a/jOOQ/src/main/java/org/jooq/impl/ConditionProviderImpl.java b/jOOQ/src/main/java/org/jooq/impl/ConditionProviderImpl.java
index a574395945..dd4aabdb77 100644
--- a/jOOQ/src/main/java/org/jooq/impl/ConditionProviderImpl.java
+++ b/jOOQ/src/main/java/org/jooq/impl/ConditionProviderImpl.java
@@ -55,14 +55,15 @@ import org.jooq.Select;
/**
* @author Lukas Eder
*/
-@SuppressWarnings("deprecation")
final class ConditionProviderImpl extends AbstractQueryPart implements ConditionProvider, Condition {
private static final long serialVersionUID = 6073328960551062973L;
private Condition condition;
- ConditionProviderImpl() {
+ ConditionProviderImpl() {}
+ ConditionProviderImpl(Condition condition) {
+ this.condition = condition;
}
final Condition getWhere() {
diff --git a/jOOQ/src/main/java/org/jooq/impl/Limit.java b/jOOQ/src/main/java/org/jooq/impl/Limit.java
index ce667ab191..6273232e31 100644
--- a/jOOQ/src/main/java/org/jooq/impl/Limit.java
+++ b/jOOQ/src/main/java/org/jooq/impl/Limit.java
@@ -84,14 +84,14 @@ final class Limit extends AbstractQueryPart {
private static final Field ONE = one();
private static final Param MAX = DSL.inline(Integer.MAX_VALUE);
- private Field> numberOfRows;
+ Param> numberOfRows;
private Field> numberOfRowsOrMax = MAX;
- private Field> offset;
+ Param> offset;
private Field> offsetOrZero = ZERO;
private Field> offsetPlusOne = ONE;
private boolean rendersParams;
- private boolean withTies;
- private boolean percent;
+ boolean withTies;
+ boolean percent;
@Override
public final void accept(Context> ctx) {
@@ -402,8 +402,7 @@ final class Limit extends AbstractQueryPart {
return !limitZero()
&& !withTies()
&& !percent()
- && numberOfRows instanceof Param
- && Long.valueOf(1L).equals(((Param>) numberOfRows).getValue());
+ && Long.valueOf(1L).equals(numberOfRows.getValue());
}
/**
@@ -452,7 +451,7 @@ final class Limit extends AbstractQueryPart {
this.offsetPlusOne = val(offset.longValue() + 1L, BIGINT);
}
- final void setOffset(Param extends Number> offset) {
+ final void setOffset(Param> offset) {
this.offset = offset;
this.offsetOrZero = offset;
this.rendersParams = rendersParams |= offset.isInline();
@@ -463,7 +462,7 @@ final class Limit extends AbstractQueryPart {
this.numberOfRowsOrMax = this.numberOfRows;
}
- final void setNumberOfRows(Param extends Number> numberOfRows) {
+ final void setNumberOfRows(Param> numberOfRows) {
this.numberOfRows = numberOfRows;
this.numberOfRowsOrMax = numberOfRows;
this.rendersParams |= numberOfRows.isInline();
@@ -484,4 +483,17 @@ final class Limit extends AbstractQueryPart {
final boolean withTies() {
return withTies;
}
+
+ final Limit from(Limit limit) {
+ if (limit.numberOfRows != null)
+ this.setNumberOfRows(limit.numberOfRows);
+
+ if (limit.offset != null)
+ this.setOffset(limit.offset);
+
+ this.setPercent(limit.percent);
+ this.setWithTies(limit.withTies);
+
+ return this;
+ }
}
diff --git a/jOOQ/src/main/java/org/jooq/impl/Plus.java b/jOOQ/src/main/java/org/jooq/impl/Plus.java
index 762c8fc6e5..cba147283f 100644
--- a/jOOQ/src/main/java/org/jooq/impl/Plus.java
+++ b/jOOQ/src/main/java/org/jooq/impl/Plus.java
@@ -89,5 +89,6 @@ package org.jooq.impl;
+
diff --git a/jOOQ/src/main/java/org/jooq/impl/Rownum.java b/jOOQ/src/main/java/org/jooq/impl/Rownum.java
new file mode 100644
index 0000000000..84a31199a1
--- /dev/null
+++ b/jOOQ/src/main/java/org/jooq/impl/Rownum.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Other licenses:
+ * -----------------------------------------------------------------------------
+ * Commercial licenses for this work are available. These replace the above
+ * ASL 2.0 and offer limited warranties, support, maintenance, and commercial
+ * database integrations.
+ *
+ * For more information, please visit: http://www.jooq.org/licenses
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+package org.jooq.impl;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java
index a7274d399a..3bf0f2b18e 100644
--- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java
+++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java
@@ -81,6 +81,7 @@ import static org.jooq.SQLDialect.MYSQL;
// ...
// ...
// ...
+// ...
import static org.jooq.SQLDialect.POSTGRES;
// ...
// ...
@@ -191,6 +192,7 @@ import java.util.stream.Stream;
import org.jooq.Asterisk;
import org.jooq.Clause;
+import org.jooq.Comparator;
import org.jooq.Condition;
import org.jooq.Configuration;
import org.jooq.Context;
@@ -228,6 +230,7 @@ import org.jooq.impl.ForLock.ForLockWaitMode;
import org.jooq.impl.Tools.BooleanDataKey;
import org.jooq.impl.Tools.DataExtendedKey;
import org.jooq.impl.Tools.DataKey;
+import org.jooq.tools.Convert;
import org.jooq.tools.JooqLogger;
import org.jooq.tools.StringUtils;
@@ -281,6 +284,8 @@ final class SelectQueryImpl extends AbstractResultQuery imp
+
+
@@ -373,6 +378,50 @@ final class SelectQueryImpl extends AbstractResultQuery imp
this.from.add(from.asTable());
}
+ SelectQueryImpl copy() {
+ SelectQueryImpl result = new SelectQueryImpl<>(configuration(), with);
+
+ result.condition.setWhere(condition.getWhere());
+ result.connectBy.setWhere(connectBy.getWhere());
+ result.connectByNoCycle = connectByNoCycle;
+ result.connectByStartWith.setWhere(connectByStartWith.getWhere());
+ result.distinct = distinct;
+ result.distinctOn = distinctOn;
+ result.forJSON = forJSON;
+ result.forLock = forLock;
+ result.forXML = forXML;
+ result.from.addAll(from);
+ result.groupBy = groupBy;
+ result.grouping = grouping;
+ result.having.setWhere(having.getWhere());
+ result.hint = hint;
+ result.into = into;
+ result.limit.from(limit);
+
+ result.option = option;
+ result.orderBy.addAll(orderBy);
+ result.orderBySiblings = orderBySiblings;
+ result.qualify.setWhere(qualify.getWhere());
+ result.seek.addAll(seek);
+ result.select.addAll(select);
+
+ // TODO: Should the remaining union subqueries also be copied?
+ result.union.addAll(union);
+ result.unionLimit.from(unionLimit);
+ result.unionOp.addAll(unionOp);
+ result.unionOrderBy.addAll(unionOrderBy);
+ result.unionOrderBySiblings = unionOrderBySiblings;
+ result.unionSeek.addAll(unionSeek);
+ result.unionSeekBefore = unionSeekBefore;
+
+ if (window != null)
+ result.addWindow(window);
+ result.withCheckOption = withCheckOption;
+ result.withReadOnly = withReadOnly;
+
+ return result;
+ }
+
@Override
public final int fetchCount() throws DataAccessException {
return DSL.using(configuration()).fetchCount(this);
@@ -862,6 +911,173 @@ final class SelectQueryImpl extends AbstractResultQuery imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -884,15 +1100,15 @@ final class SelectQueryImpl extends AbstractResultQuery imp
@Override
- public final void accept(Context> context) {
+ public final void accept(Context> ctx) {
Table> dmlTable;
// [#6583] Work around MySQL's self-reference-in-DML-subquery restriction
- if (context.subqueryLevel() == 1
- && REQUIRES_DERIVED_TABLE_DML.contains(context.dialect())
- && (dmlTable = (Table>) context.data(DATA_DML_TARGET_TABLE)) != null
+ if (ctx.subqueryLevel() == 1
+ && REQUIRES_DERIVED_TABLE_DML.contains(ctx.dialect())
+ && (dmlTable = (Table>) ctx.data(DATA_DML_TARGET_TABLE)) != null
&& containsTable(dmlTable)) {
- context.visit(DSL.select(asterisk()).from(asTable("t")));
+ ctx.visit(DSL.select(asterisk()).from(asTable("t")));
}
@@ -914,8 +1130,28 @@ final class SelectQueryImpl extends AbstractResultQuery imp
- else
- accept0(context);
+
+
+
+
+
+
+
+
+
+ else {
+
+
+
+
+
+
+ accept0(ctx);
+
+
+
+
+ }
}
public final void accept0(Context> context) {
diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java
index 8e718ec23d..3d132fbb32 100644
--- a/jOOQ/src/main/java/org/jooq/impl/Tools.java
+++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java
@@ -656,6 +656,12 @@ final class Tools {
+
+
+
+
+
+
diff --git a/jOOQ/src/main/java/org/jooq/impl/Val.java b/jOOQ/src/main/java/org/jooq/impl/Val.java
index 37e5c2d3b5..01c1ce43ad 100644
--- a/jOOQ/src/main/java/org/jooq/impl/Val.java
+++ b/jOOQ/src/main/java/org/jooq/impl/Val.java
@@ -104,6 +104,12 @@ final class Val extends AbstractParam {
return (Val) this;
}
+ final Val copy(Object newValue) {
+ Val w = new Val<>(getDataType().convert(newValue), getDataType(), getParamName());
+ w.setInline(isInline());
+ return w;
+ }
+
private final Val convertTo0(DataType type) {
Val w = new Val<>(type.convert(getValue()), type, getParamName());
w.setInline(isInline());
diff --git a/jOOQ/src/main/resources/xsd/jooq-runtime-3.14.0.xsd b/jOOQ/src/main/resources/xsd/jooq-runtime-3.14.0.xsd
index 1ddb7e8bd3..d364602c8b 100644
--- a/jOOQ/src/main/resources/xsd/jooq-runtime-3.14.0.xsd
+++ b/jOOQ/src/main/resources/xsd/jooq-runtime-3.14.0.xsd
@@ -216,6 +216,16 @@ this flag enables the transformation to ANSI join syntax.
This feature is available in the commercial distribution only.]]>
+
+ ROWNUM expressions to corresponding LIMIT clauses or ROW_NUMBER() expressions.
+
+In Oracle 11g and less, ROWNUM filtering was the most popular way to paginate. This pseudo
+column is not supported in other RDBMS, and should be replaced in Oracle 12c by the FETCH clause. This
+transformation allows for replacing such a filter by equivalent SQL, if possible.
+
+This feature is available in the commercial distribution only.]]>
+
+