From bbd02c181373d713d2c8531ab38d18bf54a791db Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Fri, 3 Feb 2012 15:50:04 +0000 Subject: [PATCH] [#1089] Add Field.contains(), .startsWith(), .endsWith() as a convenience for Field.like() (including escaping) --- .../src/org/jooq/test/jOOQAbstractTest.java | 31 ++++++-- jOOQ/src/main/java/org/jooq/Field.java | 72 +++++++++++++++++++ .../java/org/jooq/impl/AbstractField.java | 57 +++++++++++++++ jOOQ/src/main/java/org/jooq/impl/Factory.java | 11 ++- 4 files changed, 164 insertions(+), 7 deletions(-) diff --git a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java index 8246031e12..ea0eac7dcd 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java @@ -8282,13 +8282,36 @@ public abstract class jOOQAbstractTest< assertEquals(1, books.size()); assertEquals(5, (int) books.get(0).getValue(TBook_ID())); + // DERBY doesn't know any REPLACE function, hence only test those + // conditions that do not use REPLACE internally + boolean derby = getDialect() == DERBY; + // [#1106] Add checks for Factory.escape() function books = create().selectFrom(TBook()) - .where(TBook_TITLE().like(concat(val("%"), escape("(%)", '!'), val("%")), '!')) - .and(TBook_TITLE().like(concat(val("%"), escape(val("(_)"), '#'), val("%")), '#')) - .and(TBook_TITLE().notLike(concat(val("%"), escape("(!%)", '#'), val("%")), '#')) - .and(TBook_TITLE().notLike(concat(val("%"), escape(val("(#_)"), '!'), val("%")), '!')) + .where(TBook_TITLE().like(concat("%", escape("(%)", '!'), "%"), '!')) + .and(derby ? trueCondition() : + TBook_TITLE().like(concat(val("%"), escape(val("(_)"), '#'), val("%")), '#')) + .and(TBook_TITLE().notLike(concat("%", escape("(!%)", '#'), "%"), '#')) + .and(derby ? trueCondition() : + TBook_TITLE().notLike(concat(val("%"), escape(val("(#_)"), '!'), val("%")), '!')) + .fetch(); + + assertEquals(1, books.size()); + assertEquals(5, (int) books.get(0).getValue(TBook_ID())); + + // [#1089] Add checks for convenience methods + books = + create().selectFrom(TBook()) + .where(TBook_TITLE().contains("%")) + .and(derby ? trueCondition() : + TBook_TITLE().contains(val("(_"))) + .and(TBook_TITLE().startsWith("About")) + .and(derby ? trueCondition() : + TBook_TITLE().startsWith(val("Abo"))) + .and(TBook_TITLE().endsWith("review")) + .and(derby ? trueCondition() : + TBook_TITLE().endsWith(val("review"))) .fetch(); assertEquals(1, books.size()); diff --git a/jOOQ/src/main/java/org/jooq/Field.java b/jOOQ/src/main/java/org/jooq/Field.java index 00ec7153c8..e9570699b9 100644 --- a/jOOQ/src/main/java/org/jooq/Field.java +++ b/jOOQ/src/main/java/org/jooq/Field.java @@ -475,6 +475,78 @@ public interface Field extends NamedTypeProviderQueryPart, AliasProvider + * SQL: this like ('%' || escape(value, '\') || '%') escape '\' + * + * @see Factory#escape(String, char) + * @see #like(Object, char) + */ + @Support + Condition contains(T value); + + /** + * Convenience method for {@link #like(Object, char)} including proper + * adding of wildcards and escaping + *

+ * SQL: this like ('%' || escape(value, '\') || '%') escape '\' + * + * @see Factory#escape(Field, char) + * @see #like(Field, char) + */ + @Support({ ASE, DB2, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE, SQLITE }) + Condition contains(Field value); + + /** + * Convenience method for {@link #like(Object, char)} including proper + * adding of wildcards and escaping + *

+ * SQL: this like (escape(value, '\') || '%') escape '\' + * + * @see Factory#escape(String, char) + * @see #like(Object, char) + */ + @Support + Condition startsWith(T value); + + /** + * Convenience method for {@link #like(Object, char)} including proper + * adding of wildcards and escaping + *

+ * SQL: this like (escape(value, '\') || '%') escape '\' + * + * @see Factory#escape(Field, char) + * @see #like(Field, char) + */ + @Support({ ASE, DB2, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE, SQLITE }) + Condition startsWith(Field value); + + /** + * Convenience method for {@link #like(Object, char)} including proper + * adding of wildcards and escaping + *

+ * SQL: this like ('%' || escape(value, '\')) escape '\' + * + * @see Factory#escape(String, char) + * @see #like(Object, char) + */ + @Support + Condition endsWith(T value); + + /** + * Convenience method for {@link #like(Object, char)} including proper + * adding of wildcards and escaping + *

+ * SQL: this like ('%' || escape(value, '\')) escape '\' + * + * @see Factory#escape(Field, char) + * @see #like(Field, char) + */ + @Support({ ASE, DB2, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE, SQLITE }) + Condition endsWith(Field value); + /** * Create a condition to check this field against several values *

diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java index 10b70134d8..d265b1235d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java @@ -39,6 +39,7 @@ import static org.jooq.impl.ExpressionOperator.ADD; import static org.jooq.impl.ExpressionOperator.DIVIDE; import static org.jooq.impl.ExpressionOperator.MULTIPLY; import static org.jooq.impl.ExpressionOperator.SUBTRACT; +import static org.jooq.impl.Factory.escape; import static org.jooq.impl.Factory.falseCondition; import static org.jooq.impl.Factory.nullSafe; import static org.jooq.impl.Factory.trueCondition; @@ -378,6 +379,62 @@ abstract class AbstractField extends AbstractNamedTypeProviderQueryPart im return new CompareCondition(this, nullSafe(value), Comparator.NOT_LIKE, escape); } + @Override + public final Condition contains(T value) { + Field concat = Factory.concat( + Factory.literal("'%'"), + Factory.val(escape("" + value, '!')), + Factory.literal("'%'")); + + return like(concat.cast(this), '!'); + } + + @Override + public final Condition contains(Field value) { + Field concat = Factory.concat( + Factory.literal("'%'"), + escape(value.cast(String.class), '!'), + Factory.literal("'%'")); + + return like(concat.cast(this), '!'); + } + + @Override + public final Condition startsWith(T value) { + Field concat = Factory.concat( + Factory.val(escape("" + value, '!')), + Factory.literal("'%'")); + + return like(concat.cast(this), '!'); + } + + @Override + public final Condition startsWith(Field value) { + Field concat = Factory.concat( + escape(value.cast(String.class), '!'), + Factory.literal("'%'")); + + return like(concat.cast(this), '!'); + } + + @Override + public final Condition endsWith(T value) { + Field concat = Factory.concat( + Factory.literal("'%'"), + Factory.val(escape("" + value, '!'))); + + return like(concat.cast(this), '!'); + } + + @Override + public final Condition endsWith(Field value) { + Field concat = Factory.concat( + Factory.literal("'%'"), + escape(value.cast(String.class), '!')); + + return like(concat.cast(this), '!'); + } + @Override public final Condition in(T... values) { return in(vals(values).toArray(new Field[0])); diff --git a/jOOQ/src/main/java/org/jooq/impl/Factory.java b/jOOQ/src/main/java/org/jooq/impl/Factory.java index c3e495e41c..524aa7d498 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Factory.java +++ b/jOOQ/src/main/java/org/jooq/impl/Factory.java @@ -1920,8 +1920,8 @@ public class Factory implements FactoryOperations { * @see Field#like(Field, char) */ @Support - public static Field escape(String value, char escape) { - return val(value.replace("%", escape + "%").replace("_", escape + "_")); + public static String escape(String value, char escape) { + return value.replace("%", escape + "%").replace("_", escape + "_"); } /** @@ -1935,7 +1935,12 @@ public class Factory implements FactoryOperations { */ @Support({ ASE, DB2, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE, SQLITE }) public static Field escape(Field field, char escape) { - return replace(replace(field, "%", escape + "%"), "_", escape + "_"); + Field replace = field; + + replace = replace(replace, literal("'%'"), literal("'" + escape + "%'")); + replace = replace(replace, literal("'_'"), literal("'" + escape + "_'")); + + return replace; } /**