From 6a461ff5d766b055d57e469157fc11495443f45f Mon Sep 17 00:00:00 2001 From: libo Date: Wed, 28 Jan 2026 10:13:56 +0800 Subject: [PATCH 1/5] Verify nonexist measurement in an exist device if it exists. --- .../it/schema/IoTDBAlterTimeSeriesTypeIT.java | 22 ++++- .../AlterTimeSeriesDataTypeProcedure.java | 92 ++++++++++++++++++- 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBAlterTimeSeriesTypeIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBAlterTimeSeriesTypeIT.java index f7d12f10b8fe..5f1569b18894 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBAlterTimeSeriesTypeIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBAlterTimeSeriesTypeIT.java @@ -442,7 +442,27 @@ public void testAlterNonExist() throws IoTDBConnectionException, StatementExecut "ALTER TIMESERIES " + database + ".non_exist.s1 SET DATA TYPE INT64"); fail("Should throw exception"); } catch (StatementExecutionException e) { - assertEquals("508: Path [" + database + ".non_exist.s1] does not exist", e.getMessage()); + assertEquals( + "508: Timeseries [" + + database + + ".non_exist.s1] does not exist or is represented by device template", + e.getMessage()); + } + + // Make the "non_exist" device exist, test the "nonexist" measurement if it can be altered + // data type. + try { + session.executeNonQueryStatement( + "CREATE TIMESERIES " + database + ".d1.int32 WITH DATATYPE=INT32"); + session.executeNonQueryStatement( + "ALTER TIMESERIES " + database + ".d1.nonexistent SET DATA TYPE STRING"); + fail("Should throw exception"); + } catch (StatementExecutionException e) { + assertEquals( + "507: Alter timeseries data type in schema regions failed. Failures: {DataNodeId: 1=[TSStatus(code:508, message:Path [" + + database + + ".d1.nonexistent] does not exist)]}", + e.getMessage()); } } } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/AlterTimeSeriesDataTypeProcedure.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/AlterTimeSeriesDataTypeProcedure.java index 05daf8c900de..e9a14431ea6e 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/AlterTimeSeriesDataTypeProcedure.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/AlterTimeSeriesDataTypeProcedure.java @@ -25,6 +25,7 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.commons.path.MeasurementPath; +import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.path.PathDeserializeUtil; import org.apache.iotdb.commons.path.PathPatternTree; import org.apache.iotdb.confignode.client.async.CnToDnAsyncRequestType; @@ -61,6 +62,7 @@ import java.util.Objects; import java.util.Set; import java.util.function.Consumer; +import java.util.stream.Collectors; public class AlterTimeSeriesDataTypeProcedure extends StateMachineProcedure { @@ -103,7 +105,7 @@ protected StateMachineProcedure.Flow executeFromState( LOGGER.info( "Check and invalidate series {} when altering time series data type", measurementPath.getFullPath()); - checkAndPreAlterTimeSeries(); + checkAndPreAlterTimeSeries(env); break; case ALTER_TIME_SERIES_DATA_TYPE: LOGGER.info("altering time series {} data type", measurementPath.getFullPath()); @@ -142,7 +144,81 @@ protected StateMachineProcedure.Flow executeFromState( } } - private void checkAndPreAlterTimeSeries() { + private void checkAndPreAlterTimeSeries(final ConfigNodeProcedureEnv env) { + PathPatternTree patternTree = new PathPatternTree(); + patternTree.appendPathPattern(measurementPath); + patternTree.constructTree(); + final Map relatedSchemaRegionGroup = + env.getConfigManager().getRelatedSchemaRegionGroup(patternTree, false); + if (relatedSchemaRegionGroup.isEmpty()) { + setFailure( + new ProcedureException( + new PathNotExistException( + patternTree.getAllPathPatterns().stream() + .map(PartialPath::getFullPath) + .collect(Collectors.toList()), + false))); + } + + final DataNodeTSStatusTaskExecutor alterTimeSeriesDataTypeTask = + new DataNodeTSStatusTaskExecutor( + env, + env.getConfigManager().getRelatedSchemaRegionGroup(patternTree, false), + false, + CnToDnAsyncRequestType.ALTER_TIMESERIES_DATATYPE, + ((dataNodeLocation, consensusGroupIdList) -> + new TAlterTimeSeriesReq( + consensusGroupIdList, + queryId, + measurementPathBytes, + operationType, + prepareDataTypeBytesData()))) { + + @Override + protected List processResponseOfOneDataNode( + final TDataNodeLocation dataNodeLocation, + final List consensusGroupIdList, + final TSStatus response) { + final List failedRegionList = new ArrayList<>(); + if (response.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + failureMap.remove(dataNodeLocation); + return failedRegionList; + } + + if (response.getCode() == TSStatusCode.MULTIPLE_ERROR.getStatusCode()) { + final List subStatus = response.getSubStatus(); + for (int i = 0; i < subStatus.size(); i++) { + if (subStatus.get(i).getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode() + && subStatus.get(i).getCode() == TSStatusCode.PATH_NOT_EXIST.getStatusCode()) { + failedRegionList.add(consensusGroupIdList.get(i)); + } + } + } else if (response.getCode() == TSStatusCode.PATH_NOT_EXIST.getStatusCode()) { + failedRegionList.addAll(consensusGroupIdList); + } + if (!failedRegionList.isEmpty()) { + failureMap.put(dataNodeLocation, RpcUtils.extractFailureStatues(response)); + } else { + failureMap.remove(dataNodeLocation); + } + return failedRegionList; + } + + @Override + protected void onAllReplicasetFailure( + final TConsensusGroupId consensusGroupId, + final Set dataNodeLocationSet) { + setFailure( + new ProcedureException( + new MetadataException( + String.format( + "Alter timeseries data type in schema regions failed. Failures: %s", + printFailureMap())))); + interruptTask(); + } + }; + alterTimeSeriesDataTypeTask.execute(); + if (dataType != null) { setNextState(AlterTimeSeriesDataTypeState.ALTER_TIME_SERIES_DATA_TYPE); } else { @@ -334,7 +410,7 @@ public MeasurementPath getmeasurementPath() { public void setMeasurementPath(final MeasurementPath measurementPath) { this.measurementPath = measurementPath; - measurementPathBytes = prepareMeasurementPathBytesData(measurementPath); + this.measurementPathBytes = prepareMeasurementPathBytesData(measurementPath); } public static ByteBuffer prepareMeasurementPathBytesData(final MeasurementPath measurementPath) { @@ -359,6 +435,16 @@ public static ByteBuffer preparePatternTreeBytesData(final PathPatternTree patte return ByteBuffer.wrap(byteArrayOutputStream.toByteArray()); } + public ByteBuffer prepareDataTypeBytesData() { + final ByteArrayOutputStream stream = new ByteArrayOutputStream(1); + try { + ReadWriteIOUtils.write(dataType, stream); + } catch (final IOException ignored) { + // ByteArrayOutputStream won't throw IOException + } + return ByteBuffer.wrap(stream.toByteArray()); + } + @Override public void serialize(final DataOutputStream stream) throws IOException { stream.writeShort( From 1ec64b99f4dbd542efb155dd778f4a34507859af Mon Sep 17 00:00:00 2001 From: libo Date: Wed, 28 Jan 2026 10:52:41 +0800 Subject: [PATCH 2/5] Remove spare codes, and optimize codes. --- .../AlterTimeSeriesDataTypeProcedure.java | 108 ++---------------- 1 file changed, 11 insertions(+), 97 deletions(-) diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/AlterTimeSeriesDataTypeProcedure.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/AlterTimeSeriesDataTypeProcedure.java index e9a14431ea6e..7459f6b39c94 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/AlterTimeSeriesDataTypeProcedure.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/AlterTimeSeriesDataTypeProcedure.java @@ -25,7 +25,6 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.commons.path.MeasurementPath; -import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.path.PathDeserializeUtil; import org.apache.iotdb.commons.path.PathPatternTree; import org.apache.iotdb.confignode.client.async.CnToDnAsyncRequestType; @@ -62,7 +61,6 @@ import java.util.Objects; import java.util.Set; import java.util.function.Consumer; -import java.util.stream.Collectors; public class AlterTimeSeriesDataTypeProcedure extends StateMachineProcedure { @@ -105,7 +103,7 @@ protected StateMachineProcedure.Flow executeFromState( LOGGER.info( "Check and invalidate series {} when altering time series data type", measurementPath.getFullPath()); - checkAndPreAlterTimeSeries(env); + checkAndPreAlterTimeSeries(); break; case ALTER_TIME_SERIES_DATA_TYPE: LOGGER.info("altering time series {} data type", measurementPath.getFullPath()); @@ -144,81 +142,7 @@ protected StateMachineProcedure.Flow executeFromState( } } - private void checkAndPreAlterTimeSeries(final ConfigNodeProcedureEnv env) { - PathPatternTree patternTree = new PathPatternTree(); - patternTree.appendPathPattern(measurementPath); - patternTree.constructTree(); - final Map relatedSchemaRegionGroup = - env.getConfigManager().getRelatedSchemaRegionGroup(patternTree, false); - if (relatedSchemaRegionGroup.isEmpty()) { - setFailure( - new ProcedureException( - new PathNotExistException( - patternTree.getAllPathPatterns().stream() - .map(PartialPath::getFullPath) - .collect(Collectors.toList()), - false))); - } - - final DataNodeTSStatusTaskExecutor alterTimeSeriesDataTypeTask = - new DataNodeTSStatusTaskExecutor( - env, - env.getConfigManager().getRelatedSchemaRegionGroup(patternTree, false), - false, - CnToDnAsyncRequestType.ALTER_TIMESERIES_DATATYPE, - ((dataNodeLocation, consensusGroupIdList) -> - new TAlterTimeSeriesReq( - consensusGroupIdList, - queryId, - measurementPathBytes, - operationType, - prepareDataTypeBytesData()))) { - - @Override - protected List processResponseOfOneDataNode( - final TDataNodeLocation dataNodeLocation, - final List consensusGroupIdList, - final TSStatus response) { - final List failedRegionList = new ArrayList<>(); - if (response.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { - failureMap.remove(dataNodeLocation); - return failedRegionList; - } - - if (response.getCode() == TSStatusCode.MULTIPLE_ERROR.getStatusCode()) { - final List subStatus = response.getSubStatus(); - for (int i = 0; i < subStatus.size(); i++) { - if (subStatus.get(i).getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode() - && subStatus.get(i).getCode() == TSStatusCode.PATH_NOT_EXIST.getStatusCode()) { - failedRegionList.add(consensusGroupIdList.get(i)); - } - } - } else if (response.getCode() == TSStatusCode.PATH_NOT_EXIST.getStatusCode()) { - failedRegionList.addAll(consensusGroupIdList); - } - if (!failedRegionList.isEmpty()) { - failureMap.put(dataNodeLocation, RpcUtils.extractFailureStatues(response)); - } else { - failureMap.remove(dataNodeLocation); - } - return failedRegionList; - } - - @Override - protected void onAllReplicasetFailure( - final TConsensusGroupId consensusGroupId, - final Set dataNodeLocationSet) { - setFailure( - new ProcedureException( - new MetadataException( - String.format( - "Alter timeseries data type in schema regions failed. Failures: %s", - printFailureMap())))); - interruptTask(); - } - }; - alterTimeSeriesDataTypeTask.execute(); - + private void checkAndPreAlterTimeSeries() { if (dataType != null) { setNextState(AlterTimeSeriesDataTypeState.ALTER_TIME_SERIES_DATA_TYPE); } else { @@ -255,22 +179,13 @@ private boolean alterTimeSeriesDataType(final ConfigNodeProcedureEnv env) { env.getConfigManager().getRelatedSchemaRegionGroup(patternTree, false), false, CnToDnAsyncRequestType.ALTER_TIMESERIES_DATATYPE, - ((dataNodeLocation, consensusGroupIdList) -> { - ByteBuffer measurementPathBuffer = null; - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - measurementPath.serialize(baos); - measurementPathBuffer = ByteBuffer.wrap(baos.toByteArray()); - } catch (IOException ignored) { - // ByteArrayOutputStream won't throw IOException - } - - return new TAlterTimeSeriesReq( - consensusGroupIdList, - queryId, - measurementPathBuffer, - operationType, - ByteBuffer.wrap(stream.toByteArray())); - })) { + ((dataNodeLocation, consensusGroupIdList) -> + new TAlterTimeSeriesReq( + consensusGroupIdList, + queryId, + measurementPathBytes, + operationType, + prepareDataTypeBytesData()))) { @Override protected List processResponseOfOneDataNode( @@ -287,12 +202,11 @@ protected List processResponseOfOneDataNode( final List subStatus = response.getSubStatus(); for (int i = 0; i < subStatus.size(); i++) { if (subStatus.get(i).getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode() - && !(subStatus.get(i).getCode() - == TSStatusCode.PATH_NOT_EXIST.getStatusCode())) { + && subStatus.get(i).getCode() == TSStatusCode.PATH_NOT_EXIST.getStatusCode()) { failedRegionList.add(consensusGroupIdList.get(i)); } } - } else if (!(response.getCode() == TSStatusCode.PATH_NOT_EXIST.getStatusCode())) { + } else if (response.getCode() == TSStatusCode.PATH_NOT_EXIST.getStatusCode()) { failedRegionList.addAll(consensusGroupIdList); } if (!failedRegionList.isEmpty()) { From 46a58a931f875fdc3b1899827eca8e58f824f81f Mon Sep 17 00:00:00 2001 From: libo Date: Wed, 28 Jan 2026 11:15:33 +0800 Subject: [PATCH 3/5] Correct IT. --- .../iotdb/db/it/schema/IoTDBAlterTimeSeriesTypeIT.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBAlterTimeSeriesTypeIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBAlterTimeSeriesTypeIT.java index 03df8f92fcf7..28aad20a404e 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBAlterTimeSeriesTypeIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBAlterTimeSeriesTypeIT.java @@ -443,11 +443,7 @@ public void testAlterNonExist() throws IoTDBConnectionException, StatementExecut "ALTER TIMESERIES " + database + ".non_exist.s1 SET DATA TYPE INT64"); fail("Should throw exception"); } catch (StatementExecutionException e) { - assertEquals( - "508: Timeseries [" - + database - + ".non_exist.s1] does not exist or is represented by device template", - e.getMessage()); + assertEquals("508: Path [" + database + ".non_exist.s1] does not exist", e.getMessage()); } // Make the "non_exist" device exist, test the "nonexist" measurement if it can be altered @@ -460,7 +456,7 @@ public void testAlterNonExist() throws IoTDBConnectionException, StatementExecut fail("Should throw exception"); } catch (StatementExecutionException e) { assertEquals( - "507: Alter timeseries data type in schema regions failed. Failures: {DataNodeId: 1=[TSStatus(code:508, message:Path [" + "507: Alter timeseries " + database + ".d1.nonexistent data type to STRING in schema regions failed. Failures: {DataNodeId: 1=[TSStatus(code:508, message:Path [" + database + ".d1.nonexistent] does not exist)]}", e.getMessage()); From 4eff4b43eac72727e7d04f8d671107135c5aa53d Mon Sep 17 00:00:00 2001 From: libo Date: Wed, 28 Jan 2026 11:28:55 +0800 Subject: [PATCH 4/5] Make code spotless. --- .../apache/iotdb/db/it/schema/IoTDBAlterTimeSeriesTypeIT.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBAlterTimeSeriesTypeIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBAlterTimeSeriesTypeIT.java index 28aad20a404e..8f08d2c1dd32 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBAlterTimeSeriesTypeIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBAlterTimeSeriesTypeIT.java @@ -456,7 +456,9 @@ public void testAlterNonExist() throws IoTDBConnectionException, StatementExecut fail("Should throw exception"); } catch (StatementExecutionException e) { assertEquals( - "507: Alter timeseries " + database + ".d1.nonexistent data type to STRING in schema regions failed. Failures: {DataNodeId: 1=[TSStatus(code:508, message:Path [" + "507: Alter timeseries " + + database + + ".d1.nonexistent data type to STRING in schema regions failed. Failures: {DataNodeId: 1=[TSStatus(code:508, message:Path [" + database + ".d1.nonexistent] does not exist)]}", e.getMessage()); From c3edb2dfd0923b5c9409d6a2127ace684450eacc Mon Sep 17 00:00:00 2001 From: libo Date: Wed, 28 Jan 2026 11:45:10 +0800 Subject: [PATCH 5/5] Correct the logic that verify path if it exist. --- .../impl/schema/AlterTimeSeriesDataTypeProcedure.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/AlterTimeSeriesDataTypeProcedure.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/AlterTimeSeriesDataTypeProcedure.java index 5d25dfafbfc4..59398f147fd9 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/AlterTimeSeriesDataTypeProcedure.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/AlterTimeSeriesDataTypeProcedure.java @@ -201,12 +201,11 @@ protected List processResponseOfOneDataNode( if (response.getCode() == TSStatusCode.MULTIPLE_ERROR.getStatusCode()) { final List subStatus = response.getSubStatus(); for (int i = 0; i < subStatus.size(); i++) { - if (subStatus.get(i).getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode() - && subStatus.get(i).getCode() == TSStatusCode.PATH_NOT_EXIST.getStatusCode()) { + if (subStatus.get(i).getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { failedRegionList.add(consensusGroupIdList.get(i)); } } - } else if (response.getCode() == TSStatusCode.PATH_NOT_EXIST.getStatusCode()) { + } else { failedRegionList.addAll(consensusGroupIdList); } if (!failedRegionList.isEmpty()) {