diff --git a/jOOQ/src/main/java/org/jooq/Field.java b/jOOQ/src/main/java/org/jooq/Field.java index b26e9d0d85..ee641853d2 100644 --- a/jOOQ/src/main/java/org/jooq/Field.java +++ b/jOOQ/src/main/java/org/jooq/Field.java @@ -1627,6 +1627,68 @@ extends @Support({ CUBRID, DERBY, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES, SQLITE }) 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 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 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 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 4764974d83..05f57ea6a5 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java @@ -852,6 +852,40 @@ 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 + @SuppressWarnings("unchecked") + public final LikeEscapeStep likeAny(Collection values) { + return new CombinedCompareCondition(this, LIKE, Quantifier.ANY, (Collection>) 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 + @SuppressWarnings("unchecked") + public final LikeEscapeStep notLikeAll(Collection values) { + return new CombinedCompareCondition(this, NOT_LIKE, Quantifier.ALL, (Collection>) 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/CombinedCompareCondition.java b/jOOQ/src/main/java/org/jooq/impl/CombinedCompareCondition.java new file mode 100644 index 0000000000..5ba9928271 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/CombinedCompareCondition.java @@ -0,0 +1,116 @@ +/* + * 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.ArrayList; +import java.util.Collection; + +import org.jooq.Comparator; +import org.jooq.Condition; +import org.jooq.Context; +import org.jooq.Field; +import org.jooq.LikeEscapeStep; +import org.jooq.Operator; + +final class CombinedCompareCondition extends AbstractCondition implements LikeEscapeStep { + + private static final long serialVersionUID = 1850816878293293314L; + + private final Field field; + private final Comparator comparator; + private final Quantifier quantifier; + private final Collection> values; + private Character escape; + + public CombinedCompareCondition(Field field, Comparator comparator, Quantifier quantifier, Collection> values) { + this.field = field; + this.comparator = comparator; + this.quantifier = quantifier; + this.values = values; + } + + @Override + public final Condition escape(char c) { + this.escape = c; + return this; + } + + @Override + public final void accept(Context ctx) { + Collection conditions = new ArrayList(); + + switch (comparator) { + case LIKE: + for (Field value : values) + conditions.add(escape != null ? field.like(value, escape) : field.like(value)); + break; + + case NOT_LIKE: + for (Field value : values) + conditions.add(escape != null ? field.notLike(value, escape) : field.notLike(value)); + break; + + case SIMILAR_TO: + for (Field value : values) + conditions.add(escape != null ? field.similarTo(value, escape) : field.similarTo(value)); + break; + + case NOT_SIMILAR_TO: + for (Field value : values) + conditions.add(escape != null ? field.notSimilarTo(value, escape) : field.notSimilarTo(value)); + break; + + case LIKE_IGNORE_CASE: + for (Field value : values) + conditions.add(escape != null ? field.likeIgnoreCase(value, escape) : field.likeIgnoreCase(value)); + break; + + case NOT_LIKE_IGNORE_CASE: + for (Field value : values) + conditions.add(escape != null ? field.notLikeIgnoreCase(value, escape) : field.notLikeIgnoreCase(value)); + break; + + default: + break; + } + + Condition combinedCondition = CombinedCondition.of(quantifier == Quantifier.ALL ? Operator.AND : Operator.OR, conditions); + ctx.visit(combinedCondition); + } + +} diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index 7ec4830d2f..efb074bc02 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -400,6 +400,7 @@ import org.jooq.InsertValuesStepN; import org.jooq.JoinType; import org.jooq.Keyword; // ... +import org.jooq.LikeEscapeStep; // ... import org.jooq.Merge; import org.jooq.MergeFinalStep; @@ -4388,16 +4389,54 @@ final class ParserImpl implements Parser { : ((RowN) left).between((RowN) r1, (RowN) r2); } else if (left instanceof Field && parseKeywordIf(ctx, "LIKE")) { - Field right = toField(ctx, parseConcat(ctx, null)); - boolean escape = parseKeywordIf(ctx, "ESCAPE"); - char character = escape ? parseCharacterLiteral(ctx) : ' '; - return escape - ? not - ? ((Field) left).notLike(right, character) - : ((Field) left).like(right, character) - : not - ? ((Field) left).notLike(right) - : ((Field) left).like(right); + if (!not && 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, ',')); + parse(ctx, ')'); + } + boolean escape = parseKeywordIf(ctx, "ESCAPE"); + char character = escape ? parseCharacterLiteral(ctx) : ' '; + LikeEscapeStep result = ((Field) left).likeAny(fields); + return escape ? result.escape(character) : result; + } + else if (not && 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, ',')); + parse(ctx, ')'); + } + boolean escape = parseKeywordIf(ctx, "ESCAPE"); + char character = escape ? parseCharacterLiteral(ctx) : ' '; + LikeEscapeStep result = ((Field) left).notLikeAll(fields); + return escape ? result.escape(character) : result; + } + else { + Field right = toField(ctx, parseConcat(ctx, null)); + boolean escape = parseKeywordIf(ctx, "ESCAPE"); + char character = escape ? parseCharacterLiteral(ctx) : ' '; + return escape + ? not + ? ((Field) left).notLike(right, character) + : ((Field) left).like(right, character) + : not + ? ((Field) left).notLike(right) + : ((Field) left).like(right); + } } else if (left instanceof Field && parseKeywordIf(ctx, "ILIKE")) { Field right = toField(ctx, parseConcat(ctx, null)); @@ -6128,6 +6167,7 @@ final class ParserImpl implements Parser { + default: Field base = toField(ctx, parseNumericOp(ctx, N)); parse(ctx, ',');