Java Code Examples for org.apache.calcite.rel.type.RelDataType#getFieldCount()

The following examples show how to use org.apache.calcite.rel.type.RelDataType#getFieldCount() . These examples are extracted from open source projects. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source Project: flink   File: SubQueryDecorrelator.java    License: Apache License 2.0 6 votes vote down vote up
/**
 * Adds Projection to adjust the field index for join condition.
 *
 * <p>e.g. SQL: SELECT * FROM l WHERE b IN (SELECT COUNT(*) FROM r WHERE l.c = r.f
 * the rel in SubQuery is `LogicalAggregate(group=[{}], EXPR$1=[COUNT()])`.
 * After decorrelated, it was changed to `LogicalAggregate(group=[{0}], EXPR$0=[COUNT()])`,
 * and the output index of `COUNT()` was changed from 0 to 1.
 * So, add a project (`LogicalProject(EXPR$0=[$1], f=[$0])`) to adjust output fields order.
 */
private RelNode addProjectionForIn(RelNode relNode) {
	if (relNode instanceof LogicalProject) {
		return relNode;
	}

	RelDataType rowType = relNode.getRowType();
	final List<RexNode> projects = new ArrayList<>();
	for (int i = 0; i < rowType.getFieldCount(); ++i) {
		projects.add(RexInputRef.of(i, rowType));
	}

	relBuilder.clear();
	relBuilder.push(relNode);
	relBuilder.project(projects, rowType.getFieldNames(), true);
	return relBuilder.build();
}
 
Example 2
Source Project: dremio-oss   File: DremioFieldTrimmer.java    License: Apache License 2.0 6 votes vote down vote up
public TrimResult trimFields(
  LimitRel limit,
  ImmutableBitSet fieldsUsed,
  Set<RelDataTypeField> extraFields
) {
  final RelDataType rowType = limit.getRowType();
  final int fieldCount = rowType.getFieldCount();
  final RelNode input = limit.getInput();

  final Set<RelDataTypeField> inputExtraFields = Collections.emptySet();
  TrimResult trimResult =
    trimChild(limit, input, fieldsUsed, inputExtraFields);
  RelNode newInput = trimResult.left;
  final Mapping inputMapping = trimResult.right;

  if (newInput == input
    && inputMapping.isIdentity()
    && fieldsUsed.cardinality() == fieldCount) {
    return result(limit, Mappings.createIdentity(fieldCount));
  }
  return result(limit.copy(newInput.getTraitSet(), ImmutableList.of(newInput)), inputMapping);
}
 
Example 3
Source Project: flink   File: SubQueryDecorrelator.java    License: Apache License 2.0 6 votes vote down vote up
/**
 * Adds Projection to adjust the field index for join condition.
 *
 * <p>e.g. SQL: SELECT * FROM l WHERE b IN (SELECT COUNT(*) FROM r WHERE l.c = r.f
 * the rel in SubQuery is `LogicalAggregate(group=[{}], EXPR$1=[COUNT()])`.
 * After decorrelated, it was changed to `LogicalAggregate(group=[{0}], EXPR$0=[COUNT()])`,
 * and the output index of `COUNT()` was changed from 0 to 1.
 * So, add a project (`LogicalProject(EXPR$0=[$1], f=[$0])`) to adjust output fields order.
 */
private RelNode addProjectionForIn(RelNode relNode) {
	if (relNode instanceof LogicalProject) {
		return relNode;
	}

	RelDataType rowType = relNode.getRowType();
	final List<RexNode> projects = new ArrayList<>();
	for (int i = 0; i < rowType.getFieldCount(); ++i) {
		projects.add(RexInputRef.of(i, rowType));
	}

	relBuilder.clear();
	relBuilder.push(relNode);
	relBuilder.project(projects, rowType.getFieldNames(), true);
	return relBuilder.build();
}
 
Example 4
/**
 * Build a join condition based on the left/right keys
 *
 * @param leftRowType
 * @param rightRowType
 * @param leftKeys
 * @param rightKeys
 * @param filterNulls
 * @param builder
 * @return a conjunction of equi-join conditions
 */
