You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ji...@apache.org on 2025/02/18 10:35:46 UTC

(iotdb) branch master updated: Remove attribute columns after attributes are updated (#14721)

This is an automated email from the ASF dual-hosted git repository.

jiangtian pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 5f259bacc63 Remove attribute columns after attributes are updated (#14721)
5f259bacc63 is described below

commit 5f259bacc63a83f4db8d8951db710bf30d89b9d4
Author: Jiang Tian <jt...@163.com>
AuthorDate: Tue Feb 18 18:35:39 2025 +0800

    Remove attribute columns after attributes are updated (#14721)
    
    * Remove attribute columns after attributes are updated
    
    * fix tests
---
 .../iotdb/it/env/cluster/node/DataNodeWrapper.java |  8 +++
 .../it/session/IoTDBSessionRelationalIT.java       | 82 ++++++++++++++++++++++
 .../plan/analyze/schema/SchemaValidator.java       |  1 +
 .../relational/sql/ast/WrappedInsertStatement.java | 78 ++++++++++++--------
 .../plan/statement/crud/InsertBaseStatement.java   | 50 +++++++++++++
 .../crud/InsertMultiTabletsStatement.java          | 11 +++
 .../plan/statement/crud/InsertRowStatement.java    |  7 ++
 .../crud/InsertRowsOfOneDeviceStatement.java       |  5 ++
 .../plan/statement/crud/InsertRowsStatement.java   | 11 +++
 .../plan/statement/crud/InsertTabletStatement.java | 10 +++
 .../storageengine/dataregion/wal/io/WALReader.java |  2 +-
 .../plan/relational/analyzer/AnalyzerTest.java     |  7 +-
 12 files changed, 241 insertions(+), 31 deletions(-)

diff --git a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/node/DataNodeWrapper.java b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/node/DataNodeWrapper.java
index 0a61d91b468..76c81ffb3bb 100644
--- a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/node/DataNodeWrapper.java
+++ b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/node/DataNodeWrapper.java
@@ -150,6 +150,14 @@ public class DataNodeWrapper extends AbstractNodeWrapper {
     return workDirFilePath("data/datanode/system/schema", IoTDBStartCheck.PROPERTIES_FILE_NAME);
   }
 
+  public String getDataDir() {
+    return getNodePath() + File.separator + "data";
+  }
+
+  public String getWalDir() {
+    return getDataDir() + File.separator + "datanode" + File.separator + "wal";
+  }
+
   @Override
   protected MppJVMConfig initVMConfig() {
     return MppJVMConfig.builder()
diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/session/IoTDBSessionRelationalIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/session/IoTDBSessionRelationalIT.java
index 1f70341892b..9ca071fc17d 100644
--- a/integration-test/src/test/java/org/apache/iotdb/relational/it/session/IoTDBSessionRelationalIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/session/IoTDBSessionRelationalIT.java
@@ -18,10 +18,16 @@
  */
 package org.apache.iotdb.relational.it.session;
 
+import org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalInsertRowsNode;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalInsertTabletNode;
+import org.apache.iotdb.db.storageengine.dataregion.wal.buffer.WALEntry;
+import org.apache.iotdb.db.storageengine.dataregion.wal.io.WALReader;
 import org.apache.iotdb.isession.ISession;
 import org.apache.iotdb.isession.ITableSession;
 import org.apache.iotdb.isession.SessionDataSet;
 import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
 import org.apache.iotdb.it.framework.IoTDBTestRunner;
 import org.apache.iotdb.itbase.category.TableClusterIT;
 import org.apache.iotdb.itbase.category.TableLocalStandaloneIT;
@@ -43,6 +49,8 @@ import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
 
+import java.io.File;
+import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.time.LocalDate;
 import java.util.ArrayList;
@@ -56,6 +64,7 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 @RunWith(IoTDBTestRunner.class)
@@ -1529,4 +1538,77 @@ public class IoTDBSessionRelationalIT {
       assertEquals(30, cnt);
     }
   }
+
+  @Test
+  public void testAttrColumnRemoved()
+      throws IoTDBConnectionException, StatementExecutionException, IOException {
+    EnvFactory.getEnv().cleanClusterEnvironment();
+    EnvFactory.getEnv().getConfig().getCommonConfig().setWalMode("SYNC");
+    EnvFactory.getEnv().initClusterEnvironment();
+    try (ITableSession session = EnvFactory.getEnv().getTableSessionConnection()) {
+      session.executeNonQueryStatement("create database if not exists db1");
+      session.executeNonQueryStatement("use db1");
+      session.executeNonQueryStatement(
+          "CREATE TABLE remove_attr_col (tag1 string tag, attr1 string attribute, "
+              + "m1 double "
+              + "field)");
+
+      // insert tablet to WAL
+      List<IMeasurementSchema> schemaList = new ArrayList<>();
+      schemaList.add(new MeasurementSchema("tag1", TSDataType.STRING));
+      schemaList.add(new MeasurementSchema("attr1", TSDataType.STRING));
+      schemaList.add(new MeasurementSchema("m1", TSDataType.DOUBLE));
+      final List<ColumnCategory> columnTypes =
+          Arrays.asList(ColumnCategory.TAG, ColumnCategory.ATTRIBUTE, ColumnCategory.FIELD);
+
+      long timestamp = 0;
+      Tablet tablet =
+          new Tablet(
+              "remove_attr_col",
+              IMeasurementSchema.getMeasurementNameList(schemaList),
+              IMeasurementSchema.getDataTypeList(schemaList),
+              columnTypes);
+
+      for (int rowIndex = 0; rowIndex < 10; rowIndex++) {
+        tablet.addTimestamp(rowIndex, timestamp);
+        tablet.addValue("tag1", rowIndex, "tag:1");
+        tablet.addValue("attr1", rowIndex, "attr:" + timestamp);
+        tablet.addValue("m1", rowIndex, timestamp * 1.0);
+        timestamp++;
+      }
+      session.insert(tablet);
+      tablet.reset();
+
+      // insert records to WAL
+      session.executeNonQueryStatement(
+          "INSERT INTO remove_attr_col (time, tag1, attr1, m1) VALUES (10, 'tag:1', 'attr:10', 10.0)");
+
+      // check WAL
+      for (DataNodeWrapper dataNodeWrapper : EnvFactory.getEnv().getDataNodeWrapperList()) {
+        String walNodeDir = dataNodeWrapper.getWalDir() + File.separator + "0";
+        File[] walFiles = new File(walNodeDir).listFiles(f -> f.getName().endsWith(".wal"));
+        if (walFiles != null && walFiles.length > 0) {
+          File walFile = walFiles[0];
+          WALEntry entry;
+          try (WALReader walReader = new WALReader(walFile)) {
+            entry = walReader.next();
+            RelationalInsertTabletNode tabletNode = (RelationalInsertTabletNode) entry.getValue();
+            assertTrue(
+                Arrays.stream(tabletNode.getColumnCategories())
+                    .noneMatch(c -> c == TsTableColumnCategory.ATTRIBUTE));
+
+            entry = walReader.next();
+            RelationalInsertRowsNode rowsNode = (RelationalInsertRowsNode) entry.getValue();
+            assertTrue(
+                Arrays.stream(rowsNode.getInsertRowNodeList().get(0).getColumnCategories())
+                    .noneMatch(c -> c == TsTableColumnCategory.ATTRIBUTE));
+            return;
+          }
+        }
+      }
+    } finally {
+      EnvFactory.getEnv().cleanClusterEnvironment();
+      EnvFactory.getEnv().initClusterEnvironment();
+    }
+  }
 }
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/schema/SchemaValidator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/schema/SchemaValidator.java
index 441e882242a..6023ea4bea2 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/schema/SchemaValidator.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/schema/SchemaValidator.java
@@ -79,6 +79,7 @@ public class SchemaValidator {
               insertStatement.getTableName()));
       insertStatement.updateAfterSchemaValidation(context);
       insertStatement.validateDeviceSchema(metadata, context);
+      insertStatement.removeAttributeColumns();
     } catch (final QueryProcessException e) {
       throw new SemanticException(e.getMessage());
     }
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/WrappedInsertStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/WrappedInsertStatement.java
index 43b09decdef..1cb351ec9f5 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/WrappedInsertStatement.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/WrappedInsertStatement.java
@@ -163,24 +163,49 @@ public abstract class WrappedInsertStatement extends WrappedStatement
     tableSchema = null;
   }
 
