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 {
