[#1423] Add Field.likeIgnoreCase() to support Postgres' ILIKE operator

This commit is contained in:
Lukas Eder 2012-05-13 21:09:26 +02:00
parent 6eb56640df
commit b46db7c8c5
5 changed files with 168 additions and 7 deletions

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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) {