+  public static void processNonExistColumn(
+      ColumnSchema incoming, InsertBaseStatement innerTreeStatement, int i) {
+    // the column does not exist and auto-creation is disabled
+    SemanticException semanticException =
+        new SemanticException(
+            "Column " + incoming.getName() + " does not exists or fails to be " + "created",
+            TSStatusCode.COLUMN_NOT_EXISTS.getStatusCode());
+    if (incoming.getColumnCategory() != TsTableColumnCategory.FIELD
+        || !IoTDBDescriptor.getInstance().getConfig().isEnablePartialInsert()) {
+      // non-measurement columns cannot be partially inserted
+      throw semanticException;
+    } else {
+      // partial insertion
+      innerTreeStatement.markFailedMeasurement(i, semanticException);
+    }
+  }
+
+  public static void processTypeConflictColumn(
+      ColumnSchema incoming, ColumnSchema real, int i, InsertBaseStatement innerTreeStatement) {
+    SemanticException semanticException =
+        new SemanticException(
+            String.format(
+                "Incompatible data type of column %s: %s/%s",
+                incoming.getName(), incoming.getType(), real.getType()),
+            TSStatusCode.DATA_TYPE_MISMATCH.getStatusCode());
+    if (incoming.getColumnCategory() != TsTableColumnCategory.FIELD
+        || !IoTDBDescriptor.getInstance().getConfig().isEnablePartialInsert()) {
+      // non-measurement columns cannot be partially inserted
+      throw semanticException;
+    } else {
+      // partial insertion
+      innerTreeStatement.markFailedMeasurement(i, semanticException);
+    }
+  }
+
   public static void validateTableSchema(
       ColumnSchema incoming, ColumnSchema real, int i, InsertBaseStatement innerTreeStatement) {
     if (real == null) {
-      // the column does not exist and auto-creation is disabled
-      SemanticException semanticException =
-          new SemanticException(
-              "Column " + incoming.getName() + " does not exists or fails to be " + "created",
-              TSStatusCode.COLUMN_NOT_EXISTS.getStatusCode());
-      if (incoming.getColumnCategory() != TsTableColumnCategory.FIELD
-          || !IoTDBDescriptor.getInstance().getConfig().isEnablePartialInsert()) {
-        // non-measurement columns cannot be partially inserted
-        throw semanticException;
-      } else {
-        // partial insertion
-        innerTreeStatement.markFailedMeasurement(i, semanticException);
-        return;
-      }
+      processNonExistColumn(incoming, innerTreeStatement, i);
+      return;
     }
+
+    // check data type
     if (incoming.getType() == null || incoming.getColumnCategory() != TsTableColumnCategory.FIELD) {
       // sql insertion does not provide type
       // the type is inferred and can be inconsistent with the existing one
@@ -188,22 +213,11 @@ public abstract class WrappedInsertStatement extends WrappedStatement
     } else if (!InternalTypeManager.getTSDataType(real.getType())
             .isCompatible(InternalTypeManager.getTSDataType(incoming.getType()))
         && !innerTreeStatement.isForceTypeConversion()) {
-      SemanticException semanticException =
-          new SemanticException(
-              String.format(
-                  "Incompatible data type of column %s: %s/%s",
-                  incoming.getName(), incoming.getType(), real.getType()),
-              TSStatusCode.DATA_TYPE_MISMATCH.getStatusCode());
-      if (incoming.getColumnCategory() != TsTableColumnCategory.FIELD
-          || !IoTDBDescriptor.getInstance().getConfig().isEnablePartialInsert()) {
-        // non-measurement columns cannot be partially inserted
-        throw semanticException;
-      } else {
-        // partial insertion
-        innerTreeStatement.markFailedMeasurement(i, semanticException);
-        return;
-      }
+      processTypeConflictColumn(incoming, real, i, innerTreeStatement);
+      return;
     }
+
+    // check column category
     if (incoming.getColumnCategory() == null) {
       // sql insertion does not provide category
       innerTreeStatement.setColumnCategory(real.getColumnCategory(), i);
@@ -214,6 +228,8 @@ public abstract class WrappedInsertStatement extends WrappedStatement
               incoming.getName(), incoming.getColumnCategory(), real.getColumnCategory()),
           TSStatusCode.COLUMN_CATEGORY_MISMATCH.getStatusCode());
     }
