提交 8549969a authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Parse INTERVAL literals

上级 e403cc08
......@@ -36,7 +36,7 @@ public final class Interval {
}
/**
* Return qualifier of this interval.
* Returns qualifier of this interval.
*
* @return qualifier
*/
......@@ -44,13 +44,33 @@ public final class Interval {
return qualifier;
}
/**
* Returns value of leading field of this interval. For {@code SECOND}
* intervals returns integer part of seconds.
*
* @return value of leading field
*/
public long getLeading() {
return leading;
}
/**
* Returns combined value of remaining fields of this interval. For
* {@code SECOND} intervals returns nanoseconds.
*
* @return combined value of remaining fields
*/
public long getRemaining() {
return remaining;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + qualifier.hashCode();
result = prime * result + (int) (leading ^ (leading >>> 32));
result = prime * result + (int) (remaining ^ (remaining >>> 32));
result = prime * result + (int) (leading ^ leading >>> 32);
result = prime * result + (int) (remaining ^ remaining >>> 32);
return result;
}
......
......@@ -188,6 +188,7 @@ import org.h2.table.TableFilter;
import org.h2.table.TableFilter.TableFilterVisitor;
import org.h2.table.TableView;
import org.h2.util.DateTimeFunctions;
import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils;
import org.h2.util.ParserUtil;
import org.h2.util.StatementBuilder;
......@@ -3510,6 +3511,58 @@ public class Parser {
r = ValueExpression.get(ValueTimestamp.parse(timestamp, database.getMode()));
}
}
} else if (equalsToken("INTERVAL", name)) {
boolean negative = readIf(MINUS_SIGN);
if (!negative) {
readIf(PLUS_SIGN);
}
String s = readString();
IntervalQualifier qualifier;
if (readIf("YEAR")) {
if (readIf("TO")) {
read("MONTH");
qualifier = IntervalQualifier.YEAR_TO_MONTH;
} else {
qualifier = IntervalQualifier.YEAR;
}
} else if (readIf("MONTH")) {
qualifier = IntervalQualifier.MONTH;
} else if (readIf("DAY")) {
if (readIf("TO")) {
if (readIf("HOUR")) {
qualifier = IntervalQualifier.DAY_TO_HOUR;
} else if (readIf("MINUTE")) {
qualifier = IntervalQualifier.DAY_TO_MINUTE;
} else {
read("SECOND");
qualifier = IntervalQualifier.DAY_TO_SECOND;
}
} else {
qualifier = IntervalQualifier.DAY;
}
} else if (readIf("HOUR")) {
if (readIf("TO")) {
if (readIf("MINUTE")) {
qualifier = IntervalQualifier.HOUR_TO_MINUTE;
} else {
read("SECOND");
qualifier = IntervalQualifier.HOUR_TO_SECOND;
}
} else {
qualifier = IntervalQualifier.HOUR;
}
} else if (readIf("MINUTE")) {
if (readIf("TO")) {
read("SECOND");
qualifier = IntervalQualifier.MINUTE_TO_SECOND;
} else {
qualifier = IntervalQualifier.MINUTE;
}
} else {
read("SECOND");
qualifier = IntervalQualifier.SECOND;
}
r = ValueExpression.get(DateTimeUtils.parseInterval(qualifier, negative, s));
} else if (currentTokenType == VALUE &&
currentValue.getType() == Value.STRING) {
if (equalsToken("DATE", name) ||
......@@ -4930,6 +4983,7 @@ public class Parser {
throw DbException.get(ErrorCode.INVALID_VALUE_SCALE_PRECISION,
Integer.toString(originalScale));
}
scale = originalScale;
}
}
} else if (readIf(OPEN_PAREN)) {
......
......@@ -1503,31 +1503,31 @@ public class DateTimeUtils {
case DAY:
case HOUR:
case MINUTE:
leading = parseIntervalLeading(s, 0, s.length());
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
break;
case SECOND: {
int dot = s.indexOf('.');
if (dot < 0) {
leading = parseIntervalLeading(s, 0, s.length());
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, dot);
leading = parseIntervalLeading(s, 0, dot, negative);
remaining = parseNanos(s, dot + 1, s.length());
}
break;
}
case YEAR_TO_MONTH:
return parseInterval2(qualifier, s, '-', 11);
return parseInterval2(qualifier, s, '-', 11, negative);
case DAY_TO_HOUR:
return parseInterval2(qualifier, s, ' ', 23);
return parseInterval2(qualifier, s, ' ', 23, negative);
case DAY_TO_MINUTE: {
int space = s.indexOf(' ');
if (space < 0) {
leading = parseIntervalLeading(s, 0, s.length());
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, space);
leading = parseIntervalLeading(s, 0, space, negative);
int colon = s.indexOf(':', space + 1);
if (colon < 0) {
remaining = parseIntervalRemaining(s, space + 1, s.length(), 23) * 60;
......@@ -1541,10 +1541,10 @@ public class DateTimeUtils {
case DAY_TO_SECOND: {
int space = s.indexOf(' ');
if (space < 0) {
leading = parseIntervalLeading(s, 0, s.length());
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, space);
leading = parseIntervalLeading(s, 0, space, negative);
int colon = s.indexOf(':', space + 1);
if (colon < 0) {
remaining = parseIntervalRemaining(s, space + 1, s.length(), 23) * 3_600_000_000_000L;
......@@ -1563,14 +1563,14 @@ public class DateTimeUtils {
break;
}
case HOUR_TO_MINUTE:
return parseInterval2(qualifier, s, ':', 59);
return parseInterval2(qualifier, s, ':', 59, negative);
case HOUR_TO_SECOND: {
int colon = s.indexOf(':');
if (colon < 0) {
leading = parseIntervalLeading(s, 0, s.length());
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, colon);
leading = parseIntervalLeading(s, 0, colon, negative);
int colon2 = s.indexOf(':', colon + 1);
if (colon2 < 0) {
remaining = parseIntervalRemaining(s, colon + 1, s.length(), 59) * 60_000_000_000L;
......@@ -1584,10 +1584,10 @@ public class DateTimeUtils {
case MINUTE_TO_SECOND: {
int dash = s.indexOf(':');
if (dash < 0) {
leading = parseIntervalLeading(s, 0, s.length());
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, dash);
leading = parseIntervalLeading(s, 0, dash, negative);
remaining = parseIntervalRemainingSeconds(s, dash + 1);
}
return ValueInterval.from(qualifier, leading, remaining);
......@@ -1598,22 +1598,23 @@ public class DateTimeUtils {
return ValueInterval.from(qualifier, leading, remaining);
}
static ValueInterval parseInterval2(IntervalQualifier qualifier, String s, char ch, int max) {
static ValueInterval parseInterval2(IntervalQualifier qualifier, String s, char ch, int max, boolean negative) {
long leading;
long remaining;
int dash = s.indexOf(ch);
int dash = s.indexOf(ch, 1);
if (dash < 0) {
leading = parseIntervalLeading(s, 0, s.length());
leading = parseIntervalLeading(s, 0, s.length(), negative);
remaining = 0;
} else {
leading = parseIntervalLeading(s, 0, dash);
leading = parseIntervalLeading(s, 0, dash, negative);
remaining = parseIntervalRemaining(s, dash + 1, s.length(), max);
}
return ValueInterval.from(qualifier, leading, remaining);
}
private static long parseIntervalLeading(String s, int start, int end) {
return Long.parseLong(s.substring(start, end));
private static long parseIntervalLeading(String s, int start, int end, boolean negative) {
long leading = Long.parseLong(s.substring(start, end));
return negative ? -leading : leading;
}
private static long parseIntervalRemaining(String s, int start, int end, int max) {
......
......@@ -26,6 +26,7 @@ import java.util.HashMap;
import java.util.UUID;
import org.h2.api.ErrorCode;
import org.h2.api.Interval;
import org.h2.api.IntervalQualifier;
import org.h2.api.TimestampWithTimeZone;
import org.h2.engine.Mode;
......@@ -774,6 +775,26 @@ public class DataType {
}
return ValueGeometry.getFromGeometry(x);
}
case Value.INTERVAL_YEAR:
case Value.INTERVAL_MONTH:
case Value.INTERVAL_DAY:
case Value.INTERVAL_HOUR:
case Value.INTERVAL_MINUTE:
case Value.INTERVAL_SECOND:
case Value.INTERVAL_YEAR_TO_MONTH:
case Value.INTERVAL_DAY_TO_HOUR:
case Value.INTERVAL_DAY_TO_MINUTE:
case Value.INTERVAL_DAY_TO_SECOND:
case Value.INTERVAL_HOUR_TO_MINUTE:
case Value.INTERVAL_HOUR_TO_SECOND:
case Value.INTERVAL_MINUTE_TO_SECOND: {
Object x = rs.getObject(columnIndex);
if (x == null) {
return ValueNull.INSTANCE;
}
Interval interval = (Interval) x;
return ValueInterval.from(interval.getQualifier(), interval.getLeading(), interval.getRemaining());
}
default:
if (JdbcUtils.customDataTypesHandler != null) {
return JdbcUtils.customDataTypesHandler.getValue(type,
......
......@@ -5,6 +5,7 @@ import java.sql.SQLException;
import org.h2.api.Interval;
import org.h2.api.IntervalQualifier;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils;
/**
......@@ -69,7 +70,66 @@ public class ValueInterval extends Value {
@Override
public long getPrecision() {
return MAXIMUM_PRECISION;
long l = leading;
if (l < 0) {
l = 0;
}
int precision = 0;
while (l > 0) {
precision++;
l /= 10;
}
return precision > 0 ? precision : 1;
}
@Override
public Value convertScale(boolean onlyToSmallerScale, int targetScale) {
if (targetScale >= MAXIMUM_SCALE) {
return this;
}
if (targetScale < 0) {
throw DbException.getInvalidValueException("scale", targetScale);
}
IntervalQualifier qualifier = getQualifier();
if (!qualifier.hasSeconds()) {
return this;
}
long r = DateTimeUtils.convertScale(remaining, targetScale);
if (r == remaining) {
return this;
}
long l = leading;
boolean negative = l < 0;
if (negative) {
l = -l;
}
switch (type) {
case Value.INTERVAL_SECOND:
if (r >= 1_000_000_000) {
l++;
r -= 1_000_000_000;
}
break;
case Value.INTERVAL_DAY_TO_SECOND:
if (r >= DateTimeUtils.NANOS_PER_DAY) {
l++;
r -= DateTimeUtils.NANOS_PER_DAY;
}
break;
case Value.INTERVAL_HOUR_TO_SECOND:
if (r >= 3_600_000_000_000L) {
l++;
r -= 3_600_000_000_000L;
}
break;
case Value.INTERVAL_MINUTE_TO_SECOND:
if (r >= 60_000_000_000L) {
l++;
r -= 60_000_000_000L;
}
break;
}
return from(qualifier, l, r);
}
@Override
......@@ -107,8 +167,8 @@ public class ValueInterval extends Value {
final int prime = 31;
int result = 1;
result = prime * result + type;
result = prime * result + (int) (leading ^ (leading >>> 32));
result = prime * result + (int) (remaining ^ (remaining >>> 32));
result = prime * result + (int) (leading ^ leading >>> 32);
result = prime * result + (int) (remaining ^ remaining >>> 32);
return result;
}
......
......@@ -37,15 +37,77 @@ SELECT COLUMN_NAME, DATA_TYPE, TYPE_NAME, COLUMN_TYPE, NUMERIC_PRECISION, NUMERI
> J03 1111 INTERVAL INTERVAL DAY(5) 5 0 null DAY(5) 5
> J04 1111 INTERVAL INTERVAL HOUR(5) 5 0 null HOUR(5) 5
> J05 1111 INTERVAL INTERVAL MINUTE(5) 5 0 null MINUTE(5) 5
> J06 1111 INTERVAL INTERVAL SECOND(5, 9) 5 6 6 SECOND(5, 9) 5
> J06 1111 INTERVAL INTERVAL SECOND(5, 9) 5 9 9 SECOND(5, 9) 5
> J07 1111 INTERVAL INTERVAL YEAR(5) TO MONTH 5 0 null YEAR(5) TO MONTH 5
> J08 1111 INTERVAL INTERVAL DAY(5) TO HOUR 5 0 null DAY(5) TO HOUR 5
> J09 1111 INTERVAL INTERVAL DAY(5) TO MINUTE 5 0 null DAY(5) TO MINUTE 5
> J10 1111 INTERVAL INTERVAL DAY(5) TO SECOND(9) 5 6 6 DAY(5) TO SECOND(9) 5
> J10 1111 INTERVAL INTERVAL DAY(5) TO SECOND(9) 5 9 9 DAY(5) TO SECOND(9) 5
> J11 1111 INTERVAL INTERVAL HOUR(5) TO MINUTE 5 0 null HOUR(5) TO MINUTE 5
> J12 1111 INTERVAL INTERVAL HOUR(5) TO SECOND(9) 5 6 6 HOUR(5) TO SECOND(9) 5
> J13 1111 INTERVAL INTERVAL MINUTE(5) TO SECOND(9) 5 6 6 MINUTE(5) TO SECOND(9) 5
> J12 1111 INTERVAL INTERVAL HOUR(5) TO SECOND(9) 5 9 9 HOUR(5) TO SECOND(9) 5
> J13 1111 INTERVAL INTERVAL MINUTE(5) TO SECOND(9) 5 9 9 MINUTE(5) TO SECOND(9) 5
> rows (ordered): 26
INSERT INTO TEST VALUES (
INTERVAL '1' YEAR, INTERVAL '1' MONTH, INTERVAL '1' DAY, INTERVAL '1' HOUR, INTERVAL '1' MINUTE,
INTERVAL '1.123456789' SECOND, INTERVAL '1-2' YEAR TO MONTH, INTERVAL '1 2' DAY TO HOUR,
INTERVAL '1 2:3' DAY TO MINUTE, INTERVAL '1 2:3:4.123456789' DAY TO SECOND, INTERVAL '1:2' HOUR TO MINUTE,
INTERVAL '1:2:3.123456789' HOUR TO SECOND, INTERVAL '1:2.123456789' MINUTE TO SECOND,
INTERVAL '1' YEAR, INTERVAL '1' MONTH, INTERVAL '1' DAY, INTERVAL '1' HOUR, INTERVAL '1' MINUTE,
INTERVAL '1.123456789' SECOND, INTERVAL '1-2' YEAR TO MONTH, INTERVAL '1 2' DAY TO HOUR,
INTERVAL '1 2:3' DAY TO MINUTE, INTERVAL '1 2:3:4.123456789' DAY TO SECOND, INTERVAL '1:2' HOUR TO MINUTE,
INTERVAL '1:2:3.123456789' HOUR TO SECOND, INTERVAL '1:2.123456789' MINUTE TO SECOND
), (
INTERVAL '-1' YEAR, INTERVAL '-1' MONTH, INTERVAL '-1' DAY, INTERVAL '-1' HOUR, INTERVAL '-1' MINUTE,
INTERVAL '-1.123456789' SECOND, INTERVAL '-1-2' YEAR TO MONTH, INTERVAL '-1 2' DAY TO HOUR,
INTERVAL '-1 2:3' DAY TO MINUTE, INTERVAL '-1 2:3:4.123456789' DAY TO SECOND, INTERVAL '-1:2' HOUR TO MINUTE,
INTERVAL '-1:2:3.123456789' HOUR TO SECOND, INTERVAL '-1:2.123456789' MINUTE TO SECOND,
INTERVAL -'1' YEAR, INTERVAL -'1' MONTH, INTERVAL -'1' DAY, INTERVAL -'1' HOUR, INTERVAL -'1' MINUTE,
INTERVAL -'1.123456789' SECOND, INTERVAL -'1-2' YEAR TO MONTH, INTERVAL -'1 2' DAY TO HOUR,
INTERVAL -'1 2:3' DAY TO MINUTE, INTERVAL -'1 2:3:4.123456789' DAY TO SECOND, INTERVAL -'1:2' HOUR TO MINUTE,
INTERVAL -'1:2:3.123456789' HOUR TO SECOND, INTERVAL -'1:2.123456789' MINUTE TO SECOND);
> update count: 2
SELECT I01, I02, I03, I04, I05, I06 FROM TEST;
> I01 I02 I03 I04 I05 I06
> ------------------ ------------------- ----------------- ------------------ -------------------- --------------------------
> INTERVAL '1' YEAR INTERVAL '1' MONTH INTERVAL '1' DAY INTERVAL '1' HOUR INTERVAL '1' MINUTE INTERVAL '1.123457' SECOND
> INTERVAL -'1' YEAR INTERVAL -'1' MONTH INTERVAL -'1' DAY INTERVAL -'1' HOUR INTERVAL -'1' MINUTE INTERVAL '1.123457' SECOND
> rows: 2
SELECT I07, I08, I09, I10 FROM TEST;
> I07 I08 I09 I10
> ----------------------------- ---------------------------- --------------------------------- ------------------------------------------
> INTERVAL '1-2' YEAR TO MONTH INTERVAL '1 02' DAY TO HOUR INTERVAL '1 02:03' DAY TO MINUTE INTERVAL '1 02:03:04.123457' DAY TO SECOND
> INTERVAL -'1-2' YEAR TO MONTH INTERVAL -'1 02' DAY TO HOUR INTERVAL -'1 02:03' DAY TO MINUTE INTERVAL '1 02:03:04.123457' DAY TO SECOND
> rows: 2
SELECT I11, I12, I12 FROM TEST;
> I11 I12 I12
> ------------------------------- ---------------------------------------- ----------------------------------------
> INTERVAL '1:02' HOUR TO MINUTE INTERVAL '1:02:03.123457' HOUR TO SECOND INTERVAL '1:02:03.123457' HOUR TO SECOND
> INTERVAL -'1:02' HOUR TO MINUTE INTERVAL '1:02:03.123457' HOUR TO SECOND INTERVAL '1:02:03.123457' HOUR TO SECOND
> rows: 2
SELECT J01, J02, J03, J04, J05, J06 FROM TEST;
> J01 J02 J03 J04 J05 J06
> ------------------ ------------------- ----------------- ------------------ -------------------- ------------------------------
> INTERVAL '1' YEAR INTERVAL '1' MONTH INTERVAL '1' DAY INTERVAL '1' HOUR INTERVAL '1' MINUTE INTERVAL '1.123456789' SECOND
> INTERVAL -'1' YEAR INTERVAL -'1' MONTH INTERVAL -'1' DAY INTERVAL -'1' HOUR INTERVAL -'1' MINUTE INTERVAL -'1.123456789' SECOND
> rows: 2
SELECT J07, J08, J09, J10 FROM TEST;
> J07 J08 J09 J10
> ----------------------------- ---------------------------- --------------------------------- ----------------------------------------------
> INTERVAL '1-2' YEAR TO MONTH INTERVAL '1 02' DAY TO HOUR INTERVAL '1 02:03' DAY TO MINUTE INTERVAL '1 02:03:04.123456789' DAY TO SECOND
> INTERVAL -'1-2' YEAR TO MONTH INTERVAL -'1 02' DAY TO HOUR INTERVAL -'1 02:03' DAY TO MINUTE INTERVAL -'1 02:03:04.123456789' DAY TO SECOND
> rows: 2
SELECT J11, J12, J12 FROM TEST;
> J11 J12 J12
> ------------------------------- -------------------------------------------- --------------------------------------------
> INTERVAL '1:02' HOUR TO MINUTE INTERVAL '1:02:03.123456789' HOUR TO SECOND INTERVAL '1:02:03.123456789' HOUR TO SECOND
> INTERVAL -'1:02' HOUR TO MINUTE INTERVAL -'1:02:03.123456789' HOUR TO SECOND INTERVAL -'1:02:03.123456789' HOUR TO SECOND
> rows: 2
DROP TABLE TEST;
> ok
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论