From c45d65be9d6f6917d53869692ba42b7b054b0280 Mon Sep 17 00:00:00 2001 From: Caideyipi <87789683+Caideyipi@users.noreply.github.com> Date: Wed, 8 Jul 2026 17:55:32 +0800 Subject: [PATCH] Fix the AlignedTVListIterator / memtable read index problem (cherry picked from commit 344536f2b005e057dadfc5b895fd3c615cfb4491) --- .../db/utils/datastructure/AlignedTVList.java | 30 ++++++++-------- .../memtable/AlignedTVListIteratorTest.java | 34 +++++++++++++++++++ 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/AlignedTVList.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/AlignedTVList.java index 01af929d5c811..7f4f6cb84bc35 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/AlignedTVList.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/AlignedTVList.java @@ -1833,8 +1833,7 @@ protected void prepareNext() { while (index < rows && !findValidRow) { // all columns values are deleted int convertedScanOrderValueIndex = getValueIndex(getScanOrderIndex(index)); - if ((allValueColDeletedMap != null - && allValueColDeletedMap.isMarked(convertedScanOrderValueIndex)) + if (isAllValueColumnsDeleted(convertedScanOrderValueIndex) || isTimeDeleted(convertedScanOrderValueIndex, false)) { index++; continue; @@ -1875,9 +1874,7 @@ protected void prepareNext() { while (index + 1 < rows && getTime(getScanOrderIndex(index + 1)) == getTime(getScanOrderIndex(index))) { index++; - // skip all-Null rows if allValueColDeletedMap exists - if (allValueColDeletedMap == null - || !allValueColDeletedMap.isMarked(getValueIndex(getScanOrderIndex(index)))) { + if (!isAllValueColumnsDeleted(getValueIndex(getScanOrderIndex(index)))) { for (int columnIndex = 0; columnIndex < dataTypeList.size(); columnIndex++) { if (!scanOrder.isAscending() && selectedIndices[columnIndex] != -1) { // non -1 value means it already set the latest point index @@ -2034,8 +2031,7 @@ public boolean hasNextBatch() { private boolean isRowInvalid( int rowIndex, long time, int[] deleteCursor, long[] filteredRowsByTimeFilter) { - if ((allValueColDeletedMap != null - && allValueColDeletedMap.isMarked(getValueIndex(getScanOrderIndex(rowIndex)))) + if (isAllValueColumnsDeleted(getValueIndex(getScanOrderIndex(rowIndex))) || isTimeDeleted(getScanOrderIndex(rowIndex)) || isPointDeleted(time, timeColumnDeletion, deleteCursor, scanOrder)) { return true; @@ -2369,7 +2365,7 @@ public void encodeBatch(IChunkWriter chunkWriter, BatchEncodeInfo encodeInfo, lo break; } // skip empty row - if (allValueColDeletedMap != null && allValueColDeletedMap.isMarked(getValueIndex(index))) { + if (isAllValueColumnsDeleted(getValueIndex(index))) { continue; } if (isTimeDeleted(index)) { @@ -2377,9 +2373,8 @@ public void encodeBatch(IChunkWriter chunkWriter, BatchEncodeInfo encodeInfo, lo } int nextRowIndex = index + 1; while (nextRowIndex < rows - && ((allValueColDeletedMap != null - && allValueColDeletedMap.isMarked(getValueIndex(nextRowIndex))) - || (isTimeDeleted(nextRowIndex)))) { + && (isAllValueColumnsDeleted(getValueIndex(nextRowIndex)) + || isTimeDeleted(nextRowIndex))) { nextRowIndex++; } long time = getTime(index); @@ -2409,9 +2404,8 @@ public void encodeBatch(IChunkWriter chunkWriter, BatchEncodeInfo encodeInfo, lo } for (int sortedRowIndex = startIndex; sortedRowIndex < index; sortedRowIndex++) { // skip empty row - if ((allValueColDeletedMap != null - && allValueColDeletedMap.isMarked(getValueIndex(sortedRowIndex))) - || (isTimeDeleted(sortedRowIndex))) { + if (isAllValueColumnsDeleted(getValueIndex(sortedRowIndex)) + || isTimeDeleted(sortedRowIndex)) { continue; } long time = getTime(sortedRowIndex); @@ -2493,6 +2487,14 @@ public int[] getSelectedIndices() { return selectedIndices; } + private boolean isAllValueColumnsDeleted(int valueIndex) { + // A sorted row-count snapshot can point to value indices appended after the snapshot. + return allValueColDeletedMap != null + && valueIndex >= 0 + && valueIndex < allValueColDeletedMap.getSize() + && allValueColDeletedMap.isMarked(valueIndex); + } + public int getSelectedIndex(int column) { return selectedIndices[column]; } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/memtable/AlignedTVListIteratorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/memtable/AlignedTVListIteratorTest.java index c58eeb1bf6069..b816713e7c189 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/memtable/AlignedTVListIteratorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/memtable/AlignedTVListIteratorTest.java @@ -186,6 +186,40 @@ public void otherTest() throws IOException { 8); } + @Test + public void testIteratorWithStaleRowCountAfterSort() { + AlignedTVList tvList = + AlignedTVList.newAlignedList( + Arrays.asList(TSDataType.INT32, TSDataType.INT32, TSDataType.INT32)); + for (int i = 100; i < 110; i++) { + tvList.putAlignedValue(i, new Object[] {i, i, i}); + } + int staleRowCount = tvList.rowCount(); + for (int i = 1; i <= 2; i++) { + tvList.putAlignedValue(i, new Object[] {i, i, i}); + } + tvList.delete(1, 2); + tvList.sort(); + + AlignedTVList.AlignedTVListIterator iterator = + tvList.iterator( + Ordering.ASC, + staleRowCount, + null, + Arrays.asList(TSDataType.INT32, TSDataType.INT32, TSDataType.INT32), + Arrays.asList(0, 1, 2), + null, + null, + null, + 100); + int count = 0; + while (iterator.hasNextTimeValuePair()) { + iterator.nextTimeValuePair(); + count++; + } + Assert.assertEquals(staleRowCount - 2, count); + } + @Test public void testAlignedWithDeletionsInTVList() throws IOException { Map tvListMap =