+
+    // construct measurement schema
     TSDataType tsDataType = InternalTypeManager.getTSDataType(real.getType());
     MeasurementSchema measurementSchema =
         new MeasurementSchema(
@@ -244,4 +260,8 @@ public abstract class WrappedInsertStatement extends WrappedStatement
   public void toLowerCase() {
     getInnerTreeStatement().toLowerCase();
   }
+
+  public void removeAttributeColumns() {
+    getInnerTreeStatement().removeAttributeColumns();
+  }
 }
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 8897ee80ef0..af95bc4edb9 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
@@ -51,6 +51,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -368,6 +369,55 @@ public abstract class InsertBaseStatement extends Statement {
             .collect(Collectors.toList());
   }
 
+  @TableModel
+  public void removeAttributeColumns() {
+    if (columnCategories == null) {
+      return;
+    }
+
+    List<Integer> columnsToKeep = new ArrayList<>();
+    for (int i = 0; i < columnCategories.length; i++) {
+      if (!columnCategories[i].equals(TsTableColumnCategory.ATTRIBUTE)) {
+        columnsToKeep.add(i);
+      }
+    }
+
+    if (columnsToKeep.size() == columnCategories.length) {
+      return;
+    }
+
+    if (failedMeasurementIndex2Info != null) {
+      failedMeasurementIndex2Info =
+          failedMeasurementIndex2Info.entrySet().stream()
+              .collect(Collectors.toMap(e -> columnsToKeep.indexOf(e.getKey()), Entry::getValue));
+    }
+
+    if (measurementSchemas != null) {
+      measurementSchemas =
+          columnsToKeep.stream().map(i -> measurementSchemas[i]).toArray(MeasurementSchema[]::new);
+    }
+    if (measurements != null) {
+      measurements = columnsToKeep.stream().map(i -> measurements[i]).toArray(String[]::new);
+    }
+    if (dataTypes != null) {
+      dataTypes = columnsToKeep.stream().map(i -> dataTypes[i]).toArray(TSDataType[]::new);
+    }
+    if (columnCategories != null) {
+      columnCategories =
+          columnsToKeep.stream()
+              .map(i -> columnCategories[i])
+              .toArray(TsTableColumnCategory[]::new);
+    }
+
+    subRemoveAttributeColumns(columnsToKeep);
+
+    // to reconstruct indices
+    idColumnIndices = null;
+    attrColumnIndices = null;
+  }
+
+  protected abstract void subRemoveAttributeColumns(List<Integer> columnsToKeep);
+
   public static class FailedMeasurementInfo {
     protected String measurement;
     protected TSDataType dataType;
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertMultiTabletsStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertMultiTabletsStatement.java
index e103e6539c4..b4a1db885f8 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertMultiTabletsStatement.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertMultiTabletsStatement.java
@@ -31,6 +31,7 @@ import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.exception.NotImplementedException;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
@@ -172,4 +173,14 @@ public class InsertMultiTabletsStatement extends InsertBaseStatement {
     }
     return database;
   }