static RexNode buildJoinCondition(RexBuilder builder, RelDataType leftRowType, RelDataType rightRowType, List<Integer> leftKeys, List<Integer> rightKeys, List<Boolean> filterNulls) {
  final List<RexNode> equijoinList = Lists.newArrayList();
  final int numLeftFields = leftRowType.getFieldCount();
  final List<RelDataTypeField> leftTypes = leftRowType.getFieldList();
  final List<RelDataTypeField> rightTypes = rightRowType.getFieldList();

  for (int i = 0; i < leftKeys.size(); i++) {
    int leftKeyOrdinal = leftKeys.get(i);
    int rightKeyOrdinal = rightKeys.get(i);

    SqlBinaryOperator operator = filterNulls.get(i) ? SqlStdOperatorTable.EQUALS : SqlStdOperatorTable.IS_NOT_DISTINCT_FROM;
    RexNode leftInput = builder.makeInputRef(leftTypes.get(leftKeyOrdinal).getType(), leftKeyOrdinal);
    RexNode rightInput = builder.makeInputRef(rightTypes.get(rightKeyOrdinal).getType(), rightKeyOrdinal + numLeftFields);
    equijoinList.add(builder.makeCall(operator, leftInput, rightInput));
  }

  return RexUtil.composeConjunction(builder, equijoinList, false);
}
 
Example 5
Source Project: Flink-CEPplus   File: SqlValidatorImpl.java    License: Apache License 2.0 6 votes vote down vote up
public void validateWithItem(SqlWithItem withItem) {
	if (withItem.columnList != null) {
		final RelDataType rowType = getValidatedNodeType(withItem.query);
		final int fieldCount = rowType.getFieldCount();
		if (withItem.columnList.size() != fieldCount) {
			throw newValidationError(withItem.columnList,
				RESOURCE.columnCountMismatch());
		}
		SqlValidatorUtil.checkIdentifierListForDuplicates(
			withItem.columnList.getList(), validationErrorFunction);
	} else {
		// Luckily, field names have not been make unique yet.
		final List<String> fieldNames =
			getValidatedNodeType(withItem.query).getFieldNames();
		final int i = Util.firstDuplicate(fieldNames);
		if (i >= 0) {
			throw newValidationError(withItem.query,
				RESOURCE.duplicateColumnAndNoColumnList(fieldNames.get(i)));
		}
	}
}
 
Example 6
Source Project: calcite   File: SqlTypeUtil.java    License: Apache License 2.0 6 votes vote down vote up
/**
 * Returns whether two types are scalar types of the same family, or struct types whose fields
 * are pairwise of the same family.
 *
 * @param type1 First type
 * @param type2 Second type
 * @return Whether types have the same family
 */
private static boolean isSameFamily(RelDataType type1, RelDataType type2) {
  if (type1.isStruct() != type2.isStruct()) {
    return false;
  }

  if (type1.isStruct()) {
    int n = type1.getFieldCount();
    if (n != type2.getFieldCount()) {
      return false;
    }
    for (Pair<RelDataTypeField, RelDataTypeField> pair
        : Pair.zip(type1.getFieldList(), type2.getFieldList())) {
      if (!isSameFamily(pair.left.getType(), pair.right.getType())) {
        return false;
      }
    }
    return true;
  }

  final RelDataTypeFamily family1 = family(type1);
  final RelDataTypeFamily family2 = family(type2);
  return family1 == family2;
}
 
Example 7
Source Project: calcite   File: RexToLixTranslator.java    License: Apache License 2.0 6 votes vote down vote up
/**
 * Translates a {@link RexProgram} to a sequence of expressions and
 * declarations.
 *
 * @param program Program to be translated
 * @param typeFactory Type factory
 * @param conformance SQL conformance
 * @param list List of statements, populated with declarations
 * @param outputPhysType Output type, or null
 * @param root Root expression
 * @param inputGetter Generates expressions for inputs
 * @param correlates Provider of references to the values of correlated
 *                   variables
 * @return Sequence of expressions, optional condition
 */
public static List<Expression> translateProjects(RexProgram program,
    JavaTypeFactory typeFactory, SqlConformance conformance,
    BlockBuilder list, PhysType outputPhysType, Expression root,
    InputGetter inputGetter, Function1<String, InputGetter> correlates) {
  List<Type> storageTypes = null;
  if (outputPhysType != null) {
    final RelDataType rowType = outputPhysType.getRowType();
    storageTypes = new ArrayList<>(rowType.getFieldCount());
    for (int i = 0; i < rowType.getFieldCount(); i++) {
      storageTypes.add(outputPhysType.getJavaFieldType(i));
    }
  }
  return new RexToLixTranslator(program, typeFactory, root, inputGetter,
      list, new RexBuilder(typeFactory), conformance, null)
      .setCorrelates(correlates)
      .translateList(program.getProjectList(), storageTypes);
}
 
