[#1159] Support matching numbers with LIKE, e.g. ID LIKE '%33%'
This commit is contained in:
parent
283a43664e
commit
5cd584787e
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 '")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user