提交 c8cf91e6 authored 作者: Noel Grandin's avatar Noel Grandin

beginning of support for TIMESTAMP UTC datatype

上级 d21076b1
......@@ -45,6 +45,7 @@ import org.h2.value.ValueStringFixed;
import org.h2.value.ValueStringIgnoreCase;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampUtc;
import org.h2.value.ValueUuid;
/**
......@@ -276,6 +277,12 @@ public class ValueDataType implements DataType {
putVarLong(nanos);
break;
}
case Value.TIMESTAMP_UTC: {
ValueTimestampUtc ts = (ValueTimestampUtc) v;
long dateTimeValue = ts.getUtcDateTimeNanos();
buff.put((byte) type).putVarLong(dateTimeValue);
break;
}
case Value.JAVA_OBJECT: {
byte[] b = v.getBytesNoCopy();
buff.put((byte) type).
......@@ -481,6 +488,10 @@ public class ValueDataType implements DataType {
long nanos = readVarLong(buff) * 1000000 + readVarLong(buff);
return ValueTimestamp.fromDateValueAndNanos(dateValue, nanos);
}
case Value.TIMESTAMP_UTC: {
long dateTimeValue = readVarLong(buff);
return ValueTimestampUtc.fromNanos(dateTimeValue);
}
case Value.BYTES: {
int len = readVarInt(buff);
byte[] b = DataUtils.newBytes(len);
......
......@@ -17,7 +17,6 @@ import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.SysProperties;
......@@ -50,6 +49,7 @@ import org.h2.value.ValueStringFixed;
import org.h2.value.ValueStringIgnoreCase;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampUtc;
import org.h2.value.ValueUuid;
/**
......@@ -538,6 +538,12 @@ public class Data {
}
break;
}
case Value.TIMESTAMP_UTC: {
ValueTimestampUtc ts = (ValueTimestampUtc) v;
writeByte((byte) type);
writeVarLong(ts.getUtcDateTimeNanos());
break;
}
case Value.GEOMETRY:
case Value.JAVA_OBJECT: {
writeByte((byte) type);
......@@ -772,6 +778,9 @@ public class Data {
DateTimeUtils.getTimeUTCWithoutDst(readVarLong()),
readVarInt());
}
case Value.TIMESTAMP_UTC: {
return ValueTimestampUtc.fromNanos(readVarLong());
}
case Value.BYTES: {
int len = readVarInt();
byte[] b = DataUtils.newBytes(len);
......@@ -1020,6 +1029,10 @@ public class Data {
return 1 + getVarLongLen(DateTimeUtils.getTimeLocalWithoutDst(ts)) +
getVarIntLen(ts.getNanos() % 1000000);
}
case Value.TIMESTAMP_UTC: {
ValueTimestampUtc ts = (ValueTimestampUtc) v;
return 1 + getVarLongLen(ts.getUtcDateTimeNanos());
}
case Value.GEOMETRY:
case Value.JAVA_OBJECT: {
byte[] b = v.getBytesNoCopy();
......
......@@ -6,7 +6,6 @@
package org.h2.table;
import java.sql.ResultSetMetaData;
import org.h2.api.ErrorCode;
import org.h2.command.Parser;
import org.h2.engine.Constants;
......@@ -32,6 +31,7 @@ import org.h2.value.ValueNull;
import org.h2.value.ValueString;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampUtc;
import org.h2.value.ValueUuid;
/**
......@@ -294,6 +294,8 @@ public class Column {
value = ValueInt.get(0).convertTo(type);
} else if (dt.type == Value.TIMESTAMP) {
value = ValueTimestamp.fromMillis(session.getTransactionStart());
} else if (dt.type == Value.TIMESTAMP_UTC) {
value = ValueTimestampUtc.fromMillis(session.getTransactionStart());
} else if (dt.type == Value.TIME) {
value = ValueTime.fromNanos(0);
} else if (dt.type == Value.DATE) {
......
......@@ -22,7 +22,6 @@ import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.UUID;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.SessionInterface;
......@@ -315,6 +314,13 @@ public class DataType {
// 24 for ValueTimestamp, 32 for java.sql.Timestamp
56
);
add(Value.TIMESTAMP_UTC, Types.TIMESTAMP, "TimestampUtc",
createDate(ValueTimestamp.PRECISION, "TIMESTAMP_UTC",
ValueTimestamp.DEFAULT_SCALE, ValueTimestamp.DISPLAY_SIZE),
new String[]{"TIMESTAMP_UTC"},
// 24 for ValueTimestampUtc, 32 for java.sql.Timestamp
56
);
add(Value.BYTES, Types.VARBINARY, "Bytes",
createString(false),
new String[]{"VARBINARY"},
......@@ -539,6 +545,12 @@ public class DataType {
ValueTimestamp.get(value);
break;
}
case Value.TIMESTAMP_UTC: {
Timestamp value = rs.getTimestamp(columnIndex);
v = value == null ? (Value) ValueNull.INSTANCE :
ValueTimestampUtc.fromMillisNanos(value.getTime(), value.getNanos());
break;
}
case Value.DECIMAL: {
BigDecimal value = rs.getBigDecimal(columnIndex);
v = value == null ? (Value) ValueNull.INSTANCE :
......@@ -711,6 +723,7 @@ public class DataType {
// "java.sql.Date";
return Date.class.getName();
case Value.TIMESTAMP:
case Value.TIMESTAMP_UTC:
// "java.sql.Timestamp";
return Timestamp.class.getName();
case Value.BYTES:
......
......@@ -18,7 +18,6 @@ import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.SessionInterface;
......@@ -388,6 +387,11 @@ public class Transfer {
}
break;
}
case Value.TIMESTAMP_UTC: {
ValueTimestampUtc ts = (ValueTimestampUtc) v;
writeLong(ts.getUtcDateTimeNanos());
break;
}
case Value.DECIMAL:
writeString(v.getString());
break;
......@@ -574,6 +578,9 @@ public class Transfer {
return ValueTimestamp.fromMillisNanos(readLong(),
readInt() % 1000000);
}
case Value.TIMESTAMP_UTC: {
return ValueTimestampUtc.fromNanos(readLong());
}
case Value.DECIMAL:
return ValueDecimal.get(new BigDecimal(readString()));
case Value.DOUBLE:
......
......@@ -18,7 +18,6 @@ import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.SysProperties;
......@@ -159,11 +158,15 @@ public abstract class Value {
* The value type for string values with a fixed size.
*/
public static final int GEOMETRY = 22;
/**
* The value type for TIMESTAMP UTC values.
*/
public static final int TIMESTAMP_UTC = 23;
/**
* The number of value types.
*/
public static final int TYPE_COUNT = GEOMETRY + 1;
public static final int TYPE_COUNT = TIMESTAMP_UTC + 1;
private static SoftReference<Value[]> softCache =
new SoftReference<Value[]>(null);
......@@ -299,6 +302,8 @@ public abstract class Value {
return 31;
case TIMESTAMP:
return 32;
case TIMESTAMP_UTC:
return 33;
case BYTES:
return 40;
case BLOB:
......@@ -542,6 +547,7 @@ public abstract class Value {
case TIME:
case DATE:
case TIMESTAMP:
case TIMESTAMP_UTC:
case BYTES:
case JAVA_OBJECT:
case UUID:
......@@ -559,6 +565,7 @@ public abstract class Value {
case INT:
return ValueByte.get(convertToByte(getInt()));
case LONG:
case TIMESTAMP_UTC:
return ValueByte.get(convertToByte(getLong()));
case DECIMAL:
return ValueByte.get(convertToByte(convertToLong(getBigDecimal())));
......@@ -580,6 +587,7 @@ public abstract class Value {
case INT:
return ValueShort.get(convertToShort(getInt()));
case LONG:
case TIMESTAMP_UTC:
return ValueShort.get(convertToShort(getLong()));
case DECIMAL:
return ValueShort.get(convertToShort(convertToLong(getBigDecimal())));
......@@ -601,6 +609,7 @@ public abstract class Value {
case SHORT:
return ValueInt.get(getShort());
case LONG:
case TIMESTAMP_UTC:
return ValueInt.get(convertToInt(getLong()));
case DECIMAL:
return ValueInt.get(convertToInt(convertToLong(getBigDecimal())));
......@@ -637,6 +646,8 @@ public abstract class Value {
}
return ValueLong.get(Long.parseLong(getString(), 16));
}
case TIMESTAMP_UTC:
return ValueLong.get(getLong());
}
break;
}
......@@ -652,6 +663,7 @@ public abstract class Value {
case INT:
return ValueDecimal.get(BigDecimal.valueOf(getInt()));
case LONG:
case TIMESTAMP_UTC:
return ValueDecimal.get(BigDecimal.valueOf(getLong()));
case DOUBLE: {
double d = getDouble();
......@@ -684,6 +696,7 @@ public abstract class Value {
case INT:
return ValueDouble.get(getInt());
case LONG:
case TIMESTAMP_UTC:
return ValueDouble.get(getLong());
case DECIMAL:
return ValueDouble.get(getBigDecimal().doubleValue());
......@@ -703,6 +716,7 @@ public abstract class Value {
case INT:
return ValueFloat.get(getInt());
case LONG:
case TIMESTAMP_UTC:
return ValueFloat.get(getLong());
case DECIMAL:
return ValueFloat.get(getBigDecimal().floatValue());
......@@ -721,6 +735,9 @@ public abstract class Value {
case TIMESTAMP:
return ValueDate.fromDateValue(
((ValueTimestamp) this).getDateValue());
case TIMESTAMP_UTC:
return ValueDate.fromMillis(
((ValueTimestampUtc) this).getUtcDateTimeMillis());
}
break;
}
......@@ -733,6 +750,9 @@ public abstract class Value {
case TIMESTAMP:
return ValueTime.fromNanos(
((ValueTimestamp) this).getTimeNanos());
case TIMESTAMP_UTC:
return ValueTime.fromMillis(
((ValueTimestampUtc) this).getUtcDateTimeMillis());
}
break;
}
......@@ -744,6 +764,35 @@ public abstract class Value {
case DATE:
return ValueTimestamp.fromDateValueAndNanos(
((ValueDate) this).getDateValue(), 0);
case TIMESTAMP_UTC:
return ValueTimestamp.fromMillisNanos(
((ValueTimestampUtc) this).getUtcDateTimeMillis(),
((ValueTimestampUtc) this).getNanosSinceLastMilli());
}
break;
}
case TIMESTAMP_UTC: {
switch (getType()) {
case BOOLEAN:
return ValueTimestampUtc.fromNanos(getBoolean().booleanValue() ? 1 : 0);
case BYTE:
return ValueTimestampUtc.fromNanos(getByte());
case SHORT:
return ValueTimestampUtc.fromNanos(getShort());
case INT:
return ValueTimestampUtc.fromNanos(getInt());
case LONG:
return ValueTimestampUtc.fromNanos(getLong());
case DECIMAL:
return ValueTimestampUtc.fromNanos(getBigDecimal().longValue());
case FLOAT:
return ValueTimestampUtc.fromNanos((long) getFloat());
case DOUBLE:
return ValueTimestampUtc.fromNanos((long) getDouble());
case TIMESTAMP:
return ValueTimestampUtc.fromMillisNanos(
((ValueTimestamp) this).getTimestamp().getTime(),
((ValueTimestamp) this).getTimestamp().getNanos());
}
break;
}
......@@ -860,6 +909,8 @@ public abstract class Value {
return ValueDate.parse(s.trim());
case TIMESTAMP:
return ValueTimestamp.parse(s.trim());
case TIMESTAMP_UTC:
return ValueTimestampUtc.parse(s.trim());
case BYTES:
return ValueBytes.getNoCopy(
StringUtils.convertHexToBytes(s.trim()));
......
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, and the
* EPL 1.0 (http://h2database.com/html/license.html). Initial Developer: H2
* Group
*/
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import org.h2.message.DbException;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
/**
* Implementation of the TIMESTAMP data type.
*/
public final class ValueTimestampUtc extends Value {
/**
* The precision in digits.
*/
public static final int PRECISION = 23;
/**
* The display size of the textual representation of a timestamp. Example:
* 2001-01-01 23:59:59.000 UTC
*/
static final int DISPLAY_SIZE = 27;
/**
* The default scale for timestamps.
*/
static final int DEFAULT_SCALE = 10;
/**
* Time in nanoseconds since 1 Jan 1970 i.e. similar format to
* System.currentTimeMillis()
*/
private final long utcDateTimeNanos;
private ValueTimestampUtc(long utcDateTimeNanos) {
this.utcDateTimeNanos = utcDateTimeNanos;
}
/**
* Get or create a timestamp value for the given date/time in millis.
*
* @param utcDateTimeMillis the date and time in UTC milliseconds
* @param nanos the nanoseconds since the last millisecond
* @return the value
*/
public static ValueTimestampUtc fromMillisNanos(long utcDateTimeMillis, int nanos) {
if (nanos < 0 || nanos >= 1000 * 1000) {
throw new IllegalArgumentException("nanos out of range " + nanos);
}
return (ValueTimestampUtc) Value.cache(new ValueTimestampUtc(utcDateTimeMillis * 1000 * 1000 + nanos));
}
/**
* Get or create a timestamp value for the given date/time in millis.
*
* @param ms the milliseconds
* @return the value
*/
public static ValueTimestampUtc fromMillis(long ms) {
return fromMillisNanos(ms, (short) 0);
}
/**
* Get or create a timestamp value for the given date/time in nanos.
*
* @param nanos the nanos
* @return the value
*/
public static ValueTimestampUtc fromNanos(long nanos) {
return (ValueTimestampUtc) Value.cache(new ValueTimestampUtc(nanos));
}
/**
* Parse a string to a ValueTimestamp. This method supports the format
* +/-year-month-day hour:minute:seconds.fractional and an optional timezone
* part.
*
* @param s the string to parse
* @return the date
*/
public static ValueTimestampUtc parse(String s) {
ValueTimestamp t1 = ValueTimestamp.parse(s);
java.sql.Timestamp t2 = t1.getTimestamp();
return fromMillisNanos(t2.getTime(), t2.getNanos());
}
/**
* Time in nanoseconds since 1 Jan 1970 i.e. similar format to
* System.currentTimeMillis()
*/
public long getUtcDateTimeNanos() {
return utcDateTimeNanos;
}
/**
* Time in milliseconds since 1 Jan 1970 i.e. samer format to
* System.currentTimeMillis()
*/
public long getUtcDateTimeMillis() {
return utcDateTimeNanos / 1000 / 1000;
}
public int getNanosSinceLastMilli() {
return (int) (utcDateTimeNanos % (1000 * 1000));
}
@Override
public java.sql.Timestamp getTimestamp() {
java.sql.Timestamp ts = new java.sql.Timestamp(getUtcDateTimeMillis());
ts.setNanos(getNanosSinceLastMilli());
return ts;
}
@Override
public int getType() {
return Value.TIMESTAMP_UTC;
}
@Override
public String getString() {
Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(getUtcDateTimeMillis());
StringBuilder buff = new StringBuilder(DISPLAY_SIZE);
// date part
int y = cal.get(Calendar.YEAR);
int m = cal.get(Calendar.MONTH);
int d = cal.get(Calendar.DAY_OF_MONTH);
if (y > 0 && y < 10000) {
StringUtils.appendZeroPadded(buff, 4, y);
} else {
buff.append(y);
}
buff.append('-');
StringUtils.appendZeroPadded(buff, 2, m);
buff.append('-');
StringUtils.appendZeroPadded(buff, 2, d);
buff.append(' ');
// time part
long timeNanos = cal.get(Calendar.HOUR_OF_DAY);
timeNanos *= 24;
timeNanos += cal.get(Calendar.MINUTE);
timeNanos *= 60;
timeNanos += cal.get(Calendar.SECOND);
timeNanos *= 60;
timeNanos += cal.get(Calendar.MILLISECOND);
timeNanos *= 1000 * 1000;
timeNanos += getNanosSinceLastMilli();
ValueTime.appendTime(buff, timeNanos, true);
buff.append(" UTC");
return buff.toString();
}
@Override
public String getSQL() {
return "TIMESTAMP UTC '" + getString() + "'";
}
@Override
public long getPrecision() {
return PRECISION;
}
@Override
public int getScale() {
return DEFAULT_SCALE;
}
@Override
public int getDisplaySize() {
return DISPLAY_SIZE;
}
@Override
public Value convertScale(boolean onlyToSmallerScale, int targetScale) {
if (targetScale >= DEFAULT_SCALE) {
return this;
}
if (targetScale < 0) {
throw DbException.getInvalidValueException("scale", targetScale);
} /*
* TODO long n = timeNanos; BigDecimal bd = BigDecimal.valueOf(n); bd
* = bd.movePointLeft(9); bd = ValueDecimal.setScale(bd, targetScale);
* bd = bd.movePointRight(9); long n2 = bd.longValue(); if (n2 == n) {
* return this; }
*/
return this;
}
@Override
protected int compareSecure(Value o, CompareMode mode) {
ValueTimestampUtc t = (ValueTimestampUtc) o;
return MathUtils.compareLong(utcDateTimeNanos, t.utcDateTimeNanos);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (!(other instanceof ValueTimestampUtc)) {
return false;
}
ValueTimestampUtc x = (ValueTimestampUtc) other;
return utcDateTimeNanos == x.utcDateTimeNanos;
}
@Override
public int hashCode() {
return (int) (utcDateTimeNanos ^ (utcDateTimeNanos >>> 32));
}
@Override
public Object getObject() {
return getTimestamp();
}
@Override
public long getLong() {
return utcDateTimeNanos;
}
@Override
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setTimestamp(parameterIndex, getTimestamp());
}
@Override
public Value add(Value v) {
ValueTimestampUtc t = (ValueTimestampUtc) v.convertTo(Value.TIMESTAMP_UTC);
long d1 = utcDateTimeNanos + t.utcDateTimeNanos;
return new ValueTimestampUtc(d1);
}
@Override
public Value subtract(Value v) {
ValueTimestampUtc t = (ValueTimestampUtc) v.convertTo(Value.TIMESTAMP_UTC);
long d1 = utcDateTimeNanos - t.utcDateTimeNanos;
return new ValueTimestampUtc(d1);
}
}
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, and the
* EPL 1.0 (http://h2database.com/html/license.html). Initial Developer: H2
* Group
*/
package org.h2.test.unit;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.h2.test.TestBase;
/**
*/
public class TestTimeStampUtc extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
@Override
public void test() throws SQLException {
deleteDb("timestamputc");
test1();
deleteDb("timestamputc");
}
private void test1() throws SQLException {
Connection conn = getConnection("timestamputc");
Statement stat = conn.createStatement();
stat.execute("create table test(id identity, t1 timestamp_utc)");
stat.execute("insert into test(t1) values(0)");
ResultSet rs = stat.executeQuery("select t1 from test");
rs.next();
assertEquals(0, rs.getLong(1));
assertEquals(new java.sql.Timestamp(0), rs.getTimestamp(1));
conn.close();
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论