+
+  @Override
+  public void removeAttributeColumns() {
+    subRemoveAttributeColumns(Collections.emptyList());
+  }
+
+  @Override
+  protected void subRemoveAttributeColumns(List<Integer> columnsToKeep) {
+    insertTabletStatementList.forEach(InsertBaseStatement::removeAttributeColumns);
+  }
 }
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowStatement.java
index 46b81205cc4..cdf37fd2e1f 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowStatement.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowStatement.java
@@ -528,4 +528,11 @@ public class InsertRowStatement extends InsertBaseStatement implements ISchemaVa
     super.swapColumn(src, target);
     CommonUtils.swapArray(values, src, target);
   }
+
+  @Override
+  protected void subRemoveAttributeColumns(List<Integer> columnsToKeep) {
+    if (values != null) {
+      values = columnsToKeep.stream().map(i -> values[i]).toArray();
+    }
+  }
 }
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowsOfOneDeviceStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowsOfOneDeviceStatement.java
index 08630f82945..4dab9d423ce 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowsOfOneDeviceStatement.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowsOfOneDeviceStatement.java
@@ -195,4 +195,9 @@ public class InsertRowsOfOneDeviceStatement extends InsertBaseStatement {
     }
     return database;
   }
+
+  @Override
+  protected void subRemoveAttributeColumns(List<Integer> columnsToKeep) {
+    insertRowStatementList.forEach(InsertRowStatement::removeAttributeColumns);
+  }
 }
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowsStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowsStatement.java
index 2d7ba7a2ba6..f0173885bdf 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowsStatement.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowsStatement.java
@@ -35,6 +35,7 @@ import org.apache.tsfile.exception.NotImplementedException;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
@@ -209,4 +210,14 @@ public class InsertRowsStatement extends InsertBaseStatement {
   public Statement toRelationalStatement(MPPQueryContext context) {
     return new InsertRows(this, context);
   }
+
+  @Override
+  public void removeAttributeColumns() {
+    subRemoveAttributeColumns(Collections.emptyList());
+  }
+
+  @Override
+  protected void subRemoveAttributeColumns(List<Integer> columnsToKeep) {
+    insertRowStatementList.forEach(InsertBaseStatement::removeAttributeColumns);
+  }
 }
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertTabletStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertTabletStatement.java
index ae297a67271..86ee1afe089 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertTabletStatement.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertTabletStatement.java
@@ -532,4 +532,14 @@ public class InsertTabletStatement extends InsertBaseStatement implements ISchem
     }
     return nullBitMaps[col].isMarked(row);
   }
