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

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

commit 278d4e01d0a53d40fd3fb80a4beeee7622d5c30c
Author: Tian Jiang <[email protected]>
AuthorDate: Fri Sep 11 11:18:13 2026 +0800

    tmp
---
 .../it/session/IoTDBTableModelSessionIT.java       | 152 +++++++++++++++++++++
 .../org/apache/iotdb/isession/ITableSession.java   |   4 +
 .../dataregion/DataExecutionVisitor.java           |  12 ++
 .../resource/memory/InsertNodeMemoryEstimator.java |  14 ++
 .../protocol/thrift/impl/ClientRPCServiceImpl.java |  28 ++--
 .../impl/DataNodeInternalRPCServiceImpl.java       |   4 +-
 .../db/queryengine/plan/analyze/AnalyzeUtils.java  |   5 +
 .../plan/node/DataNodePlanNodeDeserializer.java    |   7 +
 .../planner/plan/node/write/InsertRowNode.java     |  24 +++-
 .../plan/relational/sql/ast/AstVisitor.java        |   4 +
 .../metrics/IoTDBInternalLocalReporter.java        |   4 +-
 .../db/storageengine/dataregion/DataRegion.java    |   7 +-
 .../memtable/AlignedWritableMemChunk.java          |  34 +++++
 .../dataregion/memtable/IMemTable.java             |   3 +-
 .../quotas/DataNodeThrottleQuotaManager.java       |   1 +
 .../rescon/quotas/DefaultOperationQuota.java       |  17 +++
 .../consensus/ConsensusLogToTabletConverter.java   |  41 +++---
 .../consensus/ConsensusPrefetchingQueue.java       |   5 +-
 .../thrift-datanode/src/main/thrift/client.thrift  |   2 +
 19 files changed, 320 insertions(+), 48 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/session/IoTDBTableModelSessionIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/session/IoTDBTableModelSessionIT.java
index b3014ff56ee..48a5a994dd5 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/session/IoTDBTableModelSessionIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/session/IoTDBTableModelSessionIT.java
@@ -21,6 +21,7 @@ package org.apache.iotdb.relational.it.session;
 
 import org.apache.iotdb.isession.ITableSession;
 import org.apache.iotdb.isession.SessionDataSet;
+import org.apache.iotdb.isession.pool.ITableSessionPool;
 import org.apache.iotdb.it.env.EnvFactory;
 import org.apache.iotdb.it.framework.IoTDBTestRunner;
 import org.apache.iotdb.itbase.category.TableClusterIT;
@@ -28,6 +29,8 @@ import 
org.apache.iotdb.itbase.category.TableLocalStandaloneIT;
 import org.apache.iotdb.rpc.IoTDBConnectionException;
 import org.apache.iotdb.rpc.StatementExecutionException;
 
+import org.apache.tsfile.enums.ColumnCategory;
+import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.read.common.RowRecord;
 import org.junit.After;
 import org.junit.Before;
@@ -35,8 +38,16 @@ import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
 
+import java.time.LocalDate;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
 import static 
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.showTablesColumnHeaders;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 @RunWith(IoTDBTestRunner.class)
@@ -53,6 +64,147 @@ public class IoTDBTableModelSessionIT {
     EnvFactory.getEnv().cleanClusterEnvironment();
   }
 
