From b46db7c8c533d580741c3a1098d5987599e55f3c Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Sun, 13 May 2012 21:09:26 +0200 Subject: [PATCH] [#1423] Add Field.likeIgnoreCase() to support Postgres' ILIKE operator --- .../jooq/test/_/testcases/PredicateTests.java | 11 +++ jOOQ/src/main/java/org/jooq/Comparator.java | 10 ++- jOOQ/src/main/java/org/jooq/Field.java | 88 +++++++++++++++++++ .../java/org/jooq/impl/AbstractField.java | 40 +++++++++ .../java/org/jooq/impl/CompareCondition.java | 26 ++++-- 5 files changed, 168 insertions(+), 7 deletions(-) diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/PredicateTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/PredicateTests.java index ffbcb89b7d..34f9770ab6 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/PredicateTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/PredicateTests.java @@ -267,6 +267,17 @@ extends BaseTest extends NamedTypeProviderQueryPart, AliasProvider + * This translates to this ilike value in + * {@link SQLDialect#POSTGRES}, or to + * lower(this) like lower(value) in all other dialects. + */ + @Support + Condition likeIgnoreCase(Field value); + + /** + * Create a condition to case-insensitively pattern-check this field against + * a value + *

+ * This translates to this ilike value in + * {@link SQLDialect#POSTGRES}, or to + * lower(this) like lower(value) in all other dialects. + */ + @Support + Condition likeIgnoreCase(Field value, char escape); + + /** + * Create a condition to case-insensitively pattern-check this field against + * a value + *

+ * This translates to this ilike value in + * {@link SQLDialect#POSTGRES}, or to + * lower(this) like lower(value) in all other dialects. + */ + @Support + Condition likeIgnoreCase(String value); + + /** + * Create a condition to case-insensitively pattern-check this field against + * a value + *

+ * This translates to this ilike value in + * {@link SQLDialect#POSTGRES}, or to + * lower(this) like lower(value) in all other dialects. + */ + @Support + Condition likeIgnoreCase(String value, char escape); + /** * Create a condition to pattern-check this field against a value *

@@ -561,6 +605,50 @@ public interface Field extends NamedTypeProviderQueryPart, AliasProvider + * This translates to this not ilike value in + * {@link SQLDialect#POSTGRES}, or to + * lower(this) not like lower(value) in all other dialects. + */ + @Support + Condition notLikeIgnoreCase(Field value); + + /** + * Create a condition to case-insensitively pattern-check this field against + * a value + *

+ * This translates to this not ilike value in + * {@link SQLDialect#POSTGRES}, or to + * lower(this) not like lower(value) in all other dialects. + */ + @Support + Condition notLikeIgnoreCase(Field value, char escape); + + /** + * Create a condition to case-insensitively pattern-check this field against + * a value + *

+ * This translates to this not ilike value in + * {@link SQLDialect#POSTGRES}, or to + * lower(this) not like lower(value) in all other dialects. + */ + @Support + Condition notLikeIgnoreCase(String value); + + /** + * Create a condition to case-insensitively pattern-check this field against + * a value + *

+ * This translates to this not ilike value in + * {@link SQLDialect#POSTGRES}, or to + * lower(this) not like lower(value) 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 diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java index 195b186fcf..478c0c588e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java @@ -341,6 +341,26 @@ abstract class AbstractField extends AbstractNamedTypeProviderQueryPart 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 value) { + return new CompareCondition(this, nullSafe(value), Comparator.LIKE_IGNORE_CASE); + } + + @Override + public final Condition likeIgnoreCase(Field 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 extends AbstractNamedTypeProviderQueryPart 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 value) { + return new CompareCondition(this, nullSafe(value), Comparator.NOT_LIKE_IGNORE_CASE); + } + + @Override + public final Condition notLikeIgnoreCase(Field 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(this, value); diff --git a/jOOQ/src/main/java/org/jooq/impl/CompareCondition.java b/jOOQ/src/main/java/org/jooq/impl/CompareCondition.java index 40dec91340..4677c3355f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CompareCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/CompareCondition.java @@ -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) {