[jOOQ/jOOQ#16310] Wrong SQL generated when nesting predicates on subqueries projecting embeddables within MULTISET

This commit is contained in:
Lukas Eder 2024-02-21 14:16:15 +01:00
parent 118cce19f2
commit bd88ac10eb
4 changed files with 43 additions and 4 deletions

View File

@ -54,6 +54,8 @@ import static org.jooq.impl.Tools.EMPTY_CLAUSE;
import static org.jooq.impl.Tools.EMPTY_QUERYPART;
import static org.jooq.impl.Tools.lazy;
import static org.jooq.impl.Tools.traverseJoins;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_MULTISET_CONDITION;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_MULTISET_CONTENT;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_NESTED_SET_OPERATIONS;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_OMIT_CLAUSE_EVENT_EMISSION;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_RENDER_IMPLICIT_JOIN;
@ -106,6 +108,7 @@ import org.jooq.conf.SettingsTools;
import org.jooq.conf.StatementType;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.QOM.UEmpty;
import org.jooq.impl.Tools.BooleanDataKey;
import org.jooq.impl.Tools.DataKey;
@ -263,7 +266,24 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
@Override
public final C visit(Condition part) {
return visit((QueryPart) part);
// [#16310] The MULTISET content flag needs to be reset for any non
// multiset projection context. For example, in any Condition.
// While Conditions can be projected, they're projecting a BOOLEAN,
// not nested records or collections
if (TRUE.equals(data(DATA_MULTISET_CONTENT))) {
try {
data(DATA_MULTISET_CONTENT, false);
visit((QueryPart) part);
}
finally {
data(DATA_MULTISET_CONTENT, true);
}
}
else
visit((QueryPart) part);
return (C) this;
}
@Override

View File

@ -42,10 +42,11 @@ import static org.jooq.impl.DSL.selectCount;
import static org.jooq.impl.Keywords.K_IS_NOT_NULL;
import static org.jooq.impl.SubqueryCharacteristics.PREDICAND;
import static org.jooq.impl.Tools.allNotNull;
import static org.jooq.impl.Tools.exactlyOne;
import static org.jooq.impl.Tools.flattenCollection;
import static org.jooq.impl.Tools.visitSubquery;
import org.jooq.Clause;
import org.jooq.Condition;
import org.jooq.Context;
import org.jooq.Function1;
import org.jooq.Select;
@ -78,7 +79,8 @@ final class SelectIsNotNull extends AbstractCondition implements QOM.SelectIsNot
if (SelectIsNull.EMULATE_NULL_QUERY.contains(ctx.dialect())) {
// [#11011] Avoid the RVE IS NULL emulation for queries of degree 1
if (select.getSelect().size() == 1) {
// [#16319] Flatten embeddables to find collection size
if (exactlyOne(flattenCollection(select.getSelect()))) {
acceptStandard(ctx);
}
else {

View File

@ -73,6 +73,8 @@ import static org.jooq.impl.DSL.selectCount;
import static org.jooq.impl.Keywords.K_IS_NULL;
import static org.jooq.impl.SubqueryCharacteristics.PREDICAND;
import static org.jooq.impl.Tools.allNull;
import static org.jooq.impl.Tools.exactlyOne;
import static org.jooq.impl.Tools.flattenCollection;
import static org.jooq.impl.Tools.visitSubquery;
import java.util.Set;
@ -113,7 +115,8 @@ final class SelectIsNull extends AbstractCondition implements QOM.SelectIsNull {
if (EMULATE_NULL_QUERY.contains(ctx.dialect())) {
// [#11011] Avoid the RVE IS NULL emulation for queries of degree 1
if (select.getSelect().size() == 1) {
// [#16319] Flatten embeddables to find collection size
if (exactlyOne(flattenCollection(select.getSelect()))) {
acceptStandard(ctx);
}
else {

View File

@ -6691,6 +6691,20 @@ final class Tools {
return !i.hasNext();
}
static final boolean exactlyOne(Iterable<?> it) {
if (it == null)
return false;
else if (it instanceof Collection<?> c)
return c.size() == 1;
Iterator<?> i = it.iterator();
return i.hasNext() && true_(i.next()) && !i.hasNext();
}
static final boolean true_(Object o) {
return true;
}
static final boolean isNotEmpty(Object[] array) {
return !isEmpty(array);
}