From 0fa5dd027d1a2b4891ad8a5c1687a9eff33e87f9 Mon Sep 17 00:00:00 2001 From: Caideyipi <87789683+Caideyipi@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:36:43 +0800 Subject: [PATCH] Fix no-tag table attribute deletion --- .../it/db/it/IoTDBDeletionTableIT.java | 18 +++ .../plan/relational/sql/ast/InsertRows.java | 115 +++++++++++++++++- 2 files changed, 132 insertions(+), 1 deletion(-) diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBDeletionTableIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBDeletionTableIT.java index 3ac335df1ada0..82b6c1a8ff6b6 100644 --- a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBDeletionTableIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBDeletionTableIT.java @@ -653,6 +653,24 @@ public void testDeleteDataByAttributeFilterWithoutTagColumns() throws SQLExcepti + "(3, 'blue', 'large', 13)," + "(4, 'red', 'small', 14)"); + TestUtils.assertResultSetEqual( + statement.executeQuery("show devices from delete_by_attr_no_tag"), + "attr1,attr2,", + Collections.singleton("red,small,")); + TestUtils.assertResultSetEqual( + statement.executeQuery( + "show devices from delete_by_attr_no_tag " + + "where attr1 = 'red' AND attr2 = 'small'"), + "attr1,attr2,", + Collections.singleton("red,small,")); + statement.execute( + "INSERT INTO delete_by_attr_no_tag(time, attr1, attr2, s1) VALUES " + + "(2, 'blue', 'large', 12)," + + "(4, 'red', 'small', 14)"); + TestUtils.assertResultSetEqual( + statement.executeQuery("show devices from delete_by_attr_no_tag"), + "attr1,attr2,", + Collections.singleton("red,small,")); statement.execute( "DELETE FROM delete_by_attr_no_tag " + "WHERE time >= 2 AND time <= 4 AND attr1 = 'red' AND attr2 = 'small'"); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/InsertRows.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/InsertRows.java index 4eb49df718360..6b5804ba4ebb8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/InsertRows.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/InsertRows.java @@ -31,11 +31,15 @@ import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowsStatement; import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.utils.Constants; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Objects; public class InsertRows extends WrappedInsertStatement { @@ -100,10 +104,26 @@ public void validateTableSchema(Metadata metadata, MPPQueryContext context) { @Override public void validateDeviceSchema(Metadata metadata, MPPQueryContext context) { + final Map> validationMap = + new LinkedHashMap<>(); for (InsertRowStatement insertRowStatement : getInnerTreeStatement().getInsertRowStatementList()) { - metadata.validateDeviceSchema(createTableSchemaValidation(insertRowStatement), context); + final ITableDeviceSchemaValidation rowValidation = + createTableSchemaValidation(insertRowStatement); + validationMap + .computeIfAbsent(rowValidation.getDatabase(), key -> new LinkedHashMap<>()) + .computeIfAbsent( + rowValidation.getTableName(), + tableName -> + new CoalescedDeviceSchemaValidation(rowValidation.getDatabase(), tableName)) + .add( + rowValidation.getDeviceIdList().get(0), + rowValidation.getAttributeColumnNameList(), + rowValidation.getAttributeValueList().get(0)); } + validationMap.values().stream() + .flatMap(tableValidationMap -> tableValidationMap.values().stream()) + .forEach(validation -> metadata.validateDeviceSchema(validation, context)); } protected ITableDeviceSchemaValidation createTableSchemaValidation( @@ -157,4 +177,97 @@ public List getAttributeValueList() { } }; } + + private static class CoalescedDeviceSchemaValidation implements ITableDeviceSchemaValidation { + + private final String database; + private final String tableName; + private final Map> deviceAttributeValueMap = + new LinkedHashMap<>(); + private final List attributeColumnNameList = new ArrayList<>(); + private final Map attributeColumnIndexMap = new LinkedHashMap<>(); + + private CoalescedDeviceSchemaValidation(final String database, final String tableName) { + this.database = database; + this.tableName = tableName; + } + + private void add( + final Object[] deviceId, + final List attributeColumnNames, + final Object[] attributeValues) { + final Map attributeValueMap = + deviceAttributeValueMap.computeIfAbsent( + new DeviceIdKey(deviceId), key -> new HashMap<>()); + for (int i = 0; i < attributeColumnNames.size(); i++) { + final int attributeColumnIndex = + attributeColumnIndexMap.computeIfAbsent( + attributeColumnNames.get(i), + attributeColumnName -> { + attributeColumnNameList.add(attributeColumnName); + return attributeColumnNameList.size() - 1; + }); + if (i < attributeValues.length + && attributeValues[i] != null + && attributeValues[i] != Constants.NONE) { + attributeValueMap.put(attributeColumnIndex, attributeValues[i]); + } + } + } + + @Override + public String getDatabase() { + return database; + } + + @Override + public String getTableName() { + return tableName; + } + + @Override + public List getDeviceIdList() { + final List deviceIdList = new ArrayList<>(deviceAttributeValueMap.size()); + deviceAttributeValueMap.keySet().forEach(key -> deviceIdList.add(key.deviceId)); + return deviceIdList; + } + + @Override + public List getAttributeColumnNameList() { + return attributeColumnNameList; + } + + @Override + public List getAttributeValueList() { + final List attributeValueList = new ArrayList<>(deviceAttributeValueMap.size()); + for (final Map attributeValueMap : deviceAttributeValueMap.values()) { + final Object[] attributeValues = new Object[attributeColumnNameList.size()]; + Arrays.fill(attributeValues, Constants.NONE); + attributeValueMap.forEach((index, value) -> attributeValues[index] = value); + attributeValueList.add(attributeValues); + } + return attributeValueList; + } + } + + private static class DeviceIdKey { + + private final Object[] deviceId; + + private DeviceIdKey(final Object[] deviceId) { + this.deviceId = (Object[]) deviceId.clone(); + } + + @Override + public boolean equals(final Object object) { + return this == object + || object instanceof DeviceIdKey + && Arrays.equals(deviceId, ((DeviceIdKey) object).deviceId); + } + + @Override + public int hashCode() { + return Arrays.hashCode(deviceId); + } + } }