diff --git a/jOOQ/src/main/java/org/jooq/Field.java b/jOOQ/src/main/java/org/jooq/Field.java index 08844b9f69..923ac74969 100644 --- a/jOOQ/src/main/java/org/jooq/Field.java +++ b/jOOQ/src/main/java/org/jooq/Field.java @@ -1495,6 +1495,25 @@ extends @Support Condition like(String value, char escape); + /** + * Create a condition to pattern-check this field against a quantified select. + *

+ * For example a query like {@code field.like(any("a%", "b%"))} translates into + * the SQL {@code (field like 'a%' or field like 'b%')}. + * + * @see DSL#all(Field) + * @see DSL#all(Field...) + * @see DSL#all(Select) + * @see DSL#all(Object...) + * @see DSL#any(Field) + * @see DSL#any(Field...) + * @see DSL#any(Select) + * @see DSL#any(Object...) + * @see LikeEscapeStep#escape(char) + */ + @Support + LikeEscapeStep like(QuantifiedSelect> query); + /** * Create a condition to case-insensitively pattern-check this field against * a field. @@ -1579,6 +1598,25 @@ extends @Support Condition notLike(String value, char escape); + /** + * Create a condition to pattern-check this field against a quantified select. + *

+ * For example a query like {@code field.notLike(any("a%", "b%"))} translates into + * the SQL {@code (field not like 'a%' or field not like 'b%')}. + * + * @see DSL#all(Field) + * @see DSL#all(Field...) + * @see DSL#all(Select) + * @see DSL#all(Object...) + * @see DSL#any(Field) + * @see DSL#any(Field...) + * @see DSL#any(Select) + * @see DSL#any(Object...) + * @see LikeEscapeStep#escape(char) + */ + @Support + LikeEscapeStep notLike(QuantifiedSelect> query); + /** * Create a condition to case-insensitively pattern-check this field against * a field. @@ -1627,130 +1665,6 @@ extends @Support Condition notLikeIgnoreCase(String value, char escape); - /** - * Create a condition to pattern-check this field against any element in an array of values. - *

- * SQL: (this like value0 or this like value1 or ...) - * - * @see LikeEscapeStep#escape(char) - */ - @Support - LikeEscapeStep likeAny(String... values); - - /** - * Create a condition to pattern-check this field against any element in an array of fields. - *

- * SQL: (this like field0 or this like field1 or ...) - * - * @see LikeEscapeStep#escape(char) - */ - @Support - @SuppressWarnings("unchecked") - LikeEscapeStep likeAny(Field... fields); - - /** - * Create a condition to pattern-check this field against any element in a collection of fields or values. - *

- * SQL: (this like field0 or this like field1 or ...) - * - * @see LikeEscapeStep#escape(char) - */ - @Support - LikeEscapeStep likeAny(Collection values); - - /** - * Create a condition to negatively pattern-check this field against any element in an array of values. - *

- * SQL: (this not like value0 or this not like value1 or ...) - * - * @see LikeEscapeStep#escape(char) - */ - @Support - LikeEscapeStep notLikeAny(String... values); - - /** - * Create a condition to negatively pattern-check this field against any element in an array of values. - *

- * SQL: (this not like value0 or this not like value1 or ...) - * - * @see LikeEscapeStep#escape(char) - */ - @Support - @SuppressWarnings("unchecked") - LikeEscapeStep notLikeAny(Field... fields); - - /** - * Create a condition to negatively pattern-check this field against any element in an array of values. - *

- * SQL: (this not like value0 or this not like value1 or ...) - * - * @see LikeEscapeStep#escape(char) - */ - @Support - LikeEscapeStep notLikeAny(Collection values); - - /** - * Create a condition to pattern-check this field against all elements in an array of values. - *

- * SQL: (this like value0 and this like value1 and ...) - * - * @see LikeEscapeStep#escape(char) - */ - @Support - LikeEscapeStep likeAll(String... values); - - /** - * Create a condition to pattern-check this field against all elements in an array of fields. - *

- * SQL: (this like field0 and this like field1 and ...) - * - * @see LikeEscapeStep#escape(char) - */ - @Support - @SuppressWarnings("unchecked") - LikeEscapeStep likeAll(Field... fields); - - /** - * Create a condition to pattern-check this field against all elements in a collection of fields or values. - *

- * SQL: (this like field0 and this like field1 and ...) - * - * @see LikeEscapeStep#escape(char) - */ - @Support - LikeEscapeStep likeAll(Collection values); - - /** - * Create a condition to negatively pattern-check this field against all elements in an array of values. - *

- * SQL: (this not like value0 and this not like value1 and ...) - * - * @see LikeEscapeStep#escape(char) - */ - @Support - LikeEscapeStep notLikeAll(String... values); - - /** - * Create a condition to negatively pattern-check this field against all elements in an array of fields. - *

- * SQL: (this not like field0 and this not like field1 and ...) - * - * @see LikeEscapeStep#escape(char) - */ - @Support - @SuppressWarnings("unchecked") - LikeEscapeStep notLikeAll(Field... fields); - - /** - * Create a condition to negatively pattern-check this field against all elements in a collection of fields or values. - *

- * SQL: (this not like field0 and this not like field1 and ...) - * - * @see LikeEscapeStep#escape(char) - */ - @Support - LikeEscapeStep notLikeAll(Collection values); - /** * Convenience method for {@link #like(String, char)} including proper * adding of wildcards and escaping. diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java index 1ea86249d5..67f23503a8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java @@ -782,6 +782,11 @@ abstract class AbstractField extends AbstractNamed implements Field { return like(field).escape(escape); } + @Override + public LikeEscapeStep like(QuantifiedSelect> query) { + return new QuantifiedComparisonCondition(query, this, LIKE); + } + @Override public final LikeEscapeStep likeIgnoreCase(String value) { return likeIgnoreCase(Tools.field(value)); @@ -832,6 +837,11 @@ abstract class AbstractField extends AbstractNamed implements Field { return notLike(field).escape(escape); } + @Override + public LikeEscapeStep notLike(QuantifiedSelect> query) { + return new QuantifiedComparisonCondition(query, this, NOT_LIKE); + } + @Override public final LikeEscapeStep notLikeIgnoreCase(String value) { return notLikeIgnoreCase(Tools.field(value)); @@ -852,70 +862,6 @@ abstract class AbstractField extends AbstractNamed implements Field { return notLikeIgnoreCase(field).escape(escape); } - @Override - public final LikeEscapeStep likeAny(String... values) { - return likeAny(Tools.fields(values)); - } - - @Override - @SuppressWarnings("unchecked") - public final LikeEscapeStep likeAny(Field... fields) { - return likeAny(Arrays.asList(fields)); - } - - @Override - public final LikeEscapeStep likeAny(Collection values) { - return new CombinedCompareCondition(this, LIKE, Quantifier.ANY, Tools.fields(values, SQLDataType.VARCHAR)); - } - - @Override - public final LikeEscapeStep notLikeAny(String... values) { - return notLikeAny(Tools.fields(values)); - } - - @Override - @SuppressWarnings("unchecked") - public final LikeEscapeStep notLikeAny(Field... fields) { - return notLikeAny(Arrays.asList(fields)); - } - - @Override - public final LikeEscapeStep notLikeAny(Collection values) { - return new CombinedCompareCondition(this, NOT_LIKE, Quantifier.ANY, Tools.fields(values, SQLDataType.VARCHAR)); - } - - @Override - public final LikeEscapeStep likeAll(String... values) { - return likeAll(Tools.fields(values)); - } - - @Override - @SuppressWarnings("unchecked") - public final LikeEscapeStep likeAll(Field... fields) { - return likeAll(Arrays.asList(fields)); - } - - @Override - public final LikeEscapeStep likeAll(Collection values) { - return new CombinedCompareCondition(this, LIKE, Quantifier.ALL, Tools.fields(values, SQLDataType.VARCHAR)); - } - - @Override - public final LikeEscapeStep notLikeAll(String... values) { - return notLikeAll(Tools.fields(values)); - } - - @Override - @SuppressWarnings("unchecked") - public final LikeEscapeStep notLikeAll(Field... fields) { - return notLikeAll(Arrays.asList(fields)); - } - - @Override - public final LikeEscapeStep notLikeAll(Collection values) { - return new CombinedCompareCondition(this, NOT_LIKE, Quantifier.ALL, Tools.fields(values, SQLDataType.VARCHAR)); - } - @Override public final Condition notLikeRegex(String pattern) { return likeRegex(pattern).not(); diff --git a/jOOQ/src/main/java/org/jooq/impl/DSL.java b/jOOQ/src/main/java/org/jooq/impl/DSL.java index 33170a18ae..89f1cc88db 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DSL.java +++ b/jOOQ/src/main/java/org/jooq/impl/DSL.java @@ -8137,6 +8137,7 @@ public class DSL { * @see Field#greaterOrEqual(QuantifiedSelect) * @see Field#lessThan(QuantifiedSelect) * @see Field#lessOrEqual(QuantifiedSelect) + * @see Field#like(QuantifiedSelect) */ @Support({ CUBRID, DERBY, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) public static QuantifiedSelect all(Select select) { @@ -8156,6 +8157,7 @@ public class DSL { * @see Field#greaterOrEqual(QuantifiedSelect) * @see Field#lessThan(QuantifiedSelect) * @see Field#lessOrEqual(QuantifiedSelect) + * @see Field#like(QuantifiedSelect) */ @Support({ CUBRID, DERBY, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) public static QuantifiedSelect> all(T... array) { @@ -8175,12 +8177,36 @@ public class DSL { * @see Field#greaterOrEqual(QuantifiedSelect) * @see Field#lessThan(QuantifiedSelect) * @see Field#lessOrEqual(QuantifiedSelect) + * @see Field#like(QuantifiedSelect) */ @Support({ H2, HSQLDB, POSTGRES }) public static QuantifiedSelect> all(Field array) { return new QuantifiedSelectImpl>(Quantifier.ALL, array); } + /** + * Create an ALL quantified select to be used in quantified + * comparison predicate expressions. + *

+ * This is natively supported by {@link SQLDialect#POSTGRES}. Other dialects + * will render a subselect unnesting the array. + * + * @see Field#equal(QuantifiedSelect) + * @see Field#notEqual(QuantifiedSelect) + * @see Field#greaterThan(QuantifiedSelect) + * @see Field#greaterOrEqual(QuantifiedSelect) + * @see Field#lessThan(QuantifiedSelect) + * @see Field#lessOrEqual(QuantifiedSelect) + * @see Field#like(QuantifiedSelect) + */ + @Support + + @SafeVarargs + + public static QuantifiedSelect> all(Field... fields) { + return new QuantifiedSelectImpl>(Quantifier.ALL, fields); + } + /** * Create an ANY quantified select to be used in quantified * comparison predicate expressions. @@ -8191,6 +8217,7 @@ public class DSL { * @see Field#greaterOrEqual(QuantifiedSelect) * @see Field#lessThan(QuantifiedSelect) * @see Field#lessOrEqual(QuantifiedSelect) + * @see Field#like(QuantifiedSelect) */ @Support({ CUBRID, DERBY, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) public static QuantifiedSelect any(Select select) { @@ -8210,6 +8237,7 @@ public class DSL { * @see Field#greaterOrEqual(QuantifiedSelect) * @see Field#lessThan(QuantifiedSelect) * @see Field#lessOrEqual(QuantifiedSelect) + * @see Field#like(QuantifiedSelect) */ @Support({ CUBRID, DERBY, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) public static QuantifiedSelect> any(T... array) { @@ -8229,12 +8257,36 @@ public class DSL { * @see Field#greaterOrEqual(QuantifiedSelect) * @see Field#lessThan(QuantifiedSelect) * @see Field#lessOrEqual(QuantifiedSelect) + * @see Field#like(QuantifiedSelect) */ @Support({ H2, HSQLDB, POSTGRES }) public static QuantifiedSelect> any(Field array) { return new QuantifiedSelectImpl>(Quantifier.ANY, array); } + /** + * Create an ANY quantified select to be used in quantified + * comparison predicate expressions. + *

+ * This is natively supported by {@link SQLDialect#POSTGRES}. Other dialects + * will render a subselect unnesting the array. + * + * @see Field#equal(QuantifiedSelect) + * @see Field#notEqual(QuantifiedSelect) + * @see Field#greaterThan(QuantifiedSelect) + * @see Field#greaterOrEqual(QuantifiedSelect) + * @see Field#lessThan(QuantifiedSelect) + * @see Field#lessOrEqual(QuantifiedSelect) + * @see Field#like(QuantifiedSelect) + */ + @Support + + @SafeVarargs + + public static QuantifiedSelect> any(Field... fields) { + return new QuantifiedSelectImpl>(Quantifier.ANY, fields); + } + // ------------------------------------------------------------------------- // XXX Access control // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index 5d086c3dbf..fd972e6951 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -51,6 +51,8 @@ import static org.jooq.conf.ParseWithMetaLookups.IGNORE_ON_FAILURE; import static org.jooq.conf.ParseWithMetaLookups.THROW_ON_FAILURE; import static org.jooq.impl.DSL.abs; import static org.jooq.impl.DSL.acos; +import static org.jooq.impl.DSL.all; +import static org.jooq.impl.DSL.any; import static org.jooq.impl.DSL.arrayAgg; import static org.jooq.impl.DSL.arrayAggDistinct; import static org.jooq.impl.DSL.ascii; @@ -4400,39 +4402,61 @@ final class ParserImpl implements Parser { else if (left instanceof Field && parseKeywordIf(ctx, "LIKE")) { if (parseKeywordIf(ctx, "ANY")) { parse(ctx, '('); - List> fields = null; - if (parseIf(ctx, ')')) - fields = Collections.> emptyList(); - else { - fields = new ArrayList>(); - do { - fields.add(toField(ctx, parseConcat(ctx, null))); - } - while (parseIf(ctx, ',')); + if (peekKeyword(ctx, "SELECT") || peekKeyword(ctx, "SEL")) { + SelectQueryImpl select = parseSelect(ctx); parse(ctx, ')'); + boolean escape = parseKeywordIf(ctx, "ESCAPE"); + char character = escape ? parseCharacterLiteral(ctx) : ' '; + LikeEscapeStep result = not ? ((Field) left).notLike(any(select)) : ((Field) left).like(any(select)); + return escape ? result.escape(character) : result; + } + else { + List> fields = null; + if (parseIf(ctx, ')')) + fields = Collections.> emptyList(); + else { + fields = new ArrayList>(); + do { + fields.add(toField(ctx, parseConcat(ctx, null))); + } + while (parseIf(ctx, ',')); + parse(ctx, ')'); + } + boolean escape = parseKeywordIf(ctx, "ESCAPE"); + char character = escape ? parseCharacterLiteral(ctx) : ' '; + Field[] fieldArray = fields.toArray(new Field[0]); + LikeEscapeStep result = not ? ((Field) left).notLike(any(fieldArray)) : ((Field) left).like(any(fieldArray)); + return escape ? result.escape(character) : result; } - boolean escape = parseKeywordIf(ctx, "ESCAPE"); - char character = escape ? parseCharacterLiteral(ctx) : ' '; - LikeEscapeStep result = not ? ((Field) left).notLikeAny(fields) : ((Field) left).likeAny(fields); - return escape ? result.escape(character) : result; } else if (parseKeywordIf(ctx, "ALL")) { parse(ctx, '('); - List> fields = null; - if (parseIf(ctx, ')')) - fields = Collections.> emptyList(); - else { - fields = new ArrayList>(); - do { - fields.add(toField(ctx, parseConcat(ctx, null))); - } - while (parseIf(ctx, ',')); + if (peekKeyword(ctx, "SELECT") || peekKeyword(ctx, "SEL")) { + SelectQueryImpl select = parseSelect(ctx); parse(ctx, ')'); + boolean escape = parseKeywordIf(ctx, "ESCAPE"); + char character = escape ? parseCharacterLiteral(ctx) : ' '; + LikeEscapeStep result = not ? ((Field) left).notLike(all(select)) : ((Field) left).like(all(select)); + return escape ? result.escape(character) : result; + } + else { + List> fields = null; + if (parseIf(ctx, ')')) + fields = Collections.> emptyList(); + else { + fields = new ArrayList>(); + do { + fields.add(toField(ctx, parseConcat(ctx, null))); + } + while (parseIf(ctx, ',')); + parse(ctx, ')'); + } + boolean escape = parseKeywordIf(ctx, "ESCAPE"); + char character = escape ? parseCharacterLiteral(ctx) : ' '; + Field[] fieldArray = fields.toArray(new Field[0]); + LikeEscapeStep result = not ? ((Field) left).notLike(all(fieldArray)) : ((Field) left).like(all(fieldArray)); + return escape ? result.escape(character) : result; } - boolean escape = parseKeywordIf(ctx, "ESCAPE"); - char character = escape ? parseCharacterLiteral(ctx) : ' '; - LikeEscapeStep result = not ? ((Field) left).notLikeAll(fields) : ((Field) left).likeAll(fields); - return escape ? result.escape(character) : result; } else { Field right = toField(ctx, parseConcat(ctx, null)); diff --git a/jOOQ/src/main/java/org/jooq/impl/QuantifiedComparisonCondition.java b/jOOQ/src/main/java/org/jooq/impl/QuantifiedComparisonCondition.java index 33f7b01aa4..70d22b6a8a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/QuantifiedComparisonCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/QuantifiedComparisonCondition.java @@ -41,27 +41,45 @@ package org.jooq.impl; import static org.jooq.Clause.CONDITION; import static org.jooq.Clause.CONDITION_BETWEEN; +import static org.jooq.impl.DSL.inline; +import static org.jooq.impl.DSL.name; import static org.jooq.impl.DSL.row; +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.isEmbeddable; +import static org.jooq.tools.Convert.convert; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; import org.jooq.Clause; import org.jooq.Comparator; +import org.jooq.Condition; import org.jooq.Context; import org.jooq.Field; +import org.jooq.LikeEscapeStep; +import org.jooq.Operator; +import org.jooq.Param; import org.jooq.QuantifiedSelect; +import org.jooq.Record1; +import org.jooq.Select; +import org.jooq.Table; /** * @author Lukas Eder */ -final class QuantifiedComparisonCondition extends AbstractCondition { +final class QuantifiedComparisonCondition extends AbstractCondition implements LikeEscapeStep { - private static final long serialVersionUID = -402776705884329740L; - private static final Clause[] CLAUSES = { CONDITION, CONDITION_BETWEEN }; + private static final long serialVersionUID = -402776705884329740L; + private static final Clause[] CLAUSES = { CONDITION, CONDITION_BETWEEN }; + private static final EnumSet SYNTHETIC_OPERATORS = EnumSet.of(Comparator.LIKE, Comparator.NOT_LIKE, Comparator.LIKE_IGNORE_CASE, Comparator.NOT_LIKE_IGNORE_CASE, Comparator.SIMILAR_TO, Comparator.NOT_SIMILAR_TO); - private final QuantifiedSelect query; - private final Field field; - private final Comparator comparator; + private final QuantifiedSelect query; + private final Field field; + private final Comparator comparator; + private Character escape; QuantifiedComparisonCondition(QuantifiedSelect query, Field field, Comparator comparator) { this.query = query; @@ -69,6 +87,12 @@ final class QuantifiedComparisonCondition extends AbstractCondition { this.comparator = comparator; } + @Override + public Condition escape(char c) { + this.escape = c; + return this; + } + @Override public final void accept(Context ctx) { if (isEmbeddable(field)) @@ -77,12 +101,135 @@ final class QuantifiedComparisonCondition extends AbstractCondition { accept0(ctx); } + @SuppressWarnings({ "unchecked", "null" }) private final void accept0(Context ctx) { + Field array = ((QuantifiedSelectImpl) query).array; + Field[] values = (Field[]) ((QuantifiedSelectImpl) query).values; + Quantifier quantifier = ((QuantifiedSelectImpl) query).quantifier; + Select subquery = ((QuantifiedSelectImpl) query).query; + + if (values != null || array instanceof Param) { + List conditions = new ArrayList(); + if (values != null) + for (Field value : values) + conditions.add(comparisonCondition(comparator, value)); + else + for (Object value : ((Param) array).getValue()) + conditions.add(value instanceof Field ? comparisonCondition(comparator, (Field) value) : comparisonCondition(comparator, value)); + + Condition combinedCondition = CombinedCondition.of(quantifier == Quantifier.ALL ? Operator.AND : Operator.OR, conditions); + ctx.visit(combinedCondition); + } + else if ((array != null || subquery != null) && SYNTHETIC_OPERATORS.contains(comparator)) { + Field pattern = DSL.field(name("pattern"), VARCHAR); + Condition cond; + Field lhs; + switch (comparator) { + case NOT_LIKE: + case NOT_SIMILAR_TO: + case NOT_LIKE_IGNORE_CASE: + cond = comparisonCondition(inverse(comparator), pattern); + lhs = inline(false); + break; + case LIKE: + case SIMILAR_TO: + case LIKE_IGNORE_CASE: + cond = comparisonCondition(comparator, pattern); + lhs = inline(true); + break; + default: + throw new IllegalStateException(); + } + + Table t = (array != null ? new ArrayTable(array) : subquery).asTable("t", "pattern"); + Select> select = select(DSL.field(cond)).from(t); + ctx.visit(lhs.eq(quantifier.apply(select))); + } + else { + accept1(ctx); + } + } + + private final void accept1(Context ctx) { ctx.visit(field) - .sql(' ') - .visit(comparator.toKeyword()) - .sql(' ') - .visit(query); + .sql(' ') + .visit(comparator.toKeyword()) + .sql(' ') + .visit(query); + } + + private Comparator inverse(Comparator operator) { + switch (operator) { + case IN: return Comparator.NOT_IN; + case NOT_IN: return Comparator.IN; + case EQUALS: return Comparator.NOT_EQUALS; + case NOT_EQUALS: return Comparator.EQUALS; + case LESS: return Comparator.GREATER_OR_EQUAL; + case LESS_OR_EQUAL: return Comparator.GREATER; + case GREATER: return Comparator.LESS_OR_EQUAL; + case GREATER_OR_EQUAL: return Comparator.LESS; + case IS_DISTINCT_FROM: return Comparator.IS_NOT_DISTINCT_FROM; + case IS_NOT_DISTINCT_FROM: return Comparator.IS_DISTINCT_FROM; + case LIKE: return Comparator.NOT_LIKE; + case NOT_LIKE: return Comparator.LIKE; + case SIMILAR_TO: return Comparator.NOT_SIMILAR_TO; + case NOT_SIMILAR_TO: return Comparator.SIMILAR_TO; + case LIKE_IGNORE_CASE: return Comparator.NOT_LIKE_IGNORE_CASE; + case NOT_LIKE_IGNORE_CASE: return Comparator.LIKE_IGNORE_CASE; + default: throw new IllegalStateException(); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private Condition comparisonCondition(Comparator operator, Field value) { + switch (operator) { + case LIKE: + return escape != null ? field.like(value, escape) : field.like(value); + + case NOT_LIKE: + return escape != null ? field.notLike(value, escape) : field.notLike(value); + + case SIMILAR_TO: + return escape != null ? field.similarTo(value, escape) : field.similarTo(value); + + case NOT_SIMILAR_TO: + return escape != null ? field.notSimilarTo(value, escape) : field.notSimilarTo(value); + + case LIKE_IGNORE_CASE: + return escape != null ? field.likeIgnoreCase(value, escape) : field.likeIgnoreCase(value); + + case NOT_LIKE_IGNORE_CASE: + return escape != null ? field.notLikeIgnoreCase(value, escape) : field.notLikeIgnoreCase(value); + + default: + return ((Field) field).compare(operator, value); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private Condition comparisonCondition(Comparator operator, Object value) { + switch (operator) { + case LIKE: + return escape != null ? field.like(convert(value, String.class), escape) : field.like(convert(value, String.class)); + + case NOT_LIKE: + return escape != null ? field.notLike(convert(value, String.class), escape) : field.notLike(convert(value, String.class)); + + case SIMILAR_TO: + return escape != null ? field.similarTo(convert(value, String.class), escape) : field.similarTo(convert(value, String.class)); + + case NOT_SIMILAR_TO: + return escape != null ? field.notSimilarTo(convert(value, String.class), escape) : field.notSimilarTo(convert(value, String.class)); + + case LIKE_IGNORE_CASE: + return escape != null ? field.likeIgnoreCase(convert(value, String.class), escape) : field.likeIgnoreCase(convert(value, String.class)); + + case NOT_LIKE_IGNORE_CASE: + return escape != null ? field.notLikeIgnoreCase(convert(value, String.class), escape) : field.notLikeIgnoreCase(convert(value, String.class)); + + default: + return ((Field) field).compare(operator, value); + } } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/QuantifiedSelectImpl.java b/jOOQ/src/main/java/org/jooq/impl/QuantifiedSelectImpl.java index 9c313d9b67..47982a95f1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/QuantifiedSelectImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/QuantifiedSelectImpl.java @@ -48,7 +48,7 @@ import org.jooq.Context; import org.jooq.Field; import org.jooq.Param; import org.jooq.QuantifiedSelect; -import org.jooq.QueryPartInternal; +import org.jooq.QueryPart; import org.jooq.Record; import org.jooq.Record1; import org.jooq.Select; @@ -62,22 +62,32 @@ final class QuantifiedSelectImpl extends AbstractQueryPart imp /** * Generated UID */ - private static final long serialVersionUID = -1224570388944748450L; + private static final long serialVersionUID = -1224570388944748450L; - private final Quantifier quantifier; - private final Select query; - private final Field array; + final Quantifier quantifier; + final Select query; + final Field array; + final Field[] values; QuantifiedSelectImpl(Quantifier quantifier, Select query) { this.quantifier = quantifier; this.query = query; this.array = null; + this.values = null; } QuantifiedSelectImpl(Quantifier quantifier, Field array) { this.quantifier = quantifier; this.query = null; this.array = array; + this.values = null; + } + + QuantifiedSelectImpl(Quantifier quantifier, Field... values) { + this.quantifier = quantifier; + this.query = null; + this.array = null; + this.values = values; } @Override @@ -106,9 +116,20 @@ final class QuantifiedSelectImpl extends AbstractQueryPart imp } - private final QueryPartInternal delegate(Configuration ctx) { + @SuppressWarnings({ "rawtypes", "unchecked" }) + private final QueryPart delegate(Configuration ctx) { if (query != null) { - return (QueryPartInternal) query; + return query; + } + else if (values != null) { + Select> select = null; + for (Field value : values) + if (select == null) + select = select(value); + else + select = select.unionAll(select(value)); + + return select; } else { switch (ctx.family()) { @@ -118,14 +139,14 @@ final class QuantifiedSelectImpl extends AbstractQueryPart imp case POSTGRES: { - return (QueryPartInternal) array; + return array; } // [#869] H2 and HSQLDB can emulate this syntax by unnesting // the array in a subselect case H2: case HSQLDB: - return (QueryPartInternal) create(ctx).select().from(table(array)); + return create(ctx).select().from(table(array)); // [#1048] All other dialects emulate unnesting of arrays using // UNION ALL-connected subselects @@ -134,19 +155,19 @@ final class QuantifiedSelectImpl extends AbstractQueryPart imp // The Informix database has an interesting bug when quantified comparison predicates // use nested derived tables with UNION ALL if (array instanceof Param) { - Object[] values = ((Param) array).getValue(); + Object[] values0 = ((Param) array).getValue(); Select> select = null; - for (Object value : values) + for (Object value : values0) if (select == null) select = select(val(value)); else select = select.unionAll(select(val(value))); - return (QueryPartInternal) select; + return select; } else { - return (QueryPartInternal) select().from(table(array)); + return select().from(table(array)); } } } diff --git a/jOOQ/src/main/java/org/jooq/impl/Quantifier.java b/jOOQ/src/main/java/org/jooq/impl/Quantifier.java index 8e1cdb167d..c946968793 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Quantifier.java +++ b/jOOQ/src/main/java/org/jooq/impl/Quantifier.java @@ -37,7 +37,13 @@ */ package org.jooq.impl; +import static org.jooq.impl.DSL.all; +import static org.jooq.impl.DSL.any; + import org.jooq.Keyword; +import org.jooq.QuantifiedSelect; +import org.jooq.Record; +import org.jooq.Select; /** * A quantifier used for quantified comparison predicates @@ -73,4 +79,15 @@ enum Quantifier { public final Keyword toKeyword() { return keyword; } + + public QuantifiedSelect apply(Select select) { + switch (this) { + case ANY: + return any(select); + case ALL: + return all(select); + default: + throw new IllegalStateException(); + } + } }