Example 8
Source Project: Bats   File: RelMdCollation.java    License: Apache License 2.0 5 votes vote down vote up
/** Helper method to determine a
 * {@link org.apache.calcite.rel.core.Values}'s collation.
 *
 * <p>We actually under-report the collations. A Values with 0 or 1 rows - an
 * edge case, but legitimate and very common - is ordered by every permutation
 * of every subset of the columns.
 *
 * <p>So, our algorithm aims to:<ul>
 *   <li>produce at most N collations (where N is the number of columns);
 *   <li>make each collation as long as possible;
 *   <li>do not repeat combinations already emitted -
 *       if we've emitted {@code (a, b)} do not later emit {@code (b, a)};
 *   <li>probe the actual values and make sure that each collation is
 *      consistent with the data
 * </ul>
 *
 * <p>So, for an empty Values with 4 columns, we would emit
 * {@code (a, b, c, d), (b, c, d), (c, d), (d)}. */
public static List<RelCollation> values(RelMetadataQuery mq,
    RelDataType rowType, ImmutableList<ImmutableList<RexLiteral>> tuples) {
  Util.discard(mq); // for future use
  final List<RelCollation> list = new ArrayList<>();
  final int n = rowType.getFieldCount();
  final List<Pair<RelFieldCollation, Ordering<List<RexLiteral>>>> pairs =
      new ArrayList<>();
outer:
  for (int i = 0; i < n; i++) {
    pairs.clear();
    for (int j = i; j < n; j++) {
      final RelFieldCollation fieldCollation = new RelFieldCollation(j);
      Ordering<List<RexLiteral>> comparator = comparator(fieldCollation);
      Ordering<List<RexLiteral>> ordering;
      if (pairs.isEmpty()) {
        ordering = comparator;
      } else {
        ordering = Util.last(pairs).right.compound(comparator);
      }
      pairs.add(Pair.of(fieldCollation, ordering));
      if (!ordering.isOrdered(tuples)) {
        if (j == i) {
          continue outer;
        }
        pairs.remove(pairs.size() - 1);
      }
    }
    if (!pairs.isEmpty()) {
      list.add(RelCollations.of(Pair.left(pairs)));
    }
  }
  return list;
}
 
Example 9
Source Project: flink   File: SubQueryDecorrelator.java    License: Apache License 2.0 5 votes vote down vote up
/**
 * Adds Projection to choose the fields used by join condition.
 */
private Frame addProjectionForExists(Frame frame) {
	final List<Integer> corIndices = new ArrayList<>(frame.getCorInputRefIndices());
	final RelNode rel = frame.r;
	final RelDataType rowType = rel.getRowType();
	if (corIndices.size() == rowType.getFieldCount()) {
		// no need projection
		return frame;
	}

	final List<RexNode> projects = new ArrayList<>();
	final Map<Integer, Integer> mapInputToOutput = new HashMap<>();

	Collections.sort(corIndices);
	int newPos = 0;
	for (int index : corIndices) {
		projects.add(RexInputRef.of(index, rowType));
		mapInputToOutput.put(index, newPos++);
	}

	relBuilder.clear();
	relBuilder.push(frame.r);
	relBuilder.project(projects);
	final RelNode newProject = relBuilder.build();
	final RexNode newCondition = adjustInputRefs(frame.c, mapInputToOutput, newProject.getRowType());

	// There is no old RelNode corresponding to newProject, so oldToNewOutputs is empty.
	return new Frame(rel, newProject, newCondition, new HashMap<>());
}
 
Example 10
Source Project: dremio-oss   File: CalciteArrowHelper.java    License: Apache License 2.0 5 votes vote down vote up
public RelDataType toCalciteRecordType(RelDataTypeFactory factory, Set<String> fieldBlacklist){
  FieldInfoBuilder builder = new FieldInfoBuilder(factory);
  for(Field f : bs) {
    if(!fieldBlacklist.contains(f.getName())){
      builder.add(f.getName(), toCalciteType(f, factory));
    }
  }
  RelDataType rowType = builder.build();
  if(rowType.getFieldCount() == 0){
    throw UserException.dataReadError().message("Selected table has no columns.").build(logger);
  }

  return rowType;
}
 
