From 489a530323ced2fea6a251cb2c48826a74b72d95 Mon Sep 17 00:00:00 2001 From: Caideyipi <87789683+Caideyipi@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:29:30 +0800 Subject: [PATCH 1/2] ob --- .../executor/RegionWriteExecutor.java | 82 +++++++++++++++---- .../queryengine/plan/parser/ASTVisitor.java | 4 +- 2 files changed, 68 insertions(+), 18 deletions(-) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/executor/RegionWriteExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/executor/RegionWriteExecutor.java index da394f62a07f5..b3eaec5f218d4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/executor/RegionWriteExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/executor/RegionWriteExecutor.java @@ -82,6 +82,7 @@ import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.trigger.api.enums.TriggerEvent; +import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.utils.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -420,7 +421,12 @@ private RegionExecutionResult executeCreateTimeSeries( final ISchemaRegion schemaRegion = schemaEngine.getSchemaRegion((SchemaRegionId) context.getRegionId()); final RegionExecutionResult result = - checkQuotaBeforeCreatingTimeSeries(schemaRegion, node.getPath().getDevicePath(), 1); + checkQuotaAndTypeBeforeCreatingTimeSeries( + schemaRegion, + node.getPath().getDevicePath(), + 1, + Collections.singletonList(node.getPath().getMeasurement()), + Collections.singletonList(node.getDataType())); if (result != null) { return result; } @@ -475,8 +481,12 @@ private RegionExecutionResult executeCreateAlignedTimeSeries( final ISchemaRegion schemaRegion = schemaEngine.getSchemaRegion((SchemaRegionId) context.getRegionId()); final RegionExecutionResult result = - checkQuotaBeforeCreatingTimeSeries( - schemaRegion, node.getDevicePath(), node.getMeasurements().size()); + checkQuotaAndTypeBeforeCreatingTimeSeries( + schemaRegion, + node.getDevicePath(), + node.getMeasurements().size(), + node.getMeasurements(), + node.getDataTypes()); if (result != null) { return result; } @@ -533,8 +543,12 @@ private RegionExecutionResult executeCreateMultiTimeSeries( for (final Map.Entry entry : node.getMeasurementGroupMap().entrySet()) { result = - checkQuotaBeforeCreatingTimeSeries( - schemaRegion, entry.getKey(), entry.getValue().getMeasurements().size()); + checkQuotaAndTypeBeforeCreatingTimeSeries( + schemaRegion, + entry.getKey(), + entry.getValue().getMeasurements().size(), + entry.getValue().getMeasurements(), + entry.getValue().getDataTypes()); if (result != null) { return result; } @@ -649,8 +663,12 @@ private RegionExecutionResult executeInternalCreateTimeSeries( final ISchemaRegion schemaRegion = schemaEngine.getSchemaRegion((SchemaRegionId) context.getRegionId()); final RegionExecutionResult result = - checkQuotaBeforeCreatingTimeSeries( - schemaRegion, node.getDevicePath(), node.getMeasurementGroup().size()); + checkQuotaAndTypeBeforeCreatingTimeSeries( + schemaRegion, + node.getDevicePath(), + node.getMeasurementGroup().size(), + node.getMeasurementGroup().getMeasurements(), + node.getMeasurementGroup().getDataTypes()); if (result != null) { return result; } @@ -736,8 +754,12 @@ private RegionExecutionResult executeInternalCreateMultiTimeSeries( for (final Map.Entry> deviceEntry : node.getDeviceMap().entrySet()) { result = - checkQuotaBeforeCreatingTimeSeries( - schemaRegion, deviceEntry.getKey(), deviceEntry.getValue().getRight().size()); + checkQuotaAndTypeBeforeCreatingTimeSeries( + schemaRegion, + deviceEntry.getKey(), + deviceEntry.getValue().getRight().size(), + deviceEntry.getValue().getRight().getMeasurements(), + deviceEntry.getValue().getRight().getDataTypes()); if (result != null) { return result; } @@ -823,8 +845,22 @@ private RegionExecutionResult executeInternalCreateMultiTimeSeries( * * @return null if the quota is not exceeded, otherwise return the execution result. */ - private RegionExecutionResult checkQuotaBeforeCreatingTimeSeries( - final ISchemaRegion schemaRegion, final PartialPath path, final int size) { + private RegionExecutionResult checkQuotaAndTypeBeforeCreatingTimeSeries( + final ISchemaRegion schemaRegion, + final PartialPath path, + final int size, + final List measurements, + final List dataTypes) { + for (int i = 0; i < measurements.size(); ++i) { + if (dataTypes.get(i) == TSDataType.OBJECT) { + final String errorStr = + "The object type series " + + path.concatAsMeasurementPath(measurements.get(i)) + + " is not supported."; + return RegionExecutionResult.create( + false, errorStr, RpcUtils.getStatus(TSStatusCode.METADATA_ERROR, errorStr)); + } + } try { schemaRegion.checkSchemaQuota(path, size); } catch (final SchemaQuotaExceededException e) { @@ -949,8 +985,12 @@ private RegionExecutionResult executeActivateTemplate( ISchemaRegion schemaRegion = schemaEngine.getSchemaRegion((SchemaRegionId) context.getRegionId()); RegionExecutionResult result = - checkQuotaBeforeCreatingTimeSeries( - schemaRegion, node.getActivatePath(), templateSetInfo.left.getMeasurementNumber()); + checkQuotaAndTypeBeforeCreatingTimeSeries( + schemaRegion, + node.getActivatePath(), + templateSetInfo.left.getMeasurementNumber(), + Collections.emptyList(), + Collections.emptyList()); if (result == null) { return receivedFromPipe ? super.visitPipeEnrichedWritePlanNode(new PipeEnrichedWritePlanNode(node), context) @@ -992,8 +1032,12 @@ private RegionExecutionResult executeBatchActivateTemplate( false, message, RpcUtils.getStatus(TSStatusCode.METADATA_ERROR, message)); } RegionExecutionResult result = - checkQuotaBeforeCreatingTimeSeries( - schemaRegion, devicePath, templateSetInfo.left.getMeasurementNumber()); + checkQuotaAndTypeBeforeCreatingTimeSeries( + schemaRegion, + devicePath, + templateSetInfo.left.getMeasurementNumber(), + Collections.emptyList(), + Collections.emptyList()); if (result != null) { return result; } @@ -1039,8 +1083,12 @@ private RegionExecutionResult executeInternalBatchActivateTemplate( false, message, RpcUtils.getStatus(TSStatusCode.METADATA_ERROR, message)); } RegionExecutionResult result = - checkQuotaBeforeCreatingTimeSeries( - schemaRegion, entry.getKey(), templateSetInfo.left.getMeasurementNumber()); + checkQuotaAndTypeBeforeCreatingTimeSeries( + schemaRegion, + entry.getKey(), + templateSetInfo.left.getMeasurementNumber(), + Collections.emptyList(), + Collections.emptyList()); if (result != null) { return result; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java index a6a0463e79b9a..421ead09c8988 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java @@ -4092,7 +4092,9 @@ private TSDataType parseDataTypeAttribute(IoTDBSqlParser.AttributeClausesContext String dataTypeString = ctx.dataType.getText().toUpperCase(); try { dataType = TSDataType.valueOf(dataTypeString); - if (TSDataType.UNKNOWN.equals(dataType) || TSDataType.VECTOR.equals(dataType)) { + if (TSDataType.UNKNOWN.equals(dataType) + || TSDataType.VECTOR.equals(dataType) + || TSDataType.OBJECT.equals(dataType)) { throw new SemanticException(String.format(UNSUPPORTED_DATATYPE_MSG, dataTypeString)); } } catch (Exception e) { From 2d8fb10297eae1426661f2c1e178ca66462714f4 Mon Sep 17 00:00:00 2001 From: Caideyipi <87789683+Caideyipi@users.noreply.github.com> Date: Fri, 27 Mar 2026 15:23:14 +0800 Subject: [PATCH 2/2] it --- .../db/it/schema/IoTDBSchemaSyntaxIT.java | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBSchemaSyntaxIT.java diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBSchemaSyntaxIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBSchemaSyntaxIT.java new file mode 100644 index 0000000000000..8b115d830776b --- /dev/null +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBSchemaSyntaxIT.java @@ -0,0 +1,154 @@ +/* + * 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.it.schema; + +import org.apache.iotdb.isession.ISession; +import org.apache.iotdb.it.env.EnvFactory; +import org.apache.iotdb.itbase.category.ClusterIT; +import org.apache.iotdb.itbase.category.LocalStandaloneIT; +import org.apache.iotdb.util.AbstractSchemaIT; + +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.file.metadata.enums.CompressionType; +import org.apache.tsfile.file.metadata.enums.TSEncoding; +import org.apache.tsfile.write.record.Tablet; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runners.Parameterized; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +@Category({LocalStandaloneIT.class, ClusterIT.class}) +public class IoTDBSchemaSyntaxIT extends AbstractSchemaIT { + public IoTDBSchemaSyntaxIT(SchemaTestMode schemaTestMode) { + super(schemaTestMode); + } + + @Parameterized.BeforeParam + public static void before() throws Exception { + setUpEnvironment(); + EnvFactory.getEnv().initClusterEnvironment(); + } + + @Parameterized.AfterParam + public static void after() throws Exception { + EnvFactory.getEnv().cleanClusterEnvironment(); + tearDownEnvironment(); + } + + @After + public void tearDown() throws Exception { + clearSchema(); + } + + @Test + public void testInvalidCreation() throws Exception { + final List invalidSQLs = + Arrays.asList( + "CREATE TIMESERIES root.sg1.d1.s1 OBJECT", + "CREATE ALIGNED TIMESERIES root.sg1.d1.vector1(s1 OBJECT encoding=PLAIN compressor=UNCOMPRESSED,s2 INT64 encoding=RLE)", + "create schema template t1 (s2 OBJECT encoding=RLE, s3 INT64 encoding=RLE compression=SNAPPY)"); + + try (final Connection connection = EnvFactory.getEnv().getConnection(); + final Statement statement = connection.createStatement()) { + for (final String invalidSQL : invalidSQLs) { + try { + statement.execute(invalidSQL); + Assert.fail(); + } catch (final Exception ignore) { + // Expected + } + } + } + + final String testObject = + System.getProperty("user.dir") + + File.separator + + "target" + + File.separator + + "test-classes" + + File.separator + + "object-example.pt"; + final byte[] objectBytes = Files.readAllBytes(Paths.get(testObject)); + final List objectSegments = new ArrayList<>(); + for (int i = 0; i < objectBytes.length; i += 512) { + objectSegments.add(Arrays.copyOfRange(objectBytes, i, Math.min(i + 512, objectBytes.length))); + } + try (final ISession session = EnvFactory.getEnv().getSessionConnection()) { + // insert table data by tablet + final List columnNameList = + Arrays.asList("region_id", "plant_id", "device_id", "temperature", "file"); + final List dataTypeList = + Arrays.asList( + TSDataType.STRING, + TSDataType.STRING, + TSDataType.STRING, + TSDataType.FLOAT, + TSDataType.OBJECT); + final Tablet tablet = new Tablet(columnNameList, dataTypeList); + tablet.setDeviceId("root.test.objectDevice"); + for (int i = 0; i < columnNameList.size() - 1; i++) { + int rowIndex = tablet.getRowSize(); + tablet.addTimestamp(rowIndex, 1); + tablet.addValue(rowIndex, 0, "1"); + tablet.addValue(rowIndex, 1, "5"); + tablet.addValue(rowIndex, 2, "3"); + tablet.addValue(rowIndex, 3, 37.6F); + tablet.addValue(rowIndex, 4, false, i * 512L, objectSegments.get(i)); + } + try { + session.insertTablet(tablet); + Assert.fail(); + } catch (final Exception ignore) { + // Expected + } + try { + session.insertAlignedTablet(tablet); + Assert.fail(); + } catch (final Exception ignore) { + // Expected + } + try { + session.createMultiTimeseries( + Collections.singletonList("root.sg1.d1.s1"), + Collections.singletonList(TSDataType.OBJECT), + Collections.singletonList(TSEncoding.PLAIN), + Collections.singletonList(CompressionType.LZ4), + null, + null, + null, + null); + } catch (final Exception ignore) { + // Expected + } + tablet.reset(); + } + } +}