diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBInsertWithQueryIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBInsertWithQueryIT.java index 300f24099a88d797e138a8e591f704912260c296..403aa35fcef02e04658224d8a4f446da839bbba4 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBInsertWithQueryIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBInsertWithQueryIT.java @@ -146,6 +146,21 @@ public class IoTDBInsertWithQueryIT { selectAndCount(2000); } + @Test + public void insertNegativeTimestampWithQueryTest() { + // insert + insertData(-1000, 1); + + // select + selectAndCount(1001); + + // insert + insertData(-2000, -1000); + + // select + selectAndCount(2001); + } + @Test public void flushWithQueryTest() throws InterruptedException { // insert @@ -393,11 +408,13 @@ public class IoTDBInsertWithQueryIT { for (int time = start; time < end; time++) { String sql = String.format("insert into root.fans.d0(timestamp,s0) values(%s,%s)", time, time % 70); - statement.execute(sql); + statement.addBatch(sql); sql = String.format("insert into root.fans.d0(timestamp,s1) values(%s,%s)", time, time % 40); - statement.execute(sql); + statement.addBatch(sql); } + statement.executeBatch(); + statement.clearBatch(); } catch (SQLException e) { e.printStackTrace(); } @@ -422,7 +439,7 @@ public class IoTDBInsertWithQueryIT { try (ResultSet resultSet = statement.executeQuery(selectSql)) { assertNotNull(resultSet); int cnt = 0; - long before = -1; + long before = -10000; while (resultSet.next()) { long cur = Long.parseLong(resultSet.getString(TestConstant.TIMESTAMP_STR)); if (cur <= before) { diff --git a/integration-test/src/test/java/org/apache/iotdb/session/it/IoTDBSessionSimpleIT.java b/integration-test/src/test/java/org/apache/iotdb/session/it/IoTDBSessionSimpleIT.java index 41bd387c7543329daae3bf650888c349f899278c..acf80a2dfc43131ca4978f02881bb2e2efe1661a 100644 --- a/integration-test/src/test/java/org/apache/iotdb/session/it/IoTDBSessionSimpleIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/session/it/IoTDBSessionSimpleIT.java @@ -492,6 +492,51 @@ public class IoTDBSessionSimpleIT { } } + @Test + @Category({LocalStandaloneIT.class, ClusterIT.class}) + public void insertTabletWithNegativeTimestampTest() { + try (ISession session = EnvFactory.getEnv().getSessionConnection()) { + List schemaList = new ArrayList<>(); + schemaList.add(new MeasurementSchema("s0", TSDataType.DOUBLE, TSEncoding.RLE)); + schemaList.add(new MeasurementSchema("s1", TSDataType.FLOAT, TSEncoding.RLE)); + schemaList.add(new MeasurementSchema("s2", TSDataType.INT64, TSEncoding.RLE)); + schemaList.add(new MeasurementSchema("s3", TSDataType.INT32, TSEncoding.RLE)); + schemaList.add(new MeasurementSchema("s4", TSDataType.BOOLEAN, TSEncoding.RLE)); + schemaList.add(new MeasurementSchema("s5", TSDataType.TEXT, TSEncoding.RLE)); + schemaList.add(new MeasurementSchema("s6", TSDataType.TEXT, TSEncoding.RLE)); + + Tablet tablet = new Tablet("root.sg1.d1", schemaList); + for (long time = 0; time < 10; time++) { + int rowIndex = tablet.rowSize++; + tablet.addTimestamp(rowIndex, -time); + + tablet.addValue(schemaList.get(0).getMeasurementId(), rowIndex, (double) time); + tablet.addValue(schemaList.get(1).getMeasurementId(), rowIndex, (float) time); + tablet.addValue(schemaList.get(2).getMeasurementId(), rowIndex, time); + tablet.addValue(schemaList.get(3).getMeasurementId(), rowIndex, (int) time); + tablet.addValue(schemaList.get(4).getMeasurementId(), rowIndex, time % 2 == 0); + tablet.addValue(schemaList.get(5).getMeasurementId(), rowIndex, new Binary("Text" + time)); + tablet.addValue(schemaList.get(6).getMeasurementId(), rowIndex, "Text" + time); + } + + if (tablet.rowSize != 0) { + session.insertTablet(tablet); + tablet.reset(); + } + + SessionDataSet dataSet = session.executeQueryStatement("select * from root.sg1.d1"); + long count = 0L; + while (dataSet.hasNext()) { + count++; + RowRecord rowRecord = dataSet.next(); + assertEquals(count - 10, rowRecord.getTimestamp()); + } + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + @Test @Category({LocalStandaloneIT.class, ClusterIT.class}) public void createTimeSeriesWithDoubleTicksTest() { diff --git a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/RpcUtils.java b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/RpcUtils.java index fbe805bd07fd40c01d865ebd50decdd32d2d30d7..7df45b5b821d58589388829cf8f81b5a210c6283 100644 --- a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/RpcUtils.java +++ b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/RpcUtils.java @@ -247,9 +247,16 @@ public class RpcUtils { @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity warning public static String parseLongToDateWithPrecision( DateTimeFormatter formatter, long timestamp, ZoneId zoneid, String timestampPrecision) { + long integerOfDate; + StringBuilder digits; if ("ms".equals(timestampPrecision)) { - long integerOfDate = timestamp / 1000; - StringBuilder digits = new StringBuilder(Long.toString(timestamp % 1000)); + if (timestamp > 0 || timestamp % 1000 == 0) { + integerOfDate = timestamp / 1000; + digits = new StringBuilder(Long.toString(timestamp % 1000)); + } else { + integerOfDate = timestamp / 1000 - 1; + digits = new StringBuilder(Long.toString(1000 + timestamp % 1000)); + } ZonedDateTime dateTime = ZonedDateTime.ofInstant(Instant.ofEpochSecond(integerOfDate), zoneid); String datetime = dateTime.format(formatter); @@ -261,8 +268,13 @@ public class RpcUtils { } return formatDatetimeStr(datetime, digits); } else if ("us".equals(timestampPrecision)) { - long integerOfDate = timestamp / 1000_000; - StringBuilder digits = new StringBuilder(Long.toString(timestamp % 1000_000)); + if (timestamp > 0 || timestamp % 1000_000 == 0) { + integerOfDate = timestamp / 1000_000; + digits = new StringBuilder(Long.toString(timestamp % 1000_000)); + } else { + integerOfDate = timestamp / 1000_000 - 1; + digits = new StringBuilder(Long.toString(1000_000 + timestamp % 1000_000)); + } ZonedDateTime dateTime = ZonedDateTime.ofInstant(Instant.ofEpochSecond(integerOfDate), zoneid); String datetime = dateTime.format(formatter); @@ -274,8 +286,13 @@ public class RpcUtils { } return formatDatetimeStr(datetime, digits); } else { - long integerOfDate = timestamp / 1000_000_000L; - StringBuilder digits = new StringBuilder(Long.toString(timestamp % 1000_000_000L)); + if (timestamp > 0 || timestamp % 1000_000_000L == 0) { + integerOfDate = timestamp / 1000_000_000L; + digits = new StringBuilder(Long.toString(timestamp % 1000_000_000L)); + } else { + integerOfDate = timestamp / 1000_000_000L - 1; + digits = new StringBuilder(Long.toString(1000_000_000L + timestamp % 1000_000_000L)); + } ZonedDateTime dateTime = ZonedDateTime.ofInstant(Instant.ofEpochSecond(integerOfDate), zoneid); String datetime = dateTime.format(formatter); diff --git a/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/RpcUtilsTest.java b/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/RpcUtilsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..12969e96904f2b2c7538b23bdb2484ef744af672 --- /dev/null +++ b/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/RpcUtilsTest.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.rpc; + +import org.junit.Assert; +import org.junit.Test; + +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; + +public class RpcUtilsTest { + + @Test + public void parseLongToDateWithPrecision() { + DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME; + ZoneId zoneId = ZoneId.of("+0000"); + Assert.assertEquals( + "1969-12-31T23:59:59.999Z", + RpcUtils.parseLongToDateWithPrecision(formatter, -1, zoneId, "ms")); + Assert.assertEquals( + "1969-12-31T23:59:59.999999Z", + RpcUtils.parseLongToDateWithPrecision(formatter, -1, zoneId, "us")); + Assert.assertEquals( + "1969-12-31T23:59:59.999999999Z", + RpcUtils.parseLongToDateWithPrecision(formatter, -1, zoneId, "ns")); + Assert.assertEquals( + "1969-12-31T23:59:59.000Z", + RpcUtils.parseLongToDateWithPrecision(formatter, -1000, zoneId, "ms")); + Assert.assertEquals( + "1969-12-31T23:59:59.000000Z", + RpcUtils.parseLongToDateWithPrecision(formatter, -1000_000, zoneId, "us")); + Assert.assertEquals( + "1969-12-31T23:59:59.000000000Z", + RpcUtils.parseLongToDateWithPrecision(formatter, -1000_000_000L, zoneId, "ns")); + Assert.assertEquals( + "1970-01-01T00:00:00.001Z", + RpcUtils.parseLongToDateWithPrecision(formatter, 1, zoneId, "ms")); + Assert.assertEquals( + "1970-01-01T00:00:00.000001Z", + RpcUtils.parseLongToDateWithPrecision(formatter, 1, zoneId, "us")); + Assert.assertEquals( + "1970-01-01T00:00:00.000000001Z", + RpcUtils.parseLongToDateWithPrecision(formatter, 1, zoneId, "ns")); + + zoneId = ZoneId.of("+0800"); + Assert.assertEquals( + "1970-01-01T07:59:59.999+08:00", + RpcUtils.parseLongToDateWithPrecision(formatter, -1, zoneId, "ms")); + } +} diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/partition/PartitionManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/partition/PartitionManager.java index e07fbf155f87d0dd07dfaa38444eb47b7b145a50..949b687531653460c5eea04fd153a528dda23c78 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/partition/PartitionManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/partition/PartitionManager.java @@ -85,6 +85,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TGetSeriesSlotListReq; import org.apache.iotdb.confignode.rpc.thrift.TGetTimeSlotListReq; import org.apache.iotdb.confignode.rpc.thrift.TTimeSlotList; import org.apache.iotdb.consensus.exception.ConsensusException; +import org.apache.iotdb.db.utils.TimePartitionUtils; import org.apache.iotdb.mpp.rpc.thrift.TCreateDataRegionReq; import org.apache.iotdb.mpp.rpc.thrift.TCreateSchemaRegionReq; import org.apache.iotdb.rpc.RpcUtils; @@ -1020,9 +1021,7 @@ public class PartitionManager { } if (req.isSetTimeStamp()) { - plan.setTimeSlotId( - new TTimePartitionSlot( - req.getTimeStamp() - req.getTimeStamp() % COMMON_CONFIG.getTimePartitionInterval())); + plan.setTimeSlotId(TimePartitionUtils.getTimePartitionSlot(req.getTimeStamp())); } try { return (GetRegionIdResp) getConsensusManager().read(plan); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java index f02c9d02f96ec4639ac18220f005baed631bfa74..b38693afc2dc12324d1c4362764b87e8f413d361 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java @@ -983,7 +983,7 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { // only one database, one device, one time interval Map> sgNameToQueryParamsMap = new HashMap<>(); TTimePartitionSlot timePartitionSlot = - TimePartitionUtils.getTimePartition(req.getStartTime()); + TimePartitionUtils.getTimePartitionSlot(req.getStartTime()); DataPartitionQueryParam queryParam = new DataPartitionQueryParam( deviceId, Collections.singletonList(timePartitionSlot), false, false); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/load/AlignedChunkData.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/load/AlignedChunkData.java index 689a6236bb03d00d5fc1454c24daa59808f8bb15..ed85602c81c3c1b7e8ed4a5be2d99b73a3f4f610 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/load/AlignedChunkData.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/load/AlignedChunkData.java @@ -388,7 +388,7 @@ public class AlignedChunkData implements ChunkData { public static AlignedChunkData deserialize(InputStream stream) throws IOException, PageException { TTimePartitionSlot timePartitionSlot = - TimePartitionUtils.getTimePartition(ReadWriteIOUtils.readLong(stream)); + TimePartitionUtils.getTimePartitionSlot(ReadWriteIOUtils.readLong(stream)); String device = ReadWriteIOUtils.readString(stream); boolean needDecodeChunk = ReadWriteIOUtils.readBool(stream); int chunkHeaderListSize = ReadWriteIOUtils.readInt(stream); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/load/NonAlignedChunkData.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/load/NonAlignedChunkData.java index f898a1f666587e1354f31b2c67c0c2186a664c2e..59b0a6bf7b80aa55437e771ca2746102070c3d3d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/load/NonAlignedChunkData.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/load/NonAlignedChunkData.java @@ -260,7 +260,7 @@ public class NonAlignedChunkData implements ChunkData { public static NonAlignedChunkData deserialize(InputStream stream) throws IOException, PageException { TTimePartitionSlot timePartitionSlot = - TimePartitionUtils.getTimePartition(ReadWriteIOUtils.readLong(stream)); + TimePartitionUtils.getTimePartitionSlot(ReadWriteIOUtils.readLong(stream)); String device = ReadWriteIOUtils.readString(stream); boolean needDecodeChunk = ReadWriteIOUtils.readBool(stream); byte chunkType = ReadWriteIOUtils.readByte(stream); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/load/TsFileSplitter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/load/TsFileSplitter.java index 7f66c9813f7f41df480a712045ff95536957720d..83bbd744cea107e55a7cf71a3f17ecb010b21395 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/load/TsFileSplitter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/load/TsFileSplitter.java @@ -114,7 +114,7 @@ public class TsFileSplitter { == TsFileConstant.TIME_COLUMN_MASK); IChunkMetadata chunkMetadata = offset2ChunkMetadata.get(chunkOffset - Byte.BYTES); TTimePartitionSlot timePartitionSlot = - TimePartitionUtils.getTimePartition(chunkMetadata.getStartTime()); + TimePartitionUtils.getTimePartitionSlot(chunkMetadata.getStartTime()); ChunkData chunkData = ChunkData.createChunkData(isAligned, curDevice, header, timePartitionSlot); @@ -157,7 +157,7 @@ public class TsFileSplitter { ? chunkMetadata.getStartTime() : pageHeader.getStartTime(); TTimePartitionSlot pageTimePartitionSlot = - TimePartitionUtils.getTimePartition(startTime); + TimePartitionUtils.getTimePartitionSlot(startTime); if (!timePartitionSlot.equals(pageTimePartitionSlot)) { if (!isAligned) { consumeChunkData(measurementId, chunkOffset, chunkData); @@ -198,7 +198,7 @@ public class TsFileSplitter { consumeChunkData(measurementId, chunkOffset, chunkData); } - timePartitionSlot = TimePartitionUtils.getTimePartition(times[i]); + timePartitionSlot = TimePartitionUtils.getTimePartitionSlot(times[i]); satisfiedLength = 0; endTime = timePartitionSlot.getStartTime() @@ -380,17 +380,17 @@ public class TsFileSplitter { } private boolean needDecodeChunk(IChunkMetadata chunkMetadata) { - return !TimePartitionUtils.getTimePartition(chunkMetadata.getStartTime()) - .equals(TimePartitionUtils.getTimePartition(chunkMetadata.getEndTime())); + return !TimePartitionUtils.getTimePartitionSlot(chunkMetadata.getStartTime()) + .equals(TimePartitionUtils.getTimePartitionSlot(chunkMetadata.getEndTime())); } private boolean needDecodePage(PageHeader pageHeader, IChunkMetadata chunkMetadata) { if (pageHeader.getStatistics() == null) { - return !TimePartitionUtils.getTimePartition(chunkMetadata.getStartTime()) - .equals(TimePartitionUtils.getTimePartition(chunkMetadata.getEndTime())); + return !TimePartitionUtils.getTimePartitionSlot(chunkMetadata.getStartTime()) + .equals(TimePartitionUtils.getTimePartitionSlot(chunkMetadata.getEndTime())); } - return !TimePartitionUtils.getTimePartition(pageHeader.getStartTime()) - .equals(TimePartitionUtils.getTimePartition(pageHeader.getEndTime())); + return !TimePartitionUtils.getTimePartitionSlot(pageHeader.getStartTime()) + .equals(TimePartitionUtils.getTimePartitionSlot(pageHeader.getEndTime())); } private Pair decodePage( diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java index aaa9edacff228dddcccd0b108352160e6e9ef4c1..63ba89b57db391ba3756f6f536251863b0c0466a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java @@ -1838,7 +1838,6 @@ public class AnalyzeVisitor extends StatementVisitor boolean needLeftAll; boolean needRightAll; - long startTime; long endTime; TTimePartitionSlot timePartitionSlot; int index = 0; @@ -1846,17 +1845,11 @@ public class AnalyzeVisitor extends StatementVisitor if (timeRangeList.get(0).getMin() == Long.MIN_VALUE) { needLeftAll = true; - startTime = - (timeRangeList.get(0).getMax() / TimePartitionUtils.timePartitionInterval) - * TimePartitionUtils.timePartitionInterval; // included - endTime = startTime + TimePartitionUtils.timePartitionInterval; // excluded - timePartitionSlot = TimePartitionUtils.getTimePartition(timeRangeList.get(0).getMax()); + endTime = TimePartitionUtils.getTimePartitionUpperBound(timeRangeList.get(0).getMax()); + timePartitionSlot = TimePartitionUtils.getTimePartitionSlot(timeRangeList.get(0).getMax()); } else { - startTime = - (timeRangeList.get(0).getMin() / TimePartitionUtils.timePartitionInterval) - * TimePartitionUtils.timePartitionInterval; // included - endTime = startTime + TimePartitionUtils.timePartitionInterval; // excluded - timePartitionSlot = TimePartitionUtils.getTimePartition(timeRangeList.get(0).getMin()); + endTime = TimePartitionUtils.getTimePartitionUpperBound(timeRangeList.get(0).getMin()); + timePartitionSlot = TimePartitionUtils.getTimePartitionSlot(timeRangeList.get(0).getMin()); needLeftAll = false; } @@ -1874,15 +1867,13 @@ public class AnalyzeVisitor extends StatementVisitor if (curLeft >= endTime) { result.add(timePartitionSlot); // next init - endTime = - (curLeft / TimePartitionUtils.timePartitionInterval + 1) - * TimePartitionUtils.timePartitionInterval; - timePartitionSlot = TimePartitionUtils.getTimePartition(curLeft); + endTime = TimePartitionUtils.getTimePartitionUpperBound(curLeft); + timePartitionSlot = TimePartitionUtils.getTimePartitionSlot(curLeft); } else if (curRight >= endTime) { result.add(timePartitionSlot); // next init timePartitionSlot = new TTimePartitionSlot(endTime); - endTime = endTime + TimePartitionUtils.timePartitionInterval; + endTime = endTime + TimePartitionUtils.getTimePartitionInterval(); } else { index++; } @@ -1891,7 +1882,8 @@ public class AnalyzeVisitor extends StatementVisitor if (needRightAll) { TTimePartitionSlot lastTimePartitionSlot = - TimePartitionUtils.getTimePartition(timeRangeList.get(timeRangeList.size() - 1).getMin()); + TimePartitionUtils.getTimePartitionSlot( + timeRangeList.get(timeRangeList.size() - 1).getMin()); if (lastTimePartitionSlot.startTime != timePartitionSlot.startTime) { result.add(lastTimePartitionSlot); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java index c8299c50a68d91b338fc47b31bbb4477b0294a61..58cd8d43bb42235234ac03fccf370929e4108624 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java @@ -255,8 +255,6 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor { private static final String GROUP_BY_COMMON_ONLY_ONE_MSG = "Only one of group by time or group by variation/series/session can be supported at a time"; - private static final String NEGATIVE_TIMESTAMP_ERROR_MSG = - "Please set the time >=0 or after 1970-01-01 00:00:00"; private static final String LIMIT_CONFIGURATION_ENABLED_ERROR_MSG = "Limit configuration is not enabled, please enable it first."; @@ -3655,11 +3653,7 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor { } if (ctx.time != null) { long timestamp = parseTimeValue(ctx.time, DateTimeUtils.currentTime()); - if (timestamp < 0) { - throw new SemanticException(NEGATIVE_TIMESTAMP_ERROR_MSG); - } else { - getRegionIdStatement.setTimeStamp(timestamp); - } + getRegionIdStatement.setTimeStamp(timestamp); } return getRegionIdStatement; } @@ -3683,19 +3677,11 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor { } if (ctx.startTime != null) { long timestamp = parseTimeValue(ctx.startTime, DateTimeUtils.currentTime()); - if (timestamp < 0) { - throw new SemanticException(NEGATIVE_TIMESTAMP_ERROR_MSG); - } else { - getTimeSlotListStatement.setStartTime(timestamp); - } + getTimeSlotListStatement.setStartTime(timestamp); } if (ctx.endTime != null) { long timestamp = parseTimeValue(ctx.endTime, DateTimeUtils.currentTime()); - if (timestamp < 0) { - throw new SemanticException(NEGATIVE_TIMESTAMP_ERROR_MSG); - } else { - getTimeSlotListStatement.setEndTime(timestamp); - } + getTimeSlotListStatement.setEndTime(timestamp); } return getTimeSlotListStatement; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/load/LoadSingleTsFileNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/load/LoadSingleTsFileNode.java index 4f3506bcb753b90ca94758da64520ff0416da142..b28db7d3abfbb07a6870af5c59bcf1580e92ad8f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/load/LoadSingleTsFileNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/load/LoadSingleTsFileNode.java @@ -85,9 +85,9 @@ public class LoadSingleTsFileNode extends WritePlanNode { .forEach( o -> { slotList.add( - new Pair<>(o, TimePartitionUtils.getTimePartition(resource.getStartTime(o)))); + new Pair<>(o, TimePartitionUtils.getTimePartitionSlot(resource.getStartTime(o)))); slotList.add( - new Pair<>(o, TimePartitionUtils.getTimePartition(resource.getEndTime(o)))); + new Pair<>(o, TimePartitionUtils.getTimePartitionSlot(resource.getEndTime(o)))); }); if (slotList.isEmpty()) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertRowNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertRowNode.java index 44a301ef9dad1682e615005d0f315b9ff9307076..1d865400293fa37bf97ec83cf1f8866a2dcca8fd 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertRowNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertRowNode.java @@ -106,7 +106,7 @@ public class InsertRowNode extends InsertNode implements WALEntryValue { @Override public List splitByPartition(Analysis analysis) { - TTimePartitionSlot timePartitionSlot = TimePartitionUtils.getTimePartition(time); + TTimePartitionSlot timePartitionSlot = TimePartitionUtils.getTimePartitionSlot(time); this.dataRegionReplicaSet = analysis .getDataPartitionInfo() @@ -191,7 +191,7 @@ public class InsertRowNode extends InsertNode implements WALEntryValue { @TestOnly public List getTimePartitionSlots() { - return Collections.singletonList(TimePartitionUtils.getTimePartition(time)); + return Collections.singletonList(TimePartitionUtils.getTimePartitionSlot(time)); } @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertRowsNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertRowsNode.java index 23428d75bec8fb971a5258b5c81a403674d30f87..439fb5a3b4826315253d16dfdb061037e4456b50 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertRowsNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertRowsNode.java @@ -223,7 +223,7 @@ public class InsertRowsNode extends InsertNode { .getDataPartitionInfo() .getDataRegionReplicaSetForWriting( insertRowNode.devicePath.getFullPath(), - TimePartitionUtils.getTimePartition(insertRowNode.getTime())); + TimePartitionUtils.getTimePartitionSlot(insertRowNode.getTime())); // collect redirectInfo redirectInfo.add(dataRegionReplicaSet.getDataNodeLocations().get(0).getClientRpcEndPoint()); InsertRowsNode tmpNode = splitMap.get(dataRegionReplicaSet); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertRowsOfOneDeviceNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertRowsOfOneDeviceNode.java index a05e2080f954686ce8977b616c38fb8b7ff84a60..0167bd7de1904fce57c18f7b8c32b1efcd95d9c5 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertRowsOfOneDeviceNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertRowsOfOneDeviceNode.java @@ -156,7 +156,7 @@ public class InsertRowsOfOneDeviceNode extends InsertNode { .getDataPartitionInfo() .getDataRegionReplicaSetForWriting( devicePath.getFullPath(), - TimePartitionUtils.getTimePartition(insertRowNode.getTime())); + TimePartitionUtils.getTimePartitionSlot(insertRowNode.getTime())); List tmpMap = splitMap.computeIfAbsent(dataRegionReplicaSet, k -> new ArrayList<>()); List tmpIndexMap = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertTabletNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertTabletNode.java index c19b4f037d0e7db3ff7d56712bded99390d8b3da..a7ecfe5f1ea4c439f228fa8b592e1c23056d0513 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertTabletNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertTabletNode.java @@ -190,29 +190,23 @@ public class InsertTabletNode extends InsertNode implements WALEntryValue { if (times.length == 0) { return Collections.emptyList(); } - long startTime = - (times[0] / TimePartitionUtils.timePartitionInterval) - * TimePartitionUtils.timePartitionInterval; // included - long endTime = startTime + TimePartitionUtils.timePartitionInterval; // excluded - TTimePartitionSlot timePartitionSlot = TimePartitionUtils.getTimePartition(times[0]); + long upperBoundOfTimePartition = TimePartitionUtils.getTimePartitionUpperBound(times[0]); + TTimePartitionSlot timePartitionSlot = TimePartitionUtils.getTimePartitionSlot(times[0]); int startLoc = 0; // included List timePartitionSlots = new ArrayList<>(); // for each List in split, they are range1.start, range1.end, range2.start, range2.end, ... List ranges = new ArrayList<>(); for (int i = 1; i < times.length; i++) { // times are sorted in session API. - if (times[i] >= endTime) { + if (times[i] >= upperBoundOfTimePartition) { // a new range. ranges.add(startLoc); // included ranges.add(i); // excluded timePartitionSlots.add(timePartitionSlot); // next init startLoc = i; - startTime = endTime; - endTime = - (times[i] / TimePartitionUtils.timePartitionInterval + 1) - * TimePartitionUtils.timePartitionInterval; - timePartitionSlot = TimePartitionUtils.getTimePartition(times[i]); + upperBoundOfTimePartition = TimePartitionUtils.getTimePartitionUpperBound(times[i]); + timePartitionSlot = TimePartitionUtils.getTimePartitionSlot(times[i]); } } @@ -292,19 +286,14 @@ public class InsertTabletNode extends InsertNode implements WALEntryValue { @TestOnly public List getTimePartitionSlots() { List result = new ArrayList<>(); - long startTime = - (times[0] / TimePartitionUtils.timePartitionInterval) - * TimePartitionUtils.timePartitionInterval; // included - long endTime = startTime + TimePartitionUtils.timePartitionInterval; // excluded - TTimePartitionSlot timePartitionSlot = TimePartitionUtils.getTimePartition(times[0]); + long upperBoundOfTimePartition = TimePartitionUtils.getTimePartitionUpperBound(times[0]); + TTimePartitionSlot timePartitionSlot = TimePartitionUtils.getTimePartitionSlot(times[0]); for (int i = 1; i < times.length; i++) { // times are sorted in session API. - if (times[i] >= endTime) { + if (times[i] >= upperBoundOfTimePartition) { result.add(timePartitionSlot); // next init - endTime = - (times[i] / TimePartitionUtils.timePartitionInterval + 1) - * TimePartitionUtils.timePartitionInterval; - timePartitionSlot = TimePartitionUtils.getTimePartition(times[i]); + upperBoundOfTimePartition = TimePartitionUtils.getTimePartitionUpperBound(times[i]); + timePartitionSlot = TimePartitionUtils.getTimePartitionSlot(times[i]); } } result.add(timePartitionSlot); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowStatement.java index 4280d18de0b52906638068dd355434f20ab353b9..88e2d77e9ee03b5fc6a0718a2552d969a93771cf 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowStatement.java @@ -154,7 +154,7 @@ public class InsertRowStatement extends InsertBaseStatement implements ISchemaVa } public TTimePartitionSlot getTimePartitionSlot() { - return TimePartitionUtils.getTimePartition(time); + return TimePartitionUtils.getTimePartitionSlot(time); } @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowsOfOneDeviceStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowsOfOneDeviceStatement.java index 78c7c8ca8b207338e5edc5e9e7a7090162d0c7a2..68688cdb4b16b498dbb5f520ee3d870040ca2270 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowsOfOneDeviceStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowsOfOneDeviceStatement.java @@ -80,7 +80,8 @@ public class InsertRowsOfOneDeviceStatement extends InsertBaseStatement { public List getTimePartitionSlots() { Set timePartitionSlotSet = new HashSet<>(); for (InsertRowStatement insertRowStatement : insertRowStatementList) { - timePartitionSlotSet.add(TimePartitionUtils.getTimePartition(insertRowStatement.getTime())); + timePartitionSlotSet.add( + TimePartitionUtils.getTimePartitionSlot(insertRowStatement.getTime())); } return new ArrayList<>(timePartitionSlotSet); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertTabletStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertTabletStatement.java index e7738efc740fa7a296b0b94da550e2439211041a..de226d0e90c6f55a5dbdfd9e87962c3b079782e5 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertTabletStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertTabletStatement.java @@ -118,19 +118,14 @@ public class InsertTabletStatement extends InsertBaseStatement implements ISchem public List getTimePartitionSlots() { List result = new ArrayList<>(); - long startTime = - (times[0] / TimePartitionUtils.timePartitionInterval) - * TimePartitionUtils.timePartitionInterval; // included - long endTime = startTime + TimePartitionUtils.timePartitionInterval; // excluded - TTimePartitionSlot timePartitionSlot = TimePartitionUtils.getTimePartition(times[0]); + long upperBoundOfTimePartition = TimePartitionUtils.getTimePartitionUpperBound(times[0]); + TTimePartitionSlot timePartitionSlot = TimePartitionUtils.getTimePartitionSlot(times[0]); for (int i = 1; i < times.length; i++) { // times are sorted in session API. - if (times[i] >= endTime) { + if (times[i] >= upperBoundOfTimePartition) { result.add(timePartitionSlot); // next init - endTime = - (times[i] / TimePartitionUtils.timePartitionInterval + 1) - * TimePartitionUtils.timePartitionInterval; - timePartitionSlot = TimePartitionUtils.getTimePartition(times[i]); + upperBoundOfTimePartition = TimePartitionUtils.getTimePartitionUpperBound(times[i]); + timePartitionSlot = TimePartitionUtils.getTimePartitionSlot(times[i]); } } result.add(timePartitionSlot); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java index d1b8cd8c1ffe7cc7450d47b21fd34fdc78479562..1cbdf6e739797d00fc9661862892e86bfbb09297 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java @@ -163,7 +163,9 @@ public class StorageEngine implements IService { if (timePartitionInterval == -1) { initTimePartition(); } - return time / timePartitionInterval; + return time > 0 || time % timePartitionInterval == 0 + ? time / timePartitionInterval + : time / timePartitionInterval - 1; } /** block insertion if the insertion is rejected by memory control */ diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/TimePartitionUtils.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/TimePartitionUtils.java index 3b5e0f8177eca109ea35d983439e6f0202b0da10..25e27789e6026f4371e757e470c46a5baa22b714 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/TimePartitionUtils.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/TimePartitionUtils.java @@ -23,12 +23,16 @@ import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.utils.TestOnly; public class TimePartitionUtils { - public static long timePartitionInterval = + private static long timePartitionInterval = CommonDescriptor.getInstance().getConfig().getTimePartitionInterval(); - public static TTimePartitionSlot getTimePartition(long time) { + public static TTimePartitionSlot getTimePartitionSlot(long time) { TTimePartitionSlot timePartitionSlot = new TTimePartitionSlot(); - timePartitionSlot.setStartTime(time - time % timePartitionInterval); + if (time > 0 || time % timePartitionInterval == 0) { + timePartitionSlot.setStartTime(time / timePartitionInterval * timePartitionInterval); + } else { + timePartitionSlot.setStartTime((time / timePartitionInterval - 1) * timePartitionInterval); + } return timePartitionSlot; } @@ -36,6 +40,20 @@ public class TimePartitionUtils { return timePartitionInterval; } + public static long getTimePartitionUpperBound(long time) { + long upperBoundOfTimePartition; + if (time > 0 || time % TimePartitionUtils.timePartitionInterval == 0) { + upperBoundOfTimePartition = + (time / TimePartitionUtils.timePartitionInterval + 1) + * TimePartitionUtils.timePartitionInterval; + } else { + upperBoundOfTimePartition = + (time / TimePartitionUtils.timePartitionInterval) + * TimePartitionUtils.timePartitionInterval; + } + return upperBoundOfTimePartition; + } + @TestOnly public static void setTimePartitionInterval(long timePartitionInterval) { TimePartitionUtils.timePartitionInterval = timePartitionInterval; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/plan/node/write/WritePlanNodeSplitTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/plan/node/write/WritePlanNodeSplitTest.java index dfe79d7177127dfa572360192e17935537aa17f4..60247ce24d808e6a33cfd9fc3c73cce98276cfd6 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/plan/node/write/WritePlanNodeSplitTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/plan/node/write/WritePlanNodeSplitTest.java @@ -100,13 +100,13 @@ public class WritePlanNodeSplitTest { new TEndPoint("127.0.0.1", 10740), new TEndPoint("127.0.0.1", 10760), new TEndPoint("127.0.0.1", 10750))); - // sg1 has 5 data regions + // sg1 has 7 data regions for (int i = 0; i < seriesSlotPartitionNum; i++) { Map> timePartitionSlotMap = new HashMap<>(); - for (int t = 0; t < 5; t++) { - long startTime = t * TimePartitionUtils.timePartitionInterval; + for (int t = -2; t < 5; t++) { + long startTime = t * TimePartitionUtils.getTimePartitionInterval() + 1; timePartitionSlotMap.put( - new TTimePartitionSlot(startTime), + TimePartitionUtils.getTimePartitionSlot(startTime), Collections.singletonList( new TRegionReplicaSet( new TConsensusGroupId( @@ -125,7 +125,7 @@ public class WritePlanNodeSplitTest { Map> timePartitionSlotMap = new HashMap<>(); for (int t = 0; t < 5; t++) { timePartitionSlotMap.put( - new TTimePartitionSlot(t * TimePartitionUtils.timePartitionInterval), + new TTimePartitionSlot(t * TimePartitionUtils.getTimePartitionInterval()), Collections.singletonList( new TRegionReplicaSet( new TConsensusGroupId(TConsensusGroupType.DataRegion, 99), locationList))); @@ -150,7 +150,7 @@ public class WritePlanNodeSplitTest { } private int getRegionIdByTime(long startTime) { - return (int) (4 - (startTime / TimePartitionUtils.timePartitionInterval)); + return (int) (4 - ((startTime - 1) / TimePartitionUtils.getTimePartitionInterval())); } protected DataPartition getDataPartition( @@ -194,9 +194,11 @@ public class WritePlanNodeSplitTest { InsertTabletNode insertTabletNode = new InsertTabletNode(new PlanNodeId("plan node 1")); insertTabletNode.setDevicePath(new PartialPath("root.sg1.d1")); - insertTabletNode.setTimes(new long[] {1, 60, 120, 180, 270, 290, 360, 375, 440, 470}); + insertTabletNode.setTimes( + new long[] {-200, -101, 1, 60, 120, 180, 270, 290, 360, 375, 440, 470}); insertTabletNode.setDataTypes(new TSDataType[] {TSDataType.INT32}); - insertTabletNode.setColumns(new Object[] {new int[] {10, 20, 30, 40, 50, 60, 70, 80, 90, 100}}); + insertTabletNode.setColumns( + new Object[] {new int[] {-20, -10, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}}); DataPartitionQueryParam dataPartitionQueryParam = new DataPartitionQueryParam(); dataPartitionQueryParam.setDevicePath(insertTabletNode.getDevicePath().getFullPath()); @@ -209,7 +211,7 @@ public class WritePlanNodeSplitTest { List insertTabletNodeList = insertTabletNode.splitByPartition(analysis); - Assert.assertEquals(5, insertTabletNodeList.size()); + Assert.assertEquals(6, insertTabletNodeList.size()); for (WritePlanNode insertNode : insertTabletNodeList) { InsertTabletNode tabletNode = (InsertTabletNode) insertNode; Assert.assertEquals(tabletNode.getTimes().length, 2); @@ -285,10 +287,10 @@ public class WritePlanNodeSplitTest { InsertRowsNode insertRowsNode = new InsertRowsNode(new PlanNodeId("plan node 3")); - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 7; i++) { InsertRowNode insertRowNode = new InsertRowNode(new PlanNodeId("plan node 3")); insertRowNode.setDevicePath(new PartialPath(String.format("root.sg1.d%d", i))); - insertRowNode.setTime(i * TimePartitionUtils.timePartitionInterval); + insertRowNode.setTime((i - 2) * TimePartitionUtils.getTimePartitionInterval()); insertRowsNode.addOneInsertRowNode(insertRowNode, 2 * i); insertRowNode = new InsertRowNode(new PlanNodeId("plan node 3")); @@ -309,9 +311,9 @@ public class WritePlanNodeSplitTest { Analysis analysis = new Analysis(); analysis.setDataPartitionInfo(dataPartition); - List insertTabletNodeList = insertRowsNode.splitByPartition(analysis); + List insertRowsNodeList = insertRowsNode.splitByPartition(analysis); - Assert.assertEquals(6, insertTabletNodeList.size()); + Assert.assertEquals(8, insertRowsNodeList.size()); } @After diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/StorageEngineTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/StorageEngineTest.java index 8cb5f167d6685b9c6f4848d66ea5d6f0a7de80a1..024651d9a0bdfe52bd424386a6b796e7373d8f55 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/StorageEngineTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/StorageEngineTest.java @@ -68,4 +68,17 @@ public class StorageEngineTest { rg1.syncDeleteDataFiles(); rg2.syncDeleteDataFiles(); } + + @Test + public void testGetTimePartitionId() { + long timePartitionInterval = StorageEngine.getTimePartitionInterval(); + Assert.assertEquals(-2, StorageEngine.getTimePartition(-timePartitionInterval - 1)); + Assert.assertEquals(-1, StorageEngine.getTimePartition(-timePartitionInterval)); + Assert.assertEquals(-1, StorageEngine.getTimePartition(-1)); + Assert.assertEquals(0, StorageEngine.getTimePartition(0)); + Assert.assertEquals(0, StorageEngine.getTimePartition(1)); + Assert.assertEquals(0, StorageEngine.getTimePartition(timePartitionInterval / 2)); + Assert.assertEquals(1, StorageEngine.getTimePartition(timePartitionInterval * 2 - 1)); + Assert.assertEquals(2, StorageEngine.getTimePartition(timePartitionInterval * 2 + 1)); + } }