[#585] Add support for DATE, TIME and INTERVAL arithmetic - Implementation done for DB2

This commit is contained in:
Lukas Eder 2012-04-01 19:14:09 +00:00
parent 82737aa437
commit 299a4bb3ba
5 changed files with 91 additions and 24 deletions

View File

@ -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<A, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T658, T725
// Interval tests
// --------------
if (getDialect() == ASE ||
getDialect() == DB2 ||
getDialect() == CUBRID ||
getDialect() == MYSQL) {

View File

@ -72,6 +72,10 @@ class DateDiff extends AbstractFunction<Integer> {
case MYSQL:
return function("datediff", getDataType(), date1, date2);
case DB2:
return function("days", getDataType(), date1).sub(
function("days", getDataType(), date2));
case CUBRID:
case ORACLE:

View File

@ -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<T> extends AbstractFunction<T> {
// [#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<T> extends AbstractFunction<T> {
}
}
case DB2:
case HSQLDB: {
if (rhs.get(0).getType() == YearToMonth.class) {
YearToMonth interval = ((Param<YearToMonth>) 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<DayToSecond>) 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<org.jooq.types.Interval<?>>) rhs.get(0)).getValue();
@ -287,13 +313,24 @@ class Expression<T> extends AbstractFunction<T> {
private final Field<T> 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: {

View File

@ -44,18 +44,18 @@ import org.jooq.RenderContext;
/**
* @author Lukas Eder
*/
class IntervalLiteral<T> extends CustomField<T> {
class IntervalLiteral extends CustomField<Object> {
/**
* Generated UID
*/
private static final long serialVersionUID = -530284767039331529L;
private final Field<T> field;
private final Field<?> field;
private final String intervalType;
IntervalLiteral(Field<T> 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<T> extends CustomField<T> {
@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;
}
}
}

View File

@ -73,11 +73,19 @@ class TimestampDiff extends AbstractFunction<DayToSecond> {
case ASE:
Field<Double> days = function("datediff", SQLDataType.DOUBLE, literal("day"), timestamp2, timestamp1);
Field<Double> 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: