提交 ff41fdeb authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Fix negative intervals with 0 in leading field

上级 e17bac80
...@@ -14,6 +14,8 @@ public final class Interval { ...@@ -14,6 +14,8 @@ public final class Interval {
private final IntervalQualifier qualifier; private final IntervalQualifier qualifier;
private final boolean negative;
private final long leading; private final long leading;
private final long remaining; private final long remaining;
...@@ -21,16 +23,24 @@ public final class Interval { ...@@ -21,16 +23,24 @@ public final class Interval {
/** /**
* @param qualifier * @param qualifier
* qualifier * qualifier
* @param negative
* whether interval is negative
* @param leading * @param leading
* value of leading field * value of leading field
* @param remaining * @param remaining
* values of all remaining fields * values of all remaining fields
*/ */
public Interval(IntervalQualifier qualifier, long leading, long remaining) { public Interval(IntervalQualifier qualifier, boolean negative, long leading, long remaining) {
if (qualifier == null) { if (qualifier == null) {
throw new NullPointerException(); throw new NullPointerException();
} }
if (leading == 0L && remaining == 0L) {
negative = false;
} else if (leading < 0L || remaining < 0L) {
throw new RuntimeException();
}
this.qualifier = qualifier; this.qualifier = qualifier;
this.negative = negative;
this.leading = leading; this.leading = leading;
this.remaining = remaining; this.remaining = remaining;
} }
...@@ -44,6 +54,15 @@ public final class Interval { ...@@ -44,6 +54,15 @@ public final class Interval {
return qualifier; return qualifier;
} }
/**
* Returns where the interval is negative.
*
* @return where the interval is negative
*/
public boolean isNegative() {
return negative;
}
/** /**
* Returns value of leading field of this interval. For {@code SECOND} * Returns value of leading field of this interval. For {@code SECOND}
* intervals returns integer part of seconds. * intervals returns integer part of seconds.
...@@ -69,6 +88,7 @@ public final class Interval { ...@@ -69,6 +88,7 @@ public final class Interval {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result + qualifier.hashCode(); result = prime * result + qualifier.hashCode();
result = prime * result + (negative ? 1231 : 1237);
result = prime * result + (int) (leading ^ leading >>> 32); result = prime * result + (int) (leading ^ leading >>> 32);
result = prime * result + (int) (remaining ^ remaining >>> 32); result = prime * result + (int) (remaining ^ remaining >>> 32);
return result; return result;
...@@ -83,12 +103,13 @@ public final class Interval { ...@@ -83,12 +103,13 @@ public final class Interval {
return false; return false;
} }
Interval other = (Interval) obj; Interval other = (Interval) obj;
return qualifier == other.qualifier && leading == other.leading || remaining == other.remaining; return qualifier == other.qualifier && negative == other.negative && leading == other.leading
&& remaining == other.remaining;
} }
@Override @Override
public String toString() { public String toString() {
return DateTimeUtils.intervalToString(qualifier, leading, remaining); return DateTimeUtils.intervalToString(qualifier, negative, leading, remaining);
} }
} }
...@@ -205,13 +205,20 @@ public class IntervalOperation extends Expression { ...@@ -205,13 +205,20 @@ public class IntervalOperation extends Expression {
case DATETIME_MINUS_DATETIME: case DATETIME_MINUS_DATETIME:
if (lType == Value.TIME && rType == Value.TIME) { if (lType == Value.TIME && rType == Value.TIME) {
long diff = ((ValueTime) l).getNanos() - ((ValueTime) r).getNanos(); long diff = ((ValueTime) l).getNanos() - ((ValueTime) r).getNanos();
return ValueInterval.from(IntervalQualifier.HOUR_TO_SECOND, diff / 3_600_000_000_000L, boolean negative = diff < 0;
Math.abs(diff % 3_600_000_000_000L)); if (negative) {
diff = -diff;
}
return ValueInterval.from(IntervalQualifier.HOUR_TO_SECOND, negative, diff / 3_600_000_000_000L,
diff % 3_600_000_000_000L);
} else if (lType == Value.DATE && rType == Value.DATE) { } else if (lType == Value.DATE && rType == Value.DATE) {
return ValueInterval.from(IntervalQualifier.DAY, long diff = DateTimeUtils.absoluteDayFromDateValue(((ValueDate) l).getDateValue())
DateTimeUtils.absoluteDayFromDateValue(((ValueDate) l).getDateValue()) - DateTimeUtils.absoluteDayFromDateValue(((ValueDate) r).getDateValue());
- DateTimeUtils.absoluteDayFromDateValue(((ValueDate) r).getDateValue()), boolean negative = diff < 0;
0L); if (negative) {
diff = -diff;
}
return ValueInterval.from(IntervalQualifier.DAY, negative, diff, 0L);
} else { } else {
long[] a = DateTimeUtils.dateAndTimeFromValue(l); long[] a = DateTimeUtils.dateAndTimeFromValue(l);
long[] b = DateTimeUtils.dateAndTimeFromValue(r); long[] b = DateTimeUtils.dateAndTimeFromValue(r);
......
...@@ -3936,7 +3936,7 @@ public class JdbcResultSet extends TraceObject implements ResultSet, JdbcResultS ...@@ -3936,7 +3936,7 @@ public class JdbcResultSet extends TraceObject implements ResultSet, JdbcResultS
value = value.convertTo(Value.INTERVAL_DAY_TO_SECOND); value = value.convertTo(Value.INTERVAL_DAY_TO_SECOND);
} }
ValueInterval v = (ValueInterval) value; ValueInterval v = (ValueInterval) value;
return type.cast(new Interval(v.getQualifier(), v.getLeading(), v.getRemaining())); return type.cast(new Interval(v.getQualifier(), false, v.getLeading(), v.getRemaining()));
} else if (DataType.isGeometryClass(type)) { } else if (DataType.isGeometryClass(type)) {
return type.cast(value.convertTo(Value.GEOMETRY).getObject()); return type.cast(value.convertTo(Value.GEOMETRY).getObject());
} else if (type == LocalDateTimeUtils.LOCAL_DATE) { } else if (type == LocalDateTimeUtils.LOCAL_DATE) {
......
...@@ -448,8 +448,12 @@ public class ValueDataType implements DataType { ...@@ -448,8 +448,12 @@ public class ValueDataType implements DataType {
case Value.INTERVAL_HOUR: case Value.INTERVAL_HOUR:
case Value.INTERVAL_MINUTE: { case Value.INTERVAL_MINUTE: {
ValueInterval interval = (ValueInterval) v; ValueInterval interval = (ValueInterval) v;
int ordinal = type - Value.INTERVAL_YEAR;
if (interval.isNegative()) {
ordinal = ~ordinal;
}
buff.put((byte) Value.INTERVAL_YEAR). buff.put((byte) Value.INTERVAL_YEAR).
put((byte) (type - Value.INTERVAL_YEAR)). put((byte) (ordinal)).
putVarLong(interval.getLeading()); putVarLong(interval.getLeading());
break; break;
} }
...@@ -462,8 +466,12 @@ public class ValueDataType implements DataType { ...@@ -462,8 +466,12 @@ public class ValueDataType implements DataType {
case Value.INTERVAL_HOUR_TO_SECOND: case Value.INTERVAL_HOUR_TO_SECOND:
case Value.INTERVAL_MINUTE_TO_SECOND: { case Value.INTERVAL_MINUTE_TO_SECOND: {
ValueInterval interval = (ValueInterval) v; ValueInterval interval = (ValueInterval) v;
int ordinal = type - Value.INTERVAL_YEAR;
if (interval.isNegative()) {
ordinal = ~ordinal;
}
buff.put((byte) Value.INTERVAL_YEAR). buff.put((byte) Value.INTERVAL_YEAR).
put((byte) (type - Value.INTERVAL_YEAR)). put((byte) (ordinal)).
putVarLong(interval.getLeading()). putVarLong(interval.getLeading()).
putVarLong(interval.getRemaining()); putVarLong(interval.getRemaining());
break; break;
...@@ -572,8 +580,12 @@ public class ValueDataType implements DataType { ...@@ -572,8 +580,12 @@ public class ValueDataType implements DataType {
case Value.STRING_FIXED: case Value.STRING_FIXED:
return ValueStringFixed.get(readString(buff)); return ValueStringFixed.get(readString(buff));
case Value.INTERVAL_YEAR: { case Value.INTERVAL_YEAR: {
int ordinal = buff.get() & 0xff; int ordinal = buff.get();
return ValueInterval.from(IntervalQualifier.valueOf(ordinal), readVarLong(buff), boolean negative = ordinal < 0;
if (negative) {
ordinal = ~ordinal;
}
return ValueInterval.from(IntervalQualifier.valueOf(ordinal), negative, readVarLong(buff),
ordinal < 5 ? 0 : readVarLong(buff)); ordinal < 5 ? 0 : readVarLong(buff));
} }
case FLOAT_0_1: case FLOAT_0_1:
......
...@@ -663,8 +663,12 @@ public class Data { ...@@ -663,8 +663,12 @@ public class Data {
case Value.INTERVAL_HOUR: case Value.INTERVAL_HOUR:
case Value.INTERVAL_MINUTE: { case Value.INTERVAL_MINUTE: {
ValueInterval interval = (ValueInterval) v; ValueInterval interval = (ValueInterval) v;
int ordinal = type - Value.INTERVAL_YEAR;
if (interval.isNegative()) {
ordinal = ~ordinal;
}
writeByte((byte) Value.INTERVAL_YEAR); writeByte((byte) Value.INTERVAL_YEAR);
writeByte((byte) (type - Value.INTERVAL_YEAR)); writeByte((byte) ordinal);
writeVarLong(interval.getLeading()); writeVarLong(interval.getLeading());
break; break;
} }
...@@ -677,8 +681,12 @@ public class Data { ...@@ -677,8 +681,12 @@ public class Data {
case Value.INTERVAL_HOUR_TO_SECOND: case Value.INTERVAL_HOUR_TO_SECOND:
case Value.INTERVAL_MINUTE_TO_SECOND: { case Value.INTERVAL_MINUTE_TO_SECOND: {
ValueInterval interval = (ValueInterval) v; ValueInterval interval = (ValueInterval) v;
int ordinal = type - Value.INTERVAL_YEAR;
if (interval.isNegative()) {
ordinal = ~ordinal;
}
writeByte((byte) Value.INTERVAL_YEAR); writeByte((byte) Value.INTERVAL_YEAR);
writeByte((byte) (type - Value.INTERVAL_YEAR)); writeByte((byte) ordinal);
writeVarLong(interval.getLeading()); writeVarLong(interval.getLeading());
writeVarLong(interval.getRemaining()); writeVarLong(interval.getRemaining());
break; break;
...@@ -872,8 +880,12 @@ public class Data { ...@@ -872,8 +880,12 @@ public class Data {
return ValueResultSet.get(rs); return ValueResultSet.get(rs);
} }
case Value.INTERVAL_YEAR: { case Value.INTERVAL_YEAR: {
int ordinal = readByte() & 0xff; int ordinal = readByte();
return ValueInterval.from(IntervalQualifier.valueOf(ordinal), readVarLong(), boolean negative = ordinal < 0;
if (negative) {
ordinal = ~ordinal;
}
return ValueInterval.from(IntervalQualifier.valueOf(ordinal), negative, readVarLong(),
ordinal < 5 ? 0 : readVarLong()); ordinal < 5 ? 0 : readVarLong());
} }
case CUSTOM_DATA_TYPE: { case CUSTOM_DATA_TYPE: {
......
...@@ -1744,12 +1744,20 @@ public class DateTimeUtils { ...@@ -1744,12 +1744,20 @@ public class DateTimeUtils {
leading = parseIntervalLeading(s, 0, dash, negative); leading = parseIntervalLeading(s, 0, dash, negative);
remaining = parseIntervalRemainingSeconds(s, dash + 1); remaining = parseIntervalRemainingSeconds(s, dash + 1);
} }
return ValueInterval.from(qualifier, leading, remaining); break;
} }
default: default:
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
return ValueInterval.from(qualifier, leading, remaining); negative = leading < 0;
if (negative) {
if (leading != Long.MIN_VALUE) {
leading = -leading;
} else {
leading = 0;
}
}
return ValueInterval.from(qualifier, negative, leading, remaining);
} }
static ValueInterval parseInterval2(IntervalQualifier qualifier, String s, char ch, int max, boolean negative) { static ValueInterval parseInterval2(IntervalQualifier qualifier, String s, char ch, int max, boolean negative) {
...@@ -1763,11 +1771,22 @@ public class DateTimeUtils { ...@@ -1763,11 +1771,22 @@ public class DateTimeUtils {
leading = parseIntervalLeading(s, 0, dash, negative); leading = parseIntervalLeading(s, 0, dash, negative);
remaining = parseIntervalRemaining(s, dash + 1, s.length(), max); remaining = parseIntervalRemaining(s, dash + 1, s.length(), max);
} }
return ValueInterval.from(qualifier, leading, remaining); negative = leading < 0;
if (negative) {
if (leading != Long.MIN_VALUE) {
leading = -leading;
} else {
leading = 0;
}
}
return ValueInterval.from(qualifier, negative, leading, remaining);
} }
private static long parseIntervalLeading(String s, int start, int end, boolean negative) { private static long parseIntervalLeading(String s, int start, int end, boolean negative) {
long leading = Long.parseLong(s.substring(start, end)); long leading = Long.parseLong(s.substring(start, end));
if (leading == 0) {
return negative ^ s.charAt(start) == '-' ? Long.MIN_VALUE : 0;
}
return negative ? -leading : leading; return negative ? -leading : leading;
} }
...@@ -1799,16 +1818,14 @@ public class DateTimeUtils { ...@@ -1799,16 +1818,14 @@ public class DateTimeUtils {
* Formats interval as a string. * Formats interval as a string.
* *
* @param qualifier qualifier of the interval * @param qualifier qualifier of the interval
* @param negative whether interval is negative
* @param leading the value of leading field * @param leading the value of leading field
* @param remaining the value of all remaining fields * @param remaining the value of all remaining fields
* @return string representation of the specified interval * @return string representation of the specified interval
*/ */
public static String intervalToString(IntervalQualifier qualifier, long leading, long remaining) { public static String intervalToString(IntervalQualifier qualifier, boolean negative, long leading, long remaining)
{
StringBuilder buff = new StringBuilder().append("INTERVAL "); StringBuilder buff = new StringBuilder().append("INTERVAL ");
boolean negative = leading < 0;
if (negative) {
leading = -leading;
}
buff.append('\''); buff.append('\'');
if (negative) { if (negative) {
buff.append('-'); buff.append('-');
...@@ -1908,36 +1925,51 @@ public class DateTimeUtils { ...@@ -1908,36 +1925,51 @@ public class DateTimeUtils {
* in nanoseconds for day-time intervals * in nanoseconds for day-time intervals
*/ */
public static BigInteger intervalToAbsolute(ValueInterval interval) { public static BigInteger intervalToAbsolute(ValueInterval interval) {
BigInteger r;
switch (interval.getQualifier()) { switch (interval.getQualifier()) {
case YEAR: case YEAR:
return BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(12)); r = BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(12));
break;
case MONTH: case MONTH:
return BigInteger.valueOf(interval.getLeading()); r = BigInteger.valueOf(interval.getLeading());
break;
case DAY: case DAY:
return BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(NANOS_PER_DAY)); r = BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(NANOS_PER_DAY));
break;
case HOUR: case HOUR:
return BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(3_600_000_000_000L)); r = BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(3_600_000_000_000L));
break;
case MINUTE: case MINUTE:
return BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(60_000_000_000L)); r = BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(60_000_000_000L));
break;
case SECOND: case SECOND:
return intervalToAbsolute(interval, 1_000_000_000); r = intervalToAbsolute(interval, 1_000_000_000);
break;
case YEAR_TO_MONTH: case YEAR_TO_MONTH:
return intervalToAbsolute(interval, 12); r = intervalToAbsolute(interval, 12);
break;
case DAY_TO_HOUR: case DAY_TO_HOUR:
return intervalToAbsolute(interval, 24, 3_600_000_000_000L); r = intervalToAbsolute(interval, 24, 3_600_000_000_000L);
break;
case DAY_TO_MINUTE: case DAY_TO_MINUTE:
return intervalToAbsolute(interval, 24 * 60, 60_000_000_000L); r = intervalToAbsolute(interval, 24 * 60, 60_000_000_000L);
break;
case DAY_TO_SECOND: case DAY_TO_SECOND:
return intervalToAbsolute(interval, NANOS_PER_DAY); r = intervalToAbsolute(interval, NANOS_PER_DAY);
break;
case HOUR_TO_MINUTE: case HOUR_TO_MINUTE:
return intervalToAbsolute(interval, 60, 60_000_000_000L); r = intervalToAbsolute(interval, 60, 60_000_000_000L);
break;
case HOUR_TO_SECOND: case HOUR_TO_SECOND:
return intervalToAbsolute(interval, 3_600_000_000_000L); r = intervalToAbsolute(interval, 3_600_000_000_000L);
break;
case MINUTE_TO_SECOND: case MINUTE_TO_SECOND:
return intervalToAbsolute(interval, 60_000_000_000L); r = intervalToAbsolute(interval, 60_000_000_000L);
break;
default: default:
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
return interval.isNegative() ? r.negate() : r;
} }
private static BigInteger intervalToAbsolute(ValueInterval interval, long multiplier, long totalMultiplier) { private static BigInteger intervalToAbsolute(ValueInterval interval, long multiplier, long totalMultiplier) {
...@@ -1945,11 +1977,8 @@ public class DateTimeUtils { ...@@ -1945,11 +1977,8 @@ public class DateTimeUtils {
} }
private static BigInteger intervalToAbsolute(ValueInterval interval, long multiplier) { private static BigInteger intervalToAbsolute(ValueInterval interval, long multiplier) {
long leading = interval.getLeading(), remaining = interval.getRemaining(); return BigInteger.valueOf(interval.getLeading()).multiply(BigInteger.valueOf(multiplier))
if (leading < 0) { .add(BigInteger.valueOf(interval.getRemaining()));
remaining = -remaining;
}
return BigInteger.valueOf(leading).multiply(BigInteger.valueOf(multiplier)).add(BigInteger.valueOf(remaining));
} }
/** /**
...@@ -1963,17 +1992,19 @@ public class DateTimeUtils { ...@@ -1963,17 +1992,19 @@ public class DateTimeUtils {
public static ValueInterval intervalFromAbsolute(IntervalQualifier qualifier, BigInteger absolute) { public static ValueInterval intervalFromAbsolute(IntervalQualifier qualifier, BigInteger absolute) {
switch (qualifier) { switch (qualifier) {
case YEAR: case YEAR:
return ValueInterval.from(qualifier, absolute.divide(BigInteger.valueOf(12)).longValue(), 0); return ValueInterval.from(qualifier, absolute.signum() < 0,
Math.abs(absolute.divide(BigInteger.valueOf(12)).longValue()), 0);
case MONTH: case MONTH:
return ValueInterval.from(qualifier, leadingExact(absolute), 0); return ValueInterval.from(qualifier, absolute.signum() < 0, leadingExact(absolute), 0);
case DAY: case DAY:
return ValueInterval.from(qualifier, leadingExact(absolute.divide(BigInteger.valueOf(NANOS_PER_DAY))), 0); return ValueInterval.from(qualifier, absolute.signum() < 0,
leadingExact(absolute.divide(BigInteger.valueOf(NANOS_PER_DAY))), 0);
case HOUR: case HOUR:
return ValueInterval.from(qualifier, leadingExact(absolute.divide(BigInteger.valueOf(3_600_000_000_000L))), return ValueInterval.from(qualifier, absolute.signum() < 0,
0); leadingExact(absolute.divide(BigInteger.valueOf(3_600_000_000_000L))), 0);
case MINUTE: case MINUTE:
return ValueInterval.from(qualifier, leadingExact(absolute.divide(BigInteger.valueOf(60_000_000_000L))), return ValueInterval.from(qualifier, absolute.signum() < 0,
0); leadingExact(absolute.divide(BigInteger.valueOf(60_000_000_000L))), 0);
case SECOND: case SECOND:
return intervalFromAbsolute(qualifier, absolute, 1_000_000_000); return intervalFromAbsolute(qualifier, absolute, 1_000_000_000);
case YEAR_TO_MONTH: case YEAR_TO_MONTH:
...@@ -1997,7 +2028,7 @@ public class DateTimeUtils { ...@@ -1997,7 +2028,7 @@ public class DateTimeUtils {
private static ValueInterval intervalFromAbsolute(IntervalQualifier qualifier, BigInteger absolute, long divisor) { private static ValueInterval intervalFromAbsolute(IntervalQualifier qualifier, BigInteger absolute, long divisor) {
BigInteger[] dr = absolute.divideAndRemainder(BigInteger.valueOf(divisor)); BigInteger[] dr = absolute.divideAndRemainder(BigInteger.valueOf(divisor));
return ValueInterval.from(qualifier, leadingExact(dr[0]), Math.abs(dr[1].longValue())); return ValueInterval.from(qualifier, absolute.signum() < 0, leadingExact(dr[0]), Math.abs(dr[1].longValue()));
} }
private static long leadingExact(BigInteger absolute) { private static long leadingExact(BigInteger absolute) {
...@@ -2005,7 +2036,7 @@ public class DateTimeUtils { ...@@ -2005,7 +2036,7 @@ public class DateTimeUtils {
|| absolute.compareTo(BigInteger.valueOf(-999_999_999_999_999_999L)) < 0) { || absolute.compareTo(BigInteger.valueOf(-999_999_999_999_999_999L)) < 0) {
throw DbException.get(ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE_1, absolute.toString()); throw DbException.get(ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE_1, absolute.toString());
} }
return absolute.longValue(); return Math.abs(absolute.longValue());
} }
} }
...@@ -562,11 +562,13 @@ public class LocalDateTimeUtils { ...@@ -562,11 +562,13 @@ public class LocalDateTimeUtils {
try { try {
long seconds = (long) DURATION_GET_SECONDS.invoke(duration); long seconds = (long) DURATION_GET_SECONDS.invoke(duration);
int nano = (int) DURATION_GET_NANO.invoke(duration); int nano = (int) DURATION_GET_NANO.invoke(duration);
if (seconds < 0 && nano != 0) { boolean negative = seconds < 0;
seconds = Math.abs(seconds);
if (negative && nano != 0) {
nano = 1_000_000_000 - nano; nano = 1_000_000_000 - nano;
seconds++; seconds--;
} }
return ValueInterval.from(IntervalQualifier.SECOND, seconds, nano); return ValueInterval.from(IntervalQualifier.SECOND, negative, seconds, nano);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw DbException.convert(e); throw DbException.convert(e);
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
......
...@@ -793,7 +793,8 @@ public class DataType { ...@@ -793,7 +793,8 @@ public class DataType {
return ValueNull.INSTANCE; return ValueNull.INSTANCE;
} }
Interval interval = (Interval) x; Interval interval = (Interval) x;
return ValueInterval.from(interval.getQualifier(), interval.getLeading(), interval.getRemaining()); return ValueInterval.from(interval.getQualifier(), interval.isNegative(),
interval.getLeading(), interval.getRemaining());
} }
default: default:
if (JdbcUtils.customDataTypesHandler != null) { if (JdbcUtils.customDataTypesHandler != null) {
...@@ -1274,7 +1275,7 @@ public class DataType { ...@@ -1274,7 +1275,7 @@ public class DataType {
return ValueTimestampTimeZone.get((TimestampWithTimeZone) x); return ValueTimestampTimeZone.get((TimestampWithTimeZone) x);
} else if (x instanceof Interval) { } else if (x instanceof Interval) {
Interval i = (Interval) x; Interval i = (Interval) x;
return ValueInterval.from(i.getQualifier(), i.getLeading(), i.getRemaining()); return ValueInterval.from(i.getQualifier(), i.isNegative(), i.getLeading(), i.getRemaining());
} else if (clazz == LocalDateTimeUtils.DURATION) { } else if (clazz == LocalDateTimeUtils.DURATION) {
return LocalDateTimeUtils.durationToValue(x); return LocalDateTimeUtils.durationToValue(x);
} else { } else {
......
...@@ -520,9 +520,12 @@ public class Transfer { ...@@ -520,9 +520,12 @@ public class Transfer {
case Value.INTERVAL_MONTH: case Value.INTERVAL_MONTH:
case Value.INTERVAL_DAY: case Value.INTERVAL_DAY:
case Value.INTERVAL_HOUR: case Value.INTERVAL_HOUR:
case Value.INTERVAL_MINUTE: case Value.INTERVAL_MINUTE: {
writeLong(((ValueInterval) v).getLeading()); ValueInterval interval = (ValueInterval) v;
writeBoolean(interval.isNegative());
writeLong(interval.getLeading());
break; break;
}
case Value.INTERVAL_SECOND: case Value.INTERVAL_SECOND:
case Value.INTERVAL_YEAR_TO_MONTH: case Value.INTERVAL_YEAR_TO_MONTH:
case Value.INTERVAL_DAY_TO_HOUR: case Value.INTERVAL_DAY_TO_HOUR:
...@@ -532,6 +535,7 @@ public class Transfer { ...@@ -532,6 +535,7 @@ public class Transfer {
case Value.INTERVAL_HOUR_TO_SECOND: case Value.INTERVAL_HOUR_TO_SECOND:
case Value.INTERVAL_MINUTE_TO_SECOND: { case Value.INTERVAL_MINUTE_TO_SECOND: {
ValueInterval interval = (ValueInterval) v; ValueInterval interval = (ValueInterval) v;
writeBoolean(interval.isNegative());
writeLong(interval.getLeading()); writeLong(interval.getLeading());
writeLong(interval.getRemaining()); writeLong(interval.getRemaining());
break; break;
...@@ -708,7 +712,7 @@ public class Transfer { ...@@ -708,7 +712,7 @@ public class Transfer {
case Value.INTERVAL_DAY: case Value.INTERVAL_DAY:
case Value.INTERVAL_HOUR: case Value.INTERVAL_HOUR:
case Value.INTERVAL_MINUTE: case Value.INTERVAL_MINUTE:
return ValueInterval.from(IntervalQualifier.valueOf(type - Value.INTERVAL_YEAR), readLong(), 0L); return ValueInterval.from(IntervalQualifier.valueOf(type - Value.INTERVAL_YEAR), readBoolean(), readLong(), 0L);
case Value.INTERVAL_SECOND: case Value.INTERVAL_SECOND:
case Value.INTERVAL_YEAR_TO_MONTH: case Value.INTERVAL_YEAR_TO_MONTH:
case Value.INTERVAL_DAY_TO_HOUR: case Value.INTERVAL_DAY_TO_HOUR:
...@@ -717,7 +721,7 @@ public class Transfer { ...@@ -717,7 +721,7 @@ public class Transfer {
case Value.INTERVAL_HOUR_TO_MINUTE: case Value.INTERVAL_HOUR_TO_MINUTE:
case Value.INTERVAL_HOUR_TO_SECOND: case Value.INTERVAL_HOUR_TO_SECOND:
case Value.INTERVAL_MINUTE_TO_SECOND: case Value.INTERVAL_MINUTE_TO_SECOND:
return ValueInterval.from(IntervalQualifier.valueOf(type - Value.INTERVAL_YEAR), readLong(), readLong()); return ValueInterval.from(IntervalQualifier.valueOf(type - Value.INTERVAL_YEAR), readBoolean(), readLong(), readLong());
default: default:
if (JdbcUtils.customDataTypesHandler != null) { if (JdbcUtils.customDataTypesHandler != null) {
return JdbcUtils.customDataTypesHandler.convert( return JdbcUtils.customDataTypesHandler.convert(
......
...@@ -35,6 +35,8 @@ public class ValueInterval extends Value { ...@@ -35,6 +35,8 @@ public class ValueInterval extends Value {
private final int type; private final int type;
private final boolean negative;
private final long leading; private final long leading;
private final long remaining; private final long remaining;
...@@ -42,18 +44,27 @@ public class ValueInterval extends Value { ...@@ -42,18 +44,27 @@ public class ValueInterval extends Value {
/** /**
* @param qualifier * @param qualifier
* qualifier * qualifier
* @param negative
* whether interval is negative
* @param leading * @param leading
* value of leading field * value of leading field
* @param remaining * @param remaining
* values of all remaining fields * values of all remaining fields
* @return interval value * @return interval value
*/ */
public static ValueInterval from(IntervalQualifier qualifier, long leading, long remaining) { public static ValueInterval from(IntervalQualifier qualifier, boolean negative, long leading, long remaining) {
return (ValueInterval) Value.cache(new ValueInterval(qualifier.ordinal() + INTERVAL_YEAR, leading, remaining)); if (leading == 0L && remaining == 0L) {
negative = false;
} else if (leading < 0L || remaining < 0L) {
throw new RuntimeException();
}
return (ValueInterval) Value.cache(
new ValueInterval(qualifier.ordinal() + INTERVAL_YEAR, negative, leading, remaining));
} }
private ValueInterval(int type, long leading, long remaining) { private ValueInterval(int type, boolean negative, long leading, long remaining) {
this.type = type; this.type = type;
this.negative = negative;
this.leading = leading; this.leading = leading;
this.remaining = remaining; this.remaining = remaining;
} }
...@@ -99,10 +110,6 @@ public class ValueInterval extends Value { ...@@ -99,10 +110,6 @@ public class ValueInterval extends Value {
return this; return this;
} }
long l = leading; long l = leading;
boolean negative = l < 0;
if (negative) {
l = -l;
}
switch (type) { switch (type) {
case Value.INTERVAL_SECOND: case Value.INTERVAL_SECOND:
if (r >= 1_000_000_000) { if (r >= 1_000_000_000) {
...@@ -129,7 +136,7 @@ public class ValueInterval extends Value { ...@@ -129,7 +136,7 @@ public class ValueInterval extends Value {
} }
break; break;
} }
return from(qualifier, negative ? -l : l, r); return from(qualifier, negative, l, r);
} }
@Override @Override
...@@ -140,12 +147,12 @@ public class ValueInterval extends Value { ...@@ -140,12 +147,12 @@ public class ValueInterval extends Value {
@Override @Override
public String getString() { public String getString() {
return DateTimeUtils.intervalToString(getQualifier(), leading, remaining); return DateTimeUtils.intervalToString(getQualifier(), negative, leading, remaining);
} }
@Override @Override
public Object getObject() { public Object getObject() {
return new Interval(getQualifier(), leading, remaining); return new Interval(getQualifier(), negative, leading, remaining);
} }
/** /**
...@@ -157,6 +164,15 @@ public class ValueInterval extends Value { ...@@ -157,6 +164,15 @@ public class ValueInterval extends Value {
return IntervalQualifier.valueOf(type - INTERVAL_YEAR); return IntervalQualifier.valueOf(type - INTERVAL_YEAR);
} }
/**
* Returns where the interval is negative.
*
* @return where the interval is negative
*/
public boolean isNegative() {
return negative;
}
/** /**
* Returns value of leading field of this interval. For {@code SECOND} * Returns value of leading field of this interval. For {@code SECOND}
* intervals returns integer part of seconds. * intervals returns integer part of seconds.
...@@ -187,6 +203,7 @@ public class ValueInterval extends Value { ...@@ -187,6 +203,7 @@ public class ValueInterval extends Value {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result + type; result = prime * result + type;
result = prime * result + (negative ? 1231 : 1237);
result = prime * result + (int) (leading ^ leading >>> 32); result = prime * result + (int) (leading ^ leading >>> 32);
result = prime * result + (int) (remaining ^ remaining >>> 32); result = prime * result + (int) (remaining ^ remaining >>> 32);
return result; return result;
...@@ -201,7 +218,8 @@ public class ValueInterval extends Value { ...@@ -201,7 +218,8 @@ public class ValueInterval extends Value {
return false; return false;
} }
ValueInterval other = (ValueInterval) obj; ValueInterval other = (ValueInterval) obj;
return type == other.type && leading == other.leading && remaining == other.remaining; return type == other.type && negative == other.negative && leading == other.leading
&& remaining == other.remaining;
} }
@Override @Override
...@@ -216,7 +234,10 @@ public class ValueInterval extends Value { ...@@ -216,7 +234,10 @@ public class ValueInterval extends Value {
@Override @Override
public Value negate() { public Value negate() {
return from(getQualifier(), -leading, remaining); if (leading == 0L && remaining == 0L) {
return this;
}
return from(getQualifier(), !negative, -leading, remaining);
} }
} }
...@@ -908,7 +908,7 @@ public class TestPreparedStatement extends TestDb { ...@@ -908,7 +908,7 @@ public class TestPreparedStatement extends TestDb {
private void testInterval(Connection conn) throws SQLException { private void testInterval(Connection conn) throws SQLException {
PreparedStatement prep = conn.prepareStatement("SELECT ?"); PreparedStatement prep = conn.prepareStatement("SELECT ?");
Interval interval = new Interval(IntervalQualifier.MINUTE, 100, 0); Interval interval = new Interval(IntervalQualifier.MINUTE, false, 100, 0);
prep.setObject(1, interval); prep.setObject(1, interval);
ResultSet rs = prep.executeQuery(); ResultSet rs = prep.executeQuery();
rs.next(); rs.next();
......
...@@ -1573,7 +1573,7 @@ public class TestResultSet extends TestDb { ...@@ -1573,7 +1573,7 @@ public class TestResultSet extends TestDb {
rs = stat.executeQuery("CALL INTERVAL '10' YEAR"); rs = stat.executeQuery("CALL INTERVAL '10' YEAR");
rs.next(); rs.next();
assertEquals("INTERVAL '10' YEAR", rs.getString(1)); assertEquals("INTERVAL '10' YEAR", rs.getString(1));
Interval expected = new Interval(IntervalQualifier.YEAR, 10, 0); Interval expected = new Interval(IntervalQualifier.YEAR, false, 10, 0);
assertEquals(expected, rs.getObject(1)); assertEquals(expected, rs.getObject(1));
assertEquals(expected, rs.getObject(1, Interval.class)); assertEquals(expected, rs.getObject(1, Interval.class));
ResultSetMetaData metaData = rs.getMetaData(); ResultSetMetaData metaData = rs.getMetaData();
......
...@@ -575,6 +575,15 @@ SELECT CAST(' interval + ''12-2'' Year To Month ' AS INTERVAL YEAR TO MO ...@@ -575,6 +575,15 @@ SELECT CAST(' interval + ''12-2'' Year To Month ' AS INTERVAL YEAR TO MO
SELECT CAST('INTERVAL''11:12''HOUR TO MINUTE' AS INTERVAL HOUR TO MINUTE); SELECT CAST('INTERVAL''11:12''HOUR TO MINUTE' AS INTERVAL HOUR TO MINUTE);
>> INTERVAL '11:12' HOUR TO MINUTE >> INTERVAL '11:12' HOUR TO MINUTE
SELECT INTERVAL '-0-1' YEAR TO MONTH;
>> INTERVAL '-0-1' YEAR TO MONTH
SELECT INTERVAL '-0.1' SECOND;
>> INTERVAL '-0.1' SECOND
SELECT INTERVAL -'0.1' SECOND;
>> INTERVAL '-0.1' SECOND
-- Arithmetic -- Arithmetic
SELECT INTERVAL '1000' SECOND + INTERVAL '10' MINUTE; SELECT INTERVAL '1000' SECOND + INTERVAL '10' MINUTE;
...@@ -673,7 +682,7 @@ SELECT TIMESTAMP WITH TIME ZONE '2000-01-01 12:00:00+01' - INTERVAL '1-2' YEAR T ...@@ -673,7 +682,7 @@ SELECT TIMESTAMP WITH TIME ZONE '2000-01-01 12:00:00+01' - INTERVAL '1-2' YEAR T
-- Date-time subtraction -- Date-time subtraction
SELECT TIME '10:30:15.123456789' - TIME '11:00:00'; SELECT TIME '10:30:15.123456789' - TIME '11:00:00';
>> INTERVAL '0:29:44.876543211' HOUR TO SECOND >> INTERVAL '-0:29:44.876543211' HOUR TO SECOND
SELECT DATE '2010-01-15' - DATE '2009-12-31'; SELECT DATE '2010-01-15' - DATE '2009-12-31';
>> INTERVAL '15' DAY >> INTERVAL '15' DAY
......
...@@ -250,13 +250,13 @@ public class TestDateTimeUtils extends TestBase { ...@@ -250,13 +250,13 @@ public class TestDateTimeUtils extends TestBase {
} }
private void testParseInterval(IntervalQualifier qualifier, long leading, long remaining, String s, String full) { private void testParseInterval(IntervalQualifier qualifier, long leading, long remaining, String s, String full) {
testParseIntervalImpl(qualifier, leading, remaining, false, s, full); testParseIntervalImpl(qualifier, false, leading, remaining, s, full);
testParseIntervalImpl(qualifier, -leading, remaining, true, s, full); testParseIntervalImpl(qualifier, true, leading, remaining, s, full);
} }
private void testParseIntervalImpl(IntervalQualifier qualifier, long leading, long remaining, boolean negative, private void testParseIntervalImpl(IntervalQualifier qualifier, boolean negative, long leading, long remaining,
String s, String full) { String s, String full) {
ValueInterval expected = ValueInterval.from(qualifier, leading, remaining); ValueInterval expected = ValueInterval.from(qualifier, negative, leading, remaining);
assertEquals(expected, DateTimeUtils.parseInterval(qualifier, negative, s)); assertEquals(expected, DateTimeUtils.parseInterval(qualifier, negative, s));
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
b.append("INTERVAL ").append('\''); b.append("INTERVAL ").append('\'');
......
...@@ -236,7 +236,7 @@ public class TestValueMemory extends TestBase implements DataHandler { ...@@ -236,7 +236,7 @@ public class TestValueMemory extends TestBase implements DataHandler {
case Value.INTERVAL_HOUR: case Value.INTERVAL_HOUR:
case Value.INTERVAL_MINUTE: case Value.INTERVAL_MINUTE:
return ValueInterval.from(IntervalQualifier.valueOf(type - Value.INTERVAL_YEAR), return ValueInterval.from(IntervalQualifier.valueOf(type - Value.INTERVAL_YEAR),
random.nextInt(), 0); random.nextBoolean(), random.nextInt(Integer.MAX_VALUE), 0);
case Value.INTERVAL_SECOND: case Value.INTERVAL_SECOND:
case Value.INTERVAL_YEAR_TO_MONTH: case Value.INTERVAL_YEAR_TO_MONTH:
case Value.INTERVAL_DAY_TO_HOUR: case Value.INTERVAL_DAY_TO_HOUR:
...@@ -246,7 +246,7 @@ public class TestValueMemory extends TestBase implements DataHandler { ...@@ -246,7 +246,7 @@ public class TestValueMemory extends TestBase implements DataHandler {
case Value.INTERVAL_HOUR_TO_SECOND: case Value.INTERVAL_HOUR_TO_SECOND:
case Value.INTERVAL_MINUTE_TO_SECOND: case Value.INTERVAL_MINUTE_TO_SECOND:
return ValueInterval.from(IntervalQualifier.valueOf(type - Value.INTERVAL_YEAR), return ValueInterval.from(IntervalQualifier.valueOf(type - Value.INTERVAL_YEAR),
random.nextInt(), random.nextInt(24)); random.nextBoolean(), random.nextInt(Integer.MAX_VALUE), random.nextInt(24));
default: default:
throw new AssertionError("type=" + type); throw new AssertionError("type=" + type);
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论