[#1159] Support matching numbers with LIKE, e.g. ID LIKE '%33%'

This commit is contained in:
Lukas Eder 2012-02-17 10:20:05 +00:00
parent 283a43664e
commit 5cd584787e
4 changed files with 85 additions and 41 deletions

View File

@ -168,6 +168,7 @@ extends BaseTest<A, B, S, B2S, BS, L, X, D, T, U, I, IPK, T658, T725, T639, T785
.execute());
// [#1072] Add checks for ESCAPE syntax
// ------------------------------------
books =
create().selectFrom(TBook())
.where(TBook_TITLE().like("%(!%)%", '!'))
@ -184,6 +185,7 @@ extends BaseTest<A, B, S, B2S, BS, L, X, D, T, U, I, IPK, T658, T725, T639, T785
boolean derby = getDialect() == DERBY;
// [#1131] DB2 doesn't like concat in LIKE expressions very much
// -------------------------------------------------------------
books =
create().selectFrom(TBook())
.where(TBook_TITLE().like(concat("19", "84")))
@ -195,6 +197,7 @@ extends BaseTest<A, B, S, B2S, BS, L, X, D, T, U, I, IPK, T658, T725, T639, T785
assertEquals(1, (int) books.get(0).getValue(TBook_ID()));
// [#1106] Add checks for Factory.escape() function
// ------------------------------------------------
books =
create().selectFrom(TBook())
.where(TBook_TITLE().like(concat("%", escape("(%)", '!'), "%"), '!'))
@ -209,6 +212,7 @@ extends BaseTest<A, B, S, B2S, BS, L, X, D, T, U, I, IPK, T658, T725, T639, T785
assertEquals(5, (int) books.get(0).getValue(TBook_ID()));
// [#1089] Add checks for convenience methods
// ------------------------------------------
books =
create().selectFrom(TBook())
.where(TBook_TITLE().contains("%"))
@ -224,6 +228,27 @@ extends BaseTest<A, B, S, B2S, BS, L, X, D, T, U, I, IPK, T658, T725, T639, T785
assertEquals(1, books.size());
assertEquals(5, (int) books.get(0).getValue(TBook_ID()));
// [#1159] Add checks for matching numbers with LIKE
// -------------------------------------------------
books =
create().selectFrom(TBook())
.where(TBook_PUBLISHED_IN().like("194%"))
.orderBy(TBook_ID())
.fetch();
assertEquals(2, books.size());
assertEquals(asList(1, 2), books.getValues(TBook_ID()));
books =
create().selectFrom(TBook())
.where(TBook_PUBLISHED_IN().like("%9%"))
.and(TBook_PUBLISHED_IN().notLike("%8%"))
.orderBy(TBook_ID())
.fetch();
assertEquals(2, books.size());
assertEquals(asList(2, 4), books.getValues(TBook_ID()));
}
@Test

View File

