This is an automated email from the ASF dual-hosted git repository.

rong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 59c46cf4f86 Pipe: Fixed the bug that sorter may use wrong value when 
there are duplicated timestamps & Refactor (#15488)
59c46cf4f86 is described below

commit 59c46cf4f86d6ee25a231b22bce42b1a3d0e8b2d
Author: Caideyipi <[email protected]>
AuthorDate: Thu May 15 16:47:04 2025 +0800

    Pipe: Fixed the bug that sorter may use wrong value when there are 
duplicated timestamps & Refactor (#15488)
---
 .../request/PipeTransferTabletRawReqV2.java        |   2 +-
 .../connector/protocol/opcua/OpcUaNameSpace.java   |   2 +-
 .../sorter/PipeTableModelTabletEventSorter.java    | 142 +++++---------------
 .../util/sorter/PipeTabletEventSorter.java         | 146 +++++++++++++++------
 .../sorter/PipeTreeModelTabletEventSorter.java     |  60 +++------
 .../pipe/connector/PipeTabletEventSorterTest.java  |  72 +++++-----
 6 files changed, 192 insertions(+), 232 deletions(-)

diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/payload/evolvable/request/PipeTransferTabletRawReqV2.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/payload/evolvable/request/PipeTransferTabletRawReqV2.java
index fd435ded99c..d0608cb7042 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/payload/evolvable/request/PipeTransferTabletRawReqV2.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/payload/evolvable/request/PipeTransferTabletRawReqV2.java
@@ -61,7 +61,7 @@ public class PipeTransferTabletRawReqV2 extends 
PipeTransferTabletRawReq {
     if (Objects.isNull(dataBaseName)) {
       new 
PipeTreeModelTabletEventSorter(tablet).deduplicateAndSortTimestampsIfNecessary();
     } else {
-      new 
PipeTableModelTabletEventSorter(tablet).sortAndDeduplicateByTimestampIfNecessary();
+      new PipeTableModelTabletEventSorter(tablet).sortByTimestampIfNecessary();
     }
 
     try {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/protocol/opcua/OpcUaNameSpace.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/protocol/opcua/OpcUaNameSpace.java
index ac689ad8114..3af05080d80 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/protocol/opcua/OpcUaNameSpace.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/protocol/opcua/OpcUaNameSpace.java
@@ -138,7 +138,7 @@ public class OpcUaNameSpace extends 
ManagedNamespaceWithLifecycle {
       transferTabletRowForClientServerModel(
           tablet.getDeviceId().split("\\."), newSchemas, timestamps, values);
     } else {
-      new 
PipeTableModelTabletEventSorter(tablet).sortAndDeduplicateByTimestampIfNecessary();
+      new PipeTableModelTabletEventSorter(tablet).sortByTimestampIfNecessary();
 
       final List<Integer> columnIndexes = new ArrayList<>();
       for (int i = 0; i < schemas.size(); ++i) {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/util/sorter/PipeTableModelTabletEventSorter.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/util/sorter/PipeTableModelTabletEventSorter.java
index 4eb8672fe81..83f118c971e 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/util/sorter/PipeTableModelTabletEventSorter.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/util/sorter/PipeTableModelTabletEventSorter.java
@@ -23,30 +23,20 @@ import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.file.metadata.IDeviceID;
 import org.apache.tsfile.utils.Pair;
 import org.apache.tsfile.write.record.Tablet;
-import org.apache.tsfile.write.schema.IMeasurementSchema;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
-public class PipeTableModelTabletEventSorter {
-
-  private final Tablet tablet;
-
-  private Integer[] index;
-  private boolean isUnSorted = false;
-  private boolean hasDuplicates = false;
-  private int deduplicatedSize;
+public class PipeTableModelTabletEventSorter extends PipeTabletEventSorter {
   private int initIndexSize;
 
   public PipeTableModelTabletEventSorter(final Tablet tablet) {
-    this.tablet = tablet;
-    deduplicatedSize = tablet == null ? 0 : tablet.getRowSize();
+    super(tablet);
+    deDuplicatedSize = tablet == null ? 0 : tablet.getRowSize();
   }
 
   /**
@@ -72,24 +62,24 @@ public class PipeTableModelTabletEventSorter {
       final int deviceComparison = deviceID.compareTo(lastDevice);
       if (deviceComparison == 0) {
         if (previousTimestamp == currentTimestamp) {
-          hasDuplicates = true;
+          isDeDuplicated = false;
           continue;
         }
         if (previousTimestamp > currentTimestamp) {
-          isUnSorted = true;
+          isSorted = false;
         }
         previousTimestamp = currentTimestamp;
         continue;
       }
       if (deviceComparison < 0) {
-        isUnSorted = true;
+        isSorted = false;
       }
 
       final List<Pair<Integer, Integer>> list =
           deviceIDToIndexMap.computeIfAbsent(lastDevice, k -> new 
ArrayList<>());
 
       if (!list.isEmpty()) {
-        isUnSorted = true;
+        isSorted = false;
       }
       list.add(new Pair<>(lasIndex, i));
       lastDevice = deviceID;
@@ -100,85 +90,69 @@ public class PipeTableModelTabletEventSorter {
     final List<Pair<Integer, Integer>> list =
         deviceIDToIndexMap.computeIfAbsent(lastDevice, k -> new ArrayList<>());
     if (!list.isEmpty()) {
-      isUnSorted = true;
+      isSorted = false;
     }
     list.add(new Pair<>(lasIndex, tablet.getRowSize()));
 
-    if (!isUnSorted && !hasDuplicates) {
+    if (isSorted && isDeDuplicated) {
       return;
     }
 
     initIndexSize = 0;
-    deduplicatedSize = 0;
+    deDuplicatedSize = 0;
     index = new Integer[tablet.getRowSize()];
+    deDuplicatedIndex = new int[tablet.getRowSize()];
     deviceIDToIndexMap.entrySet().stream()
         .sorted(Map.Entry.comparingByKey())
         .forEach(
             entry -> {
-              final int start = initIndexSize;
               int i = initIndexSize;
               for (Pair<Integer, Integer> pair : entry.getValue()) {
                 for (int j = pair.left; j < pair.right; j++) {
                   index[i++] = j;
                 }
               }
-              if (isUnSorted) {
-                sortTimestamps(start, i);
-                deduplicateTimestamps(start, i);
+              if (!isSorted) {
+                sortTimestamps(initIndexSize, i);
+                deDuplicateTimestamps(initIndexSize, i);
                 initIndexSize = i;
                 return;
               }
 
-              if (hasDuplicates) {
-                deduplicateTimestamps(start, i);
+              if (!isDeDuplicated) {
+                deDuplicateTimestamps(initIndexSize, i);
               }
               initIndexSize = i;
             });
 
-    sortAndDeduplicateValuesAndBitMaps();
+    sortAndDeduplicateValuesAndBitMapsWithTimestamp();
   }
 
-  private void sortAndDeduplicateValuesAndBitMaps() {
-    int columnIndex = 0;
+  private void sortAndDeduplicateValuesAndBitMapsWithTimestamp() {
     tablet.setTimestamps(
         (long[])
-            PipeTabletEventSorter.reorderValueList(
-                deduplicatedSize, tablet.getTimestamps(), 
TSDataType.TIMESTAMP, index));
-    for (int i = 0, size = tablet.getSchemas().size(); i < size; i++) {
-      final IMeasurementSchema schema = tablet.getSchemas().get(i);
-      if (schema != null) {
-        tablet.getValues()[columnIndex] =
-            PipeTabletEventSorter.reorderValueList(
-                deduplicatedSize, tablet.getValues()[columnIndex], 
schema.getType(), index);
-        if (tablet.getBitMaps() != null && tablet.getBitMaps()[columnIndex] != 
null) {
-          tablet.getBitMaps()[columnIndex] =
-              PipeTabletEventSorter.reorderBitMap(
-                  deduplicatedSize, tablet.getBitMaps()[columnIndex], index);
-        }
-        columnIndex++;
-      }
-    }
-
-    tablet.setRowSize(deduplicatedSize);
+            reorderValueListAndBitMap(tablet.getTimestamps(), 
TSDataType.TIMESTAMP, null, null));
+    sortAndMayDeduplicateValuesAndBitMaps();
+    tablet.setRowSize(deDuplicatedSize);
   }
 
   private void sortTimestamps(final int startIndex, final int endIndex) {
     Arrays.sort(this.index, startIndex, endIndex, 
Comparator.comparingLong(tablet::getTimestamp));
   }
 
-  private void deduplicateTimestamps(final int startIndex, final int endIndex) 
{
+  private void deDuplicateTimestamps(final int startIndex, final int endIndex) 
{
     final long[] timestamps = tablet.getTimestamps();
     long lastTime = timestamps[index[startIndex]];
-    index[deduplicatedSize++] = index[startIndex];
     for (int i = startIndex + 1; i < endIndex; i++) {
       if (lastTime != (lastTime = timestamps[index[i]])) {
-        index[deduplicatedSize++] = index[i];
+        deDuplicatedIndex[deDuplicatedSize++] = i - 1;
       }
     }
+    deDuplicatedIndex[deDuplicatedSize++] = endIndex - 1;
   }
 
-  /** Sort by time only, and remove only rows with the same DeviceID and time. 
*/
-  public void sortAndDeduplicateByTimestampIfNecessary() {
+  /** Sort by time only. */
+  public void sortByTimestampIfNecessary() {
     if (tablet == null || tablet.getRowSize() == 0) {
       return;
     }
@@ -189,15 +163,12 @@ public class PipeTableModelTabletEventSorter {
       final long previousTimestamp = timestamps[i - 1];
 
       if (currentTimestamp < previousTimestamp) {
-        isUnSorted = true;
+        isSorted = false;
         break;
       }
-      if (currentTimestamp == previousTimestamp) {
-        hasDuplicates = true;
-      }
     }
 
-    if (!isUnSorted && !hasDuplicates) {
+    if (isSorted) {
       return;
     }
 
@@ -206,68 +177,15 @@ public class PipeTableModelTabletEventSorter {
       index[i] = i;
     }
 
-    if (isUnSorted) {
+    if (!isSorted) {
       sortTimestamps();
-
-      // Do deduplicate anyway.
-      // isDeduplicated may be false positive when isUnSorted is true.
-      deduplicateTimestamps();
-      hasDuplicates = false;
     }
 
-    if (hasDuplicates) {
-      deduplicateTimestamps();
-    }
-
-    sortAndDeduplicateValuesAndBitMapsIgnoreTimestamp();
+    sortAndMayDeduplicateValuesAndBitMaps();
   }
 
   private void sortTimestamps() {
     Arrays.sort(this.index, Comparator.comparingLong(tablet::getTimestamp));
     Arrays.sort(tablet.getTimestamps(), 0, tablet.getRowSize());
   }
-
-  private void deduplicateTimestamps() {
-    deduplicatedSize = 1;
-    final long[] timestamps = tablet.getTimestamps();
-    long lastTime = timestamps[0];
-    IDeviceID deviceID = tablet.getDeviceID(index[0]);
-    final Set<IDeviceID> deviceIDSet = new HashSet<>();
-    deviceIDSet.add(deviceID);
-    for (int i = 1, size = tablet.getRowSize(); i < size; i++) {
-      deviceID = tablet.getDeviceID(index[i]);
-      if ((lastTime == (lastTime = timestamps[i]))) {
-        if (!deviceIDSet.contains(deviceID)) {
-          timestamps[deduplicatedSize] = lastTime;
-          index[deduplicatedSize++] = index[i];
-          deviceIDSet.add(deviceID);
-        }
-      } else {
-        timestamps[deduplicatedSize] = lastTime;
-        index[deduplicatedSize++] = index[i];
-        deviceIDSet.clear();
-        deviceIDSet.add(deviceID);
-      }
-    }
-  }
-
-  private void sortAndDeduplicateValuesAndBitMapsIgnoreTimestamp() {
-    int columnIndex = 0;
-    for (int i = 0, size = tablet.getSchemas().size(); i < size; i++) {
-      final IMeasurementSchema schema = tablet.getSchemas().get(i);
-      if (schema != null) {
-        tablet.getValues()[columnIndex] =
-            PipeTabletEventSorter.reorderValueList(
-                deduplicatedSize, tablet.getValues()[columnIndex], 
schema.getType(), index);
-        if (tablet.getBitMaps() != null && tablet.getBitMaps()[columnIndex] != 
null) {
-          tablet.getBitMaps()[columnIndex] =
-              PipeTabletEventSorter.reorderBitMap(
-                  deduplicatedSize, tablet.getBitMaps()[columnIndex], index);
-        }
-        columnIndex++;
-      }
-    }
-
-    tablet.setRowSize(deduplicatedSize);
-  }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/util/sorter/PipeTabletEventSorter.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/util/sorter/PipeTabletEventSorter.java
index f3756b2a46c..3ea470de38a 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/util/sorter/PipeTabletEventSorter.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/util/sorter/PipeTabletEventSorter.java
@@ -23,83 +23,153 @@ import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.utils.Binary;
 import org.apache.tsfile.utils.BitMap;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
+import org.apache.tsfile.write.record.Tablet;
+import org.apache.tsfile.write.schema.IMeasurementSchema;
 
 import java.time.LocalDate;
 
 public class PipeTabletEventSorter {
 
-  public static Object reorderValueList(
-      final int deduplicatedSize,
+  protected final Tablet tablet;
+
+  protected Integer[] index;
+  protected boolean isSorted = true;
+  protected boolean isDeDuplicated = true;
+  protected int[] deDuplicatedIndex;
+  protected int deDuplicatedSize;
+
+  public PipeTabletEventSorter(final Tablet tablet) {
+    this.tablet = tablet;
+  }
+
+  // Input:
+  // Col: [1, null, 3, 6, null]
+  // Timestamp: [2, 1, 1, 1, 1]
+  // Intermediate:
+  // Index: [1, 2, 3, 4, 0]
+  // SortedTimestamp: [1, 2]
+  // DeduplicateIndex: [3, 4]
+  // Output:
+  // (Used index: [2(3), 4(0)])
+  // Col: [6, 1]
+  protected void sortAndMayDeduplicateValuesAndBitMaps() {
+    int columnIndex = 0;
+    for (int i = 0, size = tablet.getSchemas().size(); i < size; i++) {
+      final IMeasurementSchema schema = tablet.getSchemas().get(i);
+      if (schema != null) {
+        BitMap deDuplicatedBitMap = null;
+        BitMap originalBitMap = null;
+        if (tablet.getBitMaps() != null && tablet.getBitMaps()[columnIndex] != 
null) {
+          originalBitMap = tablet.getBitMaps()[columnIndex];
+          deDuplicatedBitMap = new BitMap(originalBitMap.getSize());
+        }
+
+        tablet.getValues()[columnIndex] =
+            reorderValueListAndBitMap(
+                tablet.getValues()[columnIndex],
+                schema.getType(),
+                originalBitMap,
+                deDuplicatedBitMap);
+
+        if (tablet.getBitMaps() != null && tablet.getBitMaps()[columnIndex] != 
null) {
+          tablet.getBitMaps()[columnIndex] = deDuplicatedBitMap;
+        }
+        columnIndex++;
+      }
+    }
+  }
+
+  protected Object reorderValueListAndBitMap(
       final Object valueList,
       final TSDataType dataType,
-      final Integer[] index) {
+      final BitMap originalBitMap,
+      final BitMap deDuplicatedBitMap) {
     switch (dataType) {
       case BOOLEAN:
         final boolean[] boolValues = (boolean[]) valueList;
-        final boolean[] deduplicatedBoolValues = new 
boolean[boolValues.length];
-        for (int i = 0; i < deduplicatedSize; i++) {
-          deduplicatedBoolValues[i] = boolValues[index[i]];
+        final boolean[] deDuplicatedBoolValues = new 
boolean[boolValues.length];
+        for (int i = 0; i < deDuplicatedSize; i++) {
+          deDuplicatedBoolValues[i] =
+              boolValues[getLastNonnullIndex(i, originalBitMap, 
deDuplicatedBitMap)];
         }
-        return deduplicatedBoolValues;
+        return deDuplicatedBoolValues;
       case INT32:
         final int[] intValues = (int[]) valueList;
-        final int[] deduplicatedIntValues = new int[intValues.length];
-        for (int i = 0; i < deduplicatedSize; i++) {
-          deduplicatedIntValues[i] = intValues[index[i]];
+        final int[] deDuplicatedIntValues = new int[intValues.length];
+        for (int i = 0; i < deDuplicatedSize; i++) {
+          deDuplicatedIntValues[i] =
+              intValues[getLastNonnullIndex(i, originalBitMap, 
deDuplicatedBitMap)];
         }
-        return deduplicatedIntValues;
+        return deDuplicatedIntValues;
       case DATE:
         final LocalDate[] dateValues = (LocalDate[]) valueList;
-        final LocalDate[] deduplicatedDateValues = new 
LocalDate[dateValues.length];
-        for (int i = 0; i < deduplicatedSize; i++) {
-          deduplicatedDateValues[i] = dateValues[index[i]];
+        final LocalDate[] deDuplicatedDateValues = new 
LocalDate[dateValues.length];
+        for (int i = 0; i < deDuplicatedSize; i++) {
+          deDuplicatedDateValues[i] =
+              dateValues[getLastNonnullIndex(i, originalBitMap, 
deDuplicatedBitMap)];
         }
-        return deduplicatedDateValues;
+        return deDuplicatedDateValues;
       case INT64:
       case TIMESTAMP:
         final long[] longValues = (long[]) valueList;
-        final long[] deduplicatedLongValues = new long[longValues.length];
-        for (int i = 0; i < deduplicatedSize; i++) {
-          deduplicatedLongValues[i] = longValues[index[i]];
+        final long[] deDuplicatedLongValues = new long[longValues.length];
+        for (int i = 0; i < deDuplicatedSize; i++) {
+          deDuplicatedLongValues[i] =
+              longValues[getLastNonnullIndex(i, originalBitMap, 
deDuplicatedBitMap)];
         }
-        return deduplicatedLongValues;
+        return deDuplicatedLongValues;
       case FLOAT:
         final float[] floatValues = (float[]) valueList;
-        final float[] deduplicatedFloatValues = new float[floatValues.length];
-        for (int i = 0; i < deduplicatedSize; i++) {
-          deduplicatedFloatValues[i] = floatValues[index[i]];
+        final float[] deDuplicatedFloatValues = new float[floatValues.length];
+        for (int i = 0; i < deDuplicatedSize; i++) {
+          deDuplicatedFloatValues[i] =
+              floatValues[getLastNonnullIndex(i, originalBitMap, 
deDuplicatedBitMap)];
         }
-        return deduplicatedFloatValues;
+        return deDuplicatedFloatValues;
       case DOUBLE:
         final double[] doubleValues = (double[]) valueList;
-        final double[] deduplicatedDoubleValues = new 
double[doubleValues.length];
-        for (int i = 0; i < deduplicatedSize; i++) {
-          deduplicatedDoubleValues[i] = doubleValues[index[i]];
+        final double[] deDuplicatedDoubleValues = new 
double[doubleValues.length];
+        for (int i = 0; i < deDuplicatedSize; i++) {
+          deDuplicatedDoubleValues[i] =
+              doubleValues[getLastNonnullIndex(i, originalBitMap, 
deDuplicatedBitMap)];
         }
-        return deduplicatedDoubleValues;
+        return deDuplicatedDoubleValues;
       case TEXT:
       case BLOB:
       case STRING:
         final Binary[] binaryValues = (Binary[]) valueList;
-        final Binary[] deduplicatedBinaryValues = new 
Binary[binaryValues.length];
-        for (int i = 0; i < deduplicatedSize; i++) {
-          deduplicatedBinaryValues[i] = binaryValues[index[i]];
+        final Binary[] deDuplicatedBinaryValues = new 
Binary[binaryValues.length];
+        for (int i = 0; i < deDuplicatedSize; i++) {
+          deDuplicatedBinaryValues[i] =
+              binaryValues[getLastNonnullIndex(i, originalBitMap, 
deDuplicatedBitMap)];
         }
-        return deduplicatedBinaryValues;
+        return deDuplicatedBinaryValues;
       default:
         throw new UnSupportedDataTypeException(
             String.format("Data type %s is not supported.", dataType));
     }
   }
 
-  public static BitMap reorderBitMap(
-      final int deduplicatedSize, final BitMap bitMap, final Integer[] index) {
-    final BitMap deduplicatedBitMap = new BitMap(bitMap.getSize());
-    for (int i = 0; i < deduplicatedSize; i++) {
-      if (bitMap.isMarked(index[i])) {
-        deduplicatedBitMap.mark(i);
+  private int getLastNonnullIndex(
+      final int i, final BitMap originalBitMap, final BitMap 
deDuplicatedBitMap) {
+    if (deDuplicatedIndex == null) {
+      if (originalBitMap.isMarked(index[i])) {
+        deDuplicatedBitMap.mark(i);
+      }
+      return index[i];
+    }
+    if (originalBitMap == null) {
+      return index[deDuplicatedIndex[i]];
+    }
+    int lastNonnullIndex = deDuplicatedIndex[i];
+    int lastIndex = i > 0 ? deDuplicatedIndex[i - 1] : -1;
+    while (originalBitMap.isMarked(index[lastNonnullIndex])) {
+      --lastNonnullIndex;
+      if (lastNonnullIndex == lastIndex) {
+        deDuplicatedBitMap.mark(i);
+        return index[lastNonnullIndex + 1];
       }
     }
-    return deduplicatedBitMap;
+    return index[lastNonnullIndex];
   }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/util/sorter/PipeTreeModelTabletEventSorter.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/util/sorter/PipeTreeModelTabletEventSorter.java
index cbde0583639..a1b1923543b 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/util/sorter/PipeTreeModelTabletEventSorter.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/util/sorter/PipeTreeModelTabletEventSorter.java
@@ -20,24 +20,15 @@
 package org.apache.iotdb.db.pipe.connector.util.sorter;
 
 import org.apache.tsfile.write.record.Tablet;
-import org.apache.tsfile.write.schema.IMeasurementSchema;
 
 import java.util.Arrays;
 import java.util.Comparator;
 
-public class PipeTreeModelTabletEventSorter {
-
-  private final Tablet tablet;
-
-  private boolean isSorted = true;
-  private boolean isDeduplicated = true;
-
-  private Integer[] index;
-  private int deduplicatedSize;
+public class PipeTreeModelTabletEventSorter extends PipeTabletEventSorter {
 
   public PipeTreeModelTabletEventSorter(final Tablet tablet) {
-    this.tablet = tablet;
-    deduplicatedSize = tablet == null ? 0 : tablet.getRowSize();
+    super(tablet);
+    deDuplicatedSize = tablet == null ? 0 : tablet.getRowSize();
   }
 
   public void deduplicateAndSortTimestampsIfNecessary() {
@@ -55,15 +46,16 @@ public class PipeTreeModelTabletEventSorter {
         break;
       }
       if (currentTimestamp == previousTimestamp) {
-        isDeduplicated = false;
+        isDeDuplicated = false;
       }
     }
 
-    if (isSorted && isDeduplicated) {
+    if (isSorted && isDeDuplicated) {
       return;
     }
 
     index = new Integer[tablet.getRowSize()];
+    deDuplicatedIndex = new int[tablet.getRowSize()];
     for (int i = 0, size = tablet.getRowSize(); i < size; i++) {
       index[i] = i;
     }
@@ -71,53 +63,39 @@ public class PipeTreeModelTabletEventSorter {
     if (!isSorted) {
       sortTimestamps();
 
-      // Do deduplicate anyway.
-      // isDeduplicated may be false positive when isSorted is false.
+      // Do deDuplicated anyway.
+      // isDeDuplicated may be false positive when isSorted is false.
       deduplicateTimestamps();
-      isDeduplicated = true;
+      isDeDuplicated = true;
     }
 
-    if (!isDeduplicated) {
+    if (!isDeDuplicated) {
       deduplicateTimestamps();
     }
 
-    sortAndDeduplicateValuesAndBitMaps();
+    sortAndMayDeduplicateValuesAndBitMaps();
   }
 
   private void sortTimestamps() {
+    // Index is sorted stably because it is Integer[]
     Arrays.sort(index, Comparator.comparingLong(tablet::getTimestamp));
     Arrays.sort(tablet.getTimestamps(), 0, tablet.getRowSize());
   }
 
   private void deduplicateTimestamps() {
-    deduplicatedSize = 1;
+    deDuplicatedSize = 0;
     long[] timestamps = tablet.getTimestamps();
     for (int i = 1, size = tablet.getRowSize(); i < size; i++) {
       if (timestamps[i] != timestamps[i - 1]) {
-        index[deduplicatedSize] = index[i];
-        timestamps[deduplicatedSize] = timestamps[i];
+        deDuplicatedIndex[deDuplicatedSize] = i - 1;
+        timestamps[deDuplicatedSize] = timestamps[i - 1];
 
-        ++deduplicatedSize;
+        ++deDuplicatedSize;
       }
     }
-    tablet.setRowSize(deduplicatedSize);
-  }
 
-  private void sortAndDeduplicateValuesAndBitMaps() {
-    int columnIndex = 0;
-    for (int i = 0, size = tablet.getSchemas().size(); i < size; i++) {
-      final IMeasurementSchema schema = tablet.getSchemas().get(i);
-      if (schema != null) {
-        tablet.getValues()[columnIndex] =
-            PipeTabletEventSorter.reorderValueList(
-                deduplicatedSize, tablet.getValues()[columnIndex], 
schema.getType(), index);
-        if (tablet.getBitMaps() != null && tablet.getBitMaps()[columnIndex] != 
null) {
-          tablet.getBitMaps()[columnIndex] =
-              PipeTabletEventSorter.reorderBitMap(
-                  deduplicatedSize, tablet.getBitMaps()[columnIndex], index);
-        }
-        columnIndex++;
-      }
-    }
+    deDuplicatedIndex[deDuplicatedSize] = tablet.getRowSize() - 1;
+    timestamps[deDuplicatedSize] = timestamps[tablet.getRowSize() - 1];
+    tablet.setRowSize(++deDuplicatedSize);
   }
 }
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/connector/PipeTabletEventSorterTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/connector/PipeTabletEventSorterTest.java
index 92a4d33fcef..2745cf5d3de 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/connector/PipeTabletEventSorterTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/connector/PipeTabletEventSorterTest.java
@@ -103,23 +103,26 @@ public class PipeTabletEventSorterTest {
 
   @Test
   public void testTreeModelDeduplicate() {
-    List<IMeasurementSchema> schemaList = new ArrayList<>();
+    final List<IMeasurementSchema> schemaList = new ArrayList<>();
     schemaList.add(new MeasurementSchema("s1", TSDataType.INT64));
     schemaList.add(new MeasurementSchema("s2", TSDataType.INT64));
     schemaList.add(new MeasurementSchema("s3", TSDataType.INT64));
 
-    Tablet tablet = new Tablet("root.sg.device", schemaList, 10);
+    final Tablet tablet = new Tablet("root.sg.device", schemaList, 10);
 
-    long timestamp = 300;
+    final long timestamp = 300;
     for (long i = 0; i < 10; i++) {
-      int rowIndex = tablet.getRowSize();
+      final int rowIndex = tablet.getRowSize();
       tablet.addTimestamp(rowIndex, timestamp);
       for (int s = 0; s < 3; s++) {
-        tablet.addValue(schemaList.get(s).getMeasurementName(), rowIndex, 
timestamp);
+        tablet.addValue(
+            schemaList.get(s).getMeasurementName(),
+            rowIndex,
+            (i + s) % 3 != 0 ? timestamp + i : null);
       }
     }
 
-    Set<Integer> indices = new HashSet<>();
+    final Set<Integer> indices = new HashSet<>();
     for (int i = 0; i < 10; i++) {
       indices.add((int) tablet.getTimestamp(i));
     }
@@ -133,16 +136,9 @@ public class PipeTabletEventSorterTest {
     Assert.assertEquals(indices.size(), tablet.getRowSize());
 
     final long[] timestamps = Arrays.copyOfRange(tablet.getTimestamps(), 0, 
tablet.getRowSize());
-    for (int i = 0; i < 3; ++i) {
-      Assert.assertArrayEquals(
-          timestamps, Arrays.copyOfRange((long[]) tablet.getValues()[0], 0, 
tablet.getRowSize()));
-    }
-
-    for (int i = 1; i < tablet.getRowSize(); ++i) {
-      Assert.assertTrue(timestamps[i] > timestamps[i - 1]);
-      for (int j = 0; j < 3; ++j) {
-        Assert.assertTrue((long) tablet.getValue(i, j) > (long) 
tablet.getValue(i - 1, j));
-      }
+    Assert.assertEquals(timestamps[0] + 8, ((long[]) 
tablet.getValues()[0])[0]);
+    for (int i = 1; i < 3; ++i) {
+      Assert.assertEquals(timestamps[0] + 9, ((long[]) 
tablet.getValues()[i])[0]);
     }
   }
 
@@ -163,7 +159,7 @@ public class PipeTabletEventSorterTest {
       }
 
       rowIndex = tablet.getRowSize();
-      tablet.addTimestamp(rowIndex, (long) rowIndex);
+      tablet.addTimestamp(rowIndex, rowIndex);
       for (int s = 0; s < 3; s++) {
         tablet.addValue(schemaList.get(s).getMeasurementName(), rowIndex, 
(long) rowIndex);
       }
@@ -230,19 +226,9 @@ public class PipeTabletEventSorterTest {
     doTableModelTest(false, true);
   }
 
-  @Test
-  public void testTableModelDeduplicateAndSort1() {
-    doTableModelTest1(true, true);
-  }
-
-  @Test
-  public void testTableModelDeduplicate1() {
-    doTableModelTest1(true, false);
-  }
-
   @Test
   public void testTableModelSort1() {
-    doTableModelTest1(false, true);
+    doTableModelTest1();
   }
 
   public void doTableModelTest(final boolean hasDuplicates, final boolean 
isUnSorted) {
@@ -268,9 +254,9 @@ public class PipeTabletEventSorterTest {
     }
   }
 
-  public void doTableModelTest1(final boolean hasDuplicates, final boolean 
isUnSorted) {
-    final Tablet tablet = generateTablet("test", 10, hasDuplicates, 
isUnSorted);
-    new 
PipeTableModelTabletEventSorter(tablet).sortAndDeduplicateByTimestampIfNecessary();
+  public void doTableModelTest1() {
+    final Tablet tablet = generateTablet("test", 10, false, true);
+    new PipeTableModelTabletEventSorter(tablet).sortByTimestampIfNecessary();
     long[] timestamps = tablet.getTimestamps();
     for (int i = 1; i < tablet.getRowSize(); i++) {
       long time = timestamps[i];
@@ -343,16 +329,24 @@ public class PipeTabletEventSorterTest {
           tablet.addTimestamp(rowIndex, value);
           tablet.addValue(
               "s0", rowIndex, new 
Binary(String.valueOf(row).getBytes(StandardCharsets.UTF_8)));
-          tablet.addValue("s1", rowIndex, value);
-          tablet.addValue("s2", rowIndex, (value * 1.0f));
+          tablet.addValue("s1", rowIndex, hasDuplicates && j == 0 ? null : 
value);
+          tablet.addValue("s2", rowIndex, hasDuplicates && j == 0 ? null : 
(value * 1.0f));
           tablet.addValue(
-              "s3", rowIndex, new 
Binary(String.valueOf(value).getBytes(StandardCharsets.UTF_8)));
-          tablet.addValue("s4", rowIndex, value);
-          tablet.addValue("s5", rowIndex, (int) value);
-          tablet.addValue("s6", rowIndex, value * 0.1);
-          tablet.addValue("s7", rowIndex, getDate((int) value));
+              "s3",
+              rowIndex,
+              hasDuplicates && j == 0
+                  ? null
+                  : new 
Binary(String.valueOf(value).getBytes(StandardCharsets.UTF_8)));
+          tablet.addValue("s4", rowIndex, hasDuplicates && j == 0 ? null : 
value);
+          tablet.addValue("s5", rowIndex, hasDuplicates && j == 0 ? null : 
(int) value);
+          tablet.addValue("s6", rowIndex, hasDuplicates && j == 0 ? null : 
value * 0.1);
+          tablet.addValue("s7", rowIndex, hasDuplicates && j == 0 ? null : 
getDate((int) value));
           tablet.addValue(
-              "s8", rowIndex, new 
Binary(String.valueOf(value).getBytes(StandardCharsets.UTF_8)));
+              "s8",
+              rowIndex,
+              hasDuplicates && j == 0
+                  ? null
+                  : new 
Binary(String.valueOf(value).getBytes(StandardCharsets.UTF_8)));
           rowIndex++;
           tablet.setRowSize(rowIndex);
           if (!hasDuplicates) {

Reply via email to