[#1423] Add Field.likeIgnoreCase() to support Postgres' ILIKE operator
This commit is contained in:
parent
6eb56640df
commit
b46db7c8c5
@ -267,6 +267,17 @@ extends BaseTest<A, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T658, T725
|
||||
|
||||
assertEquals(1, books.size());
|
||||
assertEquals(asList(3), books.getValues(TBook_ID()));
|
||||
|
||||
// [#1423] Add checks for the ILIKE operator
|
||||
// -----------------------------------------
|
||||
books =
|
||||
create().selectFrom(TBook())
|
||||
.where(TBook_TITLE().likeIgnoreCase("%IM%"))
|
||||
.and(TBook_TITLE().notLikeIgnoreCase("%o%"))
|
||||
.fetch();
|
||||
|
||||
assertEquals(1, books.size());
|
||||
assertEquals(asList(2), books.getValues(TBook_ID()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -65,7 +65,15 @@ public enum Comparator {
|
||||
LIKE("like"),
|
||||
|
||||
@Support
|
||||
NOT_LIKE("not like");
|
||||
NOT_LIKE("not like"),
|
||||
|
||||
@Support
|
||||
LIKE_IGNORE_CASE("ilike"),
|
||||
|
||||
@Support
|
||||
NOT_LIKE_IGNORE_CASE("not ilike"),
|
||||
|
||||
;
|
||||
|
||||
private final String sql;
|
||||
|
||||
|
||||
@ -529,6 +529,50 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
|
||||
@Support
|
||||
Condition like(String value, char escape);
|
||||
|
||||
/**
|
||||
* Create a condition to case-insensitively pattern-check this field against
|
||||
* a value
|
||||
* <p>
|
||||
* This translates to <code>this ilike value</code> in
|
||||
* {@link SQLDialect#POSTGRES}, or to
|
||||
* <code>lower(this) like lower(value)</code> in all other dialects.
|
||||
*/
|
||||
@Support
|
||||
Condition likeIgnoreCase(Field<String> value);
|
||||
|
||||
/**
|
||||
* Create a condition to case-insensitively pattern-check this field against
|
||||
* a value
|
||||
* <p>
|
||||
* This translates to <code>this ilike value</code> in
|
||||
* {@link SQLDialect#POSTGRES}, or to
|
||||
* <code>lower(this) like lower(value)</code> in all other dialects.
|
||||
*/
|
||||
@Support
|
||||
Condition likeIgnoreCase(Field<String> value, char escape);
|
||||
|
||||
/**
|
||||
* Create a condition to case-insensitively pattern-check this field against
|
||||
* a value
|
||||
* <p>
|
||||
* This translates to <code>this ilike value</code> in
|
||||
* {@link SQLDialect#POSTGRES}, or to
|
||||
* <code>lower(this) like lower(value)</code> in all other dialects.
|
||||
*/
|
||||
@Support
|
||||
Condition likeIgnoreCase(String value);
|
||||
|
||||
/**
|
||||
* Create a condition to case-insensitively pattern-check this field against
|
||||
* a value
|
||||
* <p>
|
||||
* This translates to <code>this ilike value</code> in
|
||||
* {@link SQLDialect#POSTGRES}, or to
|
||||
* <code>lower(this) like lower(value)</code> in all other dialects.
|
||||
*/
|
||||
@Support
|
||||
Condition likeIgnoreCase(String value, char escape);
|
||||
|
||||
/**
|
||||
* Create a condition to pattern-check this field against a value
|
||||
* <p>
|
||||
@ -561,6 +605,50 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
|
||||
@Support
|
||||
Condition notLike(String value, char escape);
|
||||
|
||||
/**
|
||||
* Create a condition to case-insensitively pattern-check this field against
|
||||
* a value
|
||||
* <p>
|
||||
* This translates to <code>this not ilike value</code> in
|
||||
* {@link SQLDialect#POSTGRES}, or to
|
||||
* <code>lower(this) not like lower(value)</code> in all other dialects.
|
||||
*/
|
||||
@Support
|
||||
Condition notLikeIgnoreCase(Field<String> value);
|
||||
|
||||
/**
|
||||
* Create a condition to case-insensitively pattern-check this field against
|
||||
* a value
|
||||
* <p>
|
||||
* This translates to <code>this not ilike value</code> in
|
||||
* {@link SQLDialect#POSTGRES}, or to
|
||||
* <code>lower(this) not like lower(value)</code> in all other dialects.
|
||||
*/
|
||||
@Support
|
||||
Condition notLikeIgnoreCase(Field<String> value, char escape);
|
||||
|
||||
/**
|
||||
* Create a condition to case-insensitively pattern-check this field against
|
||||
* a value
|
||||
* <p>
|
||||
* This translates to <code>this not ilike value</code> in
|
||||
* {@link SQLDialect#POSTGRES}, or to
|
||||
* <code>lower(this) not like lower(value)</code> in all other dialects.
|
||||
*/
|
||||
@Support
|
||||
Condition notLikeIgnoreCase(String value);
|
||||
|
||||
/**
|
||||
* Create a condition to case-insensitively pattern-check this field against
|
||||
* a value
|
||||
* <p>
|
||||
* This translates to <code>this not ilike value</code> in
|
||||
* {@link SQLDialect#POSTGRES}, or to
|
||||
* <code>lower(this) not like lower(value)</code> in all other dialects.
|
||||
*/
|
||||
@Support
|
||||
Condition notLikeIgnoreCase(String value, char escape);
|
||||
|
||||
/**
|
||||
* Convenience method for {@link #like(String, char)} including proper
|
||||
* adding of wildcards and escaping
|
||||
|
||||
@ -341,6 +341,26 @@ abstract class AbstractField<T> extends AbstractNamedTypeProviderQueryPart<T> im
|
||||
return new CompareCondition(this, nullSafe(value), Comparator.LIKE, escape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Condition likeIgnoreCase(String value) {
|
||||
return likeIgnoreCase(val(value, String.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Condition likeIgnoreCase(String value, char escape) {
|
||||
return likeIgnoreCase(val(value, String.class), escape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Condition likeIgnoreCase(Field<String> value) {
|
||||
return new CompareCondition(this, nullSafe(value), Comparator.LIKE_IGNORE_CASE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Condition likeIgnoreCase(Field<String> value, char escape) {
|
||||
return new CompareCondition(this, nullSafe(value), Comparator.LIKE_IGNORE_CASE, escape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Condition notLike(String value) {
|
||||
return notLike(val(value, String.class));
|
||||
@ -361,6 +381,26 @@ abstract class AbstractField<T> extends AbstractNamedTypeProviderQueryPart<T> im
|
||||
return new CompareCondition(this, nullSafe(value), Comparator.NOT_LIKE, escape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Condition notLikeIgnoreCase(String value) {
|
||||
return notLikeIgnoreCase(val(value, String.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Condition notLikeIgnoreCase(String value, char escape) {
|
||||
return notLikeIgnoreCase(val(value, String.class), escape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Condition notLikeIgnoreCase(Field<String> value) {
|
||||
return new CompareCondition(this, nullSafe(value), Comparator.NOT_LIKE_IGNORE_CASE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Condition notLikeIgnoreCase(Field<String> value, char escape) {
|
||||
return new CompareCondition(this, nullSafe(value), Comparator.NOT_LIKE_IGNORE_CASE, escape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Condition contains(T value) {
|
||||
return new Contains<T>(this, value);
|
||||
|
||||
@ -39,8 +39,10 @@ package org.jooq.impl;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.jooq.Comparator.EQUALS;
|
||||
import static org.jooq.Comparator.LIKE;
|
||||
import static org.jooq.Comparator.LIKE_IGNORE_CASE;
|
||||
import static org.jooq.Comparator.NOT_EQUALS;
|
||||
import static org.jooq.Comparator.NOT_LIKE;
|
||||
import static org.jooq.Comparator.NOT_LIKE_IGNORE_CASE;
|
||||
import static org.jooq.SQLDialect.ASE;
|
||||
import static org.jooq.SQLDialect.DB2;
|
||||
import static org.jooq.SQLDialect.DERBY;
|
||||
@ -97,22 +99,34 @@ class CompareCondition extends AbstractCondition {
|
||||
public final void toSQL(RenderContext context) {
|
||||
SQLDialect dialect = context.getDialect();
|
||||
Field<?> lhs = field1;
|
||||
Field<?> rhs = field2;
|
||||
Comparator op = comparator;
|
||||
|
||||
// [#1159] Some dialects cannot auto-convert the LHS operand to a
|
||||
// VARCHAR when applying a LIKE predicate
|
||||
// [#293] TODO: This could apply to other operators, too
|
||||
if ((comparator == LIKE || comparator == NOT_LIKE)
|
||||
if ((op == LIKE || op == NOT_LIKE)
|
||||
&& field1.getType() != String.class
|
||||
&& asList(ASE, DERBY, POSTGRES).contains(dialect)) {
|
||||
|
||||
lhs = lhs.cast(String.class);
|
||||
}
|
||||
|
||||
// [#1423] Only Postgres knows a true ILIKE operator. Other dialects
|
||||
// need to simulate this as LOWER(lhs) LIKE LOWER(rhs)
|
||||
else if ((op == LIKE_IGNORE_CASE || op == NOT_LIKE_IGNORE_CASE)
|
||||
&& POSTGRES != dialect) {
|
||||
|
||||
lhs = lhs.lower();
|
||||
rhs = rhs.lower();
|
||||
op = (op == LIKE_IGNORE_CASE ? LIKE : NOT_LIKE);
|
||||
}
|
||||
|
||||
context.sql(lhs)
|
||||
.sql(" ");
|
||||
|
||||
if (field2.isNullLiteral()) {
|
||||
switch (comparator) {
|
||||
if (rhs.isNullLiteral()) {
|
||||
switch (op) {
|
||||
case EQUALS:
|
||||
context.keyword("is null");
|
||||
return;
|
||||
@ -126,12 +140,12 @@ class CompareCondition extends AbstractCondition {
|
||||
// [#1131] Some weird DB2 issue stops "LIKE" from working with a
|
||||
// concatenated search expression, if the expression is more than 4000
|
||||
// characters long
|
||||
boolean castRhs = (dialect == DB2 && field2 instanceof Concat);
|
||||
boolean castRhs = (dialect == DB2 && rhs instanceof Concat);
|
||||
|
||||
context.keyword(comparator.toSQL())
|
||||
context.keyword(op.toSQL())
|
||||
.sql(" ")
|
||||
.keyword(castRhs ? "cast(" : "")
|
||||
.sql(field2)
|
||||
.sql(rhs)
|
||||
.keyword(castRhs ? " as varchar(4000))" : "");
|
||||
|
||||
if (escape != null) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user