+
+  @Override
+  protected void subRemoveAttributeColumns(List<Integer> columnsToKeep) {
+    if (columns != null) {
+      columns = columnsToKeep.stream().map(i -> columns[i]).toArray();
+    }
+    if (nullBitMaps != null) {
+      nullBitMaps = columnsToKeep.stream().map(i -> nullBitMaps[i]).toArray(BitMap[]::new);
+    }
+  }
 }
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/wal/io/WALReader.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/wal/io/WALReader.java
index 261e95eb52f..0c2e5a18c00 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/wal/io/WALReader.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/wal/io/WALReader.java
@@ -103,7 +103,7 @@ public class WALReader implements Closeable {
    * @throws NoSuchElementException when not calling hasNext before.
    */
   public WALEntry next() {
-    if (nextEntry == null) {
+    if (!hasNext()) {
       throw new NoSuchElementException();
     }
     WALEntry next = nextEntry;
diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java
index fc20d094add..6152c603da1 100644
--- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java
+++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java
@@ -1182,6 +1182,9 @@ public class AnalyzerTest {
               new String[] {StatementTestUtils.tableName(), ((Binary[]) columns[0])[i].toString()}),
           insertTabletNode.getDeviceID(i));
     }
+
+    // attr column should be removed
+    columns = new Object[] {columns[0], columns[2]};
     assertArrayEquals(columns, insertTabletNode.getColumns());
     assertArrayEquals(StatementTestUtils.genTimestamps(), insertTabletNode.getTimes());
 
@@ -1224,12 +1227,14 @@ public class AnalyzerTest {
         (RelationalInsertRowNode) logicalQueryPlan.getRootNode();
 
     assertEquals(insertNode.getTableName(), StatementTestUtils.tableName());
-    final Object[] columns = StatementTestUtils.genValues(0);
+    Object[] columns = StatementTestUtils.genValues(0);
     assertEquals(
         Factory.DEFAULT_FACTORY.create(
             new String[] {StatementTestUtils.tableName(), columns[0].toString()}),
         insertNode.getDeviceID());
 
+    // attr column should be removed
+    columns = new Object[] {columns[0], columns[2]};
     assertArrayEquals(columns, insertNode.getValues());
     assertEquals(StatementTestUtils.genTimestamps()[0], insertNode.getTime());