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 028ceb4341f3b2e13e8d94e5eb60ac712cdea9a4 Author: Tian Jiang <[email protected]> AuthorDate: Thu Sep 17 10:33:50 2026 +0800 Add schema check bypass --- .../it/session/IoTDBTableModelSessionIT.java | 39 ++++ .../protocol/session/PreparedNumericRowInfo.java | 59 ++++++ .../plan/relational/sql/ast/InsertNumericRow.java | 29 +++ .../statement/crud/InsertNumericRowStatement.java | 11 + .../session/PreparedNumericRowInfoTest.java | 225 +++++++++++++++++++++ .../apache/iotdb/commons/schema/table/TsTable.java | 10 + 6 files changed, 373 insertions(+) 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 828ddba1fbd..29ee26b30c0 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 @@ -193,6 +193,45 @@ public class IoTDBTableModelSessionIT { } } + @Test + public void testPreparedNumericRowSchemaVersionChange() throws Exception { + // Reuse a prepared row before and after adding a TAG, including a flush/read round trip. + try (ITableSession session = EnvFactory.getEnv().getTableSessionConnection()) { + session.executeNonQueryStatement("CREATE DATABASE numeric_version"); + session.executeNonQueryStatement("USE numeric_version"); + session.executeNonQueryStatement("CREATE TABLE readings (tag STRING TAG, v DOUBLE FIELD)"); + NumericRowPreparedStatement prepared = + session.prepareInsertNumericRow( + "readings", + Arrays.asList("tag", "v"), + Arrays.asList(TSDataType.STRING, TSDataType.DOUBLE), + Arrays.asList(ColumnCategory.TAG, ColumnCategory.FIELD)); + for (int time = 1; time <= 3; time++) { + if (time == 3) { + session.executeNonQueryStatement("ALTER TABLE readings ADD COLUMN tag2 STRING TAG"); + } + session.executePreparedInsertNumericRow( + prepared, + time, + new long[] {Double.doubleToRawLongBits(time + 0.5)}, + new String[] {"device"}); + } + session.executeNonQueryStatement("FLUSH"); + try (SessionDataSet dataSet = + session.executeQueryStatement("SELECT time,tag,tag2,v FROM readings ORDER BY time")) { + for (int time = 1; time <= 3; time++) { + assertTrue(dataSet.hasNext()); + RowRecord row = dataSet.next(); + assertEquals(time, row.getFields().get(0).getLongV()); + assertEquals("device", row.getFields().get(1).getStringValue()); + assertNull(row.getFields().get(2).getDataType()); + assertEquals(time + 0.5, row.getFields().get(3).getDoubleV(), 0); + } + assertFalse(dataSet.hasNext()); + } + } + } + @Test public void testPreparedNumericRowAndTabletWritePerformance() throws Exception { Assume.assumeTrue( diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/PreparedNumericRowInfo.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/PreparedNumericRowInfo.java index a1a67e45100..6d5dc1fce4b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/PreparedNumericRowInfo.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/PreparedNumericRowInfo.java @@ -22,16 +22,24 @@ package org.apache.iotdb.db.protocol.session; import org.apache.iotdb.commons.exception.SemanticException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.queryengine.utils.TimestampPrecisionUtils; +import org.apache.iotdb.commons.schema.table.TsTable; import org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory; +import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchema; import org.apache.iotdb.db.i18n.DataNodeMiscMessages; +import org.apache.iotdb.db.pipe.resource.memory.InsertNodeMemoryEstimator; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertNumericRowStatement; +import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache; +import org.apache.iotdb.db.schemaengine.table.DataNodeTreeViewSchemaUtils; import org.apache.iotdb.db.utils.NumericRowValues; import org.apache.iotdb.service.rpc.thrift.TSPrepareNumericRowReq; +import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.ColumnCategory; import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.utils.Pair; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; +import org.apache.tsfile.write.schema.MeasurementSchema; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; @@ -39,6 +47,8 @@ import java.util.HashSet; import java.util.Locale; import java.util.Set; +import static org.apache.iotdb.db.utils.EncodingInferenceUtils.getDefaultEncoding; + /** * Session-owned metadata. Each execution gets fresh arrays because schema analysis mutates them. */ @@ -50,6 +60,8 @@ public final class PreparedNumericRowInfo { private final TsTableColumnCategory[] categories; private final int tagCount; private final int valueBytes; + private final Pair<Long, Long> tableVersion; + private final MeasurementSchema[] measurementSchemas; public PreparedNumericRowInfo(String database, TSPrepareNumericRowReq req) { if (req == null) { @@ -152,6 +164,44 @@ public final class PreparedNumericRowInfo { throw invalid( DataNodeMiscMessages.EXCEPTION_PREPARED_NUMERIC_ROW_HAS_NO_FIELD_COLUMNS_A76209AE); } + TsTable currentTable = DataNodeTableCache.getInstance().getTableInWrite(database, table); + Pair<Long, Long> version = currentTable == null ? null : currentTable.getInstanceVersion(); + MeasurementSchema[] schemas = prepareMeasurementSchemas(currentTable); + // Never retain a snapshot assembled across concurrent schema revisions. + if (schemas != null && currentTable.matchesInstanceVersion(version)) { + tableVersion = version; + measurementSchemas = schemas; + } else { + tableVersion = null; + measurementSchemas = null; + } + } + + private MeasurementSchema[] prepareMeasurementSchemas(TsTable currentTable) { + // Views and requests needing TAG padding/reordering or type conversion use normal analysis. + if (currentTable == null + || currentTable.getClass() != TsTable.class + || DataNodeTreeViewSchemaUtils.isTreeViewTable(currentTable) + || currentTable.getTagColumnSchemaList().size() != tagCount) { + return null; + } + MeasurementSchema[] schemas = new MeasurementSchema[measurements.length]; + for (int i = 0; i < measurements.length; i++) { + TsTableColumnSchema column = currentTable.getColumnSchema(measurements[i]); + if (column == null + || column.getDataType() != types[i] + || column.getColumnCategory() != categories[i] + || (i < tagCount && currentTable.getTagColumnOrdinal(measurements[i]) != i)) { + return null; + } + schemas[i] = + new MeasurementSchema( + column.getColumnName(), + types[i], + getDefaultEncoding(types[i]), + TSFileDescriptor.getInstance().getConfig().getCompressor(types[i])); + } + return schemas; } public InsertNumericRowStatement bind(long timestamp, ByteBuffer tags, ByteBuffer values) { @@ -217,6 +267,10 @@ public final class PreparedNumericRowInfo { statement.setColumnCategories(categories.clone()); statement.setTime(timestamp); statement.setNumericValues(row); + if (measurementSchemas != null) { + statement.setPreparedTableVersion(tableVersion); + statement.setMeasurementSchemas(measurementSchemas.clone()); + } return statement; } @@ -227,6 +281,11 @@ public final class PreparedNumericRowInfo { + RamUsageEstimator.sizeOf(measurements) + RamUsageEstimator.shallowSizeOf(types) + RamUsageEstimator.shallowSizeOf(categories) + + InsertNodeMemoryEstimator.sizeOfMeasurementSchemas(measurementSchemas) + + (tableVersion == null + ? 0 + : RamUsageEstimator.shallowSizeOf(tableVersion) + + 2 * RamUsageEstimator.shallowSizeOfInstance(Long.class)) // Account for the map entry and boxed ID retained by the session. + 64; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/InsertNumericRow.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/InsertNumericRow.java index 38f29f01d2c..4510973f753 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/InsertNumericRow.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/InsertNumericRow.java @@ -20,8 +20,13 @@ package org.apache.iotdb.db.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.IAstVisitor; +import org.apache.iotdb.commons.schema.table.TsTable; import org.apache.iotdb.db.queryengine.common.MPPQueryContext; +import org.apache.iotdb.db.queryengine.plan.analyze.lock.DataNodeSchemaLockManager; +import org.apache.iotdb.db.queryengine.plan.analyze.lock.SchemaLockType; +import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertNumericRowStatement; +import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache; import java.util.Arrays; import java.util.Collections; @@ -43,6 +48,30 @@ public class InsertNumericRow extends WrappedInsertStatement { return (InsertNumericRowStatement) super.getInnerTreeStatement(); } + @Override + public void validateTableSchema(Metadata metadata, MPPQueryContext context) { + InsertNumericRowStatement statement = getInnerTreeStatement(); + if (statement.getPreparedTableVersion() != null) { + // Check at analysis time under the same deletion lock and metadata lease as normal inserts. + DataNodeSchemaLockManager.getInstance() + .takeReadLock(context, SchemaLockType.VALIDATE_VS_DELETION_TABLE); + TsTable currentTable = + DataNodeTableCache.getInstance().getTableInWrite(getDatabase(), getTableName()); + if (currentTable != null + && currentTable.matchesInstanceVersion(statement.getPreparedTableVersion())) { + statement.setAttributeColumnsPresent(false); + // Cached schema names must not be mutated by later normalization. + statement.setToLowerCaseApplied(true); + statement.semanticCheck(); + return; + } + // Full validation may cast, reorder, or fail columns; discard all cached schema state first. + statement.setPreparedTableVersion(null); + statement.setMeasurementSchemas(null); + } + super.validateTableSchema(metadata, context); + } + @Override public void updateAfterSchemaValidation(MPPQueryContext context) { // Numeric rows are already typed; schema validation casts values in their primitive storage. diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertNumericRowStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertNumericRowStatement.java index fe77996fabe..7093b1615fb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertNumericRowStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertNumericRowStatement.java @@ -39,6 +39,7 @@ import org.apache.iotdb.db.utils.NumericRowValues; import org.apache.tsfile.enums.ColumnCategory; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.IDeviceID; +import org.apache.tsfile.utils.Pair; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.write.UnSupportedDataTypeException; @@ -55,6 +56,8 @@ public class InsertNumericRowStatement extends InsertBaseStatement { private long time; private NumericRowValues numericValues; private IDeviceID deviceID; + // A candidate snapshot, checked again under the schema analysis lock before reuse. + private Pair<Long, Long> preparedTableVersion; public InsertNumericRowStatement() { statementType = StatementType.INSERT_NUMERIC_ROW; @@ -62,6 +65,14 @@ public class InsertNumericRowStatement extends InsertBaseStatement { isAligned = true; } + public Pair<Long, Long> getPreparedTableVersion() { + return preparedTableVersion; + } + + public void setPreparedTableVersion(Pair<Long, Long> preparedTableVersion) { + this.preparedTableVersion = preparedTableVersion; + } + public long getTime() { return time; } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/protocol/session/PreparedNumericRowInfoTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/protocol/session/PreparedNumericRowInfoTest.java new file mode 100644 index 00000000000..e195d69660c --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/protocol/session/PreparedNumericRowInfoTest.java @@ -0,0 +1,225 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.protocol.session; + +import org.apache.iotdb.commons.queryengine.common.SessionInfo; +import org.apache.iotdb.commons.schema.table.TreeViewSchema; +import org.apache.iotdb.commons.schema.table.TsTable; +import org.apache.iotdb.commons.schema.table.column.FieldColumnSchema; +import org.apache.iotdb.commons.schema.table.column.TagColumnSchema; +import org.apache.iotdb.db.queryengine.common.MPPQueryContext; +import org.apache.iotdb.db.queryengine.common.QueryId; +import org.apache.iotdb.db.queryengine.plan.analyze.lock.DataNodeSchemaLockManager; +import org.apache.iotdb.db.queryengine.plan.analyze.lock.SchemaLockType; +import org.apache.iotdb.db.queryengine.plan.analyze.schema.SchemaValidator; +import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; +import org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.TableHeaderSchemaValidator; +import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControl; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.InsertNumericRow; +import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertNumericRowStatement; +import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache; +import org.apache.iotdb.service.rpc.thrift.TSPrepareNumericRowReq; + +import org.apache.tsfile.enums.ColumnCategory; +import org.apache.tsfile.enums.TSDataType; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.time.ZoneId; +import java.util.Arrays; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +public class PreparedNumericRowInfoTest { + private static final String DATABASE = "prepared_numeric_fastpath_test"; + private TsTable table; + private MPPQueryContext context; + private Metadata metadata; + + @Before + public void setUp() { + DataNodeTableCache.getInstance().invalid(DATABASE); + table = new TsTable("t"); + table.addColumnSchema(new TagColumnSchema("tag", TSDataType.STRING)); + table.addColumnSchema(new FieldColumnSchema("v", TSDataType.DOUBLE)); + install(table); + context = new MPPQueryContext(new QueryId("prepared_test")); + context.setSession(new SessionInfo(1, "root", ZoneId.of("UTC"))); + metadata = mock(Metadata.class); + doAnswer( + invocation -> { + TableHeaderSchemaValidator.getInstance() + .validateInsertNodeMeasurements( + invocation.getArgument(0), + invocation.getArgument(1), + invocation.getArgument(2), + invocation.getArgument(3), + invocation.getArgument(4), + invocation.getArgument(5)); + return null; + }) + .when(metadata) + .validateInsertNodeMeasurements(anyString(), any(), any(), anyBoolean(), any(), any()); + } + + @After + public void tearDown() { + DataNodeSchemaLockManager.getInstance().releaseReadLock(context); + DataNodeTableCache.getInstance().invalid(DATABASE); + } + + // An unchanged table skips column validation but still checks access and device metadata. + @Test + public void testUnchangedVersionRetainsOtherChecks() { + PreparedNumericRowInfo info = new PreparedNumericRowInfo(DATABASE, request("tag", "v")); + InsertNumericRowStatement statement = bind(info, 1); + InsertNumericRowStatement next = bind(info, 1); + assertNotNull(statement.getPreparedTableVersion()); + assertNotSame(statement.getMeasurementSchemas(), next.getMeasurementSchemas()); + assertSame(statement.getMeasurementSchemas()[1], next.getMeasurementSchemas()[1]); + AccessControl accessControl = mock(AccessControl.class); + InsertNumericRow insert = new InsertNumericRow(statement, context); + SchemaValidator.validate(metadata, insert, context, accessControl); + verify(metadata, never()) + .validateInsertNodeMeasurements(anyString(), any(), any(), anyBoolean(), any(), any()); + verify(metadata).validateDeviceSchema(insert, context); + verify(accessControl).checkCanInsertIntoTable(eq("root"), any(), eq(context)); + assertTrue(context.getAcquiredLocks().contains(SchemaLockType.VALIDATE_VS_DELETION_TABLE)); + assertEquals(TSDataType.DOUBLE, statement.getMeasurementSchemas()[1].getType()); + statement.getMeasurementSchemas()[1] = null; + assertNotNull(bind(info, 1).getMeasurementSchemas()[1]); + } + + // Adding a TAG after bind forces normal validation to pad the new device-id segment. + @Test + public void testVersionChangeBetweenBindAndAnalysis() { + PreparedNumericRowInfo info = new PreparedNumericRowInfo(DATABASE, request("tag", "v")); + InsertNumericRowStatement statement = bind(info, 1); + table.addColumnSchema(new TagColumnSchema("tag2", TSDataType.STRING)); + validateFallback(statement); + assertArrayEquals(new String[] {"tag", "tag2", "v"}, statement.getMeasurements()); + assertNull(statement.getFirstValueOfIndex(1)); + assertEquals(2.5D, statement.getFirstValueOfIndex(2)); + assertArrayEquals(new String[] {"tag", "v"}, bind(info, 1).getMeasurements()); + } + + // A replacement table with the same name/revision must not reuse the old instance's schema. + @Test + public void testRecreatedTableUsesFullValidation() { + InsertNumericRowStatement statement = + bind(new PreparedNumericRowInfo(DATABASE, request("tag", "v")), 1); + TsTable replacement = new TsTable("t"); + replacement.addColumnSchema(new TagColumnSchema("tag", TSDataType.STRING)); + replacement.addColumnSchema(new FieldColumnSchema("v", TSDataType.DOUBLE)); + assertEquals(table.getInstanceVersion().right, replacement.getInstanceVersion().right); + install(replacement); + validateFallback(statement); + assertNotNull(statement.getMeasurementSchemas()[1]); + } + + // Missing columns/table, views and type conversions remain on normal creation/validation. + @Test + public void testIneligibleMetadataHasNoCachedSchema() { + assertNull( + bind(new PreparedNumericRowInfo(DATABASE, request("tag", "missing")), 1) + .getPreparedTableVersion()); + TSPrepareNumericRowReq req = request("tag", "v"); + req.setDataTypes(Arrays.asList(TSDataType.STRING.ordinal(), TSDataType.INT64.ordinal())); + assertNull( + new PreparedNumericRowInfo(DATABASE, req) + .bind(1, tagBuffer(1), ByteBuffer.allocate(8)) + .getPreparedTableVersion()); + table.addProp(TreeViewSchema.TREE_PATH_PATTERN, "root.sg.**"); + assertNull( + bind(new PreparedNumericRowInfo(DATABASE, request("tag", "v")), 1) + .getPreparedTableVersion()); + DataNodeTableCache.getInstance().invalid(DATABASE); + assertNull( + bind(new PreparedNumericRowInfo(DATABASE, request("tag", "v")), 1).getMeasurementSchemas()); + } + + // Reversed TAG input must take the normal path so payload values follow schema order. + @Test + public void testTagReorderingFallsBack() { + table.addColumnSchema(new TagColumnSchema("tag2", TSDataType.STRING)); + InsertNumericRowStatement statement = + bind(new PreparedNumericRowInfo(DATABASE, request("tag2", "tag", "v")), 2); + assertNull(statement.getPreparedTableVersion()); + validateFallback(statement); + assertArrayEquals(new String[] {"tag", "tag2", "v"}, statement.getMeasurements()); + assertEquals("b", statement.getNumericValues().getTag(0)); + assertEquals("a", statement.getNumericValues().getTag(1)); + } + + private void validateFallback(InsertNumericRowStatement statement) { + new InsertNumericRow(statement, context).validateTableSchema(metadata, context); + verify(metadata) + .validateInsertNodeMeasurements(eq(DATABASE), any(), eq(context), eq(true), any(), any()); + assertNull(statement.getPreparedTableVersion()); + } + + private static void install(TsTable table) { + DataNodeTableCache.getInstance().preUpdateTable(DATABASE, table, null); + DataNodeTableCache.getInstance().commitUpdateTable(DATABASE, "t", null); + } + + private static TSPrepareNumericRowReq request(String... names) { + TSPrepareNumericRowReq req = new TSPrepareNumericRowReq(); + req.setTableName("t"); + req.setMeasurements(Arrays.asList(names)); + for (int i = 0; i < names.length; i++) { + boolean tag = i < names.length - 1; + req.addToDataTypes((tag ? TSDataType.STRING : TSDataType.DOUBLE).ordinal()); + req.addToColumnCategories((byte) (tag ? ColumnCategory.TAG : ColumnCategory.FIELD).ordinal()); + } + return req; + } + + private static ByteBuffer tagBuffer(int count) { + ByteBuffer tags = ByteBuffer.allocate(count * 5); + for (int i = 0; i < count; i++) { + tags.putInt(1).put((byte) ('a' + i)); + } + tags.flip(); + return tags; + } + + private static InsertNumericRowStatement bind(PreparedNumericRowInfo info, int tags) { + ByteBuffer values = ByteBuffer.allocate(8); + values.putDouble(2.5D).flip(); + return info.bind(1, tagBuffer(tags), values); + } +} 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 14481bfa9bc..8ddabbd96d1 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 @@ -375,6 +375,16 @@ public class TsTable { return new Pair<>(creationId, instanceVersion.get()); } + /** Checks both table identity and schema revision, waiting for any ongoing schema write. */ + public boolean matchesInstanceVersion(final Pair<Long, Long> version) { + readWriteLock.readLock().lock(); + try { + return creationId.equals(version.left) && instanceVersion.get() == version.right; + } finally { + readWriteLock.readLock().unlock(); + } + } + public boolean containsPropWithoutLock(final String propKey) { return props != null && props.containsKey(propKey); }