@ -417,7 +417,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
* SQL: <code>this like value</code>
*/
@Support
Condition like(Field<T> value);
Condition like(Field<String> value);
/**
* Create a condition to pattern-check this field against a value
@ -425,7 +425,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
* SQL: <code>this like value escape 'e'</code>
*/
@Support
Condition like(Field<T> value, char escape);
Condition like(Field<String> value, char escape);
/**
* Create a condition to pattern-check this field against a value
@ -433,7 +433,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
* SQL: <code>this like value</code>
*/
@Support
Condition like(T value);
Condition like(String value);
/**
* Create a condition to pattern-check this field against a value
@ -441,7 +441,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
* SQL: <code>this like value escape 'e'</code>
*/
@Support
Condition like(T value, char escape);
Condition like(String value, char escape);
/**
* Create a condition to pattern-check this field against a value
@ -449,7 +449,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
* SQL: <code>this not like value</code>
*/
@Support
Condition notLike(Field<T> value);
Condition notLike(Field<String> value);
/**
* Create a condition to pattern-check this field against a value
@ -457,7 +457,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
* SQL: <code>this not like value escape 'e'</code>
*/
@Support
Condition notLike(Field<T> value, char escape);
Condition notLike(Field<String> value, char escape);
/**
* Create a condition to pattern-check this field against a value
@ -465,7 +465,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
* SQL: <code>this not like value</code>
*/
@Support
Condition notLike(T value);
Condition notLike(String value);
/**
* Create a condition to pattern-check this field against a value
@ -473,7 +473,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
* SQL: <code>this not like value escape 'e'</code>
*/
@Support
Condition notLike(T value, char escape);
Condition notLike(String value, char escape);
/**
* Convenience method for {@link #like(Object, char)} including proper

View File

@ -340,43 +340,43 @@ abstract class AbstractField<T> extends AbstractNamedTypeProviderQueryPart<T> im
}
@Override
public final Condition like(T value) {
public final Condition like(String value) {
return like(val(value));
}
@Override
public final Condition like(T value, char escape) {
public final Condition like(String value, char escape) {
return like(val(value), escape);
}
@Override
public final Condition like(Field<T> value) {
return new CompareCondition<T>(this, nullSafe(value), Comparator.LIKE);
public final Condition like(Field<String> value) {
return new CompareCondition(this, nullSafe(value), Comparator.LIKE);
}
@Override
public final Condition like(Field<T> value, char escape) {
return new CompareCondition<T>(this, nullSafe(value), Comparator.LIKE, escape);
public final Condition like(Field<String> value, char escape) {
return new CompareCondition(this, nullSafe(value), Comparator.LIKE, escape);
}
@Override
public final Condition notLike(T value) {
public final Condition notLike(String value) {
return notLike(val(value));
}
@Override
public final Condition notLike(T value, char escape) {
public final Condition notLike(String value, char escape) {
return notLike(val(value), escape);
}
@Override
public final Condition notLike(Field<T> value) {
return new CompareCondition<T>(this, nullSafe(value), Comparator.NOT_LIKE);
public final Condition notLike(Field<String> value) {
return new CompareCondition(this, nullSafe(value), Comparator.NOT_LIKE);
}
@Override
public final Condition notLike(Field<T> value, char escape) {
return new CompareCondition<T>(this, nullSafe(value), Comparator.NOT_LIKE, escape);
public final Condition notLike(Field<String> value, char escape) {
return new CompareCondition(this, nullSafe(value), Comparator.NOT_LIKE, escape);
}
@Override
@ -386,7 +386,7 @@ abstract class AbstractField<T> extends AbstractNamedTypeProviderQueryPart<T> im
Factory.val(escape("" + value, '!')),
Factory.literal("'%'"));
return like(concat.cast(this), '!');
return like(concat, '!');
}
@Override
@ -396,7 +396,7 @@ abstract class AbstractField<T> extends AbstractNamedTypeProviderQueryPart<T> im
escape(value.cast(String.class), '!'),
Factory.literal("'%'"));
return like(concat.cast(this), '!');
return like(concat, '!');
}
@Override
@ -405,7 +405,7 @@ abstract class AbstractField<T> extends AbstractNamedTypeProviderQueryPart<T> im
Factory.val(escape("" + value, '!')),
Factory.literal("'%'"));
return like(concat.cast(this), '!');
return like(concat, '!');
}
@Override
@ -414,7 +414,7 @@ abstract class AbstractField<T> extends AbstractNamedTypeProviderQueryPart<T> im
escape(value.cast(String.class), '!'),
Factory.literal("'%'"));
return like(concat.cast(this), '!');
return like(concat, '!');
}
@Override
@ -423,7 +423,7 @@ abstract class AbstractField<T> extends AbstractNamedTypeProviderQueryPart<T> im
Factory.literal("'%'"),
Factory.val(escape("" + value, '!')));
return like(concat.cast(this), '!');
return like(concat, '!');
}
@Override
@ -432,7 +432,7 @@ abstract class AbstractField<T> extends AbstractNamedTypeProviderQueryPart<T> im
Factory.literal("'%'"),
escape(value.cast(String.class), '!'));
return like(concat.cast(this), '!');
return like(concat, '!');
}
@Override
@ -525,7 +525,7 @@ abstract class AbstractField<T> extends AbstractNamedTypeProviderQueryPart<T> im
@Override
public final Condition equal(Field<T> field) {
return new CompareCondition<T>(this, nullSafe(field), Comparator.EQUALS);
return new CompareCondition(this, nullSafe(field), Comparator.EQUALS);
}
@Override
@ -576,7 +576,7 @@ abstract class AbstractField<T> extends AbstractNamedTypeProviderQueryPart<T> im
@Override
public final Condition notEqual(Field<T> field) {
return new CompareCondition<T>(this, nullSafe(field), Comparator.NOT_EQUALS);
return new CompareCondition(this, nullSafe(field), Comparator.NOT_EQUALS);
}
@Override
@ -627,7 +627,7 @@ abstract class AbstractField<T> extends AbstractNamedTypeProviderQueryPart<T> im
@Override
public final Condition lessThan(Field<T> field) {
return new CompareCondition<T>(this, nullSafe(field), Comparator.LESS);
return new CompareCondition(this, nullSafe(field), Comparator.LESS);
}
@Override
@ -678,7 +678,7 @@ abstract class AbstractField<T> extends AbstractNamedTypeProviderQueryPart<T> im
@Override
public final Condition lessOrEqual(Field<T> field) {
return new CompareCondition<T>(this, nullSafe(field), Comparator.LESS_OR_EQUAL);
return new CompareCondition(this, nullSafe(field), Comparator.LESS_OR_EQUAL);
}
@Override
@ -729,7 +729,7 @@ abstract class AbstractField<T> extends AbstractNamedTypeProviderQueryPart<T> im
@Override
public final Condition greaterThan(Field<T> field) {
return new CompareCondition<T>(this, nullSafe(field), Comparator.GREATER);
return new CompareCondition(this, nullSafe(field), Comparator.GREATER);
}
@Override
@ -780,7 +780,7 @@ abstract class AbstractField<T> extends AbstractNamedTypeProviderQueryPart<T> im
@Override
public final Condition greaterOrEqual(Field<T> field) {
return new CompareCondition<T>(this, nullSafe(field), Comparator.GREATER_OR_EQUAL);
return new CompareCondition(this, nullSafe(field), Comparator.GREATER_OR_EQUAL);
}
@Override

View File

@ -38,8 +38,13 @@ 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.NOT_EQUALS;
import static org.jooq.Comparator.NOT_LIKE;
import static org.jooq.SQLDialect.ASE;
import static org.jooq.SQLDialect.DB2;
import static org.jooq.SQLDialect.DERBY;
import static org.jooq.SQLDialect.POSTGRES;
import java.util.List;
@ -48,24 +53,25 @@ import org.jooq.BindContext;
import org.jooq.Comparator;
import org.jooq.Field;
import org.jooq.RenderContext;
import org.jooq.SQLDialect;
/**
* @author Lukas Eder
*/
class CompareCondition<T> extends AbstractCondition {
class CompareCondition extends AbstractCondition {
private static final long serialVersionUID = -747240442279619486L;
private final Field<T> field1;
private final Field<T> field2;
private final Field<?> field1;
private final Field<?> field2;
private final Comparator comparator;
private final Character escape;
CompareCondition(Field<T> field1, Field<T> field2, Comparator comparator) {
CompareCondition(Field<?> field1, Field<?> field2, Comparator comparator) {
this(field1, field2, comparator, null);
}
CompareCondition(Field<T> field1, Field<T> field2, Comparator comparator, Character escape) {
CompareCondition(Field<?> field1, Field<?> field2, Comparator comparator, Character escape) {
this.field1 = field1;
this.field2 = field2;
this.comparator = comparator;
@ -89,7 +95,20 @@ class CompareCondition<T> extends AbstractCondition {
@Override
public final void toSQL(RenderContext context) {
context.sql(field1)
SQLDialect dialect = context.getDialect();
Field<?> lhs = field1;
// [#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)
&& field1.getType() != String.class
&& asList(ASE, DERBY, POSTGRES).contains(dialect)) {
lhs = lhs.cast(String.class);
}
context.sql(lhs)
.sql(" ");
if (field2.isNullLiteral()) {
@ -107,13 +126,13 @@ class CompareCondition<T> 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 cast = context.getDialect() == DB2 && field2 instanceof Concat;
boolean castRhs = (dialect == DB2 && field2 instanceof Concat);
context.sql(comparator.toSQL())
.sql(" ")
.sql(cast ? "cast(" : "")
.sql(castRhs ? "cast(" : "")
.sql(field2)
.sql(cast ? " as varchar(4000))" : "");
.sql(castRhs ? " as varchar(4000))" : "");
if (escape != null) {
context.sql(" escape '")