PHOENIX-2631 Exception when parsing boundary timestamp values
Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/818683aa Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/818683aa Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/818683aa Branch: refs/heads/calcite Commit: 818683aac866fe31b48135209158c84942a80fb6 Parents: 9e2b433 Author: James Taylor <jtay...@salesforce.com> Authored: Tue Feb 16 13:01:45 2016 -0800 Committer: James Taylor <jtay...@salesforce.com> Committed: Wed Feb 17 09:14:13 2016 -0800 ---------------------------------------------------------------------- .../end2end/ClientTimeArithmeticQueryIT.java | 11 +- .../org/apache/phoenix/end2end/DateTimeIT.java | 5 +- .../apache/phoenix/end2end/DistinctCountIT.java | 2 +- .../apache/phoenix/end2end/PercentileIT.java | 2 +- .../phoenix/end2end/ProductMetricsIT.java | 6 +- .../phoenix/end2end/RowValueConstructorIT.java | 4 +- .../phoenix/end2end/VariableLengthPKIT.java | 2 +- .../end2end/index/IndexExpressionIT.java | 7 +- .../apache/phoenix/end2end/index/IndexIT.java | 5 +- .../apache/phoenix/schema/types/PDataType.java | 3 + .../apache/phoenix/schema/types/PTimestamp.java | 407 +++++++++++-------- .../phoenix/compile/WhereOptimizerTest.java | 5 +- .../java/org/apache/phoenix/query/BaseTest.java | 7 +- .../org/apache/phoenix/query/QueryPlanTest.java | 24 +- .../phoenix/schema/types/PDataTypeTest.java | 60 +++ .../java/org/apache/phoenix/util/TestUtil.java | 7 +- 16 files changed, 344 insertions(+), 213 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/818683aa/phoenix-core/src/it/java/org/apache/phoenix/end2end/ClientTimeArithmeticQueryIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ClientTimeArithmeticQueryIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ClientTimeArithmeticQueryIT.java index e617673..8347370 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ClientTimeArithmeticQueryIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ClientTimeArithmeticQueryIT.java @@ -19,9 +19,9 @@ */ package org.apache.phoenix.end2end; +import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.B_VALUE; import static org.apache.phoenix.util.TestUtil.E_VALUE; -import static org.apache.phoenix.util.TestUtil.MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.ROW1; import static org.apache.phoenix.util.TestUtil.ROW2; import static org.apache.phoenix.util.TestUtil.ROW3; @@ -51,7 +51,6 @@ import java.util.Properties; import org.apache.phoenix.util.PhoenixRuntime; import org.apache.phoenix.util.PropertiesUtil; -import org.apache.phoenix.util.TestUtil; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -614,10 +613,10 @@ public class ClientTimeArithmeticQueryIT extends BaseQueryIT { statement.setDate(3, date); statement.execute(); statement.setString(2, ROW4); - statement.setDate(3, new Date(date.getTime() + TestUtil.MILLIS_IN_DAY - 1)); + statement.setDate(3, new Date(date.getTime() + MILLIS_IN_DAY - 1)); statement.execute(); statement.setString(2, ROW6); - statement.setDate(3, new Date(date.getTime() + TestUtil.MILLIS_IN_DAY - 1)); + statement.setDate(3, new Date(date.getTime() + MILLIS_IN_DAY - 1)); statement.execute(); statement.setString(2, ROW9); statement.setDate(3, date); @@ -738,7 +737,7 @@ public class ClientTimeArithmeticQueryIT extends BaseQueryIT { conn = DriverManager.getConnection(getUrl(), props); rs = conn.createStatement().executeQuery("SELECT ts + 1 FROM time_table"); assertTrue(rs.next()); - assertEquals(time.getTime() + TestUtil.MILLIS_IN_DAY,rs.getTimestamp(1).getTime()); + assertEquals(time.getTime() + MILLIS_IN_DAY,rs.getTimestamp(1).getTime()); } @Test @@ -772,7 +771,7 @@ public class ClientTimeArithmeticQueryIT extends BaseQueryIT { conn = DriverManager.getConnection(getUrl(), props); rs = conn.createStatement().executeQuery("SELECT ts - 1 FROM time_table"); assertTrue(rs.next()); - assertEquals(time.getTime() - TestUtil.MILLIS_IN_DAY,rs.getTimestamp(1).getTime()); + assertEquals(time.getTime() - MILLIS_IN_DAY,rs.getTimestamp(1).getTime()); } @Test http://git-wip-us.apache.org/repos/asf/phoenix/blob/818683aa/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java index 6c53f1d..e87d8d4 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java @@ -17,12 +17,12 @@ */ package org.apache.phoenix.end2end; +import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.ATABLE_NAME; import static org.apache.phoenix.util.TestUtil.A_VALUE; import static org.apache.phoenix.util.TestUtil.B_VALUE; import static org.apache.phoenix.util.TestUtil.C_VALUE; import static org.apache.phoenix.util.TestUtil.E_VALUE; -import static org.apache.phoenix.util.TestUtil.MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.ROW1; import static org.apache.phoenix.util.TestUtil.ROW2; import static org.apache.phoenix.util.TestUtil.ROW3; @@ -52,7 +52,6 @@ import java.util.Calendar; import java.util.GregorianCalendar; import org.apache.phoenix.util.DateUtil; -import org.apache.phoenix.util.TestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -672,6 +671,6 @@ public class DateTimeIT extends BaseHBaseManagedTimeIT { ResultSet rs = conn.createStatement().executeQuery("SELECT CURRENT_DATE()"); assertTrue(rs.next()); long actualTime = rs.getDate(1).getTime(); - assertTrue(Math.abs(actualTime - expectedTime) < TestUtil.MILLIS_IN_DAY); + assertTrue(Math.abs(actualTime - expectedTime) < MILLIS_IN_DAY); } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/818683aa/phoenix-core/src/it/java/org/apache/phoenix/end2end/DistinctCountIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DistinctCountIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DistinctCountIT.java index b1daacc..523e83c 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DistinctCountIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DistinctCountIT.java @@ -17,11 +17,11 @@ */ package org.apache.phoenix.end2end; +import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.ATABLE_NAME; import static org.apache.phoenix.util.TestUtil.A_VALUE; import static org.apache.phoenix.util.TestUtil.B_VALUE; import static org.apache.phoenix.util.TestUtil.C_VALUE; -import static org.apache.phoenix.util.TestUtil.MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.ROW1; import static org.apache.phoenix.util.TestUtil.ROW2; import static org.apache.phoenix.util.TestUtil.ROW3; http://git-wip-us.apache.org/repos/asf/phoenix/blob/818683aa/phoenix-core/src/it/java/org/apache/phoenix/end2end/PercentileIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/PercentileIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/PercentileIT.java index 8109694..fe8e5c3 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/PercentileIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/PercentileIT.java @@ -17,13 +17,13 @@ */ package org.apache.phoenix.end2end; +import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.ATABLE_NAME; import static org.apache.phoenix.util.TestUtil.A_VALUE; import static org.apache.phoenix.util.TestUtil.B_VALUE; import static org.apache.phoenix.util.TestUtil.C_VALUE; import static org.apache.phoenix.util.TestUtil.INDEX_DATA_SCHEMA; import static org.apache.phoenix.util.TestUtil.INDEX_DATA_TABLE; -import static org.apache.phoenix.util.TestUtil.MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.ROW1; import static org.apache.phoenix.util.TestUtil.ROW2; import static org.apache.phoenix.util.TestUtil.ROW3; http://git-wip-us.apache.org/repos/asf/phoenix/blob/818683aa/phoenix-core/src/it/java/org/apache/phoenix/end2end/ProductMetricsIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ProductMetricsIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ProductMetricsIT.java index ddc5fab..a68ba51 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ProductMetricsIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ProductMetricsIT.java @@ -17,6 +17,7 @@ */ package org.apache.phoenix.end2end; +import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -47,7 +48,6 @@ import org.apache.phoenix.util.DateUtil; import org.apache.phoenix.util.PhoenixRuntime; import org.apache.phoenix.util.PropertiesUtil; import org.apache.phoenix.util.SchemaUtil; -import org.apache.phoenix.util.TestUtil; import org.junit.Test; import com.google.common.collect.Lists; @@ -1983,8 +1983,8 @@ public class ProductMetricsIT extends BaseClientManagedTimeIT { url = getUrl() + ";" + PhoenixRuntime.CURRENT_SCN_ATTRIB + "=" + (ts); // Run query at timestamp 5 props = PropertiesUtil.deepCopy(TEST_PROPERTIES); conn = DriverManager.getConnection(url, props); - initDateTableValues(conn, tenantId, new Date(startDate.getTime()+TestUtil.MILLIS_IN_DAY*10), 2.0); - initDateTableValues(conn, tenantId, new Date(startDate.getTime()+TestUtil.MILLIS_IN_DAY*20), 2.0); + initDateTableValues(conn, tenantId, new Date(startDate.getTime()+MILLIS_IN_DAY*10), 2.0); + initDateTableValues(conn, tenantId, new Date(startDate.getTime()+MILLIS_IN_DAY*20), 2.0); conn.commit(); conn.close(); http://git-wip-us.apache.org/repos/asf/phoenix/blob/818683aa/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java index fef105c..ea91f4f 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java @@ -17,6 +17,7 @@ */ package org.apache.phoenix.end2end; +import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.ENTITYHISTID1; import static org.apache.phoenix.util.TestUtil.ENTITYHISTID3; import static org.apache.phoenix.util.TestUtil.ENTITYHISTID7; @@ -54,7 +55,6 @@ import org.apache.phoenix.util.DateUtil; import org.apache.phoenix.util.PhoenixRuntime; import org.apache.phoenix.util.PropertiesUtil; import org.apache.phoenix.util.QueryUtil; -import org.apache.phoenix.util.TestUtil; import org.junit.Test; @@ -847,7 +847,7 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT { try { PreparedStatement statement = conn.prepareStatement(query); statement.setString(1, tenantId); - Timestamp timestampWithNanos = DateUtil.getTimestamp(dateUpserted.getTime() + 2 * TestUtil.MILLIS_IN_DAY, 300); + Timestamp timestampWithNanos = DateUtil.getTimestamp(dateUpserted.getTime() + 2 * MILLIS_IN_DAY, 300); timestampWithNanos.setNanos(0); statement.setTimestamp(2, timestampWithNanos); statement.setTimestamp(3, timestampWithNanos); http://git-wip-us.apache.org/repos/asf/phoenix/blob/818683aa/phoenix-core/src/it/java/org/apache/phoenix/end2end/VariableLengthPKIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/VariableLengthPKIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/VariableLengthPKIT.java index 1e48f8c..c0b557c 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/VariableLengthPKIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/VariableLengthPKIT.java @@ -17,8 +17,8 @@ */ package org.apache.phoenix.end2end; +import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.BTABLE_NAME; -import static org.apache.phoenix.util.TestUtil.MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.PTSDB2_NAME; import static org.apache.phoenix.util.TestUtil.PTSDB_NAME; import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; http://git-wip-us.apache.org/repos/asf/phoenix/blob/818683aa/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexExpressionIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexExpressionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexExpressionIT.java index 7be8d41..ed06a71 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexExpressionIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexExpressionIT.java @@ -9,10 +9,10 @@ */ package org.apache.phoenix.end2end.index; +import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.INDEX_DATA_SCHEMA; import static org.apache.phoenix.util.TestUtil.INDEX_DATA_TABLE; import static org.apache.phoenix.util.TestUtil.MUTABLE_INDEX_DATA_TABLE; -import static org.apache.phoenix.util.TestUtil.NUM_MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -38,7 +38,6 @@ import org.apache.phoenix.util.DateUtil; import org.apache.phoenix.util.IndexUtil; import org.apache.phoenix.util.PropertiesUtil; import org.apache.phoenix.util.QueryUtil; -import org.apache.phoenix.util.TestUtil; import org.junit.Test; public class IndexExpressionIT extends BaseHBaseManagedTimeIT { @@ -76,7 +75,7 @@ public class IndexExpressionIT extends BaseHBaseManagedTimeIT { stmt.setInt(3, i); stmt.setLong(4, i); stmt.setBigDecimal(5, new BigDecimal(i*0.5d)); - Date date = new Date(DateUtil.parseDate("2015-01-01 00:00:00").getTime() + (i - 1) * TestUtil.NUM_MILLIS_IN_DAY); + Date date = new Date(DateUtil.parseDate("2015-01-01 00:00:00").getTime() + (i - 1) * MILLIS_IN_DAY); stmt.setDate(6, date); stmt.setString(7, "a.varchar" + String.valueOf(i)); stmt.setString(8, "a.char" + String.valueOf(i)); @@ -99,7 +98,7 @@ public class IndexExpressionIT extends BaseHBaseManagedTimeIT { + "_A.VARCHAR" + String.valueOf(i) + "_" + StringUtils.rightPad("B.CHAR" + String.valueOf(i), 10, ' '), rs.getString(1)); assertEquals(i * 3, rs.getInt(2)); - Date date = new Date(DateUtil.parseDate("2015-01-01 00:00:00").getTime() + (i) * TestUtil.NUM_MILLIS_IN_DAY); + Date date = new Date(DateUtil.parseDate("2015-01-01 00:00:00").getTime() + (i) * MILLIS_IN_DAY); assertEquals(date, rs.getDate(3)); assertEquals(date, rs.getDate(4)); assertEquals(date, rs.getDate(5)); http://git-wip-us.apache.org/repos/asf/phoenix/blob/818683aa/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexIT.java index 6d54076..4a7e053 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexIT.java @@ -18,6 +18,7 @@ package org.apache.phoenix.end2end.index; +import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -519,9 +520,9 @@ public class IndexIT extends BaseHBaseManagedTimeIT { assertTrue(rs.next()); assertEquals(date, rs.getDate(1)); assertTrue(rs.next()); - assertEquals(new Date(date.getTime() + TestUtil.MILLIS_IN_DAY), rs.getDate(1)); + assertEquals(new Date(date.getTime() + MILLIS_IN_DAY), rs.getDate(1)); assertTrue(rs.next()); - assertEquals(new Date(date.getTime() + 2 * TestUtil.MILLIS_IN_DAY), rs.getDate(1)); + assertEquals(new Date(date.getTime() + 2 * MILLIS_IN_DAY), rs.getDate(1)); assertFalse(rs.next()); } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/818683aa/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataType.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataType.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataType.java index 7bf54ce..1b5b695 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataType.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataType.java @@ -1116,6 +1116,9 @@ public abstract class PDataType<T> implements DataType<T>, Comparable<PDataType< */ if (lowerRange != KeyRange.UNBOUND && !lowerInclusive && isFixedWidth()) { lowerRange = ByteUtil.nextKey(lowerRange); + if (lowerRange == null) { // overflow + lowerRange = KeyRange.UNBOUND; + } lowerInclusive = true; } return KeyRange.getKeyRange(lowerRange, lowerInclusive, upperRange, upperInclusive); http://git-wip-us.apache.org/repos/asf/phoenix/blob/818683aa/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimestamp.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimestamp.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimestamp.java index 16b110e..1f654fe 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimestamp.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimestamp.java @@ -24,190 +24,243 @@ import java.text.Format; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.util.Bytes; +import org.apache.phoenix.query.KeyRange; import org.apache.phoenix.query.QueryConstants; +import org.apache.phoenix.schema.IllegalDataException; import org.apache.phoenix.schema.SortOrder; import org.apache.phoenix.util.ByteUtil; import org.apache.phoenix.util.DateUtil; public class PTimestamp extends PDataType<Timestamp> { + public static final int MAX_NANOS_VALUE_EXCLUSIVE = 1000000; + public static final PTimestamp INSTANCE = new PTimestamp(); - public static final PTimestamp INSTANCE = new PTimestamp(); - - private PTimestamp() { - super("TIMESTAMP", Types.TIMESTAMP, java.sql.Timestamp.class, - new PDate.DateCodec(), 9); - } - - @Override - public byte[] toBytes(Object object) { - byte[] bytes = new byte[getByteSize()]; - toBytes(object, bytes, 0); - return bytes; - } - - @Override - public int toBytes(Object object, byte[] bytes, int offset) { - if (object == null) { - // Create the byte[] of size MAX_TIMESTAMP_BYTES - if(bytes.length != getByteSize()) { - bytes = Bytes.padTail(bytes, (getByteSize() - bytes.length)); - } - PDate.INSTANCE.getCodec().encodeLong(0l, bytes, offset); - Bytes.putInt(bytes, offset + Bytes.SIZEOF_LONG, 0); - return getByteSize(); - } - java.sql.Timestamp value = (java.sql.Timestamp) object; - PDate.INSTANCE.getCodec().encodeLong(value.getTime(), bytes, offset); - - /* - * By not getting the stuff that got spilled over from the millis part, - * it leaves the timestamp's byte representation saner - 8 bytes of millis | 4 bytes of nanos. - * Also, it enables timestamp bytes to be directly compared with date/time bytes. - */ - Bytes.putInt(bytes, offset + Bytes.SIZEOF_LONG, value.getNanos() % 1000000); - return getByteSize(); - } - - @Override - public Object toObject(Object object, PDataType actualType) { - if (object == null) { - return null; - } - if (equalsAny(actualType, PDate.INSTANCE, PUnsignedDate.INSTANCE, PTime.INSTANCE, - PUnsignedTime.INSTANCE)) { - return new java.sql.Timestamp(((java.util.Date) object).getTime()); - } else if (equalsAny(actualType, PTimestamp.INSTANCE, PUnsignedTimestamp.INSTANCE)) { - return object; - } else if (equalsAny(actualType, PLong.INSTANCE, PUnsignedLong.INSTANCE)) { - return new java.sql.Timestamp((Long) object); - } else if (actualType == PDecimal.INSTANCE) { - BigDecimal bd = (BigDecimal) object; - long ms = bd.longValue(); - int nanos = - (bd.remainder(BigDecimal.ONE).multiply(QueryConstants.BD_MILLIS_NANOS_CONVERSION)) - .intValue(); - return DateUtil.getTimestamp(ms, nanos); - } else if (actualType == PVarchar.INSTANCE) { - return DateUtil.parseTimestamp((String) object); - } - return throwConstraintViolationException(actualType, this); - } - - @Override - public java.sql.Timestamp toObject(byte[] b, int o, int l, PDataType actualType, - SortOrder sortOrder, Integer maxLength, Integer scale) { - if (actualType == null || l == 0) { - return null; - } - java.sql.Timestamp v; - if (equalsAny(actualType, PTimestamp.INSTANCE, PUnsignedTimestamp.INSTANCE)) { - long millisDeserialized = - (actualType == PTimestamp.INSTANCE ? PDate.INSTANCE : PUnsignedDate.INSTANCE).getCodec() - .decodeLong(b, o, sortOrder); - v = new java.sql.Timestamp(millisDeserialized); - int nanosDeserialized = - PUnsignedInt.INSTANCE.getCodec().decodeInt(b, o + Bytes.SIZEOF_LONG, sortOrder); - /* - * There was a bug in serialization of timestamps which was causing the sub-second millis part - * of time stamp to be present both in the LONG and INT bytes. Having the <100000 check - * makes this serialization fix backward compatible. - */ - v.setNanos( - nanosDeserialized < 1000000 ? v.getNanos() + nanosDeserialized : nanosDeserialized); - return v; - } else if (equalsAny(actualType, PDate.INSTANCE, PUnsignedDate.INSTANCE, PTime.INSTANCE, - PUnsignedTime.INSTANCE, PLong.INSTANCE, PUnsignedLong.INSTANCE)) { - return new java.sql.Timestamp(actualType.getCodec().decodeLong(b, o, sortOrder)); - } else if (actualType == PDecimal.INSTANCE) { - BigDecimal bd = (BigDecimal) actualType.toObject(b, o, l, actualType, sortOrder); - long ms = bd.longValue(); - int nanos = (bd.remainder(BigDecimal.ONE).multiply(QueryConstants.BD_MILLIS_NANOS_CONVERSION)) - .intValue(); - v = DateUtil.getTimestamp(ms, nanos); - return v; - } - throwConstraintViolationException(actualType, this); - return null; - } - - @Override - public boolean isCastableTo(PDataType targetType) { - return PDate.INSTANCE.isCastableTo(targetType); - } - - @Override - public boolean isCoercibleTo(PDataType targetType) { - return equalsAny(targetType, this, PVarbinary.INSTANCE, PBinary.INSTANCE); - } - - @Override - public boolean isCoercibleTo(PDataType targetType, Object value) { - if (value != null) { - if (targetType.equals(PUnsignedTimestamp.INSTANCE)) { - return ((java.util.Date) value).getTime() >= 0; - } else if (equalsAny(targetType, PUnsignedDate.INSTANCE, PUnsignedTime.INSTANCE)) { - return ((java.util.Date) value).getTime() >= 0 - && ((java.sql.Timestamp) value).getNanos() == 0; - } else if (equalsAny(targetType, PDate.INSTANCE, PTime.INSTANCE)) { - return ((java.sql.Timestamp) value).getNanos() == 0; - } - } - return super.isCoercibleTo(targetType, value); - } - - @Override - public boolean isFixedWidth() { - return true; - } - - @Override - public Integer getByteSize() { - return MAX_TIMESTAMP_BYTES; - } - - @Override - public int compareTo(Object lhs, Object rhs, PDataType rhsType) { - if (equalsAny(rhsType, PTimestamp.INSTANCE, PUnsignedTimestamp.INSTANCE)) { - return ((java.sql.Timestamp) lhs).compareTo((java.sql.Timestamp) rhs); - } - int c = ((java.util.Date) rhs).compareTo((java.util.Date) lhs); - if (c != 0) return c; - return ((java.sql.Timestamp) lhs).getNanos(); - } - - @Override - public Object toObject(String value) { - if (value == null || value.length() == 0) { - return null; - } - return DateUtil.parseTimestamp(value); - } - - @Override - public String toStringLiteral(Object o, Format formatter) { - if (formatter == null) { - formatter = DateUtil.DEFAULT_TIMESTAMP_FORMATTER; + private PTimestamp() { + super("TIMESTAMP", Types.TIMESTAMP, java.sql.Timestamp.class, + new PDate.DateCodec(), 9); + } + + @Override + public byte[] toBytes(Object object) { + byte[] bytes = new byte[getByteSize()]; + toBytes(object, bytes, 0); + return bytes; + } + + @Override + public int toBytes(Object object, byte[] bytes, int offset) { + if (object == null) { + // Create the byte[] of size MAX_TIMESTAMP_BYTES + if(bytes.length != getByteSize()) { + bytes = Bytes.padTail(bytes, (getByteSize() - bytes.length)); + } + PDate.INSTANCE.getCodec().encodeLong(0l, bytes, offset); + Bytes.putInt(bytes, offset + Bytes.SIZEOF_LONG, 0); + return getByteSize(); + } + java.sql.Timestamp value = (java.sql.Timestamp) object; + // For Timestamp, the getTime() method includes milliseconds that may + // be stored in the nanos part as well. + PDate.INSTANCE.getCodec().encodeLong(value.getTime(), bytes, offset); + + /* + * By not getting the stuff that got spilled over from the millis part, + * it leaves the timestamp's byte representation saner - 8 bytes of millis | 4 bytes of nanos. + * Also, it enables timestamp bytes to be directly compared with date/time bytes. + */ + Bytes.putInt(bytes, offset + Bytes.SIZEOF_LONG, value.getNanos() % MAX_NANOS_VALUE_EXCLUSIVE); + return getByteSize(); + } + + @Override + public Object toObject(Object object, PDataType actualType) { + if (object == null) { + return null; + } + if (equalsAny(actualType, PDate.INSTANCE, PUnsignedDate.INSTANCE, PTime.INSTANCE, + PUnsignedTime.INSTANCE)) { + return new java.sql.Timestamp(((java.util.Date) object).getTime()); + } else if (equalsAny(actualType, PTimestamp.INSTANCE, PUnsignedTimestamp.INSTANCE)) { + return object; + } else if (equalsAny(actualType, PLong.INSTANCE, PUnsignedLong.INSTANCE)) { + return new java.sql.Timestamp((Long) object); + } else if (actualType == PDecimal.INSTANCE) { + BigDecimal bd = (BigDecimal) object; + long ms = bd.longValue(); + int nanos = + (bd.remainder(BigDecimal.ONE).multiply(QueryConstants.BD_MILLIS_NANOS_CONVERSION)) + .intValue(); + return DateUtil.getTimestamp(ms, nanos); + } else if (actualType == PVarchar.INSTANCE) { + return DateUtil.parseTimestamp((String) object); + } + return throwConstraintViolationException(actualType, this); + } + + @Override + public java.sql.Timestamp toObject(byte[] b, int o, int l, PDataType actualType, + SortOrder sortOrder, Integer maxLength, Integer scale) { + if (actualType == null || l == 0) { + return null; + } + java.sql.Timestamp v; + if (equalsAny(actualType, PTimestamp.INSTANCE, PUnsignedTimestamp.INSTANCE)) { + long millisDeserialized = + (actualType == PTimestamp.INSTANCE ? PDate.INSTANCE : PUnsignedDate.INSTANCE).getCodec() + .decodeLong(b, o, sortOrder); + v = new java.sql.Timestamp(millisDeserialized); + int nanosDeserialized = + PUnsignedInt.INSTANCE.getCodec().decodeInt(b, o + Bytes.SIZEOF_LONG, sortOrder); + /* + * There was a bug in serialization of timestamps which was causing the sub-second millis part + * of time stamp to be present both in the LONG and INT bytes. Having the <100000 check + * makes this serialization fix backward compatible. + */ + v.setNanos( + nanosDeserialized < MAX_NANOS_VALUE_EXCLUSIVE ? v.getNanos() + nanosDeserialized : nanosDeserialized); + return v; + } else if (equalsAny(actualType, PDate.INSTANCE, PUnsignedDate.INSTANCE, PTime.INSTANCE, + PUnsignedTime.INSTANCE, PLong.INSTANCE, PUnsignedLong.INSTANCE)) { + return new java.sql.Timestamp(actualType.getCodec().decodeLong(b, o, sortOrder)); + } else if (actualType == PDecimal.INSTANCE) { + BigDecimal bd = (BigDecimal) actualType.toObject(b, o, l, actualType, sortOrder); + long ms = bd.longValue(); + int nanos = (bd.remainder(BigDecimal.ONE).multiply(QueryConstants.BD_MILLIS_NANOS_CONVERSION)) + .intValue(); + v = DateUtil.getTimestamp(ms, nanos); + return v; + } + throwConstraintViolationException(actualType, this); + return null; + } + + @Override + public boolean isCastableTo(PDataType targetType) { + return PDate.INSTANCE.isCastableTo(targetType); + } + + @Override + public boolean isCoercibleTo(PDataType targetType) { + return equalsAny(targetType, this, PVarbinary.INSTANCE, PBinary.INSTANCE); + } + + @Override + public boolean isCoercibleTo(PDataType targetType, Object value) { + if (value != null) { + if (targetType.equals(PUnsignedTimestamp.INSTANCE)) { + return ((java.util.Date) value).getTime() >= 0; + } else if (equalsAny(targetType, PUnsignedDate.INSTANCE, PUnsignedTime.INSTANCE)) { + return ((java.util.Date) value).getTime() >= 0 + && ((java.sql.Timestamp) value).getNanos() == 0; + } else if (equalsAny(targetType, PDate.INSTANCE, PTime.INSTANCE)) { + return ((java.sql.Timestamp) value).getNanos() == 0; + } + } + return super.isCoercibleTo(targetType, value); + } + + @Override + public boolean isFixedWidth() { + return true; + } + + @Override + public Integer getByteSize() { + return MAX_TIMESTAMP_BYTES; + } + + @Override + public int compareTo(Object lhs, Object rhs, PDataType rhsType) { + if (equalsAny(rhsType, PTimestamp.INSTANCE, PUnsignedTimestamp.INSTANCE)) { + return ((java.sql.Timestamp) lhs).compareTo((java.sql.Timestamp) rhs); + } + int c = ((java.util.Date) rhs).compareTo((java.util.Date) lhs); + if (c != 0) return c; + return ((java.sql.Timestamp) lhs).getNanos(); + } + + @Override + public Object toObject(String value) { + if (value == null || value.length() == 0) { + return null; + } + return DateUtil.parseTimestamp(value); + } + + @Override + public String toStringLiteral(Object o, Format formatter) { + if (formatter == null) { + formatter = DateUtil.DEFAULT_TIMESTAMP_FORMATTER; } return "'" + super.toStringLiteral(o, formatter) + "'"; - } - - - @Override - public int getNanos(ImmutableBytesWritable ptr, SortOrder sortOrder) { - int nanos = PUnsignedInt.INSTANCE.getCodec() - .decodeInt(ptr.get(), ptr.getOffset() + PLong.INSTANCE.getByteSize(), sortOrder); - return nanos; - } - - @Override - public long getMillis(ImmutableBytesWritable ptr, SortOrder sortOrder) { - long millis = PLong.INSTANCE.getCodec().decodeLong(ptr.get(), ptr.getOffset(), sortOrder); - return millis; - } - - @Override - public Object getSampleValue(Integer maxLength, Integer arrayLength) { - return new java.sql.Timestamp( - (Long) PLong.INSTANCE.getSampleValue(maxLength, arrayLength)); - } + } + + + @Override + public int getNanos(ImmutableBytesWritable ptr, SortOrder sortOrder) { + int nanos = PUnsignedInt.INSTANCE.getCodec() + .decodeInt(ptr.get(), ptr.getOffset() + PLong.INSTANCE.getByteSize(), sortOrder); + return nanos; + } + + @Override + public long getMillis(ImmutableBytesWritable ptr, SortOrder sortOrder) { + long millis = PLong.INSTANCE.getCodec().decodeLong(ptr.get(), ptr.getOffset(), sortOrder); + return millis; + } + + @Override + public Object getSampleValue(Integer maxLength, Integer arrayLength) { + return new java.sql.Timestamp( + (Long) PLong.INSTANCE.getSampleValue(maxLength, arrayLength)); + } + + /** + * With timestamp, because our last 4 bytes store a value from [0 - 1000000), we need + * to detect when the boundary is crossed if we increment to the nextKey. + */ + @Override + public KeyRange getKeyRange(byte[] lowerRange, boolean lowerInclusive, byte[] upperRange, boolean upperInclusive) { + /* + * Force lower bound to be inclusive for fixed width keys because it makes comparisons less expensive when you + * can count on one bound or the other being inclusive. Comparing two fixed width exclusive bounds against each + * other is inherently more expensive, because you need to take into account if the bigger key is equal to the + * next key after the smaller key. For example: (A-B] compared against [A-B) An exclusive lower bound A is + * bigger than an exclusive upper bound B. Forcing a fixed width exclusive lower bound key to be inclusive + * prevents us from having to do this extra logic in the compare function. + * + */ + if (lowerRange != KeyRange.UNBOUND && !lowerInclusive && isFixedWidth()) { + if (lowerRange.length != MAX_TIMESTAMP_BYTES) { + throw new IllegalDataException("Unexpected size of " + lowerRange.length + " for " + this); + } + // Infer sortOrder based on most significant byte + SortOrder sortOrder = lowerRange[Bytes.SIZEOF_LONG] < 0 ? SortOrder.DESC : SortOrder.ASC; + int nanos = PUnsignedInt.INSTANCE.getCodec().decodeInt(lowerRange, Bytes.SIZEOF_LONG, sortOrder); + if ((sortOrder == SortOrder.DESC && nanos == 0) || (sortOrder == SortOrder.ASC && nanos == MAX_NANOS_VALUE_EXCLUSIVE-1)) { + // With timestamp, because our last 4 bytes store a value from [0 - 1000000), we need + // to detect when the boundary is crossed with our nextKey + byte[] newLowerRange = new byte[MAX_TIMESTAMP_BYTES]; + if (sortOrder == SortOrder.DESC) { + // Set nanos part as inverted 999999 as it needs to be the max nano value + // The millisecond part is moving to the previous value below + System.arraycopy(lowerRange, 0, newLowerRange, 0, Bytes.SIZEOF_LONG); + PUnsignedInt.INSTANCE.getCodec().encodeInt(MAX_NANOS_VALUE_EXCLUSIVE-1, newLowerRange, Bytes.SIZEOF_LONG); + SortOrder.invert(newLowerRange, Bytes.SIZEOF_LONG, newLowerRange, Bytes.SIZEOF_LONG, Bytes.SIZEOF_INT); + } else { + // Leave nanos part as zero as the millisecond part is rolling over to the next value + System.arraycopy(lowerRange, 0, newLowerRange, 0, Bytes.SIZEOF_LONG); + } + // Increment millisecond part, but leave nanos alone + if (ByteUtil.nextKey(newLowerRange, Bytes.SIZEOF_LONG)) { + lowerRange = newLowerRange; + } else { + lowerRange = KeyRange.UNBOUND; + } + return KeyRange.getKeyRange(lowerRange, true, upperRange, upperInclusive); + } + } + return super.getKeyRange(lowerRange, lowerInclusive, upperRange, upperInclusive); + } + } http://git-wip-us.apache.org/repos/asf/phoenix/blob/818683aa/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java index b789742..05fea8d 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java @@ -17,6 +17,7 @@ */ package org.apache.phoenix.compile; +import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.BINARY_NAME; import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; import static org.apache.phoenix.util.TestUtil.assertDegenerate; @@ -36,8 +37,6 @@ import java.math.BigDecimal; import java.sql.Connection; import java.sql.Date; import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; import java.util.Collections; @@ -1263,7 +1262,7 @@ public class WhereOptimizerTest extends BaseConnectionlessQueryTest { String query = "select /*+ RANGE_SCAN */ ORGANIZATION_ID, PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID from " + TestUtil.ENTITY_HISTORY_TABLE_NAME + " where ORGANIZATION_ID=? and SUBSTR(PARENT_ID, 1, 3) = ? and CREATED_DATE >= ? and CREATED_DATE < ? order by ORGANIZATION_ID, PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID limit 6"; Date startTime = new Date(System.currentTimeMillis()); - Date stopTime = new Date(startTime.getTime() + TestUtil.MILLIS_IN_DAY); + Date stopTime = new Date(startTime.getTime() + MILLIS_IN_DAY); List<Object> binds = Arrays.<Object>asList(tenantId, keyPrefix, startTime, stopTime); StatementContext context = compileStatement(query, binds, 6); Scan scan = context.getScan(); http://git-wip-us.apache.org/repos/asf/phoenix/blob/818683aa/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java b/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java index 9539e69..1192059 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java @@ -17,6 +17,7 @@ */ package org.apache.phoenix.query; +import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY; import static org.apache.phoenix.util.PhoenixRuntime.CURRENT_SCN_ATTRIB; import static org.apache.phoenix.util.PhoenixRuntime.JDBC_PROTOCOL; import static org.apache.phoenix.util.PhoenixRuntime.JDBC_PROTOCOL_TERMINATOR; @@ -53,7 +54,6 @@ import static org.apache.phoenix.util.TestUtil.JOIN_ORDER_TABLE_FULL_NAME; import static org.apache.phoenix.util.TestUtil.JOIN_SUPPLIER_TABLE_FULL_NAME; import static org.apache.phoenix.util.TestUtil.KEYONLY_NAME; import static org.apache.phoenix.util.TestUtil.MDTEST_NAME; -import static org.apache.phoenix.util.TestUtil.MILLIS_IN_DAY; import static org.apache.phoenix.util.TestUtil.MULTI_CF_NAME; import static org.apache.phoenix.util.TestUtil.MUTABLE_INDEX_DATA_TABLE; import static org.apache.phoenix.util.TestUtil.PARENTID1; @@ -159,7 +159,6 @@ import org.apache.phoenix.util.PropertiesUtil; import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.ReadOnlyProps; import org.apache.phoenix.util.SchemaUtil; -import org.apache.phoenix.util.TestUtil; import org.apache.twill.discovery.DiscoveryService; import org.apache.twill.discovery.ZKDiscoveryService; import org.apache.twill.internal.utils.Networks; @@ -1933,7 +1932,7 @@ public abstract class BaseTest { stmt.setInt(13, 3); stmt.setLong(14, 3L); stmt.setBigDecimal(15, new BigDecimal("3.1")); - stmt.setDate(16, date == null ? null : new Date(date.getTime() + TestUtil.MILLIS_IN_DAY)); + stmt.setDate(16, date == null ? null : new Date(date.getTime() + MILLIS_IN_DAY)); stmt.executeUpdate(); stmt.setString(1, "varchar2"); @@ -1969,7 +1968,7 @@ public abstract class BaseTest { stmt.setInt(13, 5); stmt.setLong(14, 5L); stmt.setBigDecimal(15, new BigDecimal("5.3")); - stmt.setDate(16, date == null ? null : new Date(date.getTime() + 2 * TestUtil.MILLIS_IN_DAY)); + stmt.setDate(16, date == null ? null : new Date(date.getTime() + 2 * MILLIS_IN_DAY)); stmt.executeUpdate(); conn.commit(); http://git-wip-us.apache.org/repos/asf/phoenix/blob/818683aa/phoenix-core/src/test/java/org/apache/phoenix/query/QueryPlanTest.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/query/QueryPlanTest.java b/phoenix-core/src/test/java/org/apache/phoenix/query/QueryPlanTest.java index fdae749..4ba4d16 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/query/QueryPlanTest.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/query/QueryPlanTest.java @@ -26,6 +26,7 @@ import java.sql.Statement; import java.util.Properties; import org.apache.phoenix.util.PhoenixRuntime; +import org.apache.phoenix.util.PropertiesUtil; import org.apache.phoenix.util.QueryUtil; import org.junit.Test; @@ -229,8 +230,27 @@ public class QueryPlanTest extends BaseConnectionlessQueryTest { } @Test - public void testLimitOnTenantSpecific() throws Exception { - + public void testDescTimestampAtBoundary() throws Exception { + Properties props = PropertiesUtil.deepCopy(new Properties()); + Connection conn = DriverManager.getConnection(getUrl(), props); + try { + conn.createStatement().execute("CREATE TABLE FOO(\n" + + " a VARCHAR NOT NULL,\n" + + " b TIMESTAMP NOT NULL,\n" + + " c VARCHAR,\n" + + " CONSTRAINT pk PRIMARY KEY (a, b DESC, c)\n" + + " ) IMMUTABLE_ROWS=true\n" + + " ,SALT_BUCKETS=20"); + String query = "select * from foo where a = 'a' and b >= timestamp '2016-01-28 00:00:00' and b < timestamp '2016-01-29 00:00:00'"; + ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + query); + String queryPlan = QueryUtil.getExplainPlan(rs); + assertEquals( + "CLIENT PARALLEL 20-WAY RANGE SCAN OVER FOO [0,'a',~'2016-01-28 23:59:59.999'] - [0,'a',~'2016-01-28 00:00:00.000']\n" + + " SERVER FILTER BY FIRST KEY ONLY\n" + + "CLIENT MERGE SORT", queryPlan); + } finally { + conn.close(); + } } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/818683aa/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java b/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java index 7a04aeb..085af44 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java @@ -17,6 +17,7 @@ */ package org.apache.phoenix.schema.types; +import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -38,9 +39,11 @@ import java.util.List; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.util.Bytes; import org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.query.KeyRange; import org.apache.phoenix.query.QueryConstants; import org.apache.phoenix.schema.ConstraintViolationException; import org.apache.phoenix.schema.SortOrder; +import org.apache.phoenix.util.DateUtil; import org.apache.phoenix.util.ScanUtil; import org.apache.phoenix.util.TestUtil; import org.junit.Test; @@ -1832,4 +1835,61 @@ public class PDataTypeTest { b = PBoolean.INSTANCE.toObject(bytes, 0, bytes.length, PDecimal.INSTANCE, SortOrder.DESC); assertEquals(false, b); } + + @Test + public void testTimestamp() { + long now = System.currentTimeMillis(); + Timestamp ts1 = DateUtil.getTimestamp(now, 1111); + final byte[] bytes1 = PTimestamp.INSTANCE.toBytes(ts1); + Timestamp ts2 = DateUtil.getTimestamp(now, 1112); + final byte[] bytes2 = PTimestamp.INSTANCE.toBytes(ts2); + assertTrue(Bytes.compareTo(bytes1, bytes2) < 0); + + final byte[] ibytes1 = SortOrder.invert(bytes1, 0, bytes1.length); + final byte[] ibytes2 = SortOrder.invert(bytes2, 0, bytes2.length); + assertTrue(Bytes.compareTo(ibytes1, ibytes2) > 0); + + Timestamp ts3 = new Timestamp(now+1); + final byte[] bytes3 = PTimestamp.INSTANCE.toBytes(ts3); + assertTrue(Bytes.compareTo(bytes3, bytes2) > 0); + final byte[] ibytes3 = SortOrder.invert(bytes3, 0, bytes3.length); + assertTrue(Bytes.compareTo(ibytes3, ibytes2) < 0); + + Timestamp ts4 = new Timestamp(now-1); + byte[] bytes4 = PTimestamp.INSTANCE.toBytes(ts4); + assertTrue(Bytes.compareTo(bytes4, bytes1) < 0); + byte[] ibytes4 = SortOrder.invert(bytes4, 0, bytes4.length); + assertTrue(Bytes.compareTo(ibytes4, ibytes1) > 0); + } + + @Test + public void testAscExclusiveTimestampRange() { + long now = System.currentTimeMillis(); + Timestamp ts1 = DateUtil.getTimestamp(now, 999999); + final byte[] lowerRange = PTimestamp.INSTANCE.toBytes(ts1); + Timestamp ts2 = new Timestamp(now + MILLIS_IN_DAY); + final byte[] upperRange = PTimestamp.INSTANCE.toBytes(ts2); + KeyRange range = PTimestamp.INSTANCE.getKeyRange(lowerRange, false, upperRange, false); + Timestamp ts3 = new Timestamp(now + 1); + // Rolled up to next millis + final byte[] expectedLowerRange = PTimestamp.INSTANCE.toBytes(ts3); + assertTrue(Bytes.compareTo(expectedLowerRange, range.getLowerRange()) == 0); + assertTrue(Bytes.compareTo(upperRange, range.getUpperRange()) == 0); + } + + + @Test + public void testDescExclusiveTimestampRange() { + long now = System.currentTimeMillis(); + Timestamp ts1 = new Timestamp(now + MILLIS_IN_DAY); + final byte[] lowerRange = PTimestamp.INSTANCE.toBytes(ts1, SortOrder.DESC); + Timestamp ts2 = new Timestamp(now); + final byte[] upperRange = PTimestamp.INSTANCE.toBytes(ts2, SortOrder.DESC); + KeyRange range = PTimestamp.INSTANCE.getKeyRange(lowerRange, false, upperRange, false); + Timestamp ts3 = DateUtil.getTimestamp(now + MILLIS_IN_DAY - 1, 999999); + // Rolled up to next millis + final byte[] expectedLowerRange = PTimestamp.INSTANCE.toBytes(ts3, SortOrder.DESC); + assertTrue(Bytes.compareTo(expectedLowerRange, range.getLowerRange()) == 0); + assertTrue(Bytes.compareTo(upperRange, range.getUpperRange()) == 0); + } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/818683aa/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java b/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java index 66a3c65..c73c160 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java @@ -17,6 +17,7 @@ */ package org.apache.phoenix.util; +import static org.apache.phoenix.query.QueryConstants.MILLIS_IN_DAY; import static org.apache.phoenix.util.PhoenixRuntime.CONNECTIONLESS; import static org.apache.phoenix.util.PhoenixRuntime.JDBC_PROTOCOL; import static org.apache.phoenix.util.PhoenixRuntime.JDBC_PROTOCOL_SEPARATOR; @@ -155,7 +156,6 @@ public class TestUtil { public final static List<String> ENTITYHISTIDS = Lists.newArrayList(ENTITYHISTID1, ENTITYHISTID2, ENTITYHISTID3, ENTITYHISTID4, ENTITYHISTID5, ENTITYHISTID6, ENTITYHISTID7, ENTITYHISTID8, ENTITYHISTID9); - public static final long MILLIS_IN_DAY = 1000 * 60 * 60 * 24; public static final String LOCALHOST = "localhost"; public static final String PHOENIX_JDBC_URL = JDBC_PROTOCOL + JDBC_PROTOCOL_SEPARATOR + LOCALHOST + JDBC_PROTOCOL_TERMINATOR + PHOENIX_TEST_DRIVER_URL_PARAM; public static final String PHOENIX_CONNECTIONLESS_JDBC_URL = JDBC_PROTOCOL + JDBC_PROTOCOL_SEPARATOR + CONNECTIONLESS + JDBC_PROTOCOL_TERMINATOR + PHOENIX_TEST_DRIVER_URL_PARAM; @@ -212,7 +212,6 @@ public class TestUtil { public static final String JOIN_SUPPLIER_TABLE_DISPLAY_NAME = JOIN_SCHEMA + "." + JOIN_SUPPLIER_TABLE; public static final String JOIN_COITEM_TABLE_DISPLAY_NAME = JOIN_SCHEMA + "." + JOIN_COITEM_TABLE; public static final String BINARY_NAME = "BinaryTable"; - public static final int NUM_MILLIS_IN_DAY = 86400000; /** * Read-only properties used by all tests @@ -600,7 +599,7 @@ public class TestUtil { stmt.setInt(3, i); stmt.setLong(4, i); stmt.setBigDecimal(5, new BigDecimal(i*0.5d)); - Date date = new Date(DateUtil.parseDate("2015-01-01 00:00:00").getTime() + (i - 1) * TestUtil.NUM_MILLIS_IN_DAY); + Date date = new Date(DateUtil.parseDate("2015-01-01 00:00:00").getTime() + (i - 1) * MILLIS_IN_DAY); stmt.setDate(6, date); } @@ -611,7 +610,7 @@ public class TestUtil { assertEquals(rs.getInt(3), i); assertEquals(rs.getInt(4), i); assertEquals(rs.getBigDecimal(5), new BigDecimal(i*0.5d)); - Date date = new Date(DateUtil.parseDate("2015-01-01 00:00:00").getTime() + (i - 1) * TestUtil.NUM_MILLIS_IN_DAY); + Date date = new Date(DateUtil.parseDate("2015-01-01 00:00:00").getTime() + (i - 1) * MILLIS_IN_DAY); assertEquals(rs.getDate(6), date); }