Example 11
Source Project: Bats   File: StandardConvertletTable.java    License: Apache License 2.0 5 votes vote down vote up
private static RexNode makeConstructorCall(
    SqlRexContext cx,
    SqlFunction constructor,
    List<RexNode> exprs) {
  final RexBuilder rexBuilder = cx.getRexBuilder();
  RelDataType type = rexBuilder.deriveReturnType(constructor, exprs);

  int n = type.getFieldCount();
  ImmutableList.Builder<RexNode> initializationExprs =
      ImmutableList.builder();
  final InitializerContext initializerContext = new InitializerContext() {
    public RexBuilder getRexBuilder() {
      return rexBuilder;
    }

    public RexNode convertExpression(SqlNode e) {
      throw new UnsupportedOperationException();
    }
  };
  for (int i = 0; i < n; ++i) {
    initializationExprs.add(
        cx.getInitializerExpressionFactory().newAttributeInitializer(
            type, constructor, i, exprs, initializerContext));
  }

  List<RexNode> defaultCasts =
      RexUtil.generateCastExpressions(
          rexBuilder,
          type,
          initializationExprs.build());

  return rexBuilder.makeNewInvocation(type, defaultCasts);
}
 
Example 12
Source Project: Bats   File: SqlTypeUtil.java    License: Apache License 2.0 5 votes vote down vote up
/**
 * Tests whether two types have the same name and structure, possibly with
 * differing modifiers. For example, VARCHAR(1) and VARCHAR(10) are
 * considered the same, while VARCHAR(1) and CHAR(1) are considered
 * different. Likewise, VARCHAR(1) MULTISET and VARCHAR(10) MULTISET are
 * considered the same.
 *
 * @return true if types have same name and structure
 */
public static boolean sameNamedType(RelDataType t1, RelDataType t2) {
  if (t1.isStruct() || t2.isStruct()) {
    if (!t1.isStruct() || !t2.isStruct()) {
      return false;
    }
    if (t1.getFieldCount() != t2.getFieldCount()) {
      return false;
    }
    List<RelDataTypeField> fields1 = t1.getFieldList();
    List<RelDataTypeField> fields2 = t2.getFieldList();
    for (int i = 0; i < fields1.size(); ++i) {
      if (!sameNamedType(
          fields1.get(i).getType(),
          fields2.get(i).getType())) {
        return false;
      }
    }
    return true;
  }
  RelDataType comp1 = t1.getComponentType();
  RelDataType comp2 = t2.getComponentType();
  if ((comp1 != null) || (comp2 != null)) {
    if ((comp1 == null) || (comp2 == null)) {
      return false;
    }
    if (!sameNamedType(comp1, comp2)) {
      return false;
    }
  }
  return t1.getSqlTypeName() == t2.getSqlTypeName();
}
 
Example 13
Source Project: calcite   File: SqlImplementor.java    License: Apache License 2.0 5 votes vote down vote up
private static int computeFieldCount(
    Map<String, RelDataType> aliases) {
  int x = 0;
  for (RelDataType type : aliases.values()) {
    x += type.getFieldCount();
  }
  return x;
}
 
Example 14
Source Project: Bats   File: RelOptUtil.java    License: Apache License 2.0 5 votes vote down vote up
public static boolean areRowTypesEqual(RelDataType rowType1, RelDataType rowType2, boolean compareNames) {
    if (rowType1 == rowType2) {
        return true;
    }
    if (compareNames) {
        // if types are not identity-equal, then either the names or
        // the types must be different
        return false;
    }
    if (rowType2.getFieldCount() != rowType1.getFieldCount()) {
        return false;
    }
    final List<RelDataTypeField> f1 = rowType1.getFieldList();
    final List<RelDataTypeField> f2 = rowType2.getFieldList();
    for (Pair<RelDataTypeField, RelDataTypeField> pair : Pair.zip(f1, f2)) {
        final RelDataType type1 = pair.left.getType();
        final RelDataType type2 = pair.right.getType();
        // If one of the types is ANY comparison should succeed
        if (type1.getSqlTypeName() == SqlTypeName.ANY || type2.getSqlTypeName() == SqlTypeName.ANY) {
            continue;
        }
        if (!type1.equals(type2)) {
            return false;
        }
    }
    return true;
}
 
Example 15
Source Project: calcite   File: SqlTypeUtil.java    License: Apache License 2.0 5 votes vote down vote up
/**
 * Tests whether two types have the same name and structure, possibly with
 * differing modifiers. For example, VARCHAR(1) and VARCHAR(10) are
 * considered the same, while VARCHAR(1) and CHAR(1) are considered
 * different. Likewise, VARCHAR(1) MULTISET and VARCHAR(10) MULTISET are
 * considered the same.
 *
 * @return true if types have same name and structure
 */
