diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/DataTypeTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/DataTypeTests.java index 112eca6a7f..b67d23b671 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/DataTypeTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/DataTypeTests.java @@ -43,6 +43,7 @@ import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; import static org.jooq.SQLDialect.ASE; import static org.jooq.SQLDialect.CUBRID; +import static org.jooq.SQLDialect.DB2; import static org.jooq.SQLDialect.MYSQL; import static org.jooq.SQLDialect.SQLITE; import static org.jooq.impl.Factory.cast; @@ -1215,6 +1216,7 @@ extends BaseTest { case MYSQL: return function("datediff", getDataType(), date1, date2); + case DB2: + return function("days", getDataType(), date1).sub( + function("days", getDataType(), date2)); + case CUBRID: case ORACLE: diff --git a/jOOQ/src/main/java/org/jooq/impl/Expression.java b/jOOQ/src/main/java/org/jooq/impl/Expression.java index e66f82938f..a2d0b17b8f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Expression.java +++ b/jOOQ/src/main/java/org/jooq/impl/Expression.java @@ -65,6 +65,7 @@ import static org.jooq.impl.Factory.function; import static org.jooq.impl.Factory.literal; import static org.jooq.impl.Factory.val; +import java.sql.Timestamp; import java.util.Arrays; import java.util.List; @@ -169,7 +170,8 @@ class Expression extends AbstractFunction { // [#585] Date time arithmetic for numeric or interval RHS else if (asList(ADD, SUBTRACT).contains(operator) && lhs.getDataType().isDateTime() && - !rhs.get(0).getDataType().isDateTime()) { + (rhs.get(0).getDataType().isNumeric() || + rhs.get(0).getDataType().isInterval())) { return new DateExpression(); } @@ -263,6 +265,30 @@ class Expression extends AbstractFunction { } } + case DB2: + case HSQLDB: { + if (rhs.get(0).getType() == YearToMonth.class) { + YearToMonth interval = ((Param) rhs.get(0)).getValue(); + + if (operator == ADD) { + return lhs.add(new IntervalLiteral(val(interval.intValue()), "month")); + } + else { + return lhs.sub(new IntervalLiteral(val(interval.intValue()), "month")); + } + } + else { + DayToSecond interval = ((Param) rhs.get(0)).getValue(); + + if (operator == ADD) { + return (Field) lhs.cast(Timestamp.class).add(new IntervalLiteral(val(interval.getTotalMicro()), "microseconds")); + } + else { + return (Field) lhs.cast(Timestamp.class).sub(new IntervalLiteral(val(interval.getTotalMicro()), "microseconds")); + } + } + } + case CUBRID: case MYSQL: { org.jooq.types.Interval interval = ((Param>) rhs.get(0)).getValue(); @@ -287,13 +313,24 @@ class Expression extends AbstractFunction { private final Field getNumberExpression(Configuration configuration) { switch (configuration.getDialect()) { - case ASE: + case ASE: { if (operator == ADD) { return function("dateadd", getDataType(), literal("day"), rhsAsNumber(), lhs); } else { return function("dateadd", getDataType(), literal("day"), rhsAsNumber().neg(), lhs); } + } + + case DB2: + case HSQLDB: { + if (operator == ADD) { + return lhs.add(new IntervalLiteral(rhsAsNumber(), "day")); + } + else { + return lhs.sub(new IntervalLiteral(rhsAsNumber(), "day")); + } + } case CUBRID: case MYSQL: { diff --git a/jOOQ/src/main/java/org/jooq/impl/IntervalLiteral.java b/jOOQ/src/main/java/org/jooq/impl/IntervalLiteral.java index 6c90555a89..d7e85fd450 100644 --- a/jOOQ/src/main/java/org/jooq/impl/IntervalLiteral.java +++ b/jOOQ/src/main/java/org/jooq/impl/IntervalLiteral.java @@ -44,18 +44,18 @@ import org.jooq.RenderContext; /** * @author Lukas Eder */ -class IntervalLiteral extends CustomField { +class IntervalLiteral extends CustomField { /** * Generated UID */ private static final long serialVersionUID = -530284767039331529L; - private final Field field; + private final Field field; private final String intervalType; - IntervalLiteral(Field field, String intervalType) { - super("interval", field.getDataType()); + IntervalLiteral(Field field, String intervalType) { + super("interval", SQLDataType.OTHER); this.field = field; this.intervalType = intervalType; @@ -63,23 +63,39 @@ class IntervalLiteral extends CustomField { @Override public final void toSQL(RenderContext context) { - if (context.getDialect() == CUBRID) { - boolean inline = context.inline(); + switch (context.getDialect()) { + case CUBRID: { + boolean inline = context.inline(); - context.keyword("interval ") - .inline(true) - .sql(field.getDataType().isNumeric() ? "" : "'") - .sql(field) - .sql(field.getDataType().isNumeric() ? "" : "'") - .inline(inline) - .sql(" ") - .keyword(intervalType); - } - else { - context.keyword("interval ") - .sql(field) - .sql(" ") - .keyword(intervalType); + context.keyword("interval ") + .inline(true) + .sql(field.getDataType().isNumeric() ? "" : "'") + .sql(field) + .sql(field.getDataType().isNumeric() ? "" : "'") + .inline(inline) + .sql(" ") + .keyword(intervalType); + + break; + } + + case DB2: + case HSQLDB: { + context.sql(field) + .sql(" ") + .keyword(intervalType); + + break; + } + + default: { + context.keyword("interval ") + .sql(field) + .sql(" ") + .keyword(intervalType); + + break; + } } } diff --git a/jOOQ/src/main/java/org/jooq/impl/TimestampDiff.java b/jOOQ/src/main/java/org/jooq/impl/TimestampDiff.java index e8614c8922..6eec23e443 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TimestampDiff.java +++ b/jOOQ/src/main/java/org/jooq/impl/TimestampDiff.java @@ -73,11 +73,19 @@ class TimestampDiff extends AbstractFunction { case ASE: Field days = function("datediff", SQLDataType.DOUBLE, literal("day"), timestamp2, timestamp1); Field milli = function("datediff", SQLDataType.DOUBLE, literal("ms"), timestamp2.add(days), timestamp1); - return (Field) days.add(milli.div(new DayToSecond(1).getTotalMilli())); + return (Field) days.add(milli.div(literal(new DayToSecond(1).getTotalMilli()))); // CUBRID's datetime operations operate on a millisecond level case CUBRID: - return (Field) timestamp1.sub(timestamp2).div(new DayToSecond(1).getTotalMilli()); + return (Field) timestamp1.sub(timestamp2).div(literal(new DayToSecond(1).getTotalMilli())); + + // Fun with DB2 dates. Find some info here: + // http://www.ibm.com/developerworks/data/library/techarticle/0211yip/0211yip3.html + case DB2: + return (Field) function("days", SQLDataType.INTEGER, timestamp1).sub( + function("days", SQLDataType.INTEGER, timestamp2)).add( + function("midnight_seconds", SQLDataType.INTEGER, timestamp1).sub( + function("midnight_seconds", SQLDataType.INTEGER, timestamp2)).div(literal(86400.0))); // MySQL's datetime operations operate on a microsecond level case MYSQL: