@Override
  public CloseableIterable<CombinedScanTask> planTasks() {
    // base file
    CloseableIterable<MixedFileScanTask> baseFileList;
    baseFileList = planBaseFiles();

    // change file
    CloseableIterable<MixedFileScanTask> changeFileList;
    if (table.primaryKeySpec().primaryKeyExisted()) {
      changeFileList = planChangeFiles();
    } else {
      changeFileList = CloseableIterable.empty();
    }

    // 1. group files by partition
    StructLikeMap<Collection<MixedFileScanTask>> partitionedFiles =
        groupFilesByPartition(table.spec(), changeFileList, baseFileList);
    LOG.info("planning table {} need plan partition size {}", table.id(), partitionedFiles.size());
    partitionedFiles.forEach(this::partitionPlan);
    LOG.info("planning table {} partitionPlan end", table.id());
    // 2.split node task (FileScanTask -> FileScanTask List)
    split();
    LOG.info("planning table {} split end", table.id());
    // 3.combine node task (FileScanTask List -> CombinedScanTask)
    return combineNode(
        CloseableIterable.withNoopClose(splitTasks), splitSize, lookBack, openFileCost);
  }
Config this scan with filter by the {@link Expression}. For Change Table, only filters related
to partition will take effect.

@param expr a filter expression
@return scan based on this with results filtered by the expression
amoro-format-iceberg/src/main/java/org/apache/amoro/scan/BasicKeyedTableScan.java