public static boolean sameNamedType(RelDataType t1, RelDataType t2) {
  if (t1.isStruct() || t2.isStruct()) {
    if (!t1.isStruct() || !t2.isStruct()) {
      return false;
    }
    if (t1.getFieldCount() != t2.getFieldCount()) {
      return false;
    }
    List<RelDataTypeField> fields1 = t1.getFieldList();
    List<RelDataTypeField> fields2 = t2.getFieldList();
    for (int i = 0; i < fields1.size(); ++i) {
      if (!sameNamedType(
          fields1.get(i).getType(),
          fields2.get(i).getType())) {
        return false;
      }
    }
    return true;
  }
  RelDataType comp1 = t1.getComponentType();
  RelDataType comp2 = t2.getComponentType();
  if ((comp1 != null) || (comp2 != null)) {
    if ((comp1 == null) || (comp2 == null)) {
      return false;
    }
    if (!sameNamedType(comp1, comp2)) {
      return false;
    }
  }
  return t1.getSqlTypeName() == t2.getSqlTypeName();
}
 
Example 16
Source Project: calcite   File: Window.java    License: Apache License 2.0 5 votes vote down vote up
@Override public boolean isValid(Litmus litmus, Context context) {
  // In the window specifications, an aggregate call such as
  // 'SUM(RexInputRef #10)' refers to expression #10 of inputProgram.
  // (Not its projections.)
  final RelDataType childRowType = getInput().getRowType();

  final int childFieldCount = childRowType.getFieldCount();
  final int inputSize = childFieldCount + constants.size();
  final List<RelDataType> inputTypes =
      new AbstractList<RelDataType>() {
        @Override public RelDataType get(int index) {
          return index < childFieldCount
              ? childRowType.getFieldList().get(index).getType()
              : constants.get(index - childFieldCount).getType();
        }

        @Override public int size() {
          return inputSize;
        }
      };

  final RexChecker checker = new RexChecker(inputTypes, context, litmus);
  int count = 0;
  for (Group group : groups) {
    for (RexWinAggCall over : group.aggCalls) {
      ++count;
      if (!checker.isValid(over)) {
        return litmus.fail(null);
      }
    }
  }
  if (count == 0) {
    return litmus.fail("empty");
  }
  return litmus.succeed();
}
 
Example 17
Source Project: Flink-CEPplus   File: SqlValidatorImpl.java    License: Apache License 2.0 5 votes vote down vote up
public List<List<String>> getFieldOrigins(SqlNode sqlQuery) {
	if (sqlQuery instanceof SqlExplain) {
		return Collections.emptyList();
	}
	final RelDataType rowType = getValidatedNodeType(sqlQuery);
	final int fieldCount = rowType.getFieldCount();
	if (!sqlQuery.isA(SqlKind.QUERY)) {
		return Collections.nCopies(fieldCount, null);
	}
	final List<List<String>> list = new ArrayList<>();
	for (int i = 0; i < fieldCount; i++) {
		list.add(getFieldOrigin(sqlQuery, i));
	}
	return ImmutableNullableList.copyOf(list);
}
 
Example 18
/**
 * {@inheritDoc}
 *
 * @param implementor GeodeImplementContext
 */
@Override public Result implement(EnumerableRelImplementor implementor, Prefer pref) {

  // travers all relations form this to the scan leaf
  final GeodeImplementContext geodeImplementContext = new GeodeImplementContext();
  ((GeodeRel) getInput()).implement(geodeImplementContext);

  final RelDataType rowType = getRowType();

  // PhysType is Enumerable Adapter class that maps SQL types (getRowType)
  // with physical Java types (getJavaTypes())
  final PhysType physType = PhysTypeImpl.of(
      implementor.getTypeFactory(),
      rowType,
      pref.prefer(JavaRowFormat.ARRAY));

  final List<Class> physFieldClasses = new AbstractList<Class>() {
    public Class get(int index) {
      return physType.fieldClass(index);
    }

    public int size() {
      return rowType.getFieldCount();
    }
  };

  // Expression meta-program for calling the GeodeTable.GeodeQueryable#query
  // method form the generated code
  final BlockBuilder blockBuilder = new BlockBuilder().append(
      Expressions.call(
          geodeImplementContext.table.getExpression(GeodeTable.GeodeQueryable.class),
          GEODE_QUERY_METHOD,
          // fields
          constantArrayList(Pair.zip(geodeFieldNames(rowType), physFieldClasses), Pair.class),
          // selected fields
          constantArrayList(toListMapPairs(geodeImplementContext.selectFields), Pair.class),
          // aggregate functions
          constantArrayList(
              toListMapPairs(geodeImplementContext.oqlAggregateFunctions), Pair.class),
          constantArrayList(geodeImplementContext.groupByFields, String.class),
          constantArrayList(geodeImplementContext.whereClause, String.class),
          constantArrayList(geodeImplementContext.orderByFields, String.class),
          Expressions.constant(geodeImplementContext.limitValue)));

  return implementor.result(physType, blockBuilder.toBlock());
}
 
