提交 02ef458f authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Implement more arithmetic operations with INTERVAL data type

上级 7ae35403
......@@ -8,15 +8,20 @@ package org.h2.expression;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.h2.api.ErrorCode;
import org.h2.api.IntervalQualifier;
import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.util.DateTimeFunctions;
import org.h2.util.DateTimeUtils;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.h2.value.ValueDate;
import org.h2.value.ValueInterval;
import org.h2.value.ValueNull;
import org.h2.value.ValueTime;
/**
* A mathematical operation with intervals.
......@@ -55,7 +60,7 @@ public class IntervalOperation extends Expression {
INTERVAL_DIVIDE_NUMERIC
}
private IntervalOpType opType;
private final IntervalOpType opType;
private Expression left, right;
private int dataType;
......@@ -116,8 +121,61 @@ public class IntervalOperation extends Expression {
}
case DATETIME_PLUS_INTERVAL:
case DATETIME_MINUS_INTERVAL:
// TODO
throw DbException.throwInternalError("type=" + opType);
switch (l.getType()) {
case Value.TIME: {
if (DataType.isYearMonthIntervalType(r.getType())) {
throw DbException.throwInternalError("type=" + r.getType());
}
BigInteger a1 = BigInteger.valueOf(((ValueTime) l).getNanos());
BigInteger a2 = DateTimeUtils.intervalToAbsolute((ValueInterval) r);
BigInteger n = opType == IntervalOpType.DATETIME_PLUS_INTERVAL ? a1.add(a2) : a1.subtract(a2);
if (n.signum() < 0 || n.compareTo(BigInteger.valueOf(DateTimeUtils.NANOS_PER_DAY)) >= 0) {
throw DbException.get(ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE_1, n.toString());
}
return ValueTime.fromNanos(n.longValue());
}
case Value.DATE:
case Value.TIMESTAMP:
case Value.TIMESTAMP_TZ:
if (DataType.isYearMonthIntervalType(r.getType())) {
long m = DateTimeUtils.intervalToAbsolute((ValueInterval) r).longValue();
if (opType == IntervalOpType.DATETIME_MINUS_INTERVAL) {
m = -m;
}
return DateTimeFunctions.dateadd("MONTH", m, l);
} else {
BigInteger a2 = DateTimeUtils.intervalToAbsolute((ValueInterval) r);
if (l.getType() == Value.DATE) {
BigInteger a1 = BigInteger
.valueOf(DateTimeUtils.absoluteDayFromDateValue(((ValueDate) l).getDateValue()));
a2 = a2.divide(BigInteger.valueOf(DateTimeUtils.NANOS_PER_DAY));
BigInteger n = opType == IntervalOpType.DATETIME_PLUS_INTERVAL ? a1.add(a2) : a1.subtract(a2);
return ValueDate.fromDateValue(DateTimeUtils.dateValueFromAbsoluteDay(n.longValue()));
} else {
long[] a = DateTimeUtils.dateAndTimeFromValue(l);
long absoluteDay = DateTimeUtils.absoluteDayFromDateValue(a[0]);
long timeNanos = a[1];
BigInteger[] dr = a2.divideAndRemainder(BigInteger.valueOf(DateTimeUtils.NANOS_PER_DAY));
if (opType == IntervalOpType.DATETIME_PLUS_INTERVAL) {
absoluteDay += dr[0].longValue();
timeNanos += dr[1].longValue();
} else {
absoluteDay -= dr[0].longValue();
timeNanos -= dr[1].longValue();
}
if (timeNanos >= DateTimeUtils.NANOS_PER_DAY) {
timeNanos -= DateTimeUtils.NANOS_PER_DAY;
absoluteDay++;
} else if (timeNanos < 0) {
timeNanos += DateTimeUtils.NANOS_PER_DAY;
absoluteDay--;
}
return DateTimeUtils.dateTimeToValue(l, DateTimeUtils.dateValueFromAbsoluteDay(absoluteDay),
timeNanos, false);
}
}
}
break;
case INTERVAL_MULTIPLY_NUMERIC:
case INTERVAL_DIVIDE_NUMERIC: {
BigDecimal a1 = new BigDecimal(DateTimeUtils.intervalToAbsolute((ValueInterval) l));
......@@ -126,9 +184,8 @@ public class IntervalOperation extends Expression {
(opType == IntervalOpType.INTERVAL_MULTIPLY_NUMERIC ? a1.multiply(a2) : a1.divide(a2))
.toBigInteger());
}
default:
throw DbException.throwInternalError("type=" + opType);
}
throw DbException.throwInternalError("type=" + opType);
}
@Override
......
......@@ -268,8 +268,14 @@ public class Operation extends Expression {
return new IntervalOperation(IntervalOpType.INTERVAL_PLUS_INTERVAL, left, right);
}
} else if (lInterval && rDateTime) {
if (r == Value.TIME && DataType.isYearMonthIntervalType(l)) {
break;
}
return new IntervalOperation(IntervalOpType.DATETIME_PLUS_INTERVAL, right, left);
} else if (lDateTime && rInterval) {
if (l == Value.TIME && DataType.isYearMonthIntervalType(r)) {
break;
}
return new IntervalOperation(IntervalOpType.DATETIME_PLUS_INTERVAL, left, right);
}
break;
......@@ -279,6 +285,9 @@ public class Operation extends Expression {
return new IntervalOperation(IntervalOpType.INTERVAL_MINUS_INTERVAL, left, right);
}
} else if (lDateTime && rInterval) {
if (l == Value.TIME && DataType.isYearMonthIntervalType(r)) {
break;
}
return new IntervalOperation(IntervalOpType.DATETIME_MINUS_INTERVAL, left, right);
}
break;
......
......@@ -606,3 +606,66 @@ SELECT 2 * INTERVAL '10' YEAR;
SELECT INTERVAL '10' YEAR / 2;
>> INTERVAL '5' YEAR
SELECT TIME '10:00:00' + INTERVAL '30' MINUTE;
>> 10:30:00
SELECT INTERVAL '30' MINUTE + TIME '10:00:00';
>> 10:30:00
SELECT TIME '10:00:00' - INTERVAL '30' MINUTE;
>> 09:30:00
SELECT DATE '2000-01-10' + INTERVAL '30' HOUR;
>> 2000-01-11
SELECT INTERVAL '30' HOUR + DATE '2000-01-10';
>> 2000-01-11
SELECT DATE '2000-01-10' - INTERVAL '30' HOUR;
>> 2000-01-09
SELECT DATE '2000-01-10' + INTERVAL '1-2' YEAR TO MONTH;
>> 2001-03-10
SELECT INTERVAL '1-2' YEAR TO MONTH + DATE '2000-01-10';
>> 2001-03-10
SELECT DATE '2000-01-10' - INTERVAL '1-2' YEAR TO MONTH;
>> 1998-11-10
SELECT TIMESTAMP '2000-01-01 12:00:00' + INTERVAL '25 13' DAY TO HOUR;
>> 2000-01-27 01:00:00
SELECT INTERVAL '25 13' DAY TO HOUR + TIMESTAMP '2000-01-01 12:00:00';
>> 2000-01-27 01:00:00
SELECT TIMESTAMP '2000-01-01 12:00:00' - INTERVAL '25 13' DAY TO HOUR;
>> 1999-12-06 23:00:00
SELECT TIMESTAMP '2000-01-01 12:00:00' + INTERVAL '1-2' YEAR TO MONTH;
>> 2001-03-01 12:00:00
SELECT INTERVAL '1-2' YEAR TO MONTH + TIMESTAMP '2000-01-01 12:00:00';
>> 2001-03-01 12:00:00
SELECT TIMESTAMP '2000-01-01 12:00:00' - INTERVAL '1-2' YEAR TO MONTH;
>> 1998-11-01 12:00:00
SELECT TIMESTAMP WITH TIME ZONE '2000-01-01 12:00:00+01' + INTERVAL '25 13' DAY TO HOUR;
>> 2000-01-27 01:00:00+01
SELECT INTERVAL '25 13' DAY TO HOUR + TIMESTAMP WITH TIME ZONE '2000-01-01 12:00:00+01';
>> 2000-01-27 01:00:00+01
SELECT TIMESTAMP WITH TIME ZONE '2000-01-01 12:00:00+01' - INTERVAL '25 13' DAY TO HOUR;
>> 1999-12-06 23:00:00+01
SELECT TIMESTAMP WITH TIME ZONE '2000-01-01 12:00:00+01' + INTERVAL '1-2' YEAR TO MONTH;
>> 2001-03-01 12:00:00+01
SELECT INTERVAL '1-2' YEAR TO MONTH + TIMESTAMP WITH TIME ZONE '2000-01-01 12:00:00+01';
>> 2001-03-01 12:00:00+01
SELECT TIMESTAMP WITH TIME ZONE '2000-01-01 12:00:00+01' - INTERVAL '1-2' YEAR TO MONTH;
>> 1998-11-01 12:00:00+01
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论