/** * Convert the timestamp to the specified time zone. * * @param value the timestamp (might be ValueNull) * @param calendar the calendar * @return the timestamp using the correct time zone */ public static Timestamp convertTimestamp(Value value, Calendar calendar) { if (value == ValueNull.INSTANCE) { return null; } ValueTimestamp ts = (ValueTimestamp) value.convertTo(Value.TIMESTAMP); Calendar cal = (Calendar) calendar.clone(); cal.clear(); cal.setLenient(true); long dateValue = ts.getDateValue(); long nanos = ts.getTimeNanos(); long millis = nanos / 1000000; nanos -= millis * 1000000; long s = millis / 1000; millis -= s * 1000; long m = s / 60; s -= m * 60; long h = m / 60; m -= h * 60; setCalendarFields( cal, yearFromDateValue(dateValue), monthFromDateValue(dateValue), dayFromDateValue(dateValue), (int) h, (int) m, (int) s, (int) millis); long ms = cal.getTimeInMillis(); Timestamp x = new Timestamp(ms); x.setNanos((int) (nanos + millis * 1000000)); return x; }
/** * Convert the timestamp using the specified calendar. * * @param x the time * @param calendar the calendar * @return the timestamp */ public static ValueTimestamp convertTimestamp(Timestamp x, Calendar calendar) { if (calendar == null) { throw DbException.getInvalidValueException("calendar", null); } Calendar cal = (Calendar) calendar.clone(); cal.setTimeInMillis(x.getTime()); long dateValue = dateValueFromCalendar(cal); long nanos = nanosFromCalendar(cal); nanos += x.getNanos() % 1000000; return ValueTimestamp.fromDateValueAndNanos(dateValue, nanos); }
/** * Calculate the normalized timestamp. * * @param absoluteDay the absolute day * @param nanos the nanoseconds (may be negative or larger than one day) * @return the timestamp */ public static ValueTimestamp normalizeTimestamp(long absoluteDay, long nanos) { if (nanos > NANOS_PER_DAY || nanos < 0) { long d; if (nanos > NANOS_PER_DAY) { d = nanos / NANOS_PER_DAY; } else { d = (nanos - NANOS_PER_DAY + 1) / NANOS_PER_DAY; } nanos -= d * NANOS_PER_DAY; absoluteDay += d; } return ValueTimestamp.fromDateValueAndNanos(dateValueFromAbsoluteDay(absoluteDay), nanos); }
/** * Calculate the number of bytes required to encode the given value. * * @param v the value * @param handler the data handler for lobs * @return the number of bytes required to store this value */ public static int getValueLen(Value v, DataHandler handler) { if (v == ValueNull.INSTANCE) { return 1; } switch (v.getType()) { case Value.BOOLEAN: return 1; case Value.BYTE: return 2; case Value.SHORT: return 3; case Value.INT: { int x = v.getInt(); if (x < 0) { return 1 + getVarIntLen(-x); } else if (x < 16) { return 1; } else { return 1 + getVarIntLen(x); } } case Value.LONG: { long x = v.getLong(); if (x < 0) { return 1 + getVarLongLen(-x); } else if (x < 8) { return 1; } else { return 1 + getVarLongLen(x); } } case Value.DOUBLE: { double x = v.getDouble(); if (x == 1.0d) { return 1; } long d = Double.doubleToLongBits(x); if (d == ValueDouble.ZERO_BITS) { return 1; } return 1 + getVarLongLen(Long.reverse(d)); } case Value.FLOAT: { float x = v.getFloat(); if (x == 1.0f) { return 1; } int f = Float.floatToIntBits(x); if (f == ValueFloat.ZERO_BITS) { return 1; } return 1 + getVarIntLen(Integer.reverse(f)); } case Value.STRING: { String s = v.getString(); int len = s.length(); if (len < 32) { return 1 + getStringWithoutLengthLen(s, len); } return 1 + getStringLen(s); } case Value.STRING_IGNORECASE: case Value.STRING_FIXED: return 1 + getStringLen(v.getString()); case Value.DECIMAL: { BigDecimal x = v.getBigDecimal(); if (BigDecimal.ZERO.equals(x)) { return 1; } else if (BigDecimal.ONE.equals(x)) { return 1; } int scale = x.scale(); BigInteger b = x.unscaledValue(); int bits = b.bitLength(); if (bits <= 63) { if (scale == 0) { return 1 + getVarLongLen(b.longValue()); } return 1 + getVarIntLen(scale) + getVarLongLen(b.longValue()); } byte[] bytes = b.toByteArray(); return 1 + getVarIntLen(scale) + getVarIntLen(bytes.length) + bytes.length; } case Value.TIME: if (SysProperties.STORE_LOCAL_TIME) { long nanos = ((ValueTime) v).getNanos(); long millis = nanos / 1000000; nanos -= millis * 1000000; return 1 + getVarLongLen(millis) + getVarLongLen(nanos); } return 1 + getVarLongLen(DateTimeUtils.getTimeLocalWithoutDst(v.getTime())); case Value.DATE: { if (SysProperties.STORE_LOCAL_TIME) { long dateValue = ((ValueDate) v).getDateValue(); return 1 + getVarLongLen(dateValue); } long x = DateTimeUtils.getTimeLocalWithoutDst(v.getDate()); return 1 + getVarLongLen(x / MILLIS_PER_MINUTE); } case Value.TIMESTAMP: { if (SysProperties.STORE_LOCAL_TIME) { ValueTimestamp ts = (ValueTimestamp) v; long dateValue = ts.getDateValue(); long nanos = ts.getNanos(); long millis = nanos / 1000000; nanos -= millis * 1000000; return 1 + getVarLongLen(dateValue) + getVarLongLen(millis) + getVarLongLen(nanos); } Timestamp ts = v.getTimestamp(); return 1 + getVarLongLen(DateTimeUtils.getTimeLocalWithoutDst(ts)) + getVarIntLen(ts.getNanos()); } case Value.GEOMETRY: case Value.JAVA_OBJECT: { byte[] b = v.getBytesNoCopy(); return 1 + getVarIntLen(b.length) + b.length; } case Value.BYTES: { byte[] b = v.getBytesNoCopy(); int len = b.length; if (len < 32) { return 1 + b.length; } return 1 + getVarIntLen(b.length) + b.length; } case Value.UUID: return 1 + LENGTH_LONG + LENGTH_LONG; case Value.BLOB: case Value.CLOB: { int len = 1; if (v instanceof ValueLob) { ValueLob lob = (ValueLob) v; lob.convertToFileIfRequired(handler); byte[] small = lob.getSmall(); if (small == null) { int t = -1; if (!lob.isLinked()) { t = -2; } len += getVarIntLen(t); len += getVarIntLen(lob.getTableId()); len += getVarIntLen(lob.getObjectId()); len += getVarLongLen(lob.getPrecision()); len += 1; if (t == -2) { len += getStringLen(lob.getFileName()); } } else { len += getVarIntLen(small.length); len += small.length; } } else { ValueLobDb lob = (ValueLobDb) v; byte[] small = lob.getSmall(); if (small == null) { len += getVarIntLen(-3); len += getVarIntLen(lob.getTableId()); len += getVarLongLen(lob.getLobId()); len += getVarLongLen(lob.getPrecision()); } else { len += getVarIntLen(small.length); len += small.length; } } return len; } case Value.ARRAY: { Value[] list = ((ValueArray) v).getList(); int len = 1 + getVarIntLen(list.length); for (Value x : list) { len += getValueLen(x, handler); } return len; } case Value.RESULT_SET: { int len = 1; try { ResultSet rs = ((ValueResultSet) v).getResultSet(); rs.beforeFirst(); ResultSetMetaData meta = rs.getMetaData(); int columnCount = meta.getColumnCount(); len += getVarIntLen(columnCount); for (int i = 0; i < columnCount; i++) { len += getStringLen(meta.getColumnName(i + 1)); len += getVarIntLen(meta.getColumnType(i + 1)); len += getVarIntLen(meta.getPrecision(i + 1)); len += getVarIntLen(meta.getScale(i + 1)); } while (rs.next()) { len++; for (int i = 0; i < columnCount; i++) { int t = DataType.getValueTypeFromResultSet(meta, i + 1); Value val = DataType.readValue(null, rs, i + 1, t); len += getValueLen(val, handler); } } len++; rs.beforeFirst(); } catch (SQLException e) { throw DbException.convert(e); } return len; } default: throw DbException.throwInternalError("type=" + v.getType()); } }
/** * Read a value. * * @return the value */ public Value readValue() { int type = data[pos++] & 255; switch (type) { case Value.NULL: return ValueNull.INSTANCE; case BOOLEAN_TRUE: return ValueBoolean.get(true); case BOOLEAN_FALSE: return ValueBoolean.get(false); case INT_NEG: return ValueInt.get(-readVarInt()); case Value.INT: return ValueInt.get(readVarInt()); case LONG_NEG: return ValueLong.get(-readVarLong()); case Value.LONG: return ValueLong.get(readVarLong()); case Value.BYTE: return ValueByte.get(readByte()); case Value.SHORT: return ValueShort.get(readShortInt()); case DECIMAL_0_1: return (ValueDecimal) ValueDecimal.ZERO; case DECIMAL_0_1 + 1: return (ValueDecimal) ValueDecimal.ONE; case DECIMAL_SMALL_0: return ValueDecimal.get(BigDecimal.valueOf(readVarLong())); case DECIMAL_SMALL: { int scale = readVarInt(); return ValueDecimal.get(BigDecimal.valueOf(readVarLong(), scale)); } case Value.DECIMAL: { int scale = readVarInt(); int len = readVarInt(); byte[] buff = DataUtils.newBytes(len); read(buff, 0, len); BigInteger b = new BigInteger(buff); return ValueDecimal.get(new BigDecimal(b, scale)); } case LOCAL_DATE: { return ValueDate.fromDateValue(readVarLong()); } case Value.DATE: { long x = readVarLong() * MILLIS_PER_MINUTE; return ValueDate.get(new Date(DateTimeUtils.getTimeUTCWithoutDst(x))); } case LOCAL_TIME: { long nanos = readVarLong() * 1000000 + readVarLong(); return ValueTime.fromNanos(nanos); } case Value.TIME: // need to normalize the year, month and day return ValueTime.get(new Time(DateTimeUtils.getTimeUTCWithoutDst(readVarLong()))); case LOCAL_TIMESTAMP: { long dateValue = readVarLong(); long nanos = readVarLong() * 1000000 + readVarLong(); return ValueTimestamp.fromDateValueAndNanos(dateValue, nanos); } case Value.TIMESTAMP: { Timestamp ts = new Timestamp(DateTimeUtils.getTimeUTCWithoutDst(readVarLong())); ts.setNanos(readVarInt()); return ValueTimestamp.get(ts); } case Value.BYTES: { int len = readVarInt(); byte[] b = DataUtils.newBytes(len); read(b, 0, len); return ValueBytes.getNoCopy(b); } case Value.GEOMETRY: { int len = readVarInt(); byte[] b = DataUtils.newBytes(len); read(b, 0, len); return ValueGeometry.get(b); } case Value.JAVA_OBJECT: { int len = readVarInt(); byte[] b = DataUtils.newBytes(len); read(b, 0, len); return ValueJavaObject.getNoCopy(null, b, handler); } case Value.UUID: return ValueUuid.get(readLong(), readLong()); case Value.STRING: return ValueString.get(readString()); case Value.STRING_IGNORECASE: return ValueStringIgnoreCase.get(readString()); case Value.STRING_FIXED: return ValueStringFixed.get(readString()); case FLOAT_0_1: return ValueFloat.get(0); case FLOAT_0_1 + 1: return ValueFloat.get(1); case DOUBLE_0_1: return ValueDouble.get(0); case DOUBLE_0_1 + 1: return ValueDouble.get(1); case Value.DOUBLE: return ValueDouble.get(Double.longBitsToDouble(Long.reverse(readVarLong()))); case Value.FLOAT: return ValueFloat.get(Float.intBitsToFloat(Integer.reverse(readVarInt()))); case Value.BLOB: case Value.CLOB: { int smallLen = readVarInt(); if (smallLen >= 0) { byte[] small = DataUtils.newBytes(smallLen); read(small, 0, smallLen); return ValueLobDb.createSmallLob(type, small); } else if (smallLen == -3) { int tableId = readVarInt(); long lobId = readVarLong(); long precision = readVarLong(); ValueLobDb lob = ValueLobDb.create(type, handler, tableId, lobId, null, precision); return lob; } else { int tableId = readVarInt(); int objectId = readVarInt(); long precision = 0; boolean compression = false; // -1: regular; -2: regular, but not linked (in this case: // including file name) if (smallLen == -1 || smallLen == -2) { precision = readVarLong(); compression = readByte() == 1; } if (smallLen == -2) { String filename = readString(); return ValueLob.openUnlinked( type, handler, tableId, objectId, precision, compression, filename); } return ValueLob.openLinked(type, handler, tableId, objectId, precision, compression); } } case Value.ARRAY: { int len = readVarInt(); Value[] list = new Value[len]; for (int i = 0; i < len; i++) { list[i] = readValue(); } return ValueArray.get(list); } case Value.RESULT_SET: { SimpleResultSet rs = new SimpleResultSet(); rs.setAutoClose(false); int columns = readVarInt(); for (int i = 0; i < columns; i++) { rs.addColumn(readString(), readVarInt(), readVarInt(), readVarInt()); } while (true) { if (readByte() == 0) { break; } Object[] o = new Object[columns]; for (int i = 0; i < columns; i++) { o[i] = readValue().getObject(); } rs.addRow(o); } return ValueResultSet.get(rs); } default: if (type >= INT_0_15 && type < INT_0_15 + 16) { return ValueInt.get(type - INT_0_15); } else if (type >= LONG_0_7 && type < LONG_0_7 + 8) { return ValueLong.get(type - LONG_0_7); } else if (type >= BYTES_0_31 && type < BYTES_0_31 + 32) { int len = type - BYTES_0_31; byte[] b = DataUtils.newBytes(len); read(b, 0, len); return ValueBytes.getNoCopy(b); } else if (type >= STRING_0_31 && type < STRING_0_31 + 32) { return ValueString.get(readString(type - STRING_0_31)); } throw DbException.get(ErrorCode.FILE_CORRUPTED_1, "type: " + type); } }
/** * Append a value. * * @param v the value */ public void writeValue(Value v) { int start = pos; if (v == ValueNull.INSTANCE) { data[pos++] = 0; return; } int type = v.getType(); switch (type) { case Value.BOOLEAN: writeByte((byte) (v.getBoolean().booleanValue() ? BOOLEAN_TRUE : BOOLEAN_FALSE)); break; case Value.BYTE: writeByte((byte) type); writeByte(v.getByte()); break; case Value.SHORT: writeByte((byte) type); writeShortInt(v.getShort()); break; case Value.INT: { int x = v.getInt(); if (x < 0) { writeByte((byte) INT_NEG); writeVarInt(-x); } else if (x < 16) { writeByte((byte) (INT_0_15 + x)); } else { writeByte((byte) type); writeVarInt(x); } break; } case Value.LONG: { long x = v.getLong(); if (x < 0) { writeByte((byte) LONG_NEG); writeVarLong(-x); } else if (x < 8) { writeByte((byte) (LONG_0_7 + x)); } else { writeByte((byte) type); writeVarLong(x); } break; } case Value.DECIMAL: { BigDecimal x = v.getBigDecimal(); if (BigDecimal.ZERO.equals(x)) { writeByte((byte) DECIMAL_0_1); } else if (BigDecimal.ONE.equals(x)) { writeByte((byte) (DECIMAL_0_1 + 1)); } else { int scale = x.scale(); BigInteger b = x.unscaledValue(); int bits = b.bitLength(); if (bits <= 63) { if (scale == 0) { writeByte((byte) DECIMAL_SMALL_0); writeVarLong(b.longValue()); } else { writeByte((byte) DECIMAL_SMALL); writeVarInt(scale); writeVarLong(b.longValue()); } } else { writeByte((byte) type); writeVarInt(scale); byte[] bytes = b.toByteArray(); writeVarInt(bytes.length); write(bytes, 0, bytes.length); } } break; } case Value.TIME: if (SysProperties.STORE_LOCAL_TIME) { writeByte((byte) LOCAL_TIME); ValueTime t = (ValueTime) v; long nanos = t.getNanos(); long millis = nanos / 1000000; nanos -= millis * 1000000; writeVarLong(millis); writeVarLong(nanos); } else { writeByte((byte) type); writeVarLong(DateTimeUtils.getTimeLocalWithoutDst(v.getTime())); } break; case Value.DATE: { if (SysProperties.STORE_LOCAL_TIME) { writeByte((byte) LOCAL_DATE); long x = ((ValueDate) v).getDateValue(); writeVarLong(x); } else { writeByte((byte) type); long x = DateTimeUtils.getTimeLocalWithoutDst(v.getDate()); writeVarLong(x / MILLIS_PER_MINUTE); } break; } case Value.TIMESTAMP: { if (SysProperties.STORE_LOCAL_TIME) { writeByte((byte) LOCAL_TIMESTAMP); ValueTimestamp ts = (ValueTimestamp) v; long dateValue = ts.getDateValue(); writeVarLong(dateValue); long nanos = ts.getNanos(); long millis = nanos / 1000000; nanos -= millis * 1000000; writeVarLong(millis); writeVarLong(nanos); } else { Timestamp ts = v.getTimestamp(); writeByte((byte) type); writeVarLong(DateTimeUtils.getTimeLocalWithoutDst(ts)); writeVarInt(ts.getNanos()); } break; } case Value.GEOMETRY: case Value.JAVA_OBJECT: { writeByte((byte) type); byte[] b = v.getBytesNoCopy(); int len = b.length; writeVarInt(len); write(b, 0, len); break; } case Value.BYTES: { byte[] b = v.getBytesNoCopy(); int len = b.length; if (len < 32) { writeByte((byte) (BYTES_0_31 + len)); write(b, 0, len); } else { writeByte((byte) type); writeVarInt(len); write(b, 0, len); } break; } case Value.UUID: { writeByte((byte) type); ValueUuid uuid = (ValueUuid) v; writeLong(uuid.getHigh()); writeLong(uuid.getLow()); break; } case Value.STRING: { String s = v.getString(); int len = s.length(); if (len < 32) { writeByte((byte) (STRING_0_31 + len)); writeStringWithoutLength(s, len); } else { writeByte((byte) type); writeString(s); } break; } case Value.STRING_IGNORECASE: case Value.STRING_FIXED: writeByte((byte) type); writeString(v.getString()); break; case Value.DOUBLE: { double x = v.getDouble(); if (x == 1.0d) { writeByte((byte) (DOUBLE_0_1 + 1)); } else { long d = Double.doubleToLongBits(x); if (d == ValueDouble.ZERO_BITS) { writeByte((byte) DOUBLE_0_1); } else { writeByte((byte) type); writeVarLong(Long.reverse(d)); } } break; } case Value.FLOAT: { float x = v.getFloat(); if (x == 1.0f) { writeByte((byte) (FLOAT_0_1 + 1)); } else { int f = Float.floatToIntBits(x); if (f == ValueFloat.ZERO_BITS) { writeByte((byte) FLOAT_0_1); } else { writeByte((byte) type); writeVarInt(Integer.reverse(f)); } } break; } case Value.BLOB: case Value.CLOB: { writeByte((byte) type); if (v instanceof ValueLob) { ValueLob lob = (ValueLob) v; lob.convertToFileIfRequired(handler); byte[] small = lob.getSmall(); if (small == null) { int t = -1; if (!lob.isLinked()) { t = -2; } writeVarInt(t); writeVarInt(lob.getTableId()); writeVarInt(lob.getObjectId()); writeVarLong(lob.getPrecision()); writeByte((byte) (lob.isCompressed() ? 1 : 0)); if (t == -2) { writeString(lob.getFileName()); } } else { writeVarInt(small.length); write(small, 0, small.length); } } else { ValueLobDb lob = (ValueLobDb) v; byte[] small = lob.getSmall(); if (small == null) { writeVarInt(-3); writeVarInt(lob.getTableId()); writeVarLong(lob.getLobId()); writeVarLong(lob.getPrecision()); } else { writeVarInt(small.length); write(small, 0, small.length); } } break; } case Value.ARRAY: { writeByte((byte) type); Value[] list = ((ValueArray) v).getList(); writeVarInt(list.length); for (Value x : list) { writeValue(x); } break; } case Value.RESULT_SET: { writeByte((byte) type); try { ResultSet rs = ((ValueResultSet) v).getResultSet(); rs.beforeFirst(); ResultSetMetaData meta = rs.getMetaData(); int columnCount = meta.getColumnCount(); writeVarInt(columnCount); for (int i = 0; i < columnCount; i++) { writeString(meta.getColumnName(i + 1)); writeVarInt(meta.getColumnType(i + 1)); writeVarInt(meta.getPrecision(i + 1)); writeVarInt(meta.getScale(i + 1)); } while (rs.next()) { writeByte((byte) 1); for (int i = 0; i < columnCount; i++) { int t = DataType.getValueTypeFromResultSet(meta, i + 1); Value val = DataType.readValue(null, rs, i + 1, t); writeValue(val); } } writeByte((byte) 0); rs.beforeFirst(); } catch (SQLException e) { throw DbException.convert(e); } break; } default: DbException.throwInternalError("type=" + v.getType()); } if (SysProperties.CHECK2) { if (pos - start != getValueLen(v, handler)) { throw DbException.throwInternalError( "value size error: got " + (pos - start) + " expected " + getValueLen(v, handler)); } } }