Example 19
Source Project: Bats   File: RexUtil.java    License: Apache License 2.0 4 votes vote down vote up
/** Returns whether a list of expressions projects the incoming fields. */
public static boolean isIdentity(List<? extends RexNode> exps, RelDataType inputRowType) {
    return inputRowType.getFieldCount() == exps.size() && containIdentity(exps, inputRowType, Litmus.IGNORE);
}
 
Example 20
@Override
public void onMatch(RelOptRuleCall call) {
  final Aggregate agg = (Aggregate) call.rel(0);
  final TableScan scan = (TableScan) call.rel(call.rels.length - 1);
  final Project project = call.rels.length == 3 ? (Project) call.rel(1) : null;

  // Qualifying conditions for rule:
  //    1) There's no GroupBY key,
  //    2) Agg is not a DISTINCT agg
  //    3) Additional checks are done further below ..
  if (agg.getGroupCount() > 0 ||
      agg.containsDistinctCall()) {
    return;
  }

  DrillTable drillTable = DrillRelOptUtil.getDrillTable(scan);

  if (drillTable == null) {
    logger.debug("Rule does not apply since an eligible drill table instance was not found.");
    return;
  }

  Object selection = drillTable.getSelection();

  if (!(selection instanceof FormatSelection)) {
    logger.debug("Rule does not apply since only Parquet file format is eligible.");
    return;
  }

  PlannerSettings settings = call.getPlanner().getContext().unwrap(PlannerSettings.class);

  //  Rule is applicable only if the statistics for row count and null count are available from the metadata,
  FormatSelection formatSelection = (FormatSelection) selection;

  // Rule cannot be applied if the selection had wildcard since the totalrowcount cannot be read from the parent directory
  if (formatSelection.getSelection().hadWildcard()) {
    logger.debug("Rule does not apply when there is a wild card since the COUNT could not be determined from metadata.");
    return;
  }

  Pair<Boolean, Metadata_V4.MetadataSummary> status = checkMetadataForScanStats(settings, drillTable, formatSelection);
  if (!status.getLeft()) {
    logger.debug("Rule does not apply since MetadataSummary metadata was not found.");
    return;
  }

  Metadata_V4.MetadataSummary metadataSummary = status.getRight();
  Map<String, Long> result = collectCounts(settings, metadataSummary, agg, scan, project);
  logger.trace("Calculated the following aggregate counts: ", result);

  // if counts could not be determined, rule won't be applied
  if (result.isEmpty()) {
    logger.debug("Rule does not apply since one or more COUNTs could not be determined from metadata.");
    return;
  }

  List<Path> fileList =
          ImmutableList.of(Metadata.getSummaryFileName(formatSelection.getSelection().getSelectionRoot()));

  final RelDataType scanRowType = CountToDirectScanUtils.constructDataType(agg, result.keySet());

  final DynamicPojoRecordReader<Long> reader = new DynamicPojoRecordReader<>(
      CountToDirectScanUtils.buildSchema(scanRowType.getFieldNames()),
      Collections.singletonList((List<Long>) new ArrayList<>(result.values())));

  final ScanStats scanStats = new ScanStats(ScanStats.GroupScanProperty.EXACT_ROW_COUNT, 1, 1, scanRowType.getFieldCount());
  final MetadataDirectGroupScan directScan = new MetadataDirectGroupScan(reader, fileList, scanStats, true);

  final DrillDirectScanRel newScan = new DrillDirectScanRel(scan.getCluster(), scan.getTraitSet().plus(DrillRel.DRILL_LOGICAL),
    directScan, scanRowType);

  final DrillProjectRel newProject = new DrillProjectRel(agg.getCluster(), agg.getTraitSet().plus(DrillRel.DRILL_LOGICAL),
    newScan, CountToDirectScanUtils.prepareFieldExpressions(scanRowType), agg.getRowType());

  call.transformTo(newProject);
}