This is an automated email from the ASF dual-hosted git repository. shuwenwei pushed a commit to branch fix/remap-failed-measurement-index-after-column-reorder in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit fb27013a1b2970d445b5ded61baed392e5adc243 Author: shuwenwei <[email protected]> AuthorDate: Sat Oct 10 18:49:46 2026 +0800 [Fix] Remap failed-measurement indices when reordering insert columns --- .../plan/statement/crud/InsertBaseStatement.java | 46 ++++++++++++++++++++++ .../crud/InsertStatementPartialInsertTest.java | 26 ++++++++++++ 2 files changed, 72 insertions(+) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertBaseStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertBaseStatement.java index 5168678ff67..e305a13fdbd 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertBaseStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertBaseStatement.java @@ -767,10 +767,30 @@ public abstract class InsertBaseStatement extends Statement implements Accountab if (inputLocations != null) { CommonUtils.swapArray(inputLocations, src, target); } + swapFailedMeasurementIndex(src, target); tagColumnIndices = null; attrColumnIndices = null; } + /** + * Keeps {@link #failedMeasurementIndex2Info} consistent with the measurement arrays after the + * columns at {@code src} and {@code target} are swapped, so that the failure records still point + * at the columns they were created for. + */ + private void swapFailedMeasurementIndex(final int src, final int target) { + if (failedMeasurementIndex2Info == null || failedMeasurementIndex2Info.isEmpty()) { + return; + } + final FailedMeasurementInfo srcInfo = failedMeasurementIndex2Info.remove(src); + final FailedMeasurementInfo targetInfo = failedMeasurementIndex2Info.remove(target); + if (targetInfo != null) { + failedMeasurementIndex2Info.put(src, targetInfo); + } + if (srcInfo != null) { + failedMeasurementIndex2Info.put(target, srcInfo); + } + } + /** * The oldToNewMapping array provides the mapping from old array positions to new array positions: * newArray[oldToNewMapping[oldIdx]] = oldArray[oldIdx] @@ -838,11 +858,37 @@ public abstract class InsertBaseStatement extends Statement implements Accountab typeConvertors = newTypeConvertors; inputLocations = newInputLocations; + rebuildFailedMeasurementIndex(newToOldMapping); + // Clear cached indices tagColumnIndices = null; attrColumnIndices = null; } + /** + * Rewrites the keys of {@link #failedMeasurementIndex2Info} from the old array positions to the + * new ones described by {@code newToOldMapping} ({@code newToOldMapping[newIdx] = oldIdx}), so + * that the failure records follow the reordered columns. + */ + private void rebuildFailedMeasurementIndex(final int[] newToOldMapping) { + if (failedMeasurementIndex2Info == null || failedMeasurementIndex2Info.isEmpty()) { + return; + } + final Map<Integer, FailedMeasurementInfo> remapped = + new HashMap<>(failedMeasurementIndex2Info.size()); + for (int newIdx = 0; newIdx < newToOldMapping.length; newIdx++) { + final int oldIdx = newToOldMapping[newIdx]; + if (oldIdx < 0) { + continue; + } + final FailedMeasurementInfo info = failedMeasurementIndex2Info.get(oldIdx); + if (info != null) { + remapped.put(newIdx, info); + } + } + failedMeasurementIndex2Info = remapped; + } + private int getRequiredColumnArrayLength(final int index) { return Math.max(measurements == null ? 0 : measurements.length, index + 1); } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertStatementPartialInsertTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertStatementPartialInsertTest.java index b6b26cacc8e..095681c931b 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertStatementPartialInsertTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertStatementPartialInsertTest.java @@ -247,6 +247,32 @@ public class InsertStatementPartialInsertTest { Assert.assertEquals(TsTableColumnCategory.TAG, statement.getColumnCategories()[0]); } + @Test + public void testSwapColumnRemapsFailedMeasurementIndex() throws Exception { + final InsertRowStatement statement = createInsertRowStatement(); + statement.markFailedMeasurement(0, new RuntimeException("failed")); + + statement.swapColumn(0, 1); + + Assert.assertEquals("s2", statement.getMeasurements()[0]); + Assert.assertNull(statement.getMeasurements()[1]); + Assert.assertEquals(1, statement.getFailedMeasurementInfoMap().size()); + Assert.assertEquals("s1", statement.getFailedMeasurementInfoMap().get(1).getMeasurement()); + } + + @Test + public void testRebuildArraysAfterExpansionRemapsFailedMeasurementIndex() throws Exception { + final InsertRowStatement statement = createInsertRowStatement(); + statement.markFailedMeasurement(0, new RuntimeException("failed")); + + statement.rebuildArraysAfterExpansion(new int[] {-1, 0, 1}, new String[] {"tag1", "s1", "s2"}); + + Assert.assertEquals(1, statement.getFailedMeasurementInfoMap().size()); + Assert.assertEquals("s1", statement.getFailedMeasurementInfoMap().get(1).getMeasurement()); + Assert.assertNull(statement.getMeasurements()[1]); + Assert.assertEquals("s2", statement.getMeasurements()[2]); + } + @Test public void testInsertRowStatementSwapColumnInvalidatesTableDeviceId() throws Exception { final InsertRowStatement statement = createInsertRowStatement();
