[jOOQ/jOOQ#8521] Transform MySQL IN (SELECT .. LIMIT) to derived table
This commit includes [jOOQ/jOOQ#11801] Move Tools content to the top level
This commit is contained in:
parent
69c5bd66f1
commit
e20bc6a063
@ -101,6 +101,9 @@ public class Settings
|
||||
protected Boolean bindOffsetTimeType = false;
|
||||
@XmlElement(defaultValue = "true")
|
||||
protected Boolean fetchTriggerValuesAfterSQLServerOutput = true;
|
||||
@XmlElement(defaultValue = "WHEN_NEEDED")
|
||||
@XmlSchemaType(name = "string")
|
||||
protected Transformation transformInConditionSubqueryWithLimitToDerivedTable = Transformation.WHEN_NEEDED;
|
||||
@XmlElement(defaultValue = "false")
|
||||
protected Boolean transformAnsiJoinToTableLists = false;
|
||||
@XmlElement(defaultValue = "false")
|
||||
@ -951,6 +954,30 @@ public class Settings
|
||||
this.fetchTriggerValuesAfterSQLServerOutput = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a subquery from an IN condition with LIMIT to an equivalent derived table.
|
||||
* <p>
|
||||
* This transformation works around a known MySQL limitation "ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'"
|
||||
* <p>
|
||||
* This feature is available in the commercial distribution only.
|
||||
*
|
||||
*/
|
||||
public Transformation getTransformInConditionSubqueryWithLimitToDerivedTable() {
|
||||
return transformInConditionSubqueryWithLimitToDerivedTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a subquery from an IN condition with LIMIT to an equivalent derived table.
|
||||
* <p>
|
||||
* This transformation works around a known MySQL limitation "ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'"
|
||||
* <p>
|
||||
* This feature is available in the commercial distribution only.
|
||||
*
|
||||
*/
|
||||
public void setTransformInConditionSubqueryWithLimitToDerivedTable(Transformation value) {
|
||||
this.transformInConditionSubqueryWithLimitToDerivedTable = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform ANSI join to table lists if possible.
|
||||
* <p>
|
||||
@ -2952,6 +2979,19 @@ public class Settings
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a subquery from an IN condition with LIMIT to an equivalent derived table.
|
||||
* <p>
|
||||
* This transformation works around a known MySQL limitation "ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'"
|
||||
* <p>
|
||||
* This feature is available in the commercial distribution only.
|
||||
*
|
||||
*/
|
||||
public Settings withTransformInConditionSubqueryWithLimitToDerivedTable(Transformation value) {
|
||||
setTransformInConditionSubqueryWithLimitToDerivedTable(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Settings withTransformAnsiJoinToTableLists(Boolean value) {
|
||||
setTransformAnsiJoinToTableLists(value);
|
||||
return this;
|
||||
@ -3670,6 +3710,7 @@ public class Settings
|
||||
builder.append("bindOffsetDateTimeType", bindOffsetDateTimeType);
|
||||
builder.append("bindOffsetTimeType", bindOffsetTimeType);
|
||||
builder.append("fetchTriggerValuesAfterSQLServerOutput", fetchTriggerValuesAfterSQLServerOutput);
|
||||
builder.append("transformInConditionSubqueryWithLimitToDerivedTable", transformInConditionSubqueryWithLimitToDerivedTable);
|
||||
builder.append("transformAnsiJoinToTableLists", transformAnsiJoinToTableLists);
|
||||
builder.append("transformTableListsToAnsiJoin", transformTableListsToAnsiJoin);
|
||||
builder.append("transformRownum", transformRownum);
|
||||
@ -4020,6 +4061,15 @@ public class Settings
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (transformInConditionSubqueryWithLimitToDerivedTable == null) {
|
||||
if (other.transformInConditionSubqueryWithLimitToDerivedTable!= null) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!transformInConditionSubqueryWithLimitToDerivedTable.equals(other.transformInConditionSubqueryWithLimitToDerivedTable)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (transformAnsiJoinToTableLists == null) {
|
||||
if (other.transformAnsiJoinToTableLists!= null) {
|
||||
return false;
|
||||
@ -4828,6 +4878,7 @@ public class Settings
|
||||
result = ((prime*result)+((bindOffsetDateTimeType == null)? 0 :bindOffsetDateTimeType.hashCode()));
|
||||
result = ((prime*result)+((bindOffsetTimeType == null)? 0 :bindOffsetTimeType.hashCode()));
|
||||
result = ((prime*result)+((fetchTriggerValuesAfterSQLServerOutput == null)? 0 :fetchTriggerValuesAfterSQLServerOutput.hashCode()));
|
||||
result = ((prime*result)+((transformInConditionSubqueryWithLimitToDerivedTable == null)? 0 :transformInConditionSubqueryWithLimitToDerivedTable.hashCode()));
|
||||
result = ((prime*result)+((transformAnsiJoinToTableLists == null)? 0 :transformAnsiJoinToTableLists.hashCode()));
|
||||
result = ((prime*result)+((transformTableListsToAnsiJoin == null)? 0 :transformTableListsToAnsiJoin.hashCode()));
|
||||
result = ((prime*result)+((transformRownum == null)? 0 :transformRownum.hashCode()));
|
||||
|
||||
40
jOOQ/src/main/java/org/jooq/conf/Transformation.java
Normal file
40
jOOQ/src/main/java/org/jooq/conf/Transformation.java
Normal file
@ -0,0 +1,40 @@
|
||||
|
||||
package org.jooq.conf;
|
||||
|
||||
import javax.xml.bind.annotation.XmlEnum;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Java class for Transformation.
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
* <pre>
|
||||
* <simpleType name="Transformation">
|
||||
* <restriction base="{http://www.w3.org/2001/XMLSchema}string">
|
||||
* <enumeration value="NEVER"/>
|
||||
* <enumeration value="WHEN_NEEDED"/>
|
||||
* <enumeration value="ALWAYS"/>
|
||||
* </restriction>
|
||||
* </simpleType>
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
@XmlType(name = "Transformation")
|
||||
@XmlEnum
|
||||
public enum Transformation {
|
||||
|
||||
NEVER,
|
||||
WHEN_NEEDED,
|
||||
ALWAYS;
|
||||
|
||||
public String value() {
|
||||
return name();
|
||||
}
|
||||
|
||||
public static Transformation fromValue(String v) {
|
||||
return valueOf(v);
|
||||
}
|
||||
|
||||
}
|
||||
132
jOOQ/src/main/java/org/jooq/impl/Cache.java
Normal file
132
jOOQ/src/main/java/org/jooq/impl/Cache.java
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static org.jooq.tools.StringUtils.defaultIfNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jooq.impl.CacheType;
|
||||
import org.jooq.Configuration;
|
||||
|
||||
/**
|
||||
* [#2965] This is a {@link Configuration}-based cache that can cache reflection information and other things
|
||||
*/
|
||||
final class Cache {
|
||||
|
||||
/**
|
||||
* Run a cached operation in the context of a {@link Configuration}.
|
||||
*
|
||||
* @param configuration The configuration that may cache the outcome of
|
||||
* the cached operation.
|
||||
* @param operation The expensive operation.
|
||||
* @param type The cache type to be used.
|
||||
* @param key The cache keys.
|
||||
* @return The cached value or the outcome of the cached operation.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static final <V> V run(Configuration configuration, Supplier<V> operation, CacheType type, Supplier<?> key) {
|
||||
|
||||
// If no configuration is provided take the default configuration that loads the default Settings
|
||||
if (configuration == null)
|
||||
configuration = new DefaultConfiguration();
|
||||
|
||||
// Shortcut caching when the relevant Settings flag isn't set.
|
||||
if (!type.category.predicate.test(configuration.settings()))
|
||||
return operation.get();
|
||||
|
||||
Object cacheOrNull = configuration.data(type);
|
||||
if (cacheOrNull == null) {
|
||||
synchronized (type) {
|
||||
cacheOrNull = configuration.data(type);
|
||||
|
||||
if (cacheOrNull == null)
|
||||
configuration.data(type, cacheOrNull = defaultIfNull(
|
||||
configuration.cacheProvider().provide(new DefaultCacheContext(configuration, type)),
|
||||
NULL
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheOrNull == NULL)
|
||||
return operation.get();
|
||||
|
||||
// The cache is guaranteed to be thread safe by the CacheProvider
|
||||
// contract. However since we cannot use ConcurrentHashMap.computeIfAbsent()
|
||||
// recursively, we have to revert to double checked locking nonetheless.
|
||||
Map<Object, Object> cache = (Map<Object, Object>) cacheOrNull;
|
||||
Object k = key.get();
|
||||
Object v = cache.get(k);
|
||||
if (v == null) {
|
||||
synchronized (cache) {
|
||||
v = cache.get(k);
|
||||
|
||||
if (v == null)
|
||||
cache.put(k, (v = operation.get()) == null ? NULL : v);
|
||||
}
|
||||
}
|
||||
|
||||
return (V) (v == NULL ? null : v);
|
||||
}
|
||||
|
||||
/**
|
||||
* A <code>null</code> placeholder to be put in {@link ConcurrentHashMap}.
|
||||
*/
|
||||
private static final Object NULL = new Object();
|
||||
|
||||
/**
|
||||
* Create a single-value or multi-value key for caching.
|
||||
*/
|
||||
static final Object key(Object key1, Object key2) {
|
||||
return new Key2(key1, key2);
|
||||
}
|
||||
|
||||
/**
|
||||
* A 2-value key for caching.
|
||||
*/
|
||||
private static final /* record */ class Key2 implements Serializable { private final Object key1; private final Object key2; public Key2(Object key1, Object key2) { this.key1 = key1; this.key2 = key2; } public Object key1() { return key1; } public Object key2() { return key2; } @Override public boolean equals(Object o) { if (!(o instanceof Key2)) return false; Key2 other = (Key2) o; if (!java.util.Objects.equals(this.key1, other.key1)) return false; if (!java.util.Objects.equals(this.key2, other.key2)) return false; return true; } @Override public int hashCode() { return java.util.Objects.hash(this.key1, this.key2); } @Override public String toString() { return new StringBuilder("Key2[").append("key1=").append(this.key1).append(", key2=").append(this.key2).append("]").toString(); }
|
||||
|
||||
/**
|
||||
* Generated UID.
|
||||
*/
|
||||
private static final long serialVersionUID = 5822370287443922993L;
|
||||
}
|
||||
}
|
||||
@ -40,8 +40,10 @@ package org.jooq.impl;
|
||||
|
||||
import static org.jooq.Clause.CONDITION;
|
||||
import static org.jooq.Clause.CONDITION_COMPARISON;
|
||||
import static org.jooq.Comparator.IN;
|
||||
import static org.jooq.Comparator.LIKE;
|
||||
import static org.jooq.Comparator.LIKE_IGNORE_CASE;
|
||||
import static org.jooq.Comparator.NOT_IN;
|
||||
import static org.jooq.Comparator.NOT_LIKE;
|
||||
import static org.jooq.Comparator.NOT_LIKE_IGNORE_CASE;
|
||||
import static org.jooq.Comparator.NOT_SIMILAR_TO;
|
||||
@ -72,8 +74,10 @@ import static org.jooq.SQLDialect.SQLITE;
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.conf.ParamType.INLINED;
|
||||
import static org.jooq.impl.DSL.asterisk;
|
||||
import static org.jooq.impl.DSL.inline;
|
||||
import static org.jooq.impl.DSL.row;
|
||||
import static org.jooq.impl.DSL.select;
|
||||
import static org.jooq.impl.Keywords.K_AS;
|
||||
import static org.jooq.impl.Keywords.K_CAST;
|
||||
import static org.jooq.impl.Keywords.K_ESCAPE;
|
||||
@ -82,6 +86,8 @@ import static org.jooq.impl.Tools.castIfNeeded;
|
||||
import static org.jooq.impl.Tools.embeddedFields;
|
||||
import static org.jooq.impl.Tools.nullSafe;
|
||||
import static org.jooq.impl.Tools.nullableIf;
|
||||
import static org.jooq.impl.Transformations.applyTransformationForInConditionSubqueryWithLimitToDerivedTable;
|
||||
import static org.jooq.impl.Transformations.subqueryWithLimit;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@ -92,6 +98,7 @@ import org.jooq.Context;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.LikeEscapeStep;
|
||||
import org.jooq.SQLDialect;
|
||||
import org.jooq.Select;
|
||||
import org.jooq.conf.ParamType;
|
||||
|
||||
/**
|
||||
@ -121,14 +128,23 @@ final class CompareCondition extends AbstractCondition implements LikeEscapeStep
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {
|
||||
boolean field1Embeddable = field1.getDataType().isEmbeddable();
|
||||
SelectQueryImpl<?> s;
|
||||
|
||||
if (field1Embeddable && field2 instanceof ScalarSubquery)
|
||||
ctx.visit(row(embeddedFields(field1)).compare(comparator, ((ScalarSubquery<?>) field2).query));
|
||||
else if (field1Embeddable && field2.getDataType().isEmbeddable())
|
||||
ctx.visit(row(embeddedFields(field1)).compare(comparator, embeddedFields(field2)));
|
||||
else if ((comparator == IN || comparator == NOT_IN)
|
||||
&& (s = subqueryWithLimit(field2)) != null
|
||||
&& applyTransformationForInConditionSubqueryWithLimitToDerivedTable(ctx)) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
accept0(ctx);
|
||||
}
|
||||
|
||||
@ -40,6 +40,8 @@ package org.jooq.impl;
|
||||
|
||||
import static org.jooq.Clause.CONDITION;
|
||||
import static org.jooq.Clause.CONDITION_BETWEEN;
|
||||
import static org.jooq.Comparator.EQUALS;
|
||||
import static org.jooq.Comparator.NOT_EQUALS;
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
@ -67,6 +69,7 @@ import static org.jooq.SQLDialect.SQLITE;
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.impl.DSL.asterisk;
|
||||
import static org.jooq.impl.DSL.inline;
|
||||
import static org.jooq.impl.DSL.name;
|
||||
import static org.jooq.impl.DSL.row;
|
||||
@ -74,6 +77,8 @@ import static org.jooq.impl.DSL.select;
|
||||
import static org.jooq.impl.SQLDataType.VARCHAR;
|
||||
import static org.jooq.impl.Tools.embeddedFields;
|
||||
import static org.jooq.impl.Tools.map;
|
||||
import static org.jooq.impl.Transformations.applyTransformationForInConditionSubqueryWithLimitToDerivedTable;
|
||||
import static org.jooq.impl.Transformations.subqueryWithLimit;
|
||||
import static org.jooq.tools.Convert.convert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -122,10 +127,20 @@ final class QuantifiedComparisonCondition extends AbstractCondition implements L
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {
|
||||
SelectQueryImpl<?> s;
|
||||
|
||||
if (field.getDataType().isEmbeddable())
|
||||
ctx.visit(row(embeddedFields(field)).compare(comparator, query));
|
||||
else if ((comparator == EQUALS || comparator == NOT_EQUALS)
|
||||
&& (s = subqueryWithLimit(query.query)) != null
|
||||
&& applyTransformationForInConditionSubqueryWithLimitToDerivedTable(ctx)) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
accept0(ctx);
|
||||
}
|
||||
|
||||
@ -68,6 +68,7 @@ import static org.jooq.SQLDialect.POSTGRES;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.SQLITE;
|
||||
// ...
|
||||
import static org.jooq.impl.DSL.asterisk;
|
||||
import static org.jooq.impl.DSL.exists;
|
||||
import static org.jooq.impl.DSL.name;
|
||||
import static org.jooq.impl.DSL.noCondition;
|
||||
@ -79,6 +80,8 @@ import static org.jooq.impl.Tools.embeddedFieldsRow;
|
||||
import static org.jooq.impl.Tools.fieldNames;
|
||||
import static org.jooq.impl.Tools.fieldsByName;
|
||||
import static org.jooq.impl.Tools.visitSubquery;
|
||||
import static org.jooq.impl.Transformations.applyTransformationForInConditionSubqueryWithLimitToDerivedTable;
|
||||
import static org.jooq.impl.Transformations.subqueryWithLimit;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@ -224,6 +227,29 @@ final class RowSubqueryCondition extends AbstractCondition {
|
||||
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {
|
||||
SelectQueryImpl<?> s;
|
||||
|
||||
if ((comparator == IN || comparator == NOT_IN)
|
||||
&& right != null
|
||||
&& (s = subqueryWithLimit(right)) != null
|
||||
&& applyTransformationForInConditionSubqueryWithLimitToDerivedTable(ctx)) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
else if ((comparator == EQUALS || comparator == NOT_EQUALS)
|
||||
&& rightQuantified != null
|
||||
&& (s = subqueryWithLimit(rightQuantified)) != null
|
||||
&& applyTransformationForInConditionSubqueryWithLimitToDerivedTable(ctx)) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
accept0(ctx);
|
||||
}
|
||||
|
||||
final void accept0(Context<?> ctx) {
|
||||
ctx.visit(left)
|
||||
.sql(' ')
|
||||
.visit(comparator.toKeyword())
|
||||
|
||||
96
jOOQ/src/main/java/org/jooq/impl/ThreadGuard.java
Normal file
96
jOOQ/src/main/java/org/jooq/impl/ThreadGuard.java
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* This API acts as a "guard" to prevent the same code from being executed
|
||||
* recursively within the same thread.
|
||||
*/
|
||||
final class ThreadGuard {
|
||||
|
||||
static final class Guard {
|
||||
final ThreadLocal<Object> tl = new ThreadLocal<>();
|
||||
}
|
||||
|
||||
static final ThreadGuard.Guard RECORD_TOSTRING = new Guard();
|
||||
|
||||
/**
|
||||
* A guarded operation.
|
||||
*/
|
||||
static interface GuardedOperation<V> {
|
||||
|
||||
/**
|
||||
* This callback is executed only once on the current stack.
|
||||
*/
|
||||
V unguarded();
|
||||
|
||||
/**
|
||||
* This callback is executed if {@link #unguarded()} has already been executed on the current stack.
|
||||
*/
|
||||
V guarded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an operation using a guard.
|
||||
*/
|
||||
static final void run(ThreadGuard.Guard guard, Runnable unguardedOperation, Runnable guardedOperation) {
|
||||
run(guard, () -> { unguardedOperation.run(); return null; }, () -> { guardedOperation.run(); return null; });
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an operation using a guard.
|
||||
*/
|
||||
static final <V> V run(ThreadGuard.Guard guard, Supplier<V> unguardedOperation, Supplier<V> guardedOperation) {
|
||||
boolean unguarded = (guard.tl.get() == null);
|
||||
if (unguarded)
|
||||
guard.tl.set(ThreadGuard.Guard.class);
|
||||
|
||||
try {
|
||||
if (unguarded)
|
||||
return unguardedOperation.get();
|
||||
else
|
||||
return guardedOperation.get();
|
||||
}
|
||||
finally {
|
||||
if (unguarded)
|
||||
guard.tl.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3218,11 +3218,13 @@ final class Tools {
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static final <R extends Record> SelectQueryImpl<R> selectQueryImpl(Select<R> select) {
|
||||
if (select instanceof SelectQueryImpl)
|
||||
return (SelectQueryImpl<R>) select;
|
||||
else if (select instanceof AbstractDelegatingQuery)
|
||||
return ((AbstractDelegatingQuery<R, SelectQueryImpl<R>>) select).getDelegate();
|
||||
static final <R extends Record> SelectQueryImpl<R> selectQueryImpl(QueryPart part) {
|
||||
if (part instanceof SelectQueryImpl)
|
||||
return (SelectQueryImpl<R>) part;
|
||||
else if (part instanceof AbstractDelegatingQuery)
|
||||
return ((AbstractDelegatingQuery<R, SelectQueryImpl<R>>) part).getDelegate();
|
||||
else if (part instanceof ScalarSubquery)
|
||||
return selectQueryImpl(((ScalarSubquery<?>) part).query);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
105
jOOQ/src/main/java/org/jooq/impl/Transformations.java
Normal file
105
jOOQ/src/main/java/org/jooq/impl/Transformations.java
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static org.jooq.SQLDialect.MARIADB;
|
||||
import static org.jooq.SQLDialect.MYSQL;
|
||||
import static org.jooq.conf.Transformation.WHEN_NEEDED;
|
||||
import static org.jooq.impl.Tools.selectQueryImpl;
|
||||
import static org.jooq.tools.StringUtils.defaultIfNull;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jooq.Context;
|
||||
import org.jooq.QueryPart;
|
||||
import org.jooq.SQLDialect;
|
||||
import org.jooq.Select;
|
||||
import org.jooq.conf.Transformation;
|
||||
|
||||
/**
|
||||
* Utilities related to SQL transformations.
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
final class Transformations {
|
||||
|
||||
private static final Set<SQLDialect> NO_SUPPORT_IN_LIMIT = SQLDialect.supportedBy(MARIADB, MYSQL);
|
||||
|
||||
static final SelectQueryImpl<?> subqueryWithLimit(QueryPart source) {
|
||||
SelectQueryImpl<?> s;
|
||||
return (s = selectQueryImpl(source)) != null && s.getLimit().isApplicable() ? s : null;
|
||||
}
|
||||
|
||||
static final boolean applyTransformationForInConditionSubqueryWithLimitToDerivedTable(Context<?> ctx) {
|
||||
return applyTransformation(
|
||||
ctx,
|
||||
"Settings.transformInConditionSubqueryWithLimitToDerivedTable",
|
||||
ctx.settings().getTransformInConditionSubqueryWithLimitToDerivedTable(),
|
||||
c -> NO_SUPPORT_IN_LIMIT.contains(c.dialect())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given SQL transformation needs to be applied.
|
||||
*/
|
||||
static final boolean applyTransformation(
|
||||
Context<?> ctx,
|
||||
String label,
|
||||
Transformation transformation,
|
||||
Predicate<? super Context<?>> whenNeeded
|
||||
) {
|
||||
boolean result;
|
||||
|
||||
switch (defaultIfNull(transformation, WHEN_NEEDED)) {
|
||||
case NEVER:
|
||||
result = false;
|
||||
break;
|
||||
case ALWAYS:
|
||||
result = true;
|
||||
break;
|
||||
case WHEN_NEEDED:
|
||||
result = whenNeeded.test(ctx);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Transformation configuration not supported: " + transformation);
|
||||
}
|
||||
|
||||
return result && ctx.configuration().requireCommercial(() -> "SQL transformation " + label + " required. SQL transformations are a commercial only feature. Please consider upgrading to the jOOQ Professional Edition or jOOQ Enterprise Edition.");
|
||||
}
|
||||
}
|
||||
@ -226,6 +226,14 @@ included in the <code>OUTPUT</code> clause.
|
||||
For details, see <a href="https://github.com/jOOQ/jOOQ/issues/4498">https://github.com/jOOQ/jOOQ/issues/4498</a>.]]></jxb:javadoc></jxb:property></appinfo></annotation>
|
||||
</element>
|
||||
|
||||
<element name="transformInConditionSubqueryWithLimitToDerivedTable" type="jooq-runtime:Transformation" minOccurs="0" maxOccurs="1" default="WHEN_NEEDED">
|
||||
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Transform a subquery from an IN condition with LIMIT to an equivalent derived table.
|
||||
<p>
|
||||
This transformation works around a known MySQL limitation "ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'"
|
||||
<p>
|
||||
This feature is available in the commercial distribution only.]]></jxb:javadoc></jxb:property></appinfo></annotation>
|
||||
</element>
|
||||
|
||||
<element name="transformAnsiJoinToTableLists" type="boolean" minOccurs="0" maxOccurs="1" default="false">
|
||||
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Transform ANSI join to table lists if possible.
|
||||
<p>
|
||||
@ -1249,6 +1257,20 @@ Either <input/> or <inputExpression/> must be provided]]></jxb:javad
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<simpleType name="Transformation">
|
||||
<restriction base="string">
|
||||
|
||||
<!-- Never apply the transformation -->
|
||||
<enumeration value="NEVER"/>
|
||||
|
||||
<!-- Apply the transformation when needed by a dialect -->
|
||||
<enumeration value="WHEN_NEEDED"/>
|
||||
|
||||
<!-- Always apply the transformation -->
|
||||
<enumeration value="ALWAYS"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<simpleType name="TransformUnneededArithmeticExpressions">
|
||||
<restriction base="string">
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user