[jOOQ/jOOQ#8577] Add Field#likeAny() and #notLikeAll()

(work by @knutwannheden)
This commit is contained in:
Lukas Eder 2019-06-26 10:38:14 +02:00
parent c5c8d1d256
commit 1fbc546b4a
4 changed files with 262 additions and 10 deletions

View File

@ -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.
* <p>
* SQL: <code>(this like value0 or this like value1 or ...)</code>
*
* @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.
* <p>
* SQL: <code>(this like field0 or this like field1 or ...)</code>
*
* @see LikeEscapeStep#escape(char)
*/
@Support
@SuppressWarnings("unchecked")
LikeEscapeStep likeAny(Field<String>... fields);
/**
* Create a condition to pattern-check this field against any element in a collection of fields or values.
* <p>
* SQL: <code>(this like field0 or this like field1 or ...)</code>
*
* @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.
* <p>
* SQL: <code>(this not like value0 and this not like value1 and ...)</code>
*
* @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.
* <p>
* SQL: <code>(this not like field0 and this not like field1 and ...)</code>
*
* @see LikeEscapeStep#escape(char)
*/
@Support
@SuppressWarnings("unchecked")
LikeEscapeStep notLikeAll(Field<String>... fields);
/**
* Create a condition to pattern-check this field against all elements in a collection of fields or values.
* <p>
* SQL: <code>(this not like field0 and this not like field1 and ...)</code>
*
* @see LikeEscapeStep#escape(char)
*/
@Support
LikeEscapeStep notLikeAll(Collection<?> values);
/**
* Convenience method for {@link #like(String, char)} including proper
* adding of wildcards and escaping.

View File

@ -852,6 +852,40 @@ abstract class AbstractField<T> extends AbstractNamed implements Field<T> {
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<String>... fields) {
return likeAny(Arrays.asList(fields));
}
@Override
@SuppressWarnings("unchecked")
public final LikeEscapeStep likeAny(Collection<?> values) {
return new CombinedCompareCondition(this, LIKE, Quantifier.ANY, (Collection<? extends Field<String>>) 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<String>... fields) {
return notLikeAll(Arrays.asList(fields));
}
@Override
@SuppressWarnings("unchecked")
public final LikeEscapeStep notLikeAll(Collection<?> values) {
return new CombinedCompareCondition(this, NOT_LIKE, Quantifier.ALL, (Collection<? extends Field<String>>) Tools.fields(values, SQLDataType.VARCHAR));
}
@Override
public final Condition notLikeRegex(String pattern) {
return likeRegex(pattern).not();

View File

@ -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<? extends Field<String>> values;
private Character escape;
public CombinedCompareCondition(Field<?> field, Comparator comparator, Quantifier quantifier, Collection<? extends Field<String>> 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<Condition> conditions = new ArrayList<Condition>();
switch (comparator) {
case LIKE:
for (Field<String> value : values)
conditions.add(escape != null ? field.like(value, escape) : field.like(value));
break;
case NOT_LIKE:
for (Field<String> value : values)
conditions.add(escape != null ? field.notLike(value, escape) : field.notLike(value));
break;
case SIMILAR_TO:
for (Field<String> value : values)
conditions.add(escape != null ? field.similarTo(value, escape) : field.similarTo(value));
break;
case NOT_SIMILAR_TO:
for (Field<String> value : values)
conditions.add(escape != null ? field.notSimilarTo(value, escape) : field.notSimilarTo(value));
break;
case LIKE_IGNORE_CASE:
for (Field<String> value : values)
conditions.add(escape != null ? field.likeIgnoreCase(value, escape) : field.likeIgnoreCase(value));
break;
case NOT_LIKE_IGNORE_CASE:
for (Field<String> 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);
}
}

View File

@ -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<Field<?>> fields = null;
if (parseIf(ctx, ')'))
fields = Collections.<Field<?>> emptyList();
else {
fields = new ArrayList<Field<?>>();
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<Field<?>> fields = null;
if (parseIf(ctx, ')'))
fields = Collections.<Field<?>> emptyList();
else {
fields = new ArrayList<Field<?>>();
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, ',');