+  @Test
+  public void testNumericRowInsert() throws Exception {
+    // Exercise both table-session entry points, schema reordering and missing 
STRING columns.
+    // The same values must remain readable from MemTable and after flushing 
to TsFile.
+    try (ITableSession session = 
EnvFactory.getEnv().getTableSessionConnection()) {
+      session.executeNonQueryStatement("CREATE DATABASE numeric_rows");
+      session.executeNonQueryStatement("USE numeric_rows");
+      session.executeNonQueryStatement(
+          "CREATE TABLE readings(tag STRING TAG, attr STRING ATTRIBUTE, "
+              + "event_time TIMESTAMP FIELD, event_date DATE FIELD, d DOUBLE 
FIELD, "
+              + "f FLOAT FIELD, l INT64 FIELD, i INT32 FIELD, b BOOLEAN FIELD, 
missing STRING FIELD)");
+      List<String> names = Arrays.asList("b", "i", "l", "f", "d", 
"event_date", "event_time");
+      List<TSDataType> types =
+          Arrays.asList(
+              TSDataType.BOOLEAN,
+              TSDataType.INT32,
+              TSDataType.INT64,
+              TSDataType.FLOAT,
+              TSDataType.DOUBLE,
+              TSDataType.DATE,
+              TSDataType.TIMESTAMP);
+      long[] values = {
+        -1,
+        Integer.MIN_VALUE,
+        Long.MIN_VALUE,
+        Float.floatToRawIntBits(-1.25f),
+        Double.doubleToRawLongBits(2.5),
+        20260910,
+        1234
+      };
+      session.insert("readings", 1, names, types, values);
+      try (ITableSessionPool pool = EnvFactory.getEnv().getTableSessionPool(1, 
"numeric_rows");
+          ITableSession pooledSession = pool.getSession()) {
+        pooledSession.insert(
+            "readings",
+            2,
+            names,
+            types,
+            Collections.nCopies(types.size(), ColumnCategory.FIELD),
+            values);
+      }
+      assertNumericRows(session);
+      session.executeNonQueryStatement("FLUSH");
+      assertNumericRows(session);
+    }
+  }
+
+  @Test
+  public void testNumericRowInsertWithTags() throws Exception {
+    // Different TAGs at the same timestamp must stay separate, with identical 
behavior after flush.
+    try (ITableSession session = 
EnvFactory.getEnv().getTableSessionConnection()) {
+      session.executeNonQueryStatement("CREATE DATABASE numeric_tag_rows");
+      session.executeNonQueryStatement("USE numeric_tag_rows");
+      session.executeNonQueryStatement(
+          "CREATE TABLE readings(region STRING TAG, device STRING TAG, "
+              + "missing STRING TAG, v INT64 FIELD, enabled BOOLEAN FIELD)");
+      List<String> names = Arrays.asList("enabled", "device", "v", "region");
+      List<TSDataType> types =
+          Arrays.asList(TSDataType.BOOLEAN, TSDataType.STRING, 
TSDataType.INT64, TSDataType.STRING);
+      List<ColumnCategory> categories =
+          Arrays.asList(
+              ColumnCategory.FIELD, ColumnCategory.TAG, ColumnCategory.FIELD, 
ColumnCategory.TAG);
+      session.insert(
+          "readings",
+          1,
+          names,
+          types,
+          categories,
+          new long[] {1, 10},
+          new String[] {"设备甲😀", "华北"});
+      try (ITableSessionPool pool = EnvFactory.getEnv().getTableSessionPool(1, 
"numeric_tag_rows");
+          ITableSession pooled = pool.getSession()) {
+        pooled.insert(
+            "readings",
+            1,
+            names,
+            types,
+            categories,
+            new long[] {0, 20},
+            new String[] {"设备乙", "华北"});
+      }
+      session.insert(
+          "readings", 2, names, types, categories, new long[] {1, 30}, new 
String[] {"", null});
+      session.insert(
+          "readings", 2, names, types, categories, new long[] {0, 40}, new 
String[] {null, null});
+      assertNumericTagRows(session);
+      session.executeNonQueryStatement("FLUSH");
+      assertNumericTagRows(session);
+    }
+  }
+
+  private void assertNumericTagRows(ITableSession session) throws Exception {
+    String[] devices = {"设备甲😀", "设备乙", "", null};
+    try (SessionDataSet dataSet =
+        session.executeQueryStatement(
+            "SELECT region,device,missing,v,enabled FROM readings ORDER BY 
v")) {
+      for (int i = 0; i < devices.length; i++) {
+        assertTrue(dataSet.hasNext());
+        RowRecord row = dataSet.next();
+        if (i < 2) {
+          assertEquals("华北", row.getFields().get(0).getStringValue());
+        } else {
+          assertNull(row.getFields().get(0).getDataType());
+        }
+        if (devices[i] == null) {
+          assertNull(row.getFields().get(1).getDataType());
+        } else {
+          assertEquals(devices[i], row.getFields().get(1).getStringValue());
+        }
+        assertNull(row.getFields().get(2).getDataType());
+        assertEquals((i + 1) * 10, row.getFields().get(3).getLongV());
+        assertEquals(i % 2 == 0, row.getFields().get(4).getBoolV());
+      }
+      assertFalse(dataSet.hasNext());
+    }
+  }
+
+  private void assertNumericRows(ITableSession session) throws Exception {
+    try (SessionDataSet dataSet =
+        session.executeQueryStatement(
+            "SELECT time,b,i,l,f,d,event_date,event_time,tag,attr,missing FROM 
readings ORDER BY time")) {
+      for (long time = 1; time <= 2; time++) {
+        assertTrue(dataSet.hasNext());
+        RowRecord row = dataSet.next();
+        assertEquals(time, row.getFields().get(0).getLongV());
+        assertTrue(row.getFields().get(1).getBoolV());
+        assertEquals(Integer.MIN_VALUE, row.getFields().get(2).getIntV());
+        assertEquals(Long.MIN_VALUE, row.getFields().get(3).getLongV());
+        assertEquals(-1.25f, row.getFields().get(4).getFloatV(), 0);
+        assertEquals(2.5, row.getFields().get(5).getDoubleV(), 0);
+        assertEquals(TSDataType.DATE, row.getFields().get(6).getDataType());
+        assertEquals(LocalDate.of(2026, 9, 10), 
row.getFields().get(6).getDateV());
+        assertEquals(1234, row.getFields().get(7).getLongV());
+        for (int column = 8; column <= 10; column++) {
+          assertNull(row.getFields().get(column).getDataType());
+        }
+      }
+      assertFalse(dataSet.hasNext());
+    }
+  }
+
   @Test
   public void testUseDatabase() {
     final String[] table1Names = new String[] {"table1"};
diff --git 
a/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/ITableSession.java
 
b/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/ITableSession.java
index 75a37f83847..b5868e60ecd 100644
--- 
a/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/ITableSession.java
+++ 
b/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/ITableSession.java
@@ -22,8 +22,12 @@ package org.apache.iotdb.isession;
 import org.apache.iotdb.rpc.IoTDBConnectionException;
 import org.apache.iotdb.rpc.StatementExecutionException;
 
+import org.apache.tsfile.enums.ColumnCategory;
+import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.write.record.Tablet;
 
+import java.util.List;
+
 /**
  * This interface defines a session for interacting with IoTDB tables. It 
supports operations such
  * as data insertion, executing queries, and closing the session. 
Implementations of this interface
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/statemachine/dataregion/DataExecutionVisitor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/statemachine/dataregion/DataExecutionVisitor.java
index 96bbb6ccc16..d5d48dcf38a 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/statemachine/dataregion/DataExecutionVisitor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/statemachine/dataregion/DataExecutionVisitor.java
@@ -36,6 +36,7 @@ import org.apache.iotdb.db.i18n.DataNodePipeMessages;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.pipe.PipeEnrichedDeleteDataNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.pipe.PipeEnrichedInsertNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.AbstractInsertRowNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.DeleteDataNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertMultiTabletsNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertRowNode;
@@ -44,6 +45,7 @@ import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertRowsOf
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertTabletNode;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.ObjectNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalDeleteDataNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalInsertNumericRowNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalInsertRowNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalInsertRowsNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalInsertTabletNode;
@@ -76,6 +78,16 @@ public class DataExecutionVisitor implements 
PlanVisitor<TSStatus, DataRegion> {
 
   @Override
   public TSStatus visitInsertRow(InsertRowNode node, DataRegion dataRegion) {
+    return executeInsertRow(node, dataRegion);
+  }
+
+  @Override
+  public TSStatus visitRelationalInsertNumericRow(
+      RelationalInsertNumericRowNode node, DataRegion dataRegion) {
+    return executeInsertRow(node, dataRegion);
+  }
+
+  private TSStatus executeInsertRow(AbstractInsertRowNode node, DataRegion 
dataRegion) {
     try {
       dataRegion.insert(node);
       dataRegion.insertSeparatorToWAL(node);
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/memory/InsertNodeMemoryEstimator.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/memory/InsertNodeMemoryEstimator.java
index 394d67fd6be..1ea256b2e2b 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/memory/InsertNodeMemoryEstimator.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/memory/InsertNodeMemoryEstimator.java
@@ -36,6 +36,7 @@ import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertRowNod
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertRowsNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertRowsOfOneDeviceNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertTabletNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalInsertNumericRowNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalInsertRowNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalInsertRowsNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalInsertTabletNode;
@@ -71,6 +72,7 @@ public class InsertNodeMemoryEstimator {
   private static final String INSERT_MULTI_TABLETS_NODE = 
"InsertMultiTabletsNode";
   private static final String RELATIONAL_INSERT_ROWS_NODE = 
"RelationalInsertRowsNode";
   private static final String RELATIONAL_INSERT_ROW_NODE = 
"RelationalInsertRowNode";
+  private static final String RELATIONAL_INSERT_NUMERIC_ROW_NODE = 
"RelationalInsertNumericRowNode";
   private static final String RELATIONAL_INSERT_TABLET_NODE = 
"RelationalInsertTabletNode";
 
   private static final long NUM_BYTES_OBJECT_REF = 
RamUsageEstimator.NUM_BYTES_OBJECT_REF;
@@ -103,6 +105,9 @@ public class InsertNodeMemoryEstimator {
   private static final long RELATIONAL_INSERT_ROW_NODE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(RelationalInsertRowNode.class);
 
+  private static final long RELATIONAL_INSERT_NUMERIC_ROW_NODE_SIZE =
+      
RamUsageEstimator.shallowSizeOfInstance(RelationalInsertNumericRowNode.class);
+
   private static final long RELATIONAL_INSERT_TABLET_NODE_SIZE =
       
RamUsageEstimator.shallowSizeOfInstance(RelationalInsertTabletNode.class);
 
@@ -181,6 +186,8 @@ public class InsertNodeMemoryEstimator {
           return sizeOfRelationalInsertRowsNode((RelationalInsertRowsNode) 
insertNode);
         case RELATIONAL_INSERT_ROW_NODE:
           return sizeOfRelationalInsertRowNode((RelationalInsertRowNode) 
insertNode);
+        case RELATIONAL_INSERT_NUMERIC_ROW_NODE:
+          return 
sizeOfRelationalInsertNumericRowNode((RelationalInsertNumericRowNode) 
insertNode);
         case RELATIONAL_INSERT_TABLET_NODE:
           return sizeOfRelationalInsertTabletNode((RelationalInsertTabletNode) 
insertNode);
         default:
@@ -325,6 +332,13 @@ public class InsertNodeMemoryEstimator {
     return size;
   }
 
+  private static long sizeOfRelationalInsertNumericRowNode(
+      final RelationalInsertNumericRowNode node) {
+    return RELATIONAL_INSERT_NUMERIC_ROW_NODE_SIZE
+        + calculateFullInsertNodeSize(node)
+        + node.getNumericValues().ramBytesUsed();
+  }
+
   private static long sizeOfRelationalInsertTabletNode(final 
RelationalInsertTabletNode node) {
     return sizeOfRelationalInsertTabletNode(node, null);
   }
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 be0a00bddf8..99d40b3572b 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
@@ -112,8 +112,8 @@ import 
org.apache.iotdb.db.queryengine.plan.statement.Statement;
 import org.apache.iotdb.db.queryengine.plan.statement.StatementType;
 import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering;
 import org.apache.iotdb.db.queryengine.plan.statement.crud.DeleteDataStatement;
+import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertBaseStatement;
 import 
org.apache.iotdb.db.queryengine.plan.statement.crud.InsertMultiTabletsStatement;
-import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement;
 import 
org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowsOfOneDeviceStatement;
 import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowsStatement;
 import 
org.apache.iotdb.db.queryengine.plan.statement.crud.InsertTabletStatement;
@@ -2598,22 +2598,24 @@ public class ClientRPCServiceImpl implements 
IClientRPCServiceWithHandler {
             
PathUtils.checkIsLegalSingleMeasurementsAndUpdate(req.getMeasurements()));
       }
 
-      InsertRowStatement statement = StatementGenerator.createStatement(req);
+      InsertBaseStatement statement = StatementGenerator.createStatement(req);
       // return success when this statement is empty because server doesn't 
need to execute it
       if (statement.isEmpty()) {
         return RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS);
       }
 
-      // permission check
-      TSStatus status =
-          AuthorityChecker.checkAuthority(
-              statement,
-              new TreeAccessCheckContext(
-                  clientSession.getUserId(),
-                  clientSession.getUsername(),
-                  clientSession.getClientAddress()));
-      if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
-        return status;
+      // Table-model privileges are checked during relational analysis.
+      if (!statement.isWriteToTable()) {
+        TSStatus status =
+            AuthorityChecker.checkAuthority(
+                statement,
+                new TreeAccessCheckContext(
+                    clientSession.getUserId(),
+                    clientSession.getUsername(),
+                    clientSession.getClientAddress()));
+        if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+          return status;
+        }
       }
 
       quota =
@@ -3544,7 +3546,7 @@ public class ClientRPCServiceImpl implements 
IClientRPCServiceWithHandler {
       // check whether measurement is legal according to syntax convention
       
req.setMeasurements(PathUtils.checkIsLegalSingleMeasurementsAndUpdate(req.getMeasurements()));
 
-      final InsertRowStatement statement = 
StatementGenerator.createStatement(req);
+      final InsertBaseStatement statement = 
StatementGenerator.createStatement(req);
 
       // Permission check
       final TSStatus status =
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
index 34ef951296c..aa9ee3ab06c 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
@@ -191,7 +191,7 @@ import 
org.apache.iotdb.db.queryengine.plan.relational.planner.node.schema.Table
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DeleteDevice;
 import org.apache.iotdb.db.queryengine.plan.scheduler.load.LoadTsFileScheduler;
 import org.apache.iotdb.db.queryengine.plan.statement.component.WhereCondition;
-import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement;
+import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertBaseStatement;
 import org.apache.iotdb.db.queryengine.plan.statement.crud.QueryStatement;
 import org.apache.iotdb.db.schemaengine.SchemaEngine;
 import org.apache.iotdb.db.schemaengine.lease.MetadataLeaseManager;
@@ -3404,7 +3404,7 @@ public class DataNodeInternalRPCServiceImpl implements 
IDataNodeRPCService.Iface
   @Override
   public TSStatus insertRecord(TSInsertRecordReq req) throws TException {
     try {
-      InsertRowStatement statement = StatementGenerator.createStatement(req);
+      InsertBaseStatement statement = StatementGenerator.createStatement(req);
       SessionInfo sessionInfo =
           new SessionInfo(
               0,
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java
index c4c16d5af7e..9089a9d0242 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java
@@ -218,6 +218,11 @@ public class AnalyzeUtils {
         }
       }
       return computeDataPartitionParams(timePartitionSlotMap, 
getDatabaseName(statement, context));
+    } else if (statement instanceof InsertNumericRowStatement row) {
+      return computeDataPartitionParams(
+          Collections.singletonMap(
+              row.getTableDeviceID(), 
Collections.singleton(row.getTimePartitionSlot())),
+          getDatabaseName(statement, context));
     } else if (statement instanceof InsertRowStatement) {
       final InsertRowStatement insertRowStatement = (InsertRowStatement) 
statement;
       return computeDataPartitionParams(
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/DataNodePlanNodeDeserializer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/DataNodePlanNodeDeserializer.java
index 3e5e46a3c0c..bbf66774d39 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/DataNodePlanNodeDeserializer.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/DataNodePlanNodeDeserializer.java
@@ -119,6 +119,7 @@ import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertRowsOf
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertTabletNode;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.ObjectNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalDeleteDataNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalInsertNumericRowNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalInsertRowNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalInsertRowsNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalInsertTabletNode;
@@ -164,6 +165,8 @@ public class DataNodePlanNodeDeserializer extends 
CommonPlanNodeDeserializer {
         return 
ContinuousSameSearchIndexSeparatorNode.deserializeFromWAL(stream);
       case 2000:
         return RelationalInsertTabletNode.deserializeFromWAL(stream);
+      case 2005:
+        return RelationalInsertNumericRowNode.deserializeFromWAL(stream);
       case 2001:
         return RelationalInsertRowNode.deserializeFromWAL(stream);
       case 2002:
@@ -193,6 +196,8 @@ public class DataNodePlanNodeDeserializer extends 
CommonPlanNodeDeserializer {
         return 
ContinuousSameSearchIndexSeparatorNode.deserializeFromWAL(buffer);
       case 2000:
         return RelationalInsertTabletNode.deserializeFromWAL(buffer);
+      case 2005:
+        return RelationalInsertNumericRowNode.deserializeFromWAL(buffer);
       case 2001:
         return RelationalInsertRowNode.deserializeFromWAL(buffer);
       case 2002:
@@ -473,6 +478,8 @@ public class DataNodePlanNodeDeserializer extends 
CommonPlanNodeDeserializer {
         return NonAlignedAggregationTreeDeviceViewScanNode.deserialize(buffer);
       case 2000:
         return RelationalInsertTabletNode.deserialize(buffer);
+      case 2005:
+        return RelationalInsertNumericRowNode.deserialize(buffer);
       case 2001:
         return RelationalInsertRowNode.deserialize(buffer);
       case 2002:
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 d7a09a6b49d..bb46eaf6ac1 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
@@ -33,12 +33,10 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages;
 import org.apache.iotdb.db.queryengine.plan.analyze.IAnalysis;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.WritePlanNode;
-import 
org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.cache.LastCacheUpdateSource;
 import 
org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.cache.TreeDeviceSchemaCacheManager;
 import org.apache.iotdb.db.storageengine.dataregion.memtable.AbstractMemTable;
 import 
org.apache.iotdb.db.storageengine.dataregion.memtable.IWritableMemChunkGroup;
 import 
org.apache.iotdb.db.storageengine.dataregion.wal.buffer.IWALByteBufferView;
-import org.apache.iotdb.db.storageengine.dataregion.wal.buffer.WALEntryValue;
 import org.apache.iotdb.db.storageengine.dataregion.wal.utils.WALWriteUtils;
 import org.apache.iotdb.db.utils.TypeInferenceUtils;
 
@@ -61,7 +59,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
-public class InsertRowNode extends InsertNode implements WALEntryValue, 
LastCacheUpdateSource {
+public class InsertRowNode extends AbstractInsertRowNode {
 
   private static final byte TYPE_RAW_STRING = -1;
 
@@ -213,6 +211,26 @@ public class InsertRowNode extends InsertNode implements 
WALEntryValue, LastCach
     return values;
   }
 
+  @Override
+  public int getValueCount() {
+    return values == null ? 0 : values.length;
+  }
+
+  @Override
+  public boolean isNull(int index) {
+    return index < 0 || index >= getValueCount() || values[index] == null;
+  }
+
+  @Override
+  public Object getValue(int index) {
+    return isNull(index) ? null : values[index];
+  }
+
+  @Override
+  public Object getValuesForMemoryAccounting() {
+    return values;
+  }
+
   public void setValues(Object[] values) {
     this.values = values;
   }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java
index 86084ff2c6a..bc730e37b89 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java
@@ -231,6 +231,10 @@ public interface AstVisitor<R, C> extends 
CommonQueryAstVisitor<R, C> {
     return visitStatement(node, context);
   }
 
+  default R visitInsertNumericRow(InsertNumericRow node, C context) {
+    return visitNode(node, context);
+  }
+
   default R visitInsertRow(InsertRow node, C context) {
     return visitStatement(node, context);
   }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/metrics/IoTDBInternalLocalReporter.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/metrics/IoTDBInternalLocalReporter.java
index f081a7b60fc..3d23a65a91f 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/metrics/IoTDBInternalLocalReporter.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/metrics/IoTDBInternalLocalReporter.java
@@ -46,7 +46,7 @@ import 
org.apache.iotdb.db.queryengine.plan.analyze.schema.ClusterSchemaFetcher;
 import org.apache.iotdb.db.queryengine.plan.analyze.schema.ISchemaFetcher;
 import org.apache.iotdb.db.queryengine.plan.execution.ExecutionResult;
 import org.apache.iotdb.db.queryengine.plan.parser.StatementGenerator;
-import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement;
+import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertBaseStatement;
 import 
org.apache.iotdb.db.queryengine.plan.statement.metadata.CreateMultiTimeSeriesStatement;
 import org.apache.iotdb.metrics.config.MetricConfigDescriptor;
 import org.apache.iotdb.metrics.reporter.iotdb.IoTDBInternalReporter;
@@ -211,7 +211,7 @@ public class IoTDBInternalLocalReporter extends 
IoTDBInternalReporter {
     request.setValues(buffer);
     request.setIsAligned(false);
 
-    InsertRowStatement s = StatementGenerator.createStatement(request);
+    InsertBaseStatement s = StatementGenerator.createStatement(request);
     final long queryId = SESSION_MANAGER.requestQueryId();
     ExecutionResult result =
         COORDINATOR.executeForTreeModel(
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
index 6b14b43edf5..014ca384184 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
@@ -84,6 +84,7 @@ import org.apache.iotdb.db.queryengine.common.DeviceContext;
 import org.apache.iotdb.db.queryengine.execution.fragment.QueryContext;
 import org.apache.iotdb.db.queryengine.metric.QueryResourceMetricSet;
 import 
org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.DataNodeTTLCache;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.AbstractInsertRowNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.ContinuousSameSearchIndexSeparatorNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.DeleteDataNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertMultiTabletsNode;
@@ -1174,7 +1175,7 @@ public class DataRegion implements IDataRegionForQuery {
    *
    * @param insertRowNode one row of data
    */
-  public void insert(InsertRowNode insertRowNode) throws WriteProcessException 
{
+  public void insert(AbstractInsertRowNode insertRowNode) throws 
WriteProcessException {
     // reject insertions that are out of ttl
     long ttl = getTTL(insertRowNode);
     if (!CommonUtils.isAlive(insertRowNode.getTime(), ttl)) {
@@ -1800,7 +1801,7 @@ public class DataRegion implements IDataRegionForQuery {
   }
 
   private TsFileProcessor insertToTsFileProcessor(
-      InsertRowNode insertRowNode, boolean sequence, long timePartitionId)
+      AbstractInsertRowNode insertRowNode, boolean sequence, long 
timePartitionId)
       throws WriteProcessException {
     if (insertRowNode.allMeasurementFailed()) {
       return null;
@@ -1819,7 +1820,7 @@ public class DataRegion implements IDataRegionForQuery {
     return tsFileProcessor;
   }
 
-  private void tryToUpdateInsertRowLastCache(final InsertRowNode node) {
+  private void tryToUpdateInsertRowLastCache(final AbstractInsertRowNode node) 
{
     node.updateLastCache(databaseName);
   }
 
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/AlignedWritableMemChunk.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/AlignedWritableMemChunk.java
index 37bd7d3c870..6800feb7a45 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/AlignedWritableMemChunk.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/AlignedWritableMemChunk.java
@@ -75,6 +75,8 @@ public class AlignedWritableMemChunk extends 
AbstractWritableMemChunk {
   private List<AlignedTVList> sortedList;
   private long sortedRowCount = 0;
   private final boolean ignoreAllNullRows;
+  // Allocate reusable scratch space only when the numeric row path is first 
used.
+  private int[] numericColumnMapping;
 
   private static final IoTDBConfig CONFIG = 
IoTDBDescriptor.getInstance().getConfig();
   private final int TVLIST_SORT_THRESHOLD = CONFIG.getTvListSortThreshold();
@@ -241,6 +243,38 @@ public class AlignedWritableMemChunk extends 
AbstractWritableMemChunk {
     }
   }
 
+  /** Reorders primitive slots directly, including null placeholders for 
absent columns. */
+  public void writeAlignedNumericPoints(
+      long time, NumericRowValues row, List<IMeasurementSchema> 
incomingSchemas) {
+    int[] mapping = numericColumnMapping;
+    // Other write paths may have extended the schema since the last numeric 
row.
+    if (mapping == null || mapping.length < schemaList.size()) {
+      mapping = new int[schemaList.size()];
+    }
+    // Missing columns must not reuse source indices from the previous row.
+    Arrays.fill(mapping, -1);
+    for (int i = 0; i < incomingSchemas.size(); i++) {
+      IMeasurementSchema schema = incomingSchemas.get(i);
+      if (schema == null) {
+        continue;
+      }
+      Integer index = measurementIndexMap.get(schema.getMeasurementName());
+      if (index == null) {
+        index = list.getTsDataTypes().size();
+        measurementIndexMap.put(schema.getMeasurementName(), index);
+        schemaList.add(schema);
+        list.extendColumn(schema.getType());
+        mapping = Arrays.copyOf(mapping, mapping.length + 1);
+      }
+      mapping[index] = i;
+    }
+    numericColumnMapping = mapping;
+    list.putAlignedNumericValue(time, row, mapping);
+    if (TVLIST_SORT_THRESHOLD > 0 && list.rowCount() >= TVLIST_SORT_THRESHOLD) 
{
+      handoverAlignedTvList();
+    }
+  }
+
   @Override
   public void writeAlignedTablet(
       long[] times,
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/IMemTable.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/IMemTable.java
index 4d38fea2c11..90952af6c73 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/IMemTable.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/IMemTable.java
@@ -25,6 +25,7 @@ import org.apache.iotdb.commons.path.IFullPath;
 import org.apache.iotdb.db.exception.DataTypeInconsistentException;
 import org.apache.iotdb.db.exception.WriteProcessException;
 import org.apache.iotdb.db.queryengine.execution.fragment.QueryContext;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.AbstractInsertRowNode;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertRowNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertTabletNode;
@@ -106,7 +107,7 @@ public interface IMemTable extends WALEntryValue {
    */
   int insert(InsertRowNode insertRowNode);
 
-  int insertAlignedRow(InsertRowNode insertRowNode);
+  int insertAlignedRow(AbstractInsertRowNode insertRowNode);
 
   /**
    * insert tablet into this memtable. The rows to be inserted are in the 
range [start, end). Null
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/rescon/quotas/DataNodeThrottleQuotaManager.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/rescon/quotas/DataNodeThrottleQuotaManager.java
index db7ad470930..817a46f4259 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/rescon/quotas/DataNodeThrottleQuotaManager.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/rescon/quotas/DataNodeThrottleQuotaManager.java
@@ -81,6 +81,7 @@ public class DataNodeThrottleQuotaManager {
       return NoopOperationQuota.get();
     }
     switch (s.getType()) {
+      case INSERT_NUMERIC_ROW:
       case INSERT:
       case BATCH_INSERT:
       case BATCH_INSERT_ONE_DEVICE:
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/rescon/quotas/DefaultOperationQuota.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/rescon/quotas/DefaultOperationQuota.java
index 764d6763184..b10668d3357 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/rescon/quotas/DefaultOperationQuota.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/rescon/quotas/DefaultOperationQuota.java
@@ -24,6 +24,7 @@ import org.apache.iotdb.db.i18n.StorageEngineMessages;
 import org.apache.iotdb.db.queryengine.plan.statement.Statement;
 import org.apache.iotdb.db.queryengine.plan.statement.StatementType;
 import 
org.apache.iotdb.db.queryengine.plan.statement.crud.InsertMultiTabletsStatement;
+import 
org.apache.iotdb.db.queryengine.plan.statement.crud.InsertNumericRowStatement;
 import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement;
 import 
org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowsOfOneDeviceStatement;
 import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowsStatement;
@@ -33,6 +34,7 @@ import 
org.apache.iotdb.db.queryengine.plan.statement.crud.LoadTsFileStatement;
 import 
org.apache.iotdb.db.queryengine.plan.statement.pipe.PipeEnrichedStatement;
 import org.apache.iotdb.db.utils.TypeInferenceUtils;
 
+import org.apache.tsfile.common.conf.TSFileConfig;
 import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.utils.BitMap;
 
@@ -101,6 +103,21 @@ public class DefaultOperationQuota implements 
OperationQuota {
       }
       final StatementType statementType = s.getType();
       switch (statementType) {
+        case INSERT_NUMERIC_ROW:
+          InsertNumericRowStatement numeric = (InsertNumericRowStatement) s;
+          for (int i = 0; i < numeric.getValueCount(); i++) {
+            if (!numeric.getNumericValues().isNull(i)) {
+              avgSize +=
+                  numeric.getDataType(i) == TSDataType.STRING
+                      ? numeric
+                          .getNumericValues()
+                          .getTag(i)
+                          .getBytes(TSFileConfig.STRING_CHARSET)
+                          .length
+                      : numeric.getDataType(i).getDataTypeSize();
+            }
+          }
+          break;
         case INSERT:
           // InsertStatement  InsertRowStatement
           if (s instanceof InsertStatement) {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusLogToTabletConverter.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusLogToTabletConverter.java
index 0b27de77b12..df4a12881be 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusLogToTabletConverter.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusLogToTabletConverter.java
@@ -28,6 +28,7 @@ import 
org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory;
 import org.apache.iotdb.consensus.common.request.IndexedConsensusRequest;
 import org.apache.iotdb.consensus.common.request.IoTConsensusRequest;
 import org.apache.iotdb.db.i18n.DataNodePipeMessages;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.AbstractInsertRowNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertMultiTabletsNode;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertRowNode;
@@ -222,8 +223,9 @@ public class ConsensusLogToTabletConverter {
         return convertInsertRowsOfOneDeviceNode((InsertRowsOfOneDeviceNode) 
insertNode);
       case INSERT_MULTI_TABLET:
         return convertInsertMultiTabletsNode((InsertMultiTabletsNode) 
insertNode);
+      case RELATIONAL_INSERT_NUMERIC_ROW:
       case RELATIONAL_INSERT_ROW:
-        return convertRelationalInsertRowNode((RelationalInsertRowNode) 
insertNode);
+        return convertRelationalInsertRowNode((AbstractInsertRowNode) 
insertNode);
       case RELATIONAL_INSERT_TABLET:
         return convertRelationalInsertTabletNode((RelationalInsertTabletNode) 
insertNode);
       case RELATIONAL_INSERT_ROWS:
@@ -467,8 +469,8 @@ public class ConsensusLogToTabletConverter {
 
   // ======================== Table Model Conversion ========================
 
-  private List<Tablet> convertRelationalInsertRowNode(final 
RelationalInsertRowNode node) {
-    final String tableName = node.getTableName();
+  private List<Tablet> convertRelationalInsertRowNode(final 
AbstractInsertRowNode node) {
+    final String tableName = node.getTargetPath().getFullPath();
 
     // Table-level pattern filtering
     if (tablePattern != null) {
@@ -480,13 +482,7 @@ public class ConsensusLogToTabletConverter {
       }
     }
 
-    final long time = node.getTime();
-    final String[] measurements = node.getMeasurements();
-    final TSDataType[] dataTypes = node.getDataTypes();
-    final Object[] values = node.getValues();
-    final Tablet tablet =
-        buildTableModelTabletFromRow(
-            tableName, time, measurements, dataTypes, values, 
node.getColumnCategories());
+    final Tablet tablet = buildTableModelTabletFromRow(node);
     if (Objects.isNull(tablet)) {
       return Collections.emptyList();
     }
@@ -499,7 +495,7 @@ public class ConsensusLogToTabletConverter {
   }
 
   private List<Tablet> convertRelationalInsertTabletNode(final 
RelationalInsertTabletNode node) {
-    final String tableName = node.getTableName();
+    final String tableName = node.getTargetPath().getFullPath();
 
     // Table-level pattern filtering
     if (tablePattern != null) {
@@ -578,14 +574,12 @@ public class ConsensusLogToTabletConverter {
     return tablets;
   }
 
-  private Tablet buildTableModelTabletFromRow(
-      final String tableName,
-      final long time,
-      final String[] measurements,
-      final TSDataType[] dataTypes,
-      final Object[] values,
-      final TsTableColumnCategory[] columnCategories) {
-    if (Objects.isNull(measurements) || Objects.isNull(dataTypes) || 
Objects.isNull(values)) {
+  private Tablet buildTableModelTabletFromRow(final AbstractInsertRowNode 
node) {
+    final String tableName = node.getTargetPath().getFullPath();
+    final String[] measurements = node.getMeasurements();
+    final TSDataType[] dataTypes = node.getDataTypes();
+    final TsTableColumnCategory[] columnCategories = 
node.getColumnCategories();
+    if (Objects.isNull(measurements) || Objects.isNull(dataTypes)) {
       return null;
     }
 
@@ -593,7 +587,9 @@ public class ConsensusLogToTabletConverter {
     final List<TSDataType> columnDataTypes = new 
ArrayList<>(measurements.length);
     final List<ColumnCategory> columnTypes = new 
ArrayList<>(measurements.length);
     final List<Integer> originalColumnIndexes = new 
ArrayList<>(measurements.length);
-    for (int i = 0; i < measurements.length && i < dataTypes.length && i < 
values.length; i++) {
+    for (int i = 0;
+        i < measurements.length && i < dataTypes.length && i < 
node.getValueCount();
+        i++) {
       if (Objects.isNull(measurements[i]) || Objects.isNull(dataTypes[i])) {
         continue;
       }
@@ -613,10 +609,11 @@ public class ConsensusLogToTabletConverter {
             columnDataTypes,
             columnTypes,
             1);
-    tablet.addTimestamp(0, time);
+    tablet.addTimestamp(0, node.getTime());
     for (int i = 0; i < originalColumnIndexes.size(); i++) {
       final int originalColumnIndex = originalColumnIndexes.get(i);
-      final Object value = values[originalColumnIndex];
+      // Convert one column at a time, keeping numeric rows in their primitive 
representation.
+      final Object value = node.getValue(originalColumnIndex);
       if (Objects.isNull(value)) {
         if (Objects.isNull(tablet.getBitMaps())) {
           tablet.initBitMaps();
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusPrefetchingQueue.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusPrefetchingQueue.java
index c6fd52301a4..db1a5364e0f 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusPrefetchingQueue.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusPrefetchingQueue.java
@@ -33,6 +33,7 @@ import org.apache.iotdb.db.i18n.DataNodeMiscMessages;
 import org.apache.iotdb.db.i18n.DataNodePipeMessages;
 import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent;
 import org.apache.iotdb.db.pipe.resource.memory.PipeMemoryWeightUtil;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.AbstractInsertRowNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertMultiTabletsNode;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertRowNode;
@@ -3575,8 +3576,8 @@ public class ConsensusPrefetchingQueue {
    */
   private long extractMaxTime(final InsertNode insertNode) {
     try {
-      if (insertNode instanceof InsertRowNode) {
-        return ((InsertRowNode) insertNode).getTime();
+      if (insertNode instanceof AbstractInsertRowNode row) {
+        return row.getTime();
       }
       if (insertNode instanceof InsertTabletNode) {
         final InsertTabletNode tabletNode = (InsertTabletNode) insertNode;
diff --git a/iotdb-protocol/thrift-datanode/src/main/thrift/client.thrift 
b/iotdb-protocol/thrift-datanode/src/main/thrift/client.thrift
index 7ff12ceb021..792a42dde51 100644
--- a/iotdb-protocol/thrift-datanode/src/main/thrift/client.thrift
+++ b/iotdb-protocol/thrift-datanode/src/main/thrift/client.thrift
@@ -248,6 +248,8 @@ struct TSInsertRecordReq {
   6: optional bool isAligned
   7: optional bool isWriteToTable
   8: optional list<byte> columnCategoryies
+  // Numeric table rows retain primitive storage throughout the server write 
path.
+  9: optional bool isNumericRow
 }
 
 struct TSInsertStringRecordReq {

Reply via email to