private LastQueryAggTableScanOperator constructLastQueryAggTableScanOperator(
      AggregationTableScanNode node,
      AbstractAggTableScanOperator.AbstractAggTableScanOperatorParameter parameter,
      LocalExecutionPlanContext context) {
    List<Integer> hitCachesIndexes = new ArrayList<>();
    List<Pair<OptionalLong, TsPrimitiveType[]>> hitCachedResults = new ArrayList<>();
    List<DeviceEntry> cachedDeviceEntries = new ArrayList<>();
    List<DeviceEntry> unCachedDeviceEntries = new ArrayList<>();
    long tableTTL =
        DataNodeTTLCache.getInstance()
            .getTTLForTable(
                node.getQualifiedObjectName().getDatabaseName(),
                node.getQualifiedObjectName().getObjectName());
    Filter updateTimeFilter =
        updateFilterUsingTTL(parameter.getSeriesScanOptions().getGlobalTimeFilter(), tableTTL);
    for (int i = 0; i < node.getDeviceEntries().size(); i++) {
      Optional<Pair<OptionalLong, TsPrimitiveType[]>> lastByResult =
          TableDeviceSchemaCache.getInstance()
              .getLastRow(
                  node.getQualifiedObjectName().getDatabaseName(),
                  node.getDeviceEntries().get(i).getDeviceID(),
                  "",
                  parameter.getMeasurementColumnNames());
      boolean allHitCache = true;
      if (lastByResult.isPresent() && lastByResult.get().getLeft().isPresent()) {
        for (int j = 0; j < lastByResult.get().getRight().length; j++) {
          TsPrimitiveType tsPrimitiveType = lastByResult.get().getRight()[j];
          if (tsPrimitiveType == null
              || (updateTimeFilter != null
                  && !LastQueryUtil.satisfyFilter(
                      updateTimeFilter,
                      new TimeValuePair(
                          lastByResult.get().getLeft().getAsLong(), tsPrimitiveType)))) {
            // the process logic is different from tree model which examine if
            // `isFilterGtOrGe(seriesScanOptions.getGlobalTimeFilter())`, set
            // `lastByResult.get().getRight()[j] = EMPTY_PRIMITIVE_TYPE`,
            // but it should skip in table model
            allHitCache = false;
            break;
          }
        }
      } else {
        allHitCache = false;
      }

      if (!allHitCache) {
        DeviceEntry deviceEntry = node.getDeviceEntries().get(i);
        AlignedFullPath alignedPath =
            constructAlignedPath(
                deviceEntry,
                parameter.getMeasurementColumnNames(),
                parameter.getMeasurementSchemas(),
                parameter.getAllSensors());
        ((DataDriverContext) context.getDriverContext()).addPath(alignedPath);
        unCachedDeviceEntries.add(deviceEntry);

        // last cache updateColumns need put "" as time column
        String[] updateColumns = new String[parameter.getMeasurementColumnNames().size() + 1];
        updateColumns[0] = "";
        for (int j = 1; j < updateColumns.length; j++) {
          updateColumns[j] = parameter.getMeasurementColumnNames().get(j - 1);
        }
        TableDeviceSchemaCache.getInstance()
            .initOrInvalidateLastCache(
                node.getQualifiedObjectName().getDatabaseName(),
                deviceEntry.getDeviceID(),
                updateColumns,
                false);
      } else {
        hitCachesIndexes.add(i);
        hitCachedResults.add(lastByResult.get());
        cachedDeviceEntries.add(node.getDeviceEntries().get(i));
      }
    }

    parameter.setDeviceEntries(unCachedDeviceEntries);

    // context add TableLastQueryOperator
    LastQueryAggTableScanOperator lastQueryOperator =
        new LastQueryAggTableScanOperator(
            parameter,
            cachedDeviceEntries,
            node.getQualifiedObjectName(),
            hitCachesIndexes,
            hitCachedResults);

    ((DataDriverContext) context.getDriverContext()).addSourceOperator(lastQueryOperator);
    parameter
        .getOperatorContext()
        .setOperatorType(LastQueryAggTableScanOperator.class.getSimpleName());
    return lastQueryOperator;
  }
This Visitor is responsible for transferring Table PlanNode Tree to Table Operator Tree.
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java