[jOOQ/jOOQ#13129] Add Context::predicandSubquery
This commit is contained in:
parent
f492aa31c1
commit
8346ee4fa3
@ -237,7 +237,7 @@ public interface Context<C extends Context<C>> extends Scope {
|
||||
C declareCTE(boolean declareCTE, Consumer<? super C> consumer);
|
||||
|
||||
/**
|
||||
* Whether the current context is rendering a sub-query (nested query).
|
||||
* Whether the current context is rendering a subquery (nested query).
|
||||
*/
|
||||
boolean subquery();
|
||||
|
||||
@ -247,6 +247,18 @@ public interface Context<C extends Context<C>> extends Scope {
|
||||
@NotNull
|
||||
C subquery(boolean subquery);
|
||||
|
||||
/**
|
||||
* Whether the current context is rendering a predicand subquery, i.e. a
|
||||
* subquery that is an operand of a predicate.
|
||||
*/
|
||||
boolean predicandSubquery();
|
||||
|
||||
/**
|
||||
* Set the new context value for {@link #predicandSubquery()}.
|
||||
*/
|
||||
@NotNull
|
||||
C predicandSubquery(boolean predicandSubquery);
|
||||
|
||||
/**
|
||||
* Which level of subqueries we're currently in, starting with 0 for the top
|
||||
* level query.
|
||||
|
||||
@ -95,6 +95,8 @@ import org.jooq.conf.SettingsTools;
|
||||
import org.jooq.conf.StatementType;
|
||||
import org.jooq.tools.StringUtils;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
@ -120,6 +122,7 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
|
||||
|
||||
int subquery;
|
||||
BitSet subqueryScopedNestedSetOperations;
|
||||
boolean predicandSubquery;
|
||||
int stringLiteral;
|
||||
String stringLiteralEscapedApos = "'";
|
||||
int index;
|
||||
@ -310,7 +313,7 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
|
||||
|
||||
@Override
|
||||
public final C visitSubquery(QueryPart part) {
|
||||
Tools.visitSubquery(this, part);
|
||||
Tools.visitSubquery(this, part, false);
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
@ -644,6 +647,17 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
|
||||
return subquery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean predicandSubquery() {
|
||||
return predicandSubquery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final C predicandSubquery(boolean s) {
|
||||
predicandSubquery = s;
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean subquery() {
|
||||
return subquery > 0;
|
||||
|
||||
@ -1488,7 +1488,7 @@ abstract class AbstractField<T> extends AbstractTypedNamed<T> implements Field<T
|
||||
|
||||
@Override
|
||||
public final Condition compare(Comparator comparator, Select<? extends Record1<T>> query) {
|
||||
return compare(comparator, new ScalarSubquery<>(query, getDataType()));
|
||||
return compare(comparator, new ScalarSubquery<>(query, getDataType(), true));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -214,7 +214,7 @@ final class Alias<Q extends QueryPart> extends AbstractQueryPart implements UEmp
|
||||
&& (SUPPORT_DERIVED_COLUMN_NAMES_SPECIAL1.contains(dialect))
|
||||
&& (wrapped instanceof TableImpl || wrapped instanceof CommonTableExpressionImpl)) {
|
||||
|
||||
visitSubquery(context, select(asterisk()).from(((Table<?>) wrapped).as(alias)));
|
||||
visitSubquery(context, select(asterisk()).from(((Table<?>) wrapped).as(alias)), false);
|
||||
}
|
||||
|
||||
// [#1801] Some databases do not support "derived column names".
|
||||
@ -287,7 +287,7 @@ final class Alias<Q extends QueryPart> extends AbstractQueryPart implements UEmp
|
||||
}
|
||||
}
|
||||
|
||||
visitSubquery(context, select(fields).where(falseCondition()).unionAll(wrappedAsSelect));
|
||||
visitSubquery(context, select(fields).where(falseCondition()).unionAll(wrappedAsSelect), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,9 +120,9 @@ final class AliasedSelect<R extends Record> extends AbstractTable<R> implements
|
||||
// they cannot be referenced. In that case, revert to
|
||||
// actual derived table usage.
|
||||
if (ctx.family() == DERBY && q != null && q.hasUnions())
|
||||
visitSubquery(ctx, selectFrom(query.asTable(DSL.name("t"), aliases)), false);
|
||||
visitSubquery(ctx, selectFrom(query.asTable(DSL.name("t"), aliases)), false, false);
|
||||
else
|
||||
ctx.data(DATA_SELECT_ALIASES, aliases, subquery ? c -> visitSubquery(c, query, false) : c -> c.visit(query));
|
||||
ctx.data(DATA_SELECT_ALIASES, aliases, subquery ? c -> visitSubquery(c, query, false, false) : c -> c.visit(query));
|
||||
}
|
||||
|
||||
@Override // Avoid AbstractTable implementation
|
||||
|
||||
@ -42,13 +42,9 @@ import static org.jooq.impl.Keywords.K_ARRAY;
|
||||
import static org.jooq.impl.Names.N_ARRAY;
|
||||
import static org.jooq.impl.Tools.visitSubquery;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jooq.Context;
|
||||
import org.jooq.DataType;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.Function1;
|
||||
import org.jooq.QueryPart;
|
||||
import org.jooq.Record1;
|
||||
// ...
|
||||
@ -86,7 +82,7 @@ final class ArrayQuery<T> extends AbstractField<T[]> implements QOM.ArrayQuery<T
|
||||
|
||||
// [#11053] TODO: Move ORDER BY clause from subquery to ARRAY_AGG
|
||||
// See https://github.com/jOOQ/jOOQ/issues/11053#issuecomment-735773248
|
||||
visitSubquery(ctx, DSL.select(arrayAgg(c)).from(t));
|
||||
visitSubquery(ctx, DSL.select(arrayAgg(c)).from(t), false);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -138,7 +138,7 @@ final class CommonTableExpressionImpl<R extends Record> extends AbstractTable<R>
|
||||
|
||||
}
|
||||
|
||||
visitSubquery(ctx, s);
|
||||
visitSubquery(ctx, s, false);
|
||||
|
||||
|
||||
|
||||
|
||||
@ -94,7 +94,7 @@ class DerivedTable<R extends Record> extends AbstractTable<R> implements QOM.Der
|
||||
|
||||
|
||||
|
||||
visitSubquery(ctx, query, false);
|
||||
visitSubquery(ctx, query, false, false);
|
||||
}
|
||||
|
||||
@Override // Avoid AbstractTable implementation
|
||||
|
||||
@ -105,7 +105,7 @@ implements
|
||||
|
||||
default:
|
||||
ctx.visit(K_EXISTS).sql(' ');
|
||||
visitSubquery(ctx, query);
|
||||
visitSubquery(ctx, query, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,7 +156,8 @@ implements
|
||||
ctx,
|
||||
withRecursive(name, name)
|
||||
.as(select(from).unionAll(select(iadd(f, step == null ? inline(1) : step)).from(name).where(f.lt(to))))
|
||||
.select(f).from(name)
|
||||
.select(f).from(name),
|
||||
false
|
||||
);
|
||||
}
|
||||
else if (EMULATE_SYSTEM_RANGE.contains(ctx.dialect())) {
|
||||
@ -205,6 +206,8 @@ implements
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -99,7 +99,7 @@ implements
|
||||
|
||||
|
||||
|
||||
ScalarSubquery<T> f = new ScalarSubquery<>(arg2, arg1.getDataType());
|
||||
ScalarSubquery<T> f = new ScalarSubquery<>(arg2, arg1.getDataType(), true);
|
||||
Eq.acceptCompareCondition(ctx, this, arg1, org.jooq.Comparator.IN, f, RowN::in, RowN::in, c -> c.visit(arg1).sql(' ').visit(K_IN).sql(' ').visit(f));
|
||||
}
|
||||
|
||||
|
||||
@ -228,7 +228,8 @@ implements
|
||||
: "jsonb_path_query({0}, {1}::jsonpath) {as} t(j)",
|
||||
json.getType() == JSONB.class ? json : json.cast(JSONB),
|
||||
path
|
||||
)
|
||||
),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -78,13 +78,11 @@ import static org.jooq.impl.Tools.BooleanDataKey.DATA_MULTISET_CONTENT;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jooq.AggregateFilterStep;
|
||||
import org.jooq.Context;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.Fields;
|
||||
import org.jooq.Function1;
|
||||
import org.jooq.JSON;
|
||||
import org.jooq.JSONArrayAggOrderByStep;
|
||||
import org.jooq.JSONArrayAggReturningStep;
|
||||
@ -190,7 +188,7 @@ final class Multiset<R extends Record> extends AbstractField<Result<R>> implemen
|
||||
if (multisetCondition && NO_SUPPORT_JSON_COMPARE.contains(ctx.dialect()))
|
||||
ctx.visit(DSL.field(s).cast(VARCHAR));
|
||||
else
|
||||
visitSubquery(ctx, s, true);
|
||||
visitSubquery(ctx, s, false);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -242,7 +240,7 @@ final class Multiset<R extends Record> extends AbstractField<Result<R>> implemen
|
||||
if (multisetCondition && NO_SUPPORT_JSONB_COMPARE.contains(ctx.dialect()))
|
||||
ctx.visit(DSL.field(s).cast(VARCHAR));
|
||||
else
|
||||
visitSubquery(ctx, s, true);
|
||||
visitSubquery(ctx, s, false);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -289,7 +287,7 @@ final class Multiset<R extends Record> extends AbstractField<Result<R>> implemen
|
||||
if (multisetCondition && NO_SUPPORT_XML_COMPARE.contains(ctx.dialect()))
|
||||
ctx.visit(xmlserializeContent(DSL.field(s), VARCHAR));
|
||||
else
|
||||
visitSubquery(ctx, s, true);
|
||||
visitSubquery(ctx, s, false);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -299,7 +297,7 @@ final class Multiset<R extends Record> extends AbstractField<Result<R>> implemen
|
||||
}
|
||||
|
||||
case NATIVE:
|
||||
visitSubquery(ctx.visit(K_MULTISET), select, true);
|
||||
visitSubquery(ctx.visit(K_MULTISET), select, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ implements
|
||||
|
||||
|
||||
|
||||
ScalarSubquery<T> f = new ScalarSubquery<>(arg2, arg1.getDataType());
|
||||
ScalarSubquery<T> f = new ScalarSubquery<>(arg2, arg1.getDataType(), true);
|
||||
Eq.acceptCompareCondition(ctx, this, arg1, org.jooq.Comparator.NOT_IN, f, RowN::notIn, RowN::notIn, c -> c.visit(arg1).sql(' ').visit(K_NOT_IN).sql(' ').visit(f));
|
||||
}
|
||||
|
||||
|
||||
@ -111,7 +111,7 @@ final class QuantifiedSelectImpl<R extends Record> extends AbstractQueryPart imp
|
||||
default:
|
||||
ctx.visit(quantifier.toKeyword());
|
||||
ctx.sql(extraParentheses ? " ((" : " (");
|
||||
visitSubquery(ctx, delegate(ctx.configuration()), false);
|
||||
visitSubquery(ctx, delegate(ctx.configuration()), true, false);
|
||||
ctx.sql(extraParentheses ? "))" : ")");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -138,7 +138,7 @@ final class RowIsDistinctFrom extends AbstractCondition implements UNotYetImplem
|
||||
if (rhsRow != null)
|
||||
ctx.visit(rhsRow);
|
||||
else
|
||||
visitSubquery(ctx, rhsSelect);
|
||||
visitSubquery(ctx, rhsSelect, true);
|
||||
|
||||
if (!not)
|
||||
ctx.sql(')');
|
||||
@ -154,7 +154,7 @@ final class RowIsDistinctFrom extends AbstractCondition implements UNotYetImplem
|
||||
if (rhsRow != null)
|
||||
ctx.visit(rhsRow);
|
||||
else
|
||||
visitSubquery(ctx, rhsSelect);
|
||||
visitSubquery(ctx, rhsSelect, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -251,13 +251,13 @@ final class RowSubqueryCondition extends AbstractCondition implements UNotYetImp
|
||||
boolean extraParentheses = false ;
|
||||
|
||||
ctx.sql(extraParentheses ? "((" : "(")
|
||||
.data(BooleanDataKey.DATA_ROW_VALUE_EXPRESSION_PREDICATE_SUBQUERY, true, c -> visitSubquery(c, right, false))
|
||||
.data(BooleanDataKey.DATA_ROW_VALUE_EXPRESSION_PREDICATE_SUBQUERY, true, c -> visitSubquery(c, right, true, false))
|
||||
.sql(extraParentheses ? "))" : ")");
|
||||
}
|
||||
|
||||
// [#2054] Quantified row value expression comparison predicates shouldn't have parentheses before ANY or ALL
|
||||
else
|
||||
ctx.data(BooleanDataKey.DATA_ROW_VALUE_EXPRESSION_PREDICATE_SUBQUERY, true, c -> c.subquery(true).visit(rightQuantified).subquery(false));
|
||||
ctx.data(BooleanDataKey.DATA_ROW_VALUE_EXPRESSION_PREDICATE_SUBQUERY, true, c -> c.visit(rightQuantified));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -64,11 +64,13 @@ final class ScalarSubquery<T> extends AbstractField<T> implements QOM.ScalarSubq
|
||||
|
||||
static final Set<SQLDialect> NO_SUPPORT_WITH_IN_SCALAR_SUBQUERY = SQLDialect.supportedBy(HSQLDB);
|
||||
final Select<?> query;
|
||||
final boolean predicandSubquery;
|
||||
|
||||
ScalarSubquery(Select<?> query, DataType<T> type) {
|
||||
ScalarSubquery(Select<?> query, DataType<T> type, boolean predicandSubquery) {
|
||||
super(N_SELECT, type);
|
||||
|
||||
this.query = query;
|
||||
this.predicandSubquery = predicandSubquery;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -84,9 +86,9 @@ final class ScalarSubquery<T> extends AbstractField<T> implements QOM.ScalarSubq
|
||||
// HSQLDB allows for using WITH inside of IN, see: https://sourceforge.net/p/hsqldb/bugs/1617/
|
||||
// We'll still emulate CTE in scalar subqueries with a derived tables in all cases.
|
||||
if (q != null && q.with != null && NO_SUPPORT_WITH_IN_SCALAR_SUBQUERY.contains(ctx.dialect()))
|
||||
visitSubquery(ctx, select(asterisk()).from(query.asTable("t")));
|
||||
visitSubquery(ctx, select(asterisk()).from(query.asTable("t")), predicandSubquery);
|
||||
else
|
||||
visitSubquery(ctx, query);
|
||||
visitSubquery(ctx, query, predicandSubquery);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -95,7 +97,7 @@ final class ScalarSubquery<T> extends AbstractField<T> implements QOM.ScalarSubq
|
||||
|
||||
@Override
|
||||
public final Function1<? super Select<? extends Record1<T>>, ? extends Field<T>> $constructor() {
|
||||
return s -> new ScalarSubquery<>((Select<?>) s, (DataType<T>) Tools.scalarType(s));
|
||||
return s -> new ScalarSubquery<>((Select<?>) s, (DataType<T>) Tools.scalarType(s), predicandSubquery);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -90,7 +90,7 @@ final class SelectIsNotNull extends AbstractCondition implements QOM.SelectIsNot
|
||||
}
|
||||
|
||||
private final void acceptStandard(Context<?> ctx) {
|
||||
visitSubquery(ctx, select);
|
||||
visitSubquery(ctx, select, true);
|
||||
|
||||
switch (ctx.family()) {
|
||||
|
||||
|
||||
@ -124,7 +124,7 @@ final class SelectIsNull extends AbstractCondition implements QOM.SelectIsNull {
|
||||
}
|
||||
|
||||
private final void acceptStandard(Context<?> ctx) {
|
||||
visitSubquery(ctx, select);
|
||||
visitSubquery(ctx, select, true);
|
||||
|
||||
switch (ctx.family()) {
|
||||
|
||||
|
||||
@ -630,7 +630,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public final <T> Field<T> asField() {
|
||||
return new ScalarSubquery<>(this, (DataType<T>) Tools.scalarType(this));
|
||||
return new ScalarSubquery<>(this, (DataType<T>) Tools.scalarType(this), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -175,7 +175,8 @@ implements
|
||||
|
||||
visitSubquery(
|
||||
ctx,
|
||||
withRecursive(s1, s2).select(DSL.coalesce(DSL.max(DSL.field(name("x"))), inline(""))).from(s2).where(s2.field("n").eq((Field) n))
|
||||
withRecursive(s1, s2).select(DSL.coalesce(DSL.max(DSL.field(name("x"))), inline(""))).from(s2).where(s2.field("n").eq((Field) n)),
|
||||
false
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2498,11 +2498,11 @@ final class Tools {
|
||||
return e;
|
||||
}
|
||||
|
||||
static final void visitSubquery(Context<?> ctx, QueryPart query) {
|
||||
visitSubquery(ctx, query, true);
|
||||
static final void visitSubquery(Context<?> ctx, QueryPart query, boolean predicandSubquery) {
|
||||
visitSubquery(ctx, query, predicandSubquery, true);
|
||||
}
|
||||
|
||||
static final void visitSubquery(Context<?> ctx, QueryPart query, boolean parentheses) {
|
||||
static final void visitSubquery(Context<?> ctx, QueryPart query, boolean predicandSubquery, boolean parentheses) {
|
||||
|
||||
|
||||
|
||||
@ -2511,12 +2511,16 @@ final class Tools {
|
||||
if (parentheses)
|
||||
ctx.sql('(');
|
||||
|
||||
boolean previousPredicandSubquery = ctx.predicandSubquery();
|
||||
|
||||
ctx.subquery(true)
|
||||
.predicandSubquery(predicandSubquery)
|
||||
.formatIndentStart()
|
||||
.formatNewLine()
|
||||
.visit(query)
|
||||
.formatIndentEnd()
|
||||
.formatNewLine()
|
||||
.predicandSubquery(previousPredicandSubquery)
|
||||
.subquery(false);
|
||||
|
||||
if (parentheses)
|
||||
|
||||
@ -103,7 +103,7 @@ implements
|
||||
|
||||
case H2:
|
||||
ctx.visit(K_UNIQUE).sql(' ');
|
||||
visitSubquery(ctx, query);
|
||||
visitSubquery(ctx, query, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@ -650,7 +650,7 @@ final class UpdateQueryImpl<R extends Record> extends AbstractStoreQuery<R> impl
|
||||
else
|
||||
select = multiSelect;
|
||||
|
||||
visitSubquery(ctx, select);
|
||||
visitSubquery(ctx, select, false);
|
||||
}
|
||||
|
||||
ctx.formatIndentEnd().end(UPDATE_SET_ASSIGNMENT);
|
||||
|
||||
@ -183,7 +183,7 @@ final class Values<R extends Record> extends AbstractTable<R> implements QOM.Val
|
||||
selects = selects.unionAll(select);
|
||||
}
|
||||
|
||||
visitSubquery(ctx, selects, false);
|
||||
visitSubquery(ctx, selects, false, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -261,6 +261,7 @@ implements
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private final void acceptStandard(Context<?> ctx) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user