From cd057d34d40a91eb68c4b6db9f205a00e9917c69 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Wed, 10 Apr 2019 16:16:20 +0200 Subject: [PATCH] [#461] [#473] [#2597] [#8234] Unnecessary casts should no longer be ignored Historically, SQL casts were ignored if jOOQ's Field type was of the same type T as the cast type. This makes sense for some internals, but not in the public API. When a user wants to cast an expression, this should always produce a SQL cast. --- .../java/org/jooq/impl/AbstractField.java | 33 ++++--------- .../java/org/jooq/impl/CompareCondition.java | 3 +- jOOQ/src/main/java/org/jooq/impl/Concat.java | 7 ++- jOOQ/src/main/java/org/jooq/impl/DSL.java | 18 ------- jOOQ/src/main/java/org/jooq/impl/DateAdd.java | 1 + .../src/main/java/org/jooq/impl/DateDiff.java | 3 +- .../main/java/org/jooq/impl/DateOrTime.java | 3 +- jOOQ/src/main/java/org/jooq/impl/Degrees.java | 3 +- .../main/java/org/jooq/impl/Expression.java | 11 ++++- jOOQ/src/main/java/org/jooq/impl/Extract.java | 7 +-- .../src/main/java/org/jooq/impl/Function.java | 3 +- jOOQ/src/main/java/org/jooq/impl/Radians.java | 3 +- .../java/org/jooq/impl/RatioToReport.java | 3 +- jOOQ/src/main/java/org/jooq/impl/Round.java | 3 +- .../org/jooq/impl/RowOverlapsCondition.java | 5 +- .../java/org/jooq/impl/TimestampDiff.java | 3 +- jOOQ/src/main/java/org/jooq/impl/Tools.java | 48 ++++++++++++++++++- jOOQ/src/main/java/org/jooq/impl/Trunc.java | 9 +++- .../main/java/org/jooq/impl/TruncDate.java | 4 ++ 19 files changed, 105 insertions(+), 65 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java index f88d42aa46..4764974d83 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java @@ -63,6 +63,7 @@ import static org.jooq.impl.ExpressionOperator.MULTIPLY; import static org.jooq.impl.ExpressionOperator.SUBTRACT; import static org.jooq.impl.Tools.EMPTY_FIELD; import static org.jooq.impl.Tools.EMPTY_STRING; +import static org.jooq.impl.Tools.castIfNeeded; import static org.jooq.tools.Convert.FALSE_VALUES; import static org.jooq.tools.Convert.TRUE_VALUES; @@ -245,26 +246,14 @@ abstract class AbstractField extends AbstractNamed implements Field { return cast(field.getDataType()); } - @SuppressWarnings("unchecked") @Override public final Field cast(DataType type) { - - // [#473] Prevent unnecessary casts - if (getDataType().equals(type)) - return (Field) this; - else - return new Cast(this, type); + return new Cast(this, type); } - @SuppressWarnings("unchecked") @Override public final Field cast(Class type) { - - // [#2597] Prevent unnecessary casts - if (getType() == type) - return (Field) this; - else - return cast(DefaultDataType.getDataType(null, type)); + return cast(DefaultDataType.getDataType(null, type)); } // ------------------------------------------------------------------------ @@ -276,15 +265,9 @@ abstract class AbstractField extends AbstractNamed implements Field { return coerce(field.getDataType()); } - @SuppressWarnings("unchecked") @Override public final Field coerce(DataType type) { - - // [#473] Prevent unnecessary coercions - if (getDataType().equals(type)) - return (Field) this; - else - return new Coerce(this, type); + return new Coerce(this, type); } @Override @@ -721,7 +704,7 @@ abstract class AbstractField extends AbstractNamed implements Field { else if (Boolean.class.isAssignableFrom(type)) return ((Field) this).equal(inline(true)); else - return cast(String.class).in(TRUE_VALUES); + return castIfNeeded(this, String.class).in(TRUE_VALUES); } @SuppressWarnings({ "unchecked" }) @@ -736,7 +719,7 @@ abstract class AbstractField extends AbstractNamed implements Field { else if (Boolean.class.isAssignableFrom(type)) return ((Field) this).equal(inline(false)); else - return cast(String.class).in(Tools.inline(FALSE_VALUES.toArray(EMPTY_STRING))); + return castIfNeeded(this, String.class).in(Tools.inline(FALSE_VALUES.toArray(EMPTY_STRING))); } @Override @@ -1272,7 +1255,7 @@ abstract class AbstractField extends AbstractNamed implements Field { @Override public final Condition equalIgnoreCase(Field value) { - return DSL.lower(cast(String.class)).equal(DSL.lower(value)); + return DSL.lower(castIfNeeded(this, String.class)).equal(DSL.lower(value)); } @Override @@ -1302,7 +1285,7 @@ abstract class AbstractField extends AbstractNamed implements Field { @Override public final Condition notEqualIgnoreCase(Field value) { - return DSL.lower(cast(String.class)).notEqual(DSL.lower(value)); + return DSL.lower(castIfNeeded(this, String.class)).notEqual(DSL.lower(value)); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/CompareCondition.java b/jOOQ/src/main/java/org/jooq/impl/CompareCondition.java index d0040d7260..0b0cc00000 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CompareCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/CompareCondition.java @@ -60,6 +60,7 @@ import static org.jooq.impl.Keywords.K_AS; import static org.jooq.impl.Keywords.K_CAST; import static org.jooq.impl.Keywords.K_ESCAPE; import static org.jooq.impl.Keywords.K_VARCHAR; +import static org.jooq.impl.Tools.castIfNeeded; import static org.jooq.impl.Tools.embeddedFields; import static org.jooq.impl.Tools.isEmbeddable; @@ -124,7 +125,7 @@ final class CompareCondition extends AbstractCondition implements LikeEscapeStep && field1.getType() != String.class && REQUIRES_CAST_ON_LIKE.contains(family)) { - lhs = lhs.cast(String.class); + lhs = castIfNeeded(lhs, String.class); } // [#1423] Only Postgres knows a true ILIKE operator. Other dialects diff --git a/jOOQ/src/main/java/org/jooq/impl/Concat.java b/jOOQ/src/main/java/org/jooq/impl/Concat.java index 776cca899a..fd3bae9062 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Concat.java +++ b/jOOQ/src/main/java/org/jooq/impl/Concat.java @@ -37,11 +37,11 @@ */ package org.jooq.impl; -import static org.jooq.impl.DSL.castAll; import static org.jooq.impl.DSL.function; import static org.jooq.impl.ExpressionOperator.ADD; import static org.jooq.impl.ExpressionOperator.BIT_AND; import static org.jooq.impl.ExpressionOperator.CONCAT; +import static org.jooq.impl.Tools.castAllIfNeeded; import org.jooq.Configuration; import org.jooq.Field; @@ -65,12 +65,11 @@ final class Concat extends AbstractFunction { final Field getFunction0(Configuration configuration) { // [#461] Type cast the concat expression, if this isn't a VARCHAR field - Field[] cast = castAll(String.class, getArguments()); + Field[] cast = castAllIfNeeded(getArguments(), String.class); // If there is only one argument, return it immediately - if (cast.length == 1) { + if (cast.length == 1) return cast[0]; - } Field first = cast[0]; Field[] others = new Field[cast.length - 1]; diff --git a/jOOQ/src/main/java/org/jooq/impl/DSL.java b/jOOQ/src/main/java/org/jooq/impl/DSL.java index 50d855099e..395befbe55 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DSL.java +++ b/jOOQ/src/main/java/org/jooq/impl/DSL.java @@ -12443,24 +12443,6 @@ public class DSL { return NULL().cast(type); } - /** - * Cast all fields that need casting. - * - * @param The generic field type - * @param type The type to cast to - * @param fields The fields to be cast to a uniform type - * @return The cast fields - */ - static Field[] castAll(Class type, Field... fields) { - Field[] castFields = new Field[fields.length]; - - for (int i = 0; i < fields.length; i++) { - castFields[i] = fields[i].cast(type); - } - - return (Field[]) castFields; - } - /** * The COALESCE(value1, value2, ... , value n) function. * diff --git a/jOOQ/src/main/java/org/jooq/impl/DateAdd.java b/jOOQ/src/main/java/org/jooq/impl/DateAdd.java index 7da638d31b..cbce731e70 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DateAdd.java +++ b/jOOQ/src/main/java/org/jooq/impl/DateAdd.java @@ -41,6 +41,7 @@ import static org.jooq.impl.DSL.inline; import static org.jooq.impl.DSL.keyword; import static org.jooq.impl.DSL.sql; import static org.jooq.impl.SQLDataType.VARCHAR; +import static org.jooq.impl.Tools.castIfNeeded; import org.jooq.Configuration; import org.jooq.DatePart; diff --git a/jOOQ/src/main/java/org/jooq/impl/DateDiff.java b/jOOQ/src/main/java/org/jooq/impl/DateDiff.java index 350f31e498..de4254d46f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DateDiff.java +++ b/jOOQ/src/main/java/org/jooq/impl/DateDiff.java @@ -38,6 +38,7 @@ package org.jooq.impl; import static org.jooq.impl.DSL.function; +import static org.jooq.impl.Tools.castIfNeeded; import org.jooq.Configuration; import org.jooq.Field; @@ -123,6 +124,6 @@ final class DateDiff extends AbstractFunction { } // Default implementation for equals() and hashCode() - return date1.sub(date2).cast(Integer.class); + return castIfNeeded(date1.sub(date2), Integer.class); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/DateOrTime.java b/jOOQ/src/main/java/org/jooq/impl/DateOrTime.java index bc720ba07e..46aee0eaa6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DateOrTime.java +++ b/jOOQ/src/main/java/org/jooq/impl/DateOrTime.java @@ -38,6 +38,7 @@ package org.jooq.impl; import static org.jooq.impl.DSL.keyword; +import static org.jooq.impl.Tools.castIfNeeded; import org.jooq.Configuration; import org.jooq.DataType; @@ -93,7 +94,7 @@ final class DateOrTime extends AbstractFunction { } default: - return field.cast(getDataType()); + return castIfNeeded(field, getDataType()); } } } diff --git a/jOOQ/src/main/java/org/jooq/impl/Degrees.java b/jOOQ/src/main/java/org/jooq/impl/Degrees.java index 9a15238ff7..d3a7a47fd8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Degrees.java +++ b/jOOQ/src/main/java/org/jooq/impl/Degrees.java @@ -39,6 +39,7 @@ package org.jooq.impl; import static org.jooq.impl.DSL.inline; import static org.jooq.impl.DSL.pi; +import static org.jooq.impl.Tools.castIfNeeded; import java.math.BigDecimal; @@ -76,7 +77,7 @@ final class Degrees extends AbstractFunction { case FIREBIRD: case SQLITE: - return argument.cast(BigDecimal.class).mul(inline(180)).div(pi()); + return castIfNeeded(argument, BigDecimal.class).mul(inline(180)).div(pi()); default: return DSL.field("{degrees}({0})", SQLDataType.NUMERIC, argument); diff --git a/jOOQ/src/main/java/org/jooq/impl/Expression.java b/jOOQ/src/main/java/org/jooq/impl/Expression.java index 724e12ee9f..ceb51248b9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Expression.java +++ b/jOOQ/src/main/java/org/jooq/impl/Expression.java @@ -71,6 +71,7 @@ import static org.jooq.impl.ExpressionOperator.BIT_XOR; import static org.jooq.impl.ExpressionOperator.SHL; import static org.jooq.impl.ExpressionOperator.SHR; import static org.jooq.impl.ExpressionOperator.SUBTRACT; +import static org.jooq.impl.Tools.castIfNeeded; import java.sql.Timestamp; import java.util.Arrays; @@ -179,12 +180,12 @@ final class Expression extends AbstractFunction { // Many dialects don't support shifts. Use multiplication/division instead else if (SHL == operator && EMULATE_SHR_SHL.contains(family)) - return lhs.mul((Field) DSL.power(two(), rhsAsNumber()).cast(lhs)); + return lhs.mul((Field) castIfNeeded(DSL.power(two(), rhsAsNumber()), lhs)); // [#3962] This emulation is expensive. If this is emulated, BitCount should // use division instead of SHR directly else if (SHR == operator && EMULATE_SHR_SHL.contains(family)) - return lhs.div((Field) DSL.power(two(), rhsAsNumber()).cast(lhs)); + return lhs.div((Field) castIfNeeded(DSL.power(two(), rhsAsNumber()), lhs)); // Some dialects support shifts as functions else if (SHL == operator && FIREBIRD == family) @@ -518,6 +519,12 @@ final class Expression extends AbstractFunction { + + + + + + diff --git a/jOOQ/src/main/java/org/jooq/impl/Extract.java b/jOOQ/src/main/java/org/jooq/impl/Extract.java index b6174c82ff..8a8d847aad 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Extract.java +++ b/jOOQ/src/main/java/org/jooq/impl/Extract.java @@ -43,6 +43,7 @@ import static org.jooq.impl.DSL.function; import static org.jooq.impl.DSL.inline; import static org.jooq.impl.DSL.one; import static org.jooq.impl.SQLDataType.INTEGER; +import static org.jooq.impl.Tools.castIfNeeded; import java.sql.Date; import java.sql.Timestamp; @@ -365,14 +366,14 @@ final class Extract extends AbstractFunction { private final Field getDefaultEmulation() { switch (datePart) { case DECADE: - return DSL.cast(DSL.year(field).div(inline(10)), INTEGER); + return castIfNeeded(DSL.year(field).div(inline(10)), INTEGER); case CENTURY: - return DSL.cast( + return castIfNeeded( DSL.sign(DSL.year(field)) .mul(DSL.abs(DSL.year(field)).add(inline(99))) .div(inline(100)), INTEGER); case MILLENNIUM: - return DSL.cast( + return castIfNeeded( DSL.sign(DSL.year(field)) .mul(DSL.abs(DSL.year(field)).add(inline(999))) .div(inline(1000)), INTEGER); diff --git a/jOOQ/src/main/java/org/jooq/impl/Function.java b/jOOQ/src/main/java/org/jooq/impl/Function.java index 441466b5cb..e3f819fdc3 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Function.java +++ b/jOOQ/src/main/java/org/jooq/impl/Function.java @@ -88,6 +88,7 @@ import static org.jooq.impl.Term.MEDIAN; import static org.jooq.impl.Term.MODE; import static org.jooq.impl.Term.PRODUCT; import static org.jooq.impl.Term.ROW_NUMBER; +import static org.jooq.impl.Tools.castIfNeeded; import static org.jooq.impl.Tools.BooleanDataKey.DATA_RANKING_FUNCTION; import static org.jooq.impl.Tools.BooleanDataKey.DATA_ROWNUMBER_FUNCTION; import static org.jooq.impl.Tools.DataKey.DATA_WINDOW_DEFINITIONS; @@ -366,7 +367,7 @@ class Function extends AbstractField implements ctx.visit(K_DISTINCT).sql(' '); // The explicit cast is needed in Postgres - ctx.visit(((Field) arguments.get(0)).cast(String.class)); + ctx.visit(castIfNeeded((Field) arguments.get(0), String.class)); if (arguments.size() > 1) ctx.sql(", ").visit(arguments.get(1)); diff --git a/jOOQ/src/main/java/org/jooq/impl/Radians.java b/jOOQ/src/main/java/org/jooq/impl/Radians.java index 3afb5f734a..1836f09a0c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Radians.java +++ b/jOOQ/src/main/java/org/jooq/impl/Radians.java @@ -40,6 +40,7 @@ package org.jooq.impl; import static org.jooq.impl.DSL.function; import static org.jooq.impl.DSL.inline; import static org.jooq.impl.DSL.pi; +import static org.jooq.impl.Tools.castIfNeeded; import java.math.BigDecimal; @@ -75,7 +76,7 @@ final class Radians extends AbstractFunction { case FIREBIRD: case SQLITE: - return argument.cast(BigDecimal.class).mul(pi()).div(inline(180)); + return castIfNeeded(argument, BigDecimal.class).mul(pi()).div(inline(180)); default: return function("radians", SQLDataType.NUMERIC, argument); diff --git a/jOOQ/src/main/java/org/jooq/impl/RatioToReport.java b/jOOQ/src/main/java/org/jooq/impl/RatioToReport.java index 408fcc6a11..fdc6510390 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RatioToReport.java +++ b/jOOQ/src/main/java/org/jooq/impl/RatioToReport.java @@ -40,6 +40,7 @@ package org.jooq.impl; import static org.jooq.SQLDialect.SQLITE; import static org.jooq.impl.SQLDataType.DECIMAL; import static org.jooq.impl.SQLDataType.DOUBLE; +import static org.jooq.impl.Tools.castIfNeeded; import java.math.BigDecimal; @@ -82,7 +83,7 @@ final class RatioToReport extends Function { default: - ctx.visit(field.cast((DataType) (ctx.family() == SQLITE ? DOUBLE : DECIMAL))) + ctx.visit(castIfNeeded(field, (DataType) (ctx.family() == SQLITE ? DOUBLE : DECIMAL))) .sql(" / ") .visit(DSL.sum(field)); toSQLOverClause(ctx); diff --git a/jOOQ/src/main/java/org/jooq/impl/Round.java b/jOOQ/src/main/java/org/jooq/impl/Round.java index 39fb44ce44..3c41cf511b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Round.java +++ b/jOOQ/src/main/java/org/jooq/impl/Round.java @@ -39,6 +39,7 @@ package org.jooq.impl; import static org.jooq.impl.DSL.function; import static org.jooq.impl.DSL.val; +import static org.jooq.impl.Tools.castIfNeeded; import java.math.BigDecimal; @@ -110,7 +111,7 @@ final class Round extends AbstractFunction { if (decimals == 0) return function("round", getDataType(), argument); else - return function("round", getDataType(), argument.cast(BigDecimal.class), val(decimals)); + return function("round", getDataType(), castIfNeeded(argument, BigDecimal.class), val(decimals)); } // This is the optimal implementation by most RDBMS diff --git a/jOOQ/src/main/java/org/jooq/impl/RowOverlapsCondition.java b/jOOQ/src/main/java/org/jooq/impl/RowOverlapsCondition.java index 937b5a9800..824325db30 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RowOverlapsCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/RowOverlapsCondition.java @@ -58,6 +58,7 @@ import static org.jooq.SQLDialect.SQLITE; // ... // ... import static org.jooq.impl.Keywords.K_OVERLAPS; +import static org.jooq.impl.Tools.castIfNeeded; import java.util.EnumSet; @@ -129,8 +130,8 @@ final class RowOverlapsCondition extends AbstractCondition { // All other OVERLAPS predicates can be emulated simply else { return (QueryPartInternal) - right1.le(left2.cast(right1)).and( - left1.le(right2.cast(left1))); + right1.le(castIfNeeded(left2, right1)).and( + left1.le(castIfNeeded(right2, left1))); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/TimestampDiff.java b/jOOQ/src/main/java/org/jooq/impl/TimestampDiff.java index 80be994394..3a6cd820a8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TimestampDiff.java +++ b/jOOQ/src/main/java/org/jooq/impl/TimestampDiff.java @@ -39,6 +39,7 @@ package org.jooq.impl; import static org.jooq.impl.DSL.function; import static org.jooq.impl.SQLDataType.INTEGER; +import static org.jooq.impl.Tools.castIfNeeded; import org.jooq.Configuration; import org.jooq.Field; @@ -144,6 +145,6 @@ final class TimestampDiff extends AbstractFunction { } // Default implementation for equals() and hashCode() - return timestamp1.sub(timestamp2).cast(DayToSecond.class); + return castIfNeeded(timestamp1.sub(timestamp2), DayToSecond.class); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index 6b57258653..98fc6f97b0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -1254,6 +1254,52 @@ final class Tools { return new IllegalArgumentException("Cannot interpret argument of type " + value.getClass() + " as a Field: " + value); } + /** + * [#461] [#473] [#2597] [#8234] Some internals need a cast only if necessary. + */ + @SuppressWarnings("unchecked") + static Field[] castAllIfNeeded(Field[] fields, Class type) { + Field[] castFields = new Field[fields.length]; + + for (int i = 0; i < fields.length; i++) + castFields[i] = castIfNeeded(fields[i], type); + + return castFields; + } + + /** + * [#461] [#473] [#2597] [#8234] Some internals need a cast only if necessary. + */ + @SuppressWarnings("unchecked") + static final Field castIfNeeded(Field field, Class type) { + if (field.getType().equals(type)) + return (Field) field; + else + return field.cast(type); + } + + /** + * [#461] [#473] [#2597] [#8234] Some internals need a cast only if necessary. + */ + @SuppressWarnings("unchecked") + static final Field castIfNeeded(Field field, DataType type) { + if (field.getDataType().equals(type)) + return (Field) field; + else + return field.cast(type); + } + + /** + * [#461] [#473] [#2597] [#8234] Some internals need a cast only if necessary. + */ + @SuppressWarnings("unchecked") + static final Field castIfNeeded(Field field, Field type) { + if (field.getDataType().equals(type.getDataType())) + return (Field) field; + else + return field.cast(type); + } + /** * Be sure that a given object is a field. * @@ -2794,7 +2840,7 @@ final class Tools { } } else { - return field.cast(String.class); + return castIfNeeded(field, String.class); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/Trunc.java b/jOOQ/src/main/java/org/jooq/impl/Trunc.java index ffd9c9b693..8f96abdd67 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Trunc.java +++ b/jOOQ/src/main/java/org/jooq/impl/Trunc.java @@ -42,6 +42,7 @@ import static org.jooq.impl.DSL.inline; import static org.jooq.impl.DSL.keyword; import static org.jooq.impl.DSL.one; import static org.jooq.impl.DSL.zero; +import static org.jooq.impl.Tools.castIfNeeded; import static org.jooq.impl.Tools.extractVal; import java.math.BigDecimal; @@ -111,7 +112,13 @@ final class Trunc extends AbstractFunction { case POSTGRES: - return DSL.field("{trunc}({0}, {1})", SQLDataType.NUMERIC, field.cast(BigDecimal.class), decimals).cast(field.getDataType()); + return castIfNeeded( + DSL.field("{trunc}({0}, {1})", SQLDataType.NUMERIC, + castIfNeeded(field, BigDecimal.class), + decimals + ), + field.getDataType() + ); diff --git a/jOOQ/src/main/java/org/jooq/impl/TruncDate.java b/jOOQ/src/main/java/org/jooq/impl/TruncDate.java index c84c94b616..dc6febc858 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TruncDate.java +++ b/jOOQ/src/main/java/org/jooq/impl/TruncDate.java @@ -38,6 +38,7 @@ package org.jooq.impl; import static org.jooq.impl.DSL.inline; +import static org.jooq.impl.Tools.castIfNeeded; import java.sql.Date; @@ -219,6 +220,9 @@ final class TruncDate extends AbstractFunction { + + +