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

jiangtian 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 854f1dc60f8 Fix NPE when encoding continous nulls in Tablets (#16107)
854f1dc60f8 is described below

commit 854f1dc60f8d69d1942c3072c7d1eaf89da637fd
Author: Jiang Tian <[email protected]>
AuthorDate: Wed Aug 6 11:26:35 2025 +0800

    Fix NPE when encoding continous nulls in Tablets (#16107)
---
 .../iotdb/session/it/IoTDBSessionCompressedIT.java | 177 +++++++++------------
 .../apache/iotdb/session/util/SessionUtils.java    |  82 +++-------
 2 files changed, 98 insertions(+), 161 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/session/it/IoTDBSessionCompressedIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/session/it/IoTDBSessionCompressedIT.java
index 058ebb8e7b0..c82bdfcdd07 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/session/it/IoTDBSessionCompressedIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/session/it/IoTDBSessionCompressedIT.java
@@ -27,6 +27,7 @@ import org.apache.iotdb.rpc.IoTDBConnectionException;
 import org.apache.iotdb.rpc.StatementExecutionException;
 import org.apache.iotdb.session.TableSessionBuilder;
 
+import org.apache.tsfile.enums.ColumnCategory;
 import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.file.metadata.enums.CompressionType;
 import org.apache.tsfile.file.metadata.enums.TSEncoding;
@@ -43,16 +44,13 @@ import org.junit.Test;
 
 import java.time.LocalDate;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
 
 public class IoTDBSessionCompressedIT {
 
-  private static ITableSession session1;
-  private static ITableSession session2;
-  private static ITableSession session3;
-  private static ITableSession session4;
-  private static ITableSession session5;
+  private static List<ITableSession> sessions;
 
   @BeforeClass
   public static void setUpClass() throws IoTDBConnectionException {
@@ -62,8 +60,7 @@ public class IoTDBSessionCompressedIT {
         EnvFactory.getEnv().getDataNodeWrapperList().stream()
             .map(DataNodeWrapper::getIpAndPortString)
             .collect(Collectors.toList());
-    //    List<String> nodeUrls = Collections.singletonList("127.0.0.1:6667");
-    session1 =
+    ITableSession session1 =
         new TableSessionBuilder()
             .nodeUrls(nodeUrls)
             
.username(CommonDescriptor.getInstance().getConfig().getAdminName())
@@ -83,7 +80,7 @@ public class IoTDBSessionCompressedIT {
             .withDateEncoding(TSEncoding.PLAIN)
             .withTimeStampEncoding(TSEncoding.PLAIN)
             .build();
-    session2 =
+    ITableSession session2 =
         new TableSessionBuilder()
             .nodeUrls(nodeUrls)
             
.username(CommonDescriptor.getInstance().getConfig().getAdminName())
@@ -103,7 +100,7 @@ public class IoTDBSessionCompressedIT {
             .withDateEncoding(TSEncoding.PLAIN)
             .withTimeStampEncoding(TSEncoding.SPRINTZ)
             .build();
-    session3 =
+    ITableSession session3 =
         new TableSessionBuilder()
             .nodeUrls(nodeUrls)
             
.username(CommonDescriptor.getInstance().getConfig().getAdminName())
@@ -123,7 +120,7 @@ public class IoTDBSessionCompressedIT {
             .withDateEncoding(TSEncoding.RLE)
             .withTimeStampEncoding(TSEncoding.RLE)
             .build();
-    session4 =
+    ITableSession session4 =
         new TableSessionBuilder()
             .nodeUrls(nodeUrls)
             
.username(CommonDescriptor.getInstance().getConfig().getAdminName())
@@ -143,7 +140,7 @@ public class IoTDBSessionCompressedIT {
             .withDateEncoding(TSEncoding.RLE)
             .withTimeStampEncoding(TSEncoding.ZIGZAG)
             .build();
-    session5 =
+    ITableSession session5 =
         new TableSessionBuilder()
             .nodeUrls(nodeUrls)
             
.username(CommonDescriptor.getInstance().getConfig().getAdminName())
@@ -153,26 +150,55 @@ public class IoTDBSessionCompressedIT {
             .enableAutoFetch(false)
             .enableCompaction(false)
             .build();
+    sessions = Arrays.asList(session1, session2, session3, session4, session5);
   }
 
   @AfterClass
   public static void tearDownClass() throws IoTDBConnectionException {
-    if (session1 != null) {
-      session1.close();
+    for (ITableSession session : sessions) {
+      session.close();
     }
-    if (session2 != null) {
-      session2.close();
+    EnvFactory.getEnv().cleanClusterEnvironment();
+  }
+
+  @Test
+  public void testAllNullColumn() throws IoTDBConnectionException, 
StatementExecutionException {
+    Tablet tablet =
+        new Tablet(
+            "t1",
+            Arrays.asList("tag1", "attr1", "s1"),
+            Arrays.asList(TSDataType.STRING, TSDataType.STRING, 
TSDataType.DATE),
+            Arrays.asList(ColumnCategory.TAG, ColumnCategory.ATTRIBUTE, 
ColumnCategory.FIELD),
+            100);
+    for (int i = 0; i < 10; i++) {
+      tablet.addTimestamp(i, i);
+      tablet.addValue("tag1", i, "d1");
+      tablet.addValue("attr1", i, "blue");
     }
-    if (session3 != null) {
-      session3.close();
+    for (ITableSession session : sessions) {
+      session.executeNonQueryStatement("CREATE DATABASE IF NOT EXISTS test");
+      session.executeNonQueryStatement("USE test");
+      session.insert(tablet);
     }
-    if (session4 != null) {
-      session4.close();
+  }
+
+  @Test
+  public void testAllNull() throws IoTDBConnectionException, 
StatementExecutionException {
+    Tablet tablet =
+        new Tablet(
+            "t1",
+            Arrays.asList("tag1", "attr1", "s1"),
+            Arrays.asList(TSDataType.STRING, TSDataType.STRING, 
TSDataType.DATE),
+            Arrays.asList(ColumnCategory.TAG, ColumnCategory.ATTRIBUTE, 
ColumnCategory.FIELD),
+            100);
+    for (int i = 0; i < 10; i++) {
+      tablet.addTimestamp(i, i);
     }
-    if (session5 != null) {
-      session5.close();
+    for (ITableSession session : sessions) {
+      session.executeNonQueryStatement("CREATE DATABASE IF NOT EXISTS test");
+      session.executeNonQueryStatement("USE test");
+      session.insert(tablet);
     }
-    EnvFactory.getEnv().cleanClusterEnvironment();
   }
 
   @Test
@@ -273,91 +299,36 @@ public class IoTDBSessionCompressedIT {
     String tableName = "table_13";
     Tablet tablet = new Tablet(tableName, schemas, timestamp, values, 
partBitMap, 4);
 
-    session1.executeNonQueryStatement("create database IF NOT EXISTS 
dbTest_0");
-    session1.executeNonQueryStatement("use dbTest_0");
-    session2.executeNonQueryStatement("use dbTest_0");
-    session3.executeNonQueryStatement("use dbTest_0");
-    session4.executeNonQueryStatement("use dbTest_0");
-    session5.executeNonQueryStatement("use dbTest_0");
+    sessions.get(0).executeNonQueryStatement("create database IF NOT EXISTS 
dbTest_0");
+    for (ITableSession session : sessions) {
+      session.executeNonQueryStatement("use dbTest_0");
+    }
 
     // 1. insert
-    session1.insert(tablet);
-    session2.insert(tablet);
-    session3.insert(tablet);
-    session4.insert(tablet);
-    session5.insert(tablet);
+    for (ITableSession session : sessions) {
+      session.insert(tablet);
+    }
 
     // 2. assert
-    SessionDataSet sessionDataSet1 =
-        session1.executeQueryStatement("select * from dbTest_0." + tableName);
-    SessionDataSet sessionDataSet2 =
-        session2.executeQueryStatement("select * from dbTest_0." + tableName);
-    SessionDataSet sessionDataSet3 =
-        session3.executeQueryStatement("select * from dbTest_0." + tableName);
-    SessionDataSet sessionDataSet4 =
-        session4.executeQueryStatement("select * from dbTest_0." + tableName);
-    SessionDataSet sessionDataSet5 =
-        session5.executeQueryStatement("select * from dbTest_0." + tableName);
-
-    if (sessionDataSet1.hasNext()) {
-      RowRecord next = sessionDataSet1.next();
-      Assert.assertEquals(3L, next.getFields().get(0).getLongV());
-      Assert.assertEquals(1, next.getFields().get(1).getIntV());
-      Assert.assertEquals(1L, next.getFields().get(2).getLongV());
-      Assert.assertEquals(1.1f, next.getFields().get(3).getFloatV(), 0.01);
-      Assert.assertEquals(0.707, next.getFields().get(4).getDoubleV(), 0.01);
-      Assert.assertEquals(new Binary(new byte[] {(byte) 32}), 
next.getFields().get(5).getBinaryV());
-      Assert.assertEquals(true, next.getFields().get(6).getBoolV());
-      Assert.assertEquals(new Binary(new byte[] {(byte) 32}), 
next.getFields().get(7).getBinaryV());
-      Assert.assertEquals(new Binary(new byte[] {(byte) 32}), 
next.getFields().get(8).getBinaryV());
-    }
-    if (sessionDataSet2.hasNext()) {
-      RowRecord next = sessionDataSet2.next();
-      Assert.assertEquals(3L, next.getFields().get(0).getLongV());
-      Assert.assertEquals(1, next.getFields().get(1).getIntV());
-      Assert.assertEquals(1L, next.getFields().get(2).getLongV());
-      Assert.assertEquals(1.1f, next.getFields().get(3).getFloatV(), 0.01);
-      Assert.assertEquals(0.707, next.getFields().get(4).getDoubleV(), 0.01);
-      Assert.assertEquals(new Binary(new byte[] {(byte) 32}), 
next.getFields().get(5).getBinaryV());
-      Assert.assertEquals(true, next.getFields().get(6).getBoolV());
-      Assert.assertEquals(new Binary(new byte[] {(byte) 32}), 
next.getFields().get(7).getBinaryV());
-      Assert.assertEquals(new Binary(new byte[] {(byte) 32}), 
next.getFields().get(8).getBinaryV());
-    }
-    if (sessionDataSet3.hasNext()) {
-      RowRecord next = sessionDataSet3.next();
-      Assert.assertEquals(3L, next.getFields().get(0).getLongV());
-      Assert.assertEquals(1, next.getFields().get(1).getIntV());
-      Assert.assertEquals(1L, next.getFields().get(2).getLongV());
-      Assert.assertEquals(1.1f, next.getFields().get(3).getFloatV(), 0.01);
-      Assert.assertEquals(0.707, next.getFields().get(4).getDoubleV(), 0.01);
-      Assert.assertEquals(new Binary(new byte[] {(byte) 32}), 
next.getFields().get(5).getBinaryV());
-      Assert.assertEquals(true, next.getFields().get(6).getBoolV());
-      Assert.assertEquals(new Binary(new byte[] {(byte) 32}), 
next.getFields().get(7).getBinaryV());
-      Assert.assertEquals(new Binary(new byte[] {(byte) 32}), 
next.getFields().get(8).getBinaryV());
-    }
-    if (sessionDataSet4.hasNext()) {
-      RowRecord next = sessionDataSet4.next();
-      Assert.assertEquals(3L, next.getFields().get(0).getLongV());
-      Assert.assertEquals(1, next.getFields().get(1).getIntV());
-      Assert.assertEquals(1L, next.getFields().get(2).getLongV());
-      Assert.assertEquals(1.1f, next.getFields().get(3).getFloatV(), 0.01);
-      Assert.assertEquals(0.707, next.getFields().get(4).getDoubleV(), 0.01);
-      Assert.assertEquals(new Binary(new byte[] {(byte) 32}), 
next.getFields().get(5).getBinaryV());
-      Assert.assertEquals(true, next.getFields().get(6).getBoolV());
-      Assert.assertEquals(new Binary(new byte[] {(byte) 32}), 
next.getFields().get(7).getBinaryV());
-      Assert.assertEquals(new Binary(new byte[] {(byte) 32}), 
next.getFields().get(8).getBinaryV());
-    }
-    if (sessionDataSet5.hasNext()) {
-      RowRecord next = sessionDataSet5.next();
-      Assert.assertEquals(3L, next.getFields().get(0).getLongV());
-      Assert.assertEquals(1, next.getFields().get(1).getIntV());
-      Assert.assertEquals(1L, next.getFields().get(2).getLongV());
-      Assert.assertEquals(1.1f, next.getFields().get(3).getFloatV(), 0.01);
-      Assert.assertEquals(0.707, next.getFields().get(4).getDoubleV(), 0.01);
-      Assert.assertEquals(new Binary(new byte[] {(byte) 32}), 
next.getFields().get(5).getBinaryV());
-      Assert.assertEquals(true, next.getFields().get(6).getBoolV());
-      Assert.assertEquals(new Binary(new byte[] {(byte) 32}), 
next.getFields().get(7).getBinaryV());
-      Assert.assertEquals(new Binary(new byte[] {(byte) 32}), 
next.getFields().get(8).getBinaryV());
+    for (ITableSession session : sessions) {
+      try (SessionDataSet sessionDataSet =
+          session.executeQueryStatement("select * from dbTest_0." + 
tableName)) {
+        if (sessionDataSet.hasNext()) {
+          RowRecord next = sessionDataSet.next();
+          Assert.assertEquals(3L, next.getFields().get(0).getLongV());
+          Assert.assertEquals(1, next.getFields().get(1).getIntV());
+          Assert.assertEquals(1L, next.getFields().get(2).getLongV());
+          Assert.assertEquals(1.1f, next.getFields().get(3).getFloatV(), 0.01);
+          Assert.assertEquals(0.707, next.getFields().get(4).getDoubleV(), 
0.01);
+          Assert.assertEquals(
+              new Binary(new byte[] {(byte) 32}), 
next.getFields().get(5).getBinaryV());
+          Assert.assertTrue(next.getFields().get(6).getBoolV());
+          Assert.assertEquals(
+              new Binary(new byte[] {(byte) 32}), 
next.getFields().get(7).getBinaryV());
+          Assert.assertEquals(
+              new Binary(new byte[] {(byte) 32}), 
next.getFields().get(8).getBinaryV());
+        }
+      }
     }
   }
 }
diff --git 
a/iotdb-client/session/src/main/java/org/apache/iotdb/session/util/SessionUtils.java
 
b/iotdb-client/session/src/main/java/org/apache/iotdb/session/util/SessionUtils.java
index 6359a60f890..dd30496090c 100644
--- 
a/iotdb-client/session/src/main/java/org/apache/iotdb/session/util/SessionUtils.java
+++ 
b/iotdb-client/session/src/main/java/org/apache/iotdb/session/util/SessionUtils.java
@@ -363,6 +363,7 @@ public class SessionUtils {
     }
   }
 
+  @SuppressWarnings({"java:S3776", "java:S6541"})
   public static void encodeValue(
       TSDataType dataType,
       Tablet tablet,
@@ -373,111 +374,76 @@ public class SessionUtils {
     switch (dataType) {
       case INT32:
         int[] intValues = (int[]) tablet.getValues()[i];
+        int lastNonNullIntValue = 0;
         for (int index = 0; index < tablet.getRowSize(); index++) {
           if (!tablet.isNull(index, i)) {
-            encoder.encode(intValues[index], outputStream);
-          } else {
-            // use the previous value as the placeholder of nulls to increase 
encoding performance
-            if (index == 0) {
-              encoder.encode(intValues[index], outputStream);
-            } else {
-              encoder.encode(intValues[index - 1], outputStream);
-            }
+            lastNonNullIntValue = intValues[index];
           }
+          encoder.encode(lastNonNullIntValue, outputStream);
         }
         break;
       case INT64:
       case TIMESTAMP:
         long[] longValues = (long[]) tablet.getValues()[i];
+        long lastNonNullLongValue = 0;
         for (int index = 0; index < tablet.getRowSize(); index++) {
           if (!tablet.isNull(index, i)) {
-            encoder.encode(longValues[index], outputStream);
-          } else {
-            // use the previous value as the placeholder of nulls to increase 
encoding performance
-            if (index == 0) {
-              encoder.encode(longValues[index], outputStream);
-            } else {
-              encoder.encode(longValues[index - 1], outputStream);
-            }
+            lastNonNullLongValue = longValues[index];
           }
+          encoder.encode(lastNonNullLongValue, outputStream);
         }
         break;
       case FLOAT:
         float[] floatValues = (float[]) tablet.getValues()[i];
+        float lastNonNullFloatValue = 0.0f;
         for (int index = 0; index < tablet.getRowSize(); index++) {
           if (!tablet.isNull(index, i)) {
-            encoder.encode(floatValues[index], outputStream);
-          } else {
-            // use the previous value as the placeholder of nulls to increase 
encoding performance
-            if (index == 0) {
-              encoder.encode(floatValues[index], outputStream);
-            } else {
-              encoder.encode(floatValues[index - 1], outputStream);
-            }
+            lastNonNullFloatValue = floatValues[index];
           }
+          encoder.encode(lastNonNullFloatValue, outputStream);
         }
         break;
       case DOUBLE:
         double[] doubleValues = (double[]) tablet.getValues()[i];
+        double lastNonNullDoubleValue = 0.0;
         for (int index = 0; index < tablet.getRowSize(); index++) {
           if (!tablet.isNull(index, i)) {
-            encoder.encode(doubleValues[index], outputStream);
-          } else {
-            // use the previous value as the placeholder of nulls to increase 
encoding performance
-            if (index == 0) {
-              encoder.encode(doubleValues[index], outputStream);
-            } else {
-              encoder.encode(doubleValues[index - 1], outputStream);
-            }
+            lastNonNullDoubleValue = doubleValues[index];
           }
+          encoder.encode(lastNonNullDoubleValue, outputStream);
         }
         break;
       case BOOLEAN:
         boolean[] boolValues = (boolean[]) tablet.getValues()[i];
+        boolean lastNonNullBooleanValue = false;
         for (int index = 0; index < tablet.getRowSize(); index++) {
           if (!tablet.isNull(index, i)) {
-            encoder.encode(boolValues[index], outputStream);
-          } else {
-            // use the previous value as the placeholder of nulls to increase 
encoding performance
-            if (index == 0) {
-              encoder.encode(boolValues[index], outputStream);
-            } else {
-              encoder.encode(boolValues[index - 1], outputStream);
-            }
+            lastNonNullBooleanValue = boolValues[index];
           }
+          encoder.encode(lastNonNullBooleanValue, outputStream);
         }
         break;
       case TEXT:
       case STRING:
       case BLOB:
         Binary[] binaryValues = (Binary[]) tablet.getValues()[i];
+        Binary lastNonNullBinaryValue = Binary.EMPTY_VALUE;
         for (int index = 0; index < tablet.getRowSize(); index++) {
-          if (!tablet.isNull(index, i)) {
-            encoder.encode(binaryValues[index], outputStream);
-          } else {
-            // use the previous value as the placeholder of nulls to increase 
encoding performance
-            if (index == 0) {
-              encoder.encode(Binary.EMPTY_VALUE, outputStream);
-            } else {
-              encoder.encode(binaryValues[index - 1], outputStream);
-            }
+          if (!tablet.isNull(index, i) && binaryValues[index] != null) {
+            lastNonNullBinaryValue = binaryValues[index];
           }
+          encoder.encode(lastNonNullBinaryValue, outputStream);
         }
         break;
       case DATE:
         LocalDate[] dateValues = (LocalDate[]) tablet.getValues()[i];
+        int lastNonNullDateValue = EMPTY_DATE_INT;
         for (int index = 0; index < tablet.getRowSize(); index++) {
           if (!tablet.isNull(index, i)) {
-            
encoder.encode(DateUtils.parseDateExpressionToInt(dateValues[index]), 
outputStream);
-          } else {
-            // use the previous value as the placeholder of nulls to increase 
encoding performance
-            if (index == 0) {
-              encoder.encode(EMPTY_DATE_INT, outputStream);
-            } else {
-              encoder.encode(
-                  DateUtils.parseDateExpressionToInt(dateValues[index - 1]), 
outputStream);
-            }
+            lastNonNullDateValue = 
DateUtils.parseDateExpressionToInt(dateValues[index]);
           }
+          // use the previous value as the placeholder of nulls to increase 
encoding performance
+          encoder.encode(lastNonNullDateValue, outputStream);
         }
         break;
       default:

Reply via email to