This is an automated email from the ASF dual-hosted git repository.
jackietien pushed a commit to branch UserDefinedTime
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/UserDefinedTime by this push:
new 38c40b6319f Implemented load function for different time columns
(#17085)
38c40b6319f is described below
commit 38c40b6319f9ec2a8cd686f332c26396473cabf2
Author: Caideyipi <[email protected]>
AuthorDate: Wed Jan 28 08:55:01 2026 +0800
Implemented load function for different time columns (#17085)
---
.../iotdb/it/utils/TsFileTableGenerator.java | 30 +++-
.../relational/it/db/it/IoTDBLoadTsFileIT.java | 173 ++++++++++++++++++++-
.../plan/analyze/load/LoadTsFileAnalyzer.java | 3 +
.../analyze/load/LoadTsFileTableSchemaCache.java | 12 +-
.../plan/planner/LogicalPlanVisitor.java | 3 +-
.../plan/node/load/LoadSingleTsFileNode.java | 18 ++-
.../planner/plan/node/load/LoadTsFileNode.java | 14 +-
.../plan/relational/metadata/Metadata.java | 22 +--
.../relational/metadata/TableMetadataImpl.java | 23 ++-
.../fetcher/TableHeaderSchemaValidator.java | 72 +++++++--
.../plan/relational/planner/RelationPlanner.java | 6 +-
.../plan/relational/sql/ast/LoadTsFile.java | 11 +-
.../plan/planner/node/load/LoadTsFileNodeTest.java | 2 +-
.../plan/relational/analyzer/AnalyzerTest.java | 6 +-
.../plan/relational/analyzer/TSBSMetadata.java | 6 +-
.../plan/relational/analyzer/TestMetadata.java | 6 +-
.../plan/statement/InsertStatementTest.java | 16 +-
.../apache/iotdb/commons/schema/table/TsTable.java | 2 +-
18 files changed, 350 insertions(+), 75 deletions(-)
diff --git
a/integration-test/src/main/java/org/apache/iotdb/it/utils/TsFileTableGenerator.java
b/integration-test/src/main/java/org/apache/iotdb/it/utils/TsFileTableGenerator.java
index 7c2bd1623de..613ea8bd159 100644
---
a/integration-test/src/main/java/org/apache/iotdb/it/utils/TsFileTableGenerator.java
+++
b/integration-test/src/main/java/org/apache/iotdb/it/utils/TsFileTableGenerator.java
@@ -37,6 +37,7 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -81,15 +82,30 @@ public class TsFileTableGenerator implements AutoCloseable {
public void generateData(final String tableName, final int number, final
long timeGap)
throws IOException, WriteProcessException {
final List<IMeasurementSchema> schemas =
table2MeasurementSchema.get(tableName);
+ final List<ColumnCategory> columnCategoryList =
table2ColumnCategory.get(tableName);
+ int timeIndex = -1;
+ for (int i = 0; i < columnCategoryList.size(); ++i) {
+ if (columnCategoryList.get(i) == ColumnCategory.TIME) {
+ timeIndex = i;
+ break;
+ }
+ }
+ final List<IMeasurementSchema> schemaWithoutTime = new
ArrayList<>(schemas);
+ final List<ColumnCategory> columnCategoriesWithoutTime = new
ArrayList<>(columnCategoryList);
+ if (timeIndex > -1) {
+ schemaWithoutTime.remove(timeIndex);
+ columnCategoriesWithoutTime.remove(timeIndex);
+ }
final List<String> columnNameList =
-
schemas.stream().map(IMeasurementSchema::getMeasurementName).collect(Collectors.toList());
+ schemaWithoutTime.stream()
+ .map(IMeasurementSchema::getMeasurementName)
+ .collect(Collectors.toList());
final List<TSDataType> dataTypeList =
-
schemas.stream().map(IMeasurementSchema::getType).collect(Collectors.toList());
- final List<ColumnCategory> columnCategoryList =
table2ColumnCategory.get(tableName);
+
schemaWithoutTime.stream().map(IMeasurementSchema::getType).collect(Collectors.toList());
final TreeSet<Long> timeSet = table2TimeSet.get(tableName);
- final Tablet tablet = new Tablet(tableName, columnNameList, dataTypeList,
columnCategoryList);
- final Object[] values = tablet.getValues();
- final long sensorNum = schemas.size();
+ final Tablet tablet =
+ new Tablet(tableName, columnNameList, dataTypeList,
columnCategoriesWithoutTime);
+ final long sensorNum = schemaWithoutTime.size();
long startTime = timeSet.isEmpty() ? 0L : timeSet.last();
for (long r = 0; r < number; r++) {
@@ -98,7 +114,7 @@ public class TsFileTableGenerator implements AutoCloseable {
tablet.addTimestamp(row, startTime);
timeSet.add(startTime);
for (int i = 0; i < sensorNum; i++) {
- generateDataPoint(tablet, i, row, schemas.get(i));
+ generateDataPoint(tablet, i, row, schemaWithoutTime.get(i));
}
// write
if (tablet.getRowSize() == tablet.getMaxRowNumber()) {
diff --git
a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBLoadTsFileIT.java
b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBLoadTsFileIT.java
index 5f88d50c9d9..d14c0896816 100644
---
a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBLoadTsFileIT.java
+++
b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBLoadTsFileIT.java
@@ -47,7 +47,9 @@ import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -116,6 +118,40 @@ public class IoTDBLoadTsFileIT {
return pairs;
}
+ private List<Pair<MeasurementSchema, MeasurementSchema>>
generateMeasurementSchemasWithTime(
+ final int timeColumnIndex, final String timeColumnName) {
+ List<TSDataType> dataTypes =
+ new ArrayList<>(
+ Arrays.asList(
+ TSDataType.STRING,
+ TSDataType.TEXT,
+ TSDataType.BLOB,
+ TSDataType.TIMESTAMP,
+ TSDataType.BOOLEAN,
+ TSDataType.DATE,
+ TSDataType.DOUBLE,
+ TSDataType.FLOAT,
+ TSDataType.INT32,
+ TSDataType.INT64));
+ List<Pair<MeasurementSchema, MeasurementSchema>> pairs = new ArrayList<>();
+ for (TSDataType type : dataTypes) {
+ for (TSDataType dataType : dataTypes) {
+ String id = String.format("%s2%s", type.name(), dataType.name());
+ pairs.add(new Pair<>(new MeasurementSchema(id, type), new
MeasurementSchema(id, dataType)));
+ }
+ }
+
+ if (timeColumnIndex >= 0) {
+ pairs.add(
+ timeColumnIndex,
+ new Pair<>(
+ new MeasurementSchema(timeColumnName, TSDataType.TIMESTAMP),
+ new MeasurementSchema(timeColumnName, TSDataType.TIMESTAMP)));
+ }
+
+ return pairs;
+ }
+
@Test
public void testLoadWithEmptyDatabaseForTableModel() throws Exception {
final int lineCount = 10000;
@@ -123,7 +159,7 @@ public class IoTDBLoadTsFileIT {
final List<Pair<MeasurementSchema, MeasurementSchema>> measurementSchemas =
generateMeasurementSchemas();
final List<ColumnCategory> columnCategories =
- generateTabletColumnCategory(0, measurementSchemas.size());
+ generateTabletColumnCategory(measurementSchemas.size());
final File file = new File(tmpDir, "1-0-0-0.tsfile");
@@ -177,8 +213,7 @@ public class IoTDBLoadTsFileIT {
List<Pair<MeasurementSchema, MeasurementSchema>> measurementSchemas =
generateMeasurementSchemas();
- List<ColumnCategory> columnCategories =
- generateTabletColumnCategory(0, measurementSchemas.size());
+ List<ColumnCategory> columnCategories =
generateTabletColumnCategory(measurementSchemas.size());
final File file = new File(tmpDir, "1-0-0-0.tsfile");
@@ -219,8 +254,7 @@ public class IoTDBLoadTsFileIT {
List<Pair<MeasurementSchema, MeasurementSchema>> measurementSchemas =
generateMeasurementSchemas();
- List<ColumnCategory> columnCategories =
- generateTabletColumnCategory(0, measurementSchemas.size());
+ List<ColumnCategory> columnCategories =
generateTabletColumnCategory(measurementSchemas.size());
final File file = new File(tmpDir, "1-0-0-0.tsfile");
@@ -260,14 +294,137 @@ public class IoTDBLoadTsFileIT {
}
}
- private List<ColumnCategory> generateTabletColumnCategory(int tagNum, int
filedNum) {
- List<ColumnCategory> columnTypes = new ArrayList<>(tagNum + filedNum);
+ @Test
+ public void testLoadWithTimeColumn() throws Exception {
+ final int lineCount = 10000;
+
+ List<Pair<MeasurementSchema, MeasurementSchema>> measurementSchemas =
+ generateMeasurementSchemasWithTime(1, "time");
+ List<ColumnCategory> columnCategories =
+ generateTabletColumnCategory(0, measurementSchemas.size(), 1);
+
+ File file = new File(tmpDir, "1-0-0-0.tsfile");
+
+ List<MeasurementSchema> schemaList1 =
+ measurementSchemas.stream().map(pair ->
pair.left).collect(Collectors.toList());
+
+ try (final TsFileTableGenerator generator = new
TsFileTableGenerator(file)) {
+ generator.registerTable(SchemaConfig.TABLE_0, new
ArrayList<>(schemaList1), columnCategories);
+ generator.generateData(SchemaConfig.TABLE_0, lineCount,
PARTITION_INTERVAL / 10_000);
+ }
+
+ testWithTimeColumn(lineCount, schemaList1, columnCategories, file);
+ testWithTimeColumn(lineCount, null, null, file);
+
+ measurementSchemas = generateMeasurementSchemasWithTime(2, "time");
+ columnCategories = generateTabletColumnCategory(0,
measurementSchemas.size(), 2);
+ schemaList1 = measurementSchemas.stream().map(pair ->
pair.left).collect(Collectors.toList());
+ testWithTimeColumn(lineCount, schemaList1, columnCategories, file);
+
+ measurementSchemas = generateMeasurementSchemasWithTime(-1, "time");
+ columnCategories = generateTabletColumnCategory(0,
measurementSchemas.size(), -1);
+ schemaList1 = measurementSchemas.stream().map(pair ->
pair.left).collect(Collectors.toList());
+ testWithTimeColumn(lineCount, schemaList1, columnCategories, file);
+
+ measurementSchemas = generateMeasurementSchemasWithTime(2, "time1");
+ columnCategories = generateTabletColumnCategory(0,
measurementSchemas.size(), 2);
+ schemaList1 = measurementSchemas.stream().map(pair ->
pair.left).collect(Collectors.toList());
+ testWithTimeColumn(lineCount, schemaList1, columnCategories, file);
+
+ file = new File(tmpDir, "2-0-0-0.tsfile");
+ try (final TsFileTableGenerator generator = new
TsFileTableGenerator(file)) {
+ generator.registerTable(SchemaConfig.TABLE_0, new
ArrayList<>(schemaList1), columnCategories);
+ generator.generateData(SchemaConfig.TABLE_0, lineCount,
PARTITION_INTERVAL / 10_000);
+ }
+
+ measurementSchemas = generateMeasurementSchemasWithTime(2, "time");
+ columnCategories = generateTabletColumnCategory(0,
measurementSchemas.size(), 2);
+ schemaList1 = measurementSchemas.stream().map(pair ->
pair.left).collect(Collectors.toList());
+ testWithTimeColumn(lineCount, schemaList1, columnCategories, file);
+
+ measurementSchemas = generateMeasurementSchemasWithTime(1, "time1");
+ columnCategories = generateTabletColumnCategory(0,
measurementSchemas.size(), 1);
+ schemaList1 = measurementSchemas.stream().map(pair ->
pair.left).collect(Collectors.toList());
+ testWithTimeColumn(lineCount, schemaList1, columnCategories, file);
+
+ testWithTimeColumn(lineCount, null, null, file);
+
+ measurementSchemas = generateMeasurementSchemasWithTime(-1, "time");
+ columnCategories = generateTabletColumnCategory(0,
measurementSchemas.size(), -1);
+ schemaList1 = measurementSchemas.stream().map(pair ->
pair.left).collect(Collectors.toList());
+ testWithTimeColumn(lineCount, schemaList1, columnCategories, file);
+
+ file = new File(tmpDir, "3-0-0-0.tsfile");
+ try (final TsFileTableGenerator generator = new
TsFileTableGenerator(file)) {
+ generator.registerTable(SchemaConfig.TABLE_0, new
ArrayList<>(schemaList1), columnCategories);
+ generator.generateData(SchemaConfig.TABLE_0, lineCount,
PARTITION_INTERVAL / 10_000);
+ }
+
+ measurementSchemas = generateMeasurementSchemasWithTime(2, "time");
+ columnCategories = generateTabletColumnCategory(0,
measurementSchemas.size(), 2);
+ schemaList1 = measurementSchemas.stream().map(pair ->
pair.left).collect(Collectors.toList());
+ testWithTimeColumn(lineCount, schemaList1, columnCategories, file);
+
+ measurementSchemas = generateMeasurementSchemasWithTime(1, "time1");
+ columnCategories = generateTabletColumnCategory(0,
measurementSchemas.size(), 1);
+ schemaList1 = measurementSchemas.stream().map(pair ->
pair.left).collect(Collectors.toList());
+ testWithTimeColumn(lineCount, schemaList1, columnCategories, file);
+
+ testWithTimeColumn(lineCount, null, null, file);
+ }
+
+ private void testWithTimeColumn(
+ final long lineCount,
+ final List<MeasurementSchema> schemaList1,
+ final List<ColumnCategory> columnCategories,
+ final File file)
+ throws Exception {
+ try (final Connection connection =
+ EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(String.format("create database if not exists %s",
SchemaConfig.DATABASE_0));
+ statement.execute(String.format("use %s", SchemaConfig.DATABASE_0));
+ if (Objects.nonNull(schemaList1)) {
+ statement.execute(convert2TableSQL(SchemaConfig.TABLE_0, schemaList1,
columnCategories));
+ }
+ statement.execute(
+ String.format(
+ "load '%s' with ('database'='%s')", file.getAbsolutePath(),
SchemaConfig.DATABASE_0));
+ try (final ResultSet resultSet =
+ statement.executeQuery(String.format("select count(*) from %s",
SchemaConfig.TABLE_0))) {
+ if (resultSet.next()) {
+ Assert.assertEquals(lineCount, resultSet.getLong(1));
+ } else {
+ Assert.fail("This ResultSet is empty.");
+ }
+ }
+
+ try (final ResultSet resultSet = statement.executeQuery("show tables")) {
+ Assert.assertTrue(resultSet.next());
+ Assert.assertFalse(resultSet.next());
+ }
+
+ statement.execute(String.format("drop database %s",
SchemaConfig.DATABASE_0));
+ }
+ }
+
+ private List<ColumnCategory> generateTabletColumnCategory(final int
fieldNum) {
+ return generateTabletColumnCategory(0, fieldNum, -1);
+ }
+
+ private List<ColumnCategory> generateTabletColumnCategory(
+ int tagNum, int fieldNum, final int timeIndex) {
+ List<ColumnCategory> columnTypes =
+ new ArrayList<>(tagNum + fieldNum + (timeIndex >= 0 ? 1 : 0));
for (int i = 0; i < tagNum; i++) {
columnTypes.add(ColumnCategory.TAG);
}
- for (int i = 0; i < filedNum; i++) {
+ for (int i = 0; i < fieldNum; i++) {
columnTypes.add(ColumnCategory.FIELD);
}
+ if (timeIndex >= 0) {
+ columnTypes.add(timeIndex, ColumnCategory.TIME);
+ }
return columnTypes;
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzer.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzer.java
index 8202de5c499..1bcb6a26426 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzer.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzer.java
@@ -573,6 +573,9 @@ public class LoadTsFileAnalyzer implements AutoCloseable {
}
getOrCreateTableSchemaCache().flush();
+ if (getOrCreateTableSchemaCache().isNeedDecode4DifferentTimeColumn()) {
+ loadTsFileTableStatement.enableNeedDecode4TimeColumn();
+ }
getOrCreateTableSchemaCache().clearTagColumnMapper();
TimestampPrecisionUtils.checkTimestampPrecision(tsFileResource.getFileEndTime());
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileTableSchemaCache.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileTableSchemaCache.java
index 690a18dbd46..79ede0f459e 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileTableSchemaCache.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileTableSchemaCache.java
@@ -68,6 +68,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import static
org.apache.iotdb.commons.schema.MemUsageUtil.computeStringMemUsage;
@@ -111,6 +112,7 @@ public class LoadTsFileTableSchemaCache {
private long currentTimeIndexMemoryUsageSizeInBytes = 0;
private int currentBatchDevicesCount = 0;
+ private final AtomicBoolean needDecode4DifferentTimeColumn = new
AtomicBoolean(false);
public LoadTsFileTableSchemaCache(
final Metadata metadata, final MPPQueryContext context, final boolean
needToCreateDatabase)
@@ -298,7 +300,10 @@ public class LoadTsFileTableSchemaCache {
org.apache.iotdb.db.queryengine.plan.relational.metadata.TableSchema.fromTsFileTableSchema(
tableName, schema);
final TableSchema realSchema =
- metadata.validateTableHeaderSchema(database, fileSchema, context,
true, true).orElse(null);
+ metadata
+ .validateTableHeaderSchema4TsFile(
+ database, fileSchema, context, true, true,
needDecode4DifferentTimeColumn)
+ .orElse(null);
if (Objects.isNull(realSchema)) {
throw new LoadAnalyzeException(
String.format(
@@ -308,6 +313,10 @@ public class LoadTsFileTableSchemaCache {
verifyTableDataTypeAndGenerateTagColumnMapper(fileSchema, realSchema);
}
+ public boolean isNeedDecode4DifferentTimeColumn() {
+ return needDecode4DifferentTimeColumn.get();
+ }
+
private void autoCreateTableDatabaseIfAbsent(final String database) throws
LoadAnalyzeException {
validateDatabaseName(database);
if (DataNodeTableCache.getInstance().isDatabaseExist(database)) {
@@ -449,6 +458,7 @@ public class LoadTsFileTableSchemaCache {
currentBatchTable2Devices = null;
tableTagColumnMapper = null;
+ needDecode4DifferentTimeColumn.set(false);
}
private void clearDevices() {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanVisitor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanVisitor.java
index 2274869c355..a36564878df 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanVisitor.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanVisitor.java
@@ -548,7 +548,8 @@ public class LogicalPlanVisitor extends
StatementVisitor<PlanNode, MPPQueryConte
context.getQueryId().genPlanNodeId(),
loadTsFileStatement.getResources(),
isTableModel,
- loadTsFileStatement.getDatabase());
+ loadTsFileStatement.getDatabase(),
+ false);
}
@Override
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/load/LoadSingleTsFileNode.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/load/LoadSingleTsFileNode.java
index 604fda6e1e8..c8170a4880a 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/load/LoadSingleTsFileNode.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/load/LoadSingleTsFileNode.java
@@ -66,12 +66,13 @@ public class LoadSingleTsFileNode extends WritePlanNode {
private TRegionReplicaSet localRegionReplicaSet;
public LoadSingleTsFileNode(
- PlanNodeId id,
- TsFileResource resource,
- boolean isTableModel,
- String database,
- boolean deleteAfterLoad,
- long writePointCount) {
+ final PlanNodeId id,
+ final TsFileResource resource,
+ final boolean isTableModel,
+ final String database,
+ final boolean deleteAfterLoad,
+ final long writePointCount,
+ final boolean needDecodeTsFile) {
super(id);
this.tsFile = resource.getTsFile();
this.resource = resource;
@@ -79,6 +80,7 @@ public class LoadSingleTsFileNode extends WritePlanNode {
this.database = database;
this.deleteAfterLoad = deleteAfterLoad;
this.writePointCount = writePointCount;
+ this.needDecodeTsFile = needDecodeTsFile;
}
public boolean isTsFileEmpty() {
@@ -89,6 +91,10 @@ public class LoadSingleTsFileNode extends WritePlanNode {
public boolean needDecodeTsFile(
Function<List<Pair<IDeviceID, TTimePartitionSlot>>,
List<TRegionReplicaSet>>
partitionFetcher) {
+ if (needDecodeTsFile) {
+ return true;
+ }
+
List<Pair<IDeviceID, TTimePartitionSlot>> slotList = new ArrayList<>();
resource
.getDevices()
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/load/LoadTsFileNode.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/load/LoadTsFileNode.java
index 3588b6ddbb0..25ad9f3a5c7 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/load/LoadTsFileNode.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/load/LoadTsFileNode.java
@@ -46,13 +46,19 @@ public class LoadTsFileNode extends WritePlanNode {
private final List<TsFileResource> resources;
private final List<Boolean> isTableModel;
private final String database;
+ private final boolean needDecode4TimeColumn;
public LoadTsFileNode(
- PlanNodeId id, List<TsFileResource> resources, List<Boolean>
isTableModel, String database) {
+ final PlanNodeId id,
+ final List<TsFileResource> resources,
+ final List<Boolean> isTableModel,
+ final String database,
+ final boolean needDecode4TimeColumn) {
super(id);
this.resources = resources;
this.isTableModel = isTableModel;
this.database = database;
+ this.needDecode4TimeColumn = needDecode4TimeColumn;
}
@Override
@@ -121,7 +127,8 @@ public class LoadTsFileNode extends WritePlanNode {
isTableModel.get(i),
database,
statement.isDeleteAfterLoad(),
- statement.getWritePointCount(i)));
+ statement.getWritePointCount(i),
+ false));
}
return res;
}
@@ -143,7 +150,8 @@ public class LoadTsFileNode extends WritePlanNode {
isTableModel.get(i),
database,
statement.isDeleteAfterLoad(),
- statement.getWritePointCount(i)));
+ statement.getWritePointCount(i),
+ needDecode4TimeColumn));
}
}
return res;
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/Metadata.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/Metadata.java
index db706d4980c..82ee3323614 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/Metadata.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/Metadata.java
@@ -43,6 +43,7 @@ import org.apache.tsfile.read.common.type.Type;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
// All the input databases shall not contain "root"
public interface Metadata {
@@ -102,28 +103,31 @@ public interface Metadata {
* <p>When table or column is missing, this method will execute auto
creation if the user have
* corresponding authority.
*
- * <p>When using SQL, the columnSchemaList could be null and there won't be
any validation.
+ * <p>When using SQL, the columnSchemaList could be {@code null} and there
won't be any
+ * validation.
*
- * <p>When the input dataType or category of one column is null, the column
won't be auto created.
+ * <p>When the input dataType or category of one column is {@code null}, the
column won't be auto
+ * created.
*
* <p>The caller need to recheck the dataType of measurement columns to
decide whether to do
* partial insert
*
- * @param isStrictIdColumn if true, when the table already exists, the id
columns in the existing
- * table should be the prefix of those in the input tableSchema, or
input id columns be the
- * prefix of existing id columns.
+ * @param isStrictTagColumn if {@code true}, when the table already exists,
the tag columns in the
+ * existing table should be the prefix of those in the input
tableSchema, or input tag columns
+ * be the prefix of existing tag columns.
* @return If table doesn't exist and the user have no authority to create
table, Optional.empty()
* will be returned. The returned table may not include all the columns
* in @param{tableSchema}, if the user have no authority to alter table.
- * @throws SemanticException if column category mismatch or data types of id
or attribute column
- * are not STRING or Category, Type of any missing ColumnSchema is null
+ * @throws SemanticException if column category mismatch or data types of
tag or attribute column
+ * are not STRING or Category, Type of any missing ColumnSchema is
{@code null}
*/
- Optional<TableSchema> validateTableHeaderSchema(
+ Optional<TableSchema> validateTableHeaderSchema4TsFile(
final String database,
final TableSchema tableSchema,
final MPPQueryContext context,
final boolean allowCreateTable,
- final boolean isStrictIdColumn)
+ final boolean isStrictTagColumn,
+ final AtomicBoolean needDecode4DifferentTimeColumn)
throws LoadAnalyzeTableColumnDisorderException;
void validateInsertNodeMeasurements(
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
index 5df442d4d32..ed0be5b10d0 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
@@ -74,6 +74,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import static
org.apache.iotdb.db.queryengine.transformation.dag.column.FailFunctionColumnTransformer.FAIL_FUNCTION_NAME;
@@ -1396,16 +1397,22 @@ public class TableMetadataImpl implements Metadata {
}
@Override
- public Optional<TableSchema> validateTableHeaderSchema(
- String database,
- TableSchema tableSchema,
- MPPQueryContext context,
- boolean allowCreateTable,
- boolean isStrictTagColumn)
+ public Optional<TableSchema> validateTableHeaderSchema4TsFile(
+ final String database,
+ final TableSchema tableSchema,
+ final MPPQueryContext context,
+ final boolean allowCreateTable,
+ final boolean isStrictTagColumn,
+ final AtomicBoolean needDecode4DifferentTimeColumn)
throws LoadAnalyzeTableColumnDisorderException {
return TableHeaderSchemaValidator.getInstance()
- .validateTableHeaderSchema(
- database, tableSchema, context, allowCreateTable,
isStrictTagColumn);
+ .validateTableHeaderSchema4TsFile(
+ database,
+ tableSchema,
+ context,
+ allowCreateTable,
+ isStrictTagColumn,
+ needDecode4DifferentTimeColumn);
}
@Override
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java
index 2d629cb26ee..8becef3efb1 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java
@@ -60,6 +60,7 @@ import org.apache.tsfile.read.common.type.TypeFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
@@ -68,8 +69,10 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
+import static org.apache.iotdb.commons.schema.table.TsTable.TIME_COLUMN_NAME;
import static
org.apache.iotdb.db.queryengine.plan.relational.type.InternalTypeManager.getTSDataType;
import static
org.apache.iotdb.db.utils.EncodingInferenceUtils.getDefaultEncoding;
@@ -93,12 +96,13 @@ public class TableHeaderSchemaValidator {
return TableHeaderSchemaValidatorHolder.INSTANCE;
}
- public Optional<TableSchema> validateTableHeaderSchema(
+ public Optional<TableSchema> validateTableHeaderSchema4TsFile(
final String database,
final TableSchema tableSchema,
final MPPQueryContext context,
final boolean allowCreateTable,
- final boolean isStrictTagColumn)
+ final boolean isStrictTagColumn,
+ final @Nonnull AtomicBoolean needDecode4DifferentTimeColumn)
throws LoadAnalyzeTableColumnDisorderException {
// The schema cache R/W and fetch operation must be locked together thus
the cache clean
// operation executed by delete timeSeries will be effective.
@@ -136,8 +140,7 @@ public class TableHeaderSchemaValidator {
} else {
DataNodeTreeViewSchemaUtils.checkTableInWrite(database, table);
// If table with this name already exists and isStrictTagColumn is true,
make sure the
- // existing
- // id columns are the prefix of the incoming tag columns, or vice versa
+ // existing tag columns are a prefix of the incoming tag columns, or
vice versa
if (isStrictTagColumn) {
final List<TsTableColumnSchema> realTagColumns =
table.getTagColumnSchemaList();
final List<ColumnSchema> incomingTagColumns =
tableSchema.getTagColumns();
@@ -173,6 +176,32 @@ public class TableHeaderSchemaValidator {
}
}
}
+ long realTimeIndex = 0;
+ boolean realWithoutTimeColumn = true;
+
+ for (final TsTableColumnSchema schema : table.getColumnList()) {
+ if (schema.getColumnCategory() == TsTableColumnCategory.TIME) {
+ realWithoutTimeColumn = false;
+ break;
+ }
+ if (schema.getColumnCategory() != TsTableColumnCategory.ATTRIBUTE) {
+ ++realTimeIndex;
+ }
+ }
+
+ long inputTimeIndex = 0;
+ boolean inputWithoutTimeColumn = true;
+ for (final ColumnSchema schema : tableSchema.getColumns()) {
+ if (schema.getColumnCategory() == TsTableColumnCategory.TIME) {
+ inputWithoutTimeColumn = false;
+ break;
+ }
+ ++inputTimeIndex;
+ }
+ if (inputWithoutTimeColumn != realWithoutTimeColumn
+ || !inputWithoutTimeColumn && inputTimeIndex != realTimeIndex) {
+ needDecode4DifferentTimeColumn.set(true);
+ }
}
boolean refreshed = false;
@@ -212,7 +241,7 @@ public class TableHeaderSchemaValidator {
noField = false;
}
} else {
- // leave measurement columns' dataType checking to the caller, then
the caller can decide
+ // leave field columns' dataType checking to the caller, then the
caller can decide
// whether to do partial insert
// only check column category
@@ -234,7 +263,7 @@ public class TableHeaderSchemaValidator {
final List<ColumnSchema> resultColumnList = new ArrayList<>();
if (!missingColumnList.isEmpty() && isAutoCreateSchemaEnabled) {
// TODO table metadata: authority check for table alter
- // check id or attribute column data type in this method
+ // check tag or attribute column data type in this method
autoCreateColumn(database, tableSchema.getTableName(),
missingColumnList, context);
table = DataNodeTableCache.getInstance().getTable(database,
tableSchema.getTableName());
} else if (!missingColumnList.isEmpty()
@@ -572,12 +601,10 @@ public class TableHeaderSchemaValidator {
final ListenableFuture<ConfigTaskResult> future =
task.execute(configTaskExecutor);
final ConfigTaskResult result = future.get();
if (result.getStatusCode().getStatusCode() !=
TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
- throw new RuntimeException(
- new IoTDBException(
- String.format(
- "Auto add table column failed: %s.%s",
- database, measurementInfo.getTableName()),
- result.getStatusCode().getStatusCode()));
+ throw new IoTDBRuntimeException(
+ String.format(
+ "Auto add table column failed: %s.%s", database,
measurementInfo.getTableName()),
+ result.getStatusCode().getStatusCode());
}
DataNodeSchemaLockManager.getInstance()
.takeReadLock(context, SchemaLockType.VALIDATE_VS_DELETION_TABLE);
@@ -676,6 +703,21 @@ public class TableHeaderSchemaValidator {
}
private void addColumnSchema(final List<ColumnSchema> columnSchemas, final
TsTable tsTable) {
+ // check if the time column has been specified
+ long timeColumnCount =
+ columnSchemas.stream()
+ .filter(
+ columnDefinition ->
+ columnDefinition.getColumnCategory() ==
TsTableColumnCategory.TIME)
+ .count();
+ if (timeColumnCount > 1) {
+ throw new SemanticException("A table cannot have more than one time
column");
+ }
+ if (timeColumnCount == 0) {
+ // append the time column with default name "time" if user do not
specify the time column
+ tsTable.addColumnSchema(new TimeColumnSchema(TIME_COLUMN_NAME,
TSDataType.TIMESTAMP));
+ }
+
for (final ColumnSchema columnSchema : columnSchemas) {
TsTableColumnCategory category = columnSchema.getColumnCategory();
if (category == null) {
@@ -813,10 +855,8 @@ public class TableHeaderSchemaValidator {
TSFileDescriptor.getInstance().getConfig().getCompressor(dataType)));
break;
case TIME:
- throw new SemanticException(
- "Adding column for column category "
- + inputColumn.getColumnCategory()
- + " is not supported");
+ columnSchemaList.add(new TimeColumnSchema(inputColumn.getName(),
TSDataType.TIMESTAMP));
+ break;
default:
throw new IllegalStateException(
"Unknown ColumnCategory for adding column: " +
inputColumn.getColumnCategory());
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java
index d442d51a214..ded699588c1 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java
@@ -1388,7 +1388,11 @@ public class RelationPlanner extends
AstVisitor<RelationPlan, Void> {
}
return new RelationPlan(
new LoadTsFileNode(
- idAllocator.genPlanNodeId(), node.getResources(), isTableModel,
node.getDatabase()),
+ idAllocator.genPlanNodeId(),
+ node.getResources(),
+ isTableModel,
+ node.getDatabase(),
+ node.isNeedDecode4TimeColumn()),
analysis.getRootScope(),
Collections.emptyList(),
outerContext);
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/LoadTsFile.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/LoadTsFile.java
index 9b7cd372ee9..8deb97c2e5a 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/LoadTsFile.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/LoadTsFile.java
@@ -58,12 +58,13 @@ public class LoadTsFile extends Statement {
private boolean isGeneratedByPipe = false;
- private Map<String, String> loadAttributes;
+ private final Map<String, String> loadAttributes;
private List<File> tsFiles;
private List<TsFileResource> resources;
private List<Long> writePointCountList;
private List<Boolean> isTableModel;
+ private boolean needDecode4TimeColumn;
public LoadTsFile(NodeLocation location, String filePath, Map<String,
String> loadAttributes) {
super(location);
@@ -167,6 +168,14 @@ public class LoadTsFile extends Statement {
this.isTableModel = isTableModel;
}
+ public boolean isNeedDecode4TimeColumn() {
+ return needDecode4TimeColumn;
+ }
+
+ public void enableNeedDecode4TimeColumn() {
+ this.needDecode4TimeColumn = true;
+ }
+
public List<File> getTsFiles() {
return tsFiles;
}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/load/LoadTsFileNodeTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/load/LoadTsFileNodeTest.java
index e425c709815..a22ab8f6739 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/load/LoadTsFileNodeTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/load/LoadTsFileNodeTest.java
@@ -41,7 +41,7 @@ public class LoadTsFileNodeTest {
TsFileResource resource = new TsFileResource(new File("1"));
String database = "root.db";
LoadSingleTsFileNode node =
- new LoadSingleTsFileNode(new PlanNodeId(""), resource, false,
database, true, 0L);
+ new LoadSingleTsFileNode(new PlanNodeId(""), resource, false,
database, true, 0L, false);
Assert.assertTrue(node.isDeleteAfterLoad());
Assert.assertEquals(resource, node.getTsFileResource());
Assert.assertEquals(database, node.getDatabase());
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java
index 6666c180c5e..36bed6932a8 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java
@@ -96,6 +96,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -1058,12 +1059,13 @@ public class AnalyzerTest {
DataNodeTableCache.getInstance().commitUpdateTable(database, table, null);
return new TestMetadata() {
@Override
- public Optional<TableSchema> validateTableHeaderSchema(
+ public Optional<TableSchema> validateTableHeaderSchema4TsFile(
String database,
TableSchema schema,
MPPQueryContext context,
boolean allowCreateTable,
- boolean isStrictIdColumn) {
+ boolean isStrictTagColumn,
+ final AtomicBoolean needDecode4DifferentTimeColumn) {
TableSchema tableSchema = StatementTestUtils.genTableSchema();
assertEquals(tableSchema, schema);
return Optional.of(tableSchema);
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TSBSMetadata.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TSBSMetadata.java
index 79c03156097..49f23e0358c 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TSBSMetadata.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TSBSMetadata.java
@@ -64,6 +64,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
import static
org.apache.iotdb.db.queryengine.plan.relational.analyzer.MockTSBSDataPartition.T1_DEVICE_1;
import static
org.apache.iotdb.db.queryengine.plan.relational.analyzer.MockTSBSDataPartition.T1_DEVICE_2;
@@ -342,12 +343,13 @@ public class TSBSMetadata implements Metadata {
}
@Override
- public Optional<TableSchema> validateTableHeaderSchema(
+ public Optional<TableSchema> validateTableHeaderSchema4TsFile(
String database,
TableSchema tableSchema,
MPPQueryContext context,
boolean allowCreateTable,
- boolean isStrictIdColumn) {
+ boolean isStrictTagColumn,
+ final AtomicBoolean needDecode4DifferentTimeColumn) {
throw new UnsupportedOperationException();
}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMetadata.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMetadata.java
index 4b1d18944b7..722212b78b7 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMetadata.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMetadata.java
@@ -80,6 +80,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import static
org.apache.iotdb.commons.schema.table.InformationSchema.INFORMATION_DATABASE;
@@ -481,12 +482,13 @@ public class TestMetadata implements Metadata {
}
@Override
- public Optional<TableSchema> validateTableHeaderSchema(
+ public Optional<TableSchema> validateTableHeaderSchema4TsFile(
final String database,
final TableSchema tableSchema,
final MPPQueryContext context,
final boolean allowCreateTable,
- final boolean isStrictIdColumn) {
+ final boolean isStrictTagColumn,
+ final AtomicBoolean needDecode4DifferentTimeColumn) {
throw new UnsupportedOperationException();
}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/statement/InsertStatementTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/statement/InsertStatementTest.java
index 81a9d88d3d3..dbbadd1be91 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/statement/InsertStatementTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/statement/InsertStatementTest.java
@@ -51,6 +51,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThrows;
@@ -100,12 +101,13 @@ public class InsertStatementTest {
DataNodeTableCache.getInstance().preUpdateTable("test", tsTable, null);
DataNodeTableCache.getInstance().commitUpdateTable("test", "table1", null);
- when(metadata.validateTableHeaderSchema(
+ when(metadata.validateTableHeaderSchema4TsFile(
any(String.class),
any(TableSchema.class),
any(MPPQueryContext.class),
any(Boolean.class),
- any(Boolean.class)))
+ any(Boolean.class),
+ any(AtomicBoolean.class)))
.thenReturn(Optional.of(tableSchema));
doAnswer(
@@ -252,12 +254,13 @@ public class InsertStatementTest {
DataNodeTableCache.getInstance().preUpdateTable("test", tsTable, null);
DataNodeTableCache.getInstance().commitUpdateTable("test", "table1", null);
- when(metadata.validateTableHeaderSchema(
+ when(metadata.validateTableHeaderSchema4TsFile(
any(String.class),
any(TableSchema.class),
any(MPPQueryContext.class),
any(Boolean.class),
- any(Boolean.class)))
+ any(Boolean.class),
+ any(AtomicBoolean.class)))
.thenReturn(Optional.of(tableSchema));
assertThrows(
@@ -287,12 +290,13 @@ public class InsertStatementTest {
DataNodeTableCache.getInstance().preUpdateTable("test", tsTable, null);
DataNodeTableCache.getInstance().commitUpdateTable("test", "table1", null);
- when(metadata.validateTableHeaderSchema(
+ when(metadata.validateTableHeaderSchema4TsFile(
any(String.class),
any(TableSchema.class),
any(MPPQueryContext.class),
any(Boolean.class),
- any(Boolean.class)))
+ any(Boolean.class),
+ any(AtomicBoolean.class)))
.thenReturn(Optional.of(tableSchema));
assertThrows(
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/TsTable.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/TsTable.java
index 61b00d060bd..df3bbc045e5 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/TsTable.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/TsTable.java
@@ -69,7 +69,7 @@ public class TsTable {
"When there are object fields, the %s %s shall not be '.', '..' or
contain './', '.\\'.";
protected String tableName;
- private Map<String, TsTableColumnSchema> columnSchemaMap = new
LinkedHashMap<>();
+ private final Map<String, TsTableColumnSchema> columnSchemaMap = new
LinkedHashMap<>();
private final Map<String, Integer> tagColumnIndexMap = new HashMap<>();
private final Map<String, Integer> idColumnIndexMap = new HashMap<>();