[#585] Add support for DATE, TIME and INTERVAL arithmetic - Implementation done for SQLServer, Sybase, implementation attempts for SQLite

This commit is contained in:
Lukas Eder 2012-04-04 21:31:36 +00:00
parent a0dacbcc0e
commit 4d50e15b16
6 changed files with 63 additions and 13 deletions

View File

@ -48,6 +48,8 @@ import static org.jooq.SQLDialect.DERBY;
import static org.jooq.SQLDialect.H2;
import static org.jooq.SQLDialect.MYSQL;
import static org.jooq.SQLDialect.SQLITE;
import static org.jooq.SQLDialect.SQLSERVER;
import static org.jooq.SQLDialect.SYBASE;
import static org.jooq.impl.Factory.cast;
import static org.jooq.impl.Factory.castNull;
import static org.jooq.impl.Factory.dateDiff;
@ -533,6 +535,16 @@ extends BaseTest<A, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T658, T725
.fetch().getValue(0, 0));
}
@Test
public void testNestedCasting() throws Exception {
assertEquals(1,
create().select(val(1).cast(Long.class).cast(Integer.class)).fetchOne(0));
assertEquals(3,
create().select(val(1).cast(Long.class).add(val(2).cast(String.class).cast(Integer.class)).cast(Integer.class)).fetchOne(0));
assertEquals("3",
create().select(val(1).add(val("2")).cast(String.class)).fetchOne(0));
}
@Test
public void testConversionResult() throws Exception {
// .fetch(..., Class)
@ -1222,7 +1234,10 @@ extends BaseTest<A, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T658, T725
getDialect() == DERBY ||
getDialect() == CUBRID ||
getDialect() == H2 ||
getDialect() == MYSQL) {
getDialect() == MYSQL ||
getDialect() == SQLSERVER ||
getDialect() == SQLITE ||
getDialect() == SYBASE) {
log.info("SKIPPING", "Interval tests");
}

View File

@ -848,6 +848,11 @@ public abstract class jOOQAbstractTest<
new DataTypeTests(this).testCastingToJavaClass();
}
@Test
public void testNestedCasting() throws Exception {
new DataTypeTests(this).testNestedCasting();
}
@Test
public void testCastingToSQLDataType() throws Exception {
new DataTypeTests(this).testCastingToSQLDataType();

View File

@ -68,6 +68,8 @@ class DateDiff extends AbstractFunction<Integer> {
final Field<Integer> getFunction0(Configuration configuration) {
switch (configuration.getDialect()) {
case ASE:
case SQLSERVER:
case SYBASE:
return function("datediff", getDataType(), literal("day"), date2, date1);
case MYSQL:

View File

@ -228,7 +228,9 @@ class Expression<T> extends AbstractFunction<T> {
SQLDialect dialect = configuration.getDialect();
switch (dialect) {
case ASE: {
case ASE:
case SYBASE:
case SQLSERVER: {
if (rhs.get(0).getType() == YearToMonth.class) {
YearToMonth interval = ((Param<YearToMonth>) rhs.get(0)).getValue();
@ -240,8 +242,9 @@ class Expression<T> extends AbstractFunction<T> {
}
}
else {
// SQL Server needs this cast.
Field<?> result = lhs.cast(Timestamp.class);
DayToSecond interval = ((Param<DayToSecond>) rhs.get(0)).getValue();
Field<T> result = lhs;
if (operator == ADD) {
if (interval.getNano() != 0) {
@ -266,7 +269,7 @@ class Expression<T> extends AbstractFunction<T> {
}
}
return result;
return (Field) result;
}
}
@ -363,7 +366,9 @@ class Expression<T> extends AbstractFunction<T> {
*/
private final Field<T> getNumberExpression(Configuration configuration) {
switch (configuration.getDialect()) {
case ASE: {
case ASE:
case SQLSERVER:
case SYBASE: {
if (operator == ADD) {
return function("dateadd", getDataType(), literal("day"), rhsAsNumber(), lhs);
}
@ -403,7 +408,7 @@ class Expression<T> extends AbstractFunction<T> {
}
}
// That implementation seems a bit off...
// Ingres is not working yet
case INGRES: {
if (operator == ADD) {
return lhs.add(field("date('" + rhsAsNumber() + " days')", Object.class));
@ -423,11 +428,17 @@ class Expression<T> extends AbstractFunction<T> {
}
}
case SQLITE:
if (operator == ADD) {
return function("datetime", getDataType(), lhs, literal("+" + rhsAsNumber() + " day"));
}
else {
return function("datetime", getDataType(), lhs, literal("-" + rhsAsNumber() + " day"));
}
// These dialects can add / subtract days using +/- operators
case H2:
case ORACLE:
case SYBASE:
case SQLSERVER:
default:
return new DefaultExpression();
}

View File

@ -86,19 +86,23 @@ class TimestampDiff extends AbstractFunction<DayToSecond> {
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)));
function("midnight_seconds", SQLDataType.INTEGER, timestamp2)).div(literal(new DayToSecond(1).getTotalSeconds())));
case DERBY:
return (Field) new FnPrefixFunction<Integer>("timestampdiff",
SQLDataType.INTEGER, field("SQL_TSI_SECOND"), timestamp2, timestamp1).div(literal(86400.0));
SQLDataType.INTEGER, field("SQL_TSI_SECOND"), timestamp2, timestamp1).div(literal(new DayToSecond(1).getTotalSeconds()));
case H2:
case HSQLDB:
return function("datediff", getDataType(), literal("'ms'"), timestamp2, timestamp1).div(literal(86400000.0));
return function("datediff", getDataType(), literal("'ms'"), timestamp2, timestamp1).div(literal(new DayToSecond(1).getTotalMilli()));
// MySQL's datetime operations operate on a microsecond level
case MYSQL:
return function("timestampdiff", getDataType(), literal("microsecond"), timestamp2, timestamp1).div(new DayToSecond(1).getTotalMicro());
return function("timestampdiff", getDataType(), literal("microsecond"), timestamp2, timestamp1).div(literal(new DayToSecond(1).getTotalMicro()));
case SQLSERVER:
case SYBASE:
return function("datediff", getDataType(), literal("ms"), timestamp2, timestamp1).div(literal(new DayToSecond(1).getTotalMilli()));
case ORACLE:
case POSTGRES:

View File

@ -37,6 +37,9 @@ package org.jooq.types;
import java.io.Serializable;
import org.jooq.Field;
import org.jooq.SQLDialect;
/**
* A substitute for JDBC's missing <code>java.sql.Interval</code> data type.
* <p>
@ -97,7 +100,17 @@ import java.io.Serializable;
* </tr>
* </table>
* <p>
* Interval implementations can be expected to also also extend {@link Number}
* Interval implementations can be expected to also also extend {@link Number}.
* True SQL standard INTERVAL data types have been observed to be supported by
* any of these dialects:
* <ul>
* <li> {@link SQLDialect#INGRES}</li>
* <li> {@link SQLDialect#ORACLE}</li>
* <li> {@link SQLDialect#POSTGRES}</li>
* </ul>
* <p>
* In other dialects, jOOQ allows for using them for date time arithmetic. See
* {@link Field#add(Field)}, {@link Field#sub(Field)}
*
* @author Lukas Eder
*/