diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java index 3d0afc2c30..d7d18b7be0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java @@ -767,8 +767,26 @@ abstract class AbstractField extends AbstractQueryPart implements Field { return like(concat, Utils.ESCAPE); } + private final boolean isAccidentalSelect(T[] values) { + return (values != null && values.length == 1 && values[0] instanceof Select); + } + + private final boolean isAccidentalCollection(T[] values) { + return (values != null && values.length == 1 && values[0] instanceof Collection); + } + + @SuppressWarnings("unchecked") @Override public final Condition in(T... values) { + + // [#3362] Prevent "rogue" API usage when using Field.in(Object... values) + if (isAccidentalSelect(values)) + return in((Select>) values[0]); + + // [#3347] Prevent "rogue" API usage when using Field.in(Object... values) + if (isAccidentalCollection(values)) + return in((Collection) values[0]); + return in(Utils.fields(values, this).toArray(new Field[0])); } @@ -798,8 +816,18 @@ abstract class AbstractField extends AbstractQueryPart implements Field { return compare(IN, query); } + @SuppressWarnings("unchecked") @Override public final Condition notIn(T... values) { + + // [#3362] Prevent "rogue" API usage when using Field.in(Object... values) + if (isAccidentalSelect(values)) + return notIn((Select>) values[0]); + + // [#3347] Prevent "rogue" API usage when using Field.in(Object... values) + if (isAccidentalCollection(values)) + return notIn((Collection) values[0]); + if (values == null || values.length == 0) { return trueCondition(); } diff --git a/jOOQ/src/main/java/org/jooq/impl/Utils.java b/jOOQ/src/main/java/org/jooq/impl/Utils.java index c22d57d60c..0aaf462696 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Utils.java +++ b/jOOQ/src/main/java/org/jooq/impl/Utils.java @@ -786,13 +786,7 @@ final class Utils { if (values != null && field != null) { for (int i = 0; i < values.length; i++) { - - // [#3347] Defend against rogue API usage, e.g. when calling - // Field.in(T...) with a Collection argument - if (values[i] instanceof Collection) - result.addAll(fields(((Collection) values[i]).toArray(), field)); - else - result.add(field(values[i], field)); + result.add(field(values[i], field)); } } diff --git a/jOOQ/src/test/java/org/jooq/test/RogueAPIUsageTest.java b/jOOQ/src/test/java/org/jooq/test/RogueAPIUsageTest.java index 6b127793c8..00c0f82433 100644 --- a/jOOQ/src/test/java/org/jooq/test/RogueAPIUsageTest.java +++ b/jOOQ/src/test/java/org/jooq/test/RogueAPIUsageTest.java @@ -40,11 +40,14 @@ */ package org.jooq.test; +import static org.jooq.impl.DSL.val; + import java.util.Arrays; import java.util.List; import org.jooq.Condition; import org.jooq.Field; +import org.jooq.Select; import org.jooq.impl.DSL; import org.junit.Test; @@ -60,7 +63,7 @@ import org.junit.Test; public class RogueAPIUsageTest extends AbstractTest { @Test - public void testInPredicate() { + public void testInPredicateWithCollection() { Field a = DSL.field("a"); List values = Arrays.asList("a", "b"); @@ -70,4 +73,16 @@ public class RogueAPIUsageTest extends AbstractTest { assertEquals("a in ('a', 'b')", create.renderInlined(c)); assertEquals("a in (:1, :2)", create.renderNamedParams(c)); } + + @Test + public void testInPredicateWithSelect() { + Field a = DSL.field("a"); + Select values = DSL.select(val(1)); + + Condition c = a.in(values); + + assertEquals("a in (select ? from dual)", create.render(c)); + assertEquals("a in (select 1 from dual)", create.renderInlined(c)); + assertEquals("a in (select :1 from dual)", create.renderNamedParams(c)); + } }