diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/DataTypeTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/DataTypeTests.java index b6d72d3df9..b5562629fa 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/DataTypeTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/DataTypeTests.java @@ -1250,15 +1250,22 @@ extends BaseTest { case CUBRID: case ORACLE: case POSTGRES: - - // TODO [#585] This cast shouldn't be necessary - return date1.sub(date2).cast(Integer.class); + return field("{0} - {1}", getDataType(), date1, date2); } - return null; + // Default implementation for equals() and hashCode() + return date1.sub(date2).cast(Integer.class); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/Expression.java b/jOOQ/src/main/java/org/jooq/impl/Expression.java index 19c92df20f..0fc7b4718c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Expression.java +++ b/jOOQ/src/main/java/org/jooq/impl/Expression.java @@ -66,7 +66,6 @@ import static org.jooq.impl.Factory.function; import static org.jooq.impl.Factory.two; import static org.jooq.impl.Factory.val; -import java.sql.Date; import java.sql.Timestamp; import java.util.Arrays; import java.util.List; @@ -428,14 +427,15 @@ class Expression extends AbstractFunction { } case POSTGRES: { - // Postgres can add / subtract days using +/- operators only to DATE - DataType type = lhs.getDataType(); - if (type.getType() == Date.class) { - return new DefaultExpression(); + // This seems to be the most reliable way to avoid issues + // with incompatible data types and timezones + // ? + CAST (? || ' days' as interval) + if (operator == ADD) { + return lhs.add(rhsAsNumber().concat(" day").cast(DayToSecond.class)); } else { - return new Expression(operator, lhs.cast(Date.class), rhsAsNumber()).cast(type); + return lhs.sub(rhsAsNumber().concat(" day").cast(DayToSecond.class)); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/TimestampDiff.java b/jOOQ/src/main/java/org/jooq/impl/TimestampDiff.java index 175ec0a057..b70a942aa8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TimestampDiff.java +++ b/jOOQ/src/main/java/org/jooq/impl/TimestampDiff.java @@ -114,11 +114,10 @@ class TimestampDiff extends AbstractFunction { case ORACLE: case POSTGRES: - - // TODO [#585] This cast shouldn't be necessary - return timestamp1.sub(timestamp2).cast(DayToSecond.class); + return field("{0} - {1}", getDataType(), timestamp1, timestamp2); } - return null; + // Default implementation for equals() and hashCode() + return timestamp1.sub(timestamp2).cast(DayToSecond.class); } } diff --git a/jOOQ/src/main/java/org/jooq/util/postgres/PGIntervalConverter.java b/jOOQ/src/main/java/org/jooq/util/postgres/PGIntervalConverter.java index 7ebd903f9a..68f604f291 100644 --- a/jOOQ/src/main/java/org/jooq/util/postgres/PGIntervalConverter.java +++ b/jOOQ/src/main/java/org/jooq/util/postgres/PGIntervalConverter.java @@ -47,42 +47,81 @@ import org.jooq.types.YearToMonth; * Postgres returns an undisclosed internal type for intervals. This converter * takes care of converting the internal type to jOOQ's interval data types * {@link DayToSecond} and {@link YearToMonth} + *

+ * Note, that Postgres uses some non-standard ways of describing negative + * intervals. Negative intervals have a sign before every date part! * * @author Lukas Eder */ public class PGIntervalConverter { + /** + * Convert a jOOQ DAY TO SECOND interval to a Postgres representation + */ public static Object toPGInterval(DayToSecond interval) { return on("org.postgresql.util.PGInterval").create(0, 0, - interval.getDays(), - interval.getHours(), - interval.getMinutes(), - interval.getSeconds() + - interval.getNano() / 1000000000.0).get(); + interval.getSign() * interval.getDays(), + interval.getSign() * interval.getHours(), + interval.getSign() * interval.getMinutes(), + interval.getSign() * interval.getSeconds() + + interval.getSign() * interval.getNano() / 1000000000.0).get(); } + /** + * Convert a jOOQ YEAR TO MONTH interval to a Postgres representation + */ public static Object toPGInterval(YearToMonth interval) { return on("org.postgresql.util.PGInterval").create( - interval.getYears(), - interval.getMonths(), + interval.getSign() * interval.getYears(), + interval.getSign() * interval.getMonths(), 0, 0, 0, 0.0).get(); } + /** + * Convert a Postgres interval to a jOOQ DAY TO SECOND interval + */ public static DayToSecond toDayToSecond(Object pgInterval) { + boolean negative = pgInterval.toString().contains("-"); + Reflect i = on(pgInterval); + if (negative) { + i.call("scale", -1); + } + Double seconds = i.call("getSeconds").get(); - return new DayToSecond( + DayToSecond result = new DayToSecond( i.call("getDays").get(), i.call("getHours").get(), i.call("getMinutes").get(), seconds.intValue(), (int) (1000000000 * (seconds - seconds.intValue()))); + + if (negative) { + result = result.neg(); + } + + return result; } + /** + * Convert a Postgres interval to a jOOQ YEAR TO MONTH interval + */ public static YearToMonth toYearToMonth(Object pgInterval) { + boolean negative = pgInterval.toString().contains("-"); + Reflect i = on(pgInterval); - return new YearToMonth( + if (negative) { + i.call("scale", -1); + } + + YearToMonth result = new YearToMonth( i.call("getYears").get(), i.call("getMonths").get()); + + if (negative) { + result = result.neg(); + } + + return result; } }