Java Code Examples for org.apache.calcite.rel.metadata.RelMetadataQuery#getPulledUpPredicates()

The following examples show how to use org.apache.calcite.rel.metadata.RelMetadataQuery#getPulledUpPredicates() . 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 File: ReduceExpressionsRule.java    From Bats with Apache License 2.0 6 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Project project = call.rel(0);
  final RelMetadataQuery mq = call.getMetadataQuery();
  final RelOptPredicateList predicates =
      mq.getPulledUpPredicates(project.getInput());
  final List<RexNode> expList =
      Lists.newArrayList(project.getProjects());
  if (reduceExpressions(project, expList, predicates, false,
      matchNullability)) {
    call.transformTo(
        call.builder()
            .push(project.getInput())
            .project(expList, project.getRowType().getFieldNames())
            .build());

    // New plan is absolutely better than old plan.
    call.getPlanner().setImportance(project, 0.0);
  }
}
 
Example 2
Source File: ReduceExpressionsUtil.java    From dremio-oss with Apache License 2.0 6 votes vote down vote up
public static int numReducibleExprs(Project project) {
  final List<RexNode> expList = Lists.newArrayList(project.getProjects());
  final RelMetadataQuery mq = project.getCluster().getMetadataQuery();
  final RelOptPredicateList predicates = mq.getPulledUpPredicates(project.getInput());
  boolean reducible = reduceExpressions(project, expList, predicates);
  if (reducible) {
    int numReducible = 0;
    for (int i = 0; i < project.getProjects().size(); i++) {
      if (!project.getProjects().get(i).toString().equals(expList.get(i).toString())) {
        numReducible++;
      }
    }
    return numReducible;
  } else {
    return 0;
  }
}
 
Example 3
Source File: ReduceExpressionsRule.java    From Bats with Apache License 2.0 5 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Join join = call.rel(0);
  final List<RexNode> expList = Lists.newArrayList(join.getCondition());
  final int fieldCount = join.getLeft().getRowType().getFieldCount();
  final RelMetadataQuery mq = call.getMetadataQuery();
  final RelOptPredicateList leftPredicates =
      mq.getPulledUpPredicates(join.getLeft());
  final RelOptPredicateList rightPredicates =
      mq.getPulledUpPredicates(join.getRight());
  final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
  final RelOptPredicateList predicates =
      leftPredicates.union(rexBuilder,
          rightPredicates.shift(rexBuilder, fieldCount));
  if (!reduceExpressions(join, expList, predicates, true,
      matchNullability)) {
    return;
  }
  if (join instanceof EquiJoin) {
    final JoinInfo joinInfo =
        JoinInfo.of(join.getLeft(), join.getRight(), expList.get(0));
    if (!joinInfo.isEqui()) {
      // This kind of join must be an equi-join, and the condition is
      // no longer an equi-join. SemiJoin is an example of this.
      return;
    }
  }
  call.transformTo(
      join.copy(
          join.getTraitSet(),
          expList.get(0),
          join.getLeft(),
          join.getRight(),
          join.getJoinType(),
          join.isSemiJoinDone()));

  // New plan is absolutely better than old plan.
  call.getPlanner().setImportance(join, 0.0);
}
 
Example 4
Source File: RelMetadataTest.java    From calcite with Apache License 2.0 5 votes vote down vote up
@Test void testPullUpPredicatesOnNullableConstant() {
  final String sql = "select nullif(1, 1) as c\n"
      + "  from emp\n"
      + "  where mgr is null and deptno < 10";
  final RelNode rel = convertSql(sql);
  final RelMetadataQuery mq = rel.getCluster().getMetadataQuery();
  RelOptPredicateList list = mq.getPulledUpPredicates(rel);
  // Uses "IS NOT DISTINCT FROM" rather than "=" because cannot guarantee not null.
  assertThat(list.pulledUpPredicates,
      sortsAs("[IS NULL($0)]"));
}
 
Example 5
Source File: RelMetadataTest.java    From calcite with Apache License 2.0 5 votes vote down vote up
@Test void testPullUpPredicatesOnConstant() {
  final String sql = "select deptno, mgr, x, 'y' as y, z from (\n"
      + "  select deptno, mgr, cast(null as integer) as x, cast('1' as int) as z\n"
      + "  from emp\n"
      + "  where mgr is null and deptno < 10)";
  final RelNode rel = convertSql(sql);
  final RelMetadataQuery mq = rel.getCluster().getMetadataQuery();
  RelOptPredicateList list = mq.getPulledUpPredicates(rel);
  assertThat(list.pulledUpPredicates,
      sortsAs("[<($0, 10), =($3, 'y'), =($4, 1), IS NULL($1), IS NULL($2)]"));
}
 
Example 6
Source File: RelMetadataTest.java    From calcite with Apache License 2.0 5 votes vote down vote up
/** Test case for
 * <a href="https://issues.apache.org/jira/browse/CALCITE-1960">[CALCITE-1960]
 * RelMdPredicates.getPredicates is slow if there are many equivalent
 * columns</a>. There are much less duplicates after
 * <a href="https://issues.apache.org/jira/browse/CALCITE-2205">[CALCITE-2205]</a>.
 * Since this is a performance problem, the test result does not
 * change, but takes over 15 minutes before the fix and 6 seconds after. */
@Test void testPullUpPredicatesForExprsItr() {
  final String sql = "select a.EMPNO, a.ENAME\n"
      + "from (select * from sales.emp ) a\n"
      + "join (select * from sales.emp  ) b\n"
      + "on a.empno = b.deptno\n"
      + "  and a.comm = b.comm\n"
      + "  and a.mgr=b.mgr\n"
      + "  and (a.empno < 10 or a.comm < 3 or a.deptno < 10\n"
      + "    or a.job ='abc' or a.ename='abc' or a.sal='30' or a.mgr >3\n"
      + "    or a.slacker is not null  or a.HIREDATE is not null\n"
      + "    or b.empno < 9 or b.comm < 3 or b.deptno < 10 or b.job ='abc'\n"
      + "    or b.ename='abc' or b.sal='30' or b.mgr >3 or b.slacker )\n"
      + "join emp c\n"
      + "on b.mgr =a.mgr and a.empno =b.deptno and a.comm=b.comm\n"
      + "  and a.deptno=b.deptno and a.job=b.job and a.ename=b.ename\n"
      + "  and a.mgr=b.deptno and a.slacker=b.slacker";
  // Lock to ensure that only one test is using this method at a time.
  try (JdbcAdapterTest.LockWrapper ignore =
           JdbcAdapterTest.LockWrapper.lock(LOCK)) {
    // FIXME: fix timeout when enable implicit type coercion.
    final RelNode rel = convertSql(sql, false);
    final RelMetadataQuery mq = rel.getCluster().getMetadataQuery();
    RelOptPredicateList inputSet = mq.getPulledUpPredicates(rel.getInput(0));
    assertThat(inputSet.pulledUpPredicates.size(), is(11));
  }
}
 
Example 7
Source File: RelMetadataTest.java    From calcite with Apache License 2.0 5 votes vote down vote up
/**
 * Unit test for
 * {@link org.apache.calcite.rel.metadata.RelMdPredicates#getPredicates(Aggregate, RelMetadataQuery)}.
 */
@Test void testPullUpPredicatesFromAggregation() {
  final String sql = "select a, max(b) from (\n"
      + "  select 1 as a, 2 as b from emp)subq\n"
      + "group by a";
  final Aggregate rel = (Aggregate) convertSql(sql);
  final RelMetadataQuery mq = rel.getCluster().getMetadataQuery();
  RelOptPredicateList inputSet = mq.getPulledUpPredicates(rel);
  ImmutableList<RexNode> pulledUpPredicates = inputSet.pulledUpPredicates;
  assertThat(pulledUpPredicates, sortsAs("[=($0, 1)]"));
}
 
Example 8
Source File: PlannerTest.java    From calcite with Apache License 2.0 5 votes vote down vote up
/** Helper method for testing {@link RelMetadataQuery#getPulledUpPredicates}
 * metadata. */
private void checkMetadataPredicates(String sql,
    String expectedPredicates) throws Exception {
  Planner planner = getPlanner(null);
  SqlNode parse = planner.parse(sql);
  SqlNode validate = planner.validate(parse);
  RelNode rel = planner.rel(validate).project();
  final RelMetadataQuery mq = rel.getCluster().getMetadataQuery();
  final RelOptPredicateList predicates = mq.getPulledUpPredicates(rel);
  assertThat(predicates.pulledUpPredicates, sortsAs(expectedPredicates));
}
 
Example 9
Source File: SortRemoveConstantKeysRule.java    From calcite with Apache License 2.0 5 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Sort sort = call.rel(0);
  final RelMetadataQuery mq = call.getMetadataQuery();
  final RelNode input = sort.getInput();
  final RelOptPredicateList predicates = mq.getPulledUpPredicates(input);
  if (predicates == null) {
    return;
  }

  final RexBuilder rexBuilder = sort.getCluster().getRexBuilder();
  final List<RelFieldCollation> collationsList =
      sort.getCollation().getFieldCollations().stream()
          .filter(fc ->
              !predicates.constantMap.containsKey(
                  rexBuilder.makeInputRef(input, fc.getFieldIndex())))
          .collect(Collectors.toList());

  if (collationsList.size() == sort.collation.getFieldCollations().size()) {
    return;
  }

  // No active collations. Remove the sort completely
  if (collationsList.isEmpty() && sort.offset == null && sort.fetch == null) {
    call.transformTo(input);
    call.getPlanner().prune(sort);
    return;
  }

  final Sort result =
      sort.copy(sort.getTraitSet(), input, RelCollations.of(collationsList));
  call.transformTo(result);
  call.getPlanner().prune(sort);
}
 
Example 10
Source File: ExchangeRemoveConstantKeysRule.java    From calcite with Apache License 2.0 5 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Exchange exchange = call.rel(0);
  final RelMetadataQuery mq = call.getMetadataQuery();
  final RelNode input = exchange.getInput();
  final RelOptPredicateList predicates = mq.getPulledUpPredicates(input);
  if (predicates == null) {
    return;
  }

  final Set<Integer> constants = new HashSet<>();
  predicates.constantMap.keySet().forEach(key -> {
    if (key instanceof RexInputRef) {
      constants.add(((RexInputRef) key).getIndex());
    }
  });
  if (constants.isEmpty()) {
    return;
  }

  final List<Integer> distributionKeys = simplifyDistributionKeys(
      exchange.getDistribution(), constants);

  if (distributionKeys.size() != exchange.getDistribution().getKeys()
      .size()) {
    call.transformTo(call.builder()
        .push(exchange.getInput())
        .exchange(distributionKeys.isEmpty()
            ? RelDistributions.SINGLETON
            : RelDistributions.hash(distributionKeys))
        .build());
    call.getPlanner().prune(exchange);
  }
}
 
Example 11
Source File: ReduceExpressionsRule.java    From calcite with Apache License 2.0 5 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Join join = call.rel(0);
  final List<RexNode> expList = Lists.newArrayList(join.getCondition());
  final int fieldCount = join.getLeft().getRowType().getFieldCount();
  final RelMetadataQuery mq = call.getMetadataQuery();
  final RelOptPredicateList leftPredicates =
      mq.getPulledUpPredicates(join.getLeft());
  final RelOptPredicateList rightPredicates =
      mq.getPulledUpPredicates(join.getRight());
  final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
  final RelOptPredicateList predicates =
      leftPredicates.union(rexBuilder,
          rightPredicates.shift(rexBuilder, fieldCount));
  if (!reduceExpressions(join, expList, predicates, true,
      matchNullability)) {
    return;
  }
  call.transformTo(
      join.copy(
          join.getTraitSet(),
          expList.get(0),
          join.getLeft(),
          join.getRight(),
          join.getJoinType(),
          join.isSemiJoinDone()));

  // New plan is absolutely better than old plan.
  call.getPlanner().prune(join);
}
 
Example 12
Source File: RelMdPredicates.java    From dremio-oss with Apache License 2.0 5 votes vote down vote up
public RelOptPredicateList getPredicates(RelSubset subset,
    RelMetadataQuery mq) {
  // Currently disabled in Calcite upstream
  // Only go over the best node if it exists, and try the original node otherwise
  RelOptPredicateList predicates = mq.getPulledUpPredicates(Util.first(subset.getBest(), subset.getOriginal()));
  return predicates;
}
 
Example 13
Source File: RelOptUtil.java    From Bats with Apache License 2.0 5 votes vote down vote up
/**
 * Determines whether any of the fields in a given relational expression may
 * contain null values, taking into account constraints on the field types and
 * also deduced predicates.
 *
 * <p>The method is cautious: It may sometimes return {@code true} when the
 * actual answer is {@code false}. In particular, it does this when there
 * is no executor, or the executor is not a sub-class of
 * {@link RexExecutorImpl}.
 */
private static boolean containsNullableFields(RelNode r) {
    final RexBuilder rexBuilder = r.getCluster().getRexBuilder();
    final RelDataType rowType = r.getRowType();
    final List<RexNode> list = new ArrayList<>();
    final RelMetadataQuery mq = r.getCluster().getMetadataQuery();
    for (RelDataTypeField field : rowType.getFieldList()) {
        if (field.getType().isNullable()) {
            list.add(rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL,
                    rexBuilder.makeInputRef(field.getType(), field.getIndex())));
        }
    }
    if (list.isEmpty()) {
        // All columns are declared NOT NULL.
        return false;
    }
    final RelOptPredicateList predicates = mq.getPulledUpPredicates(r);
    if (predicates.pulledUpPredicates.isEmpty()) {
        // We have no predicates, so cannot deduce that any of the fields
        // declared NULL are really NOT NULL.
        return true;
    }
    final RexExecutor executor = r.getCluster().getPlanner().getExecutor();
    if (!(executor instanceof RexExecutorImpl)) {
        // Cannot proceed without an executor.
        return true;
    }
    final RexImplicationChecker checker = new RexImplicationChecker(rexBuilder, (RexExecutorImpl) executor,
            rowType);
    final RexNode first = RexUtil.composeConjunction(rexBuilder, predicates.pulledUpPredicates);
    final RexNode second = RexUtil.composeConjunction(rexBuilder, list);
    // Suppose we have EMP(empno INT NOT NULL, mgr INT),
    // and predicates [empno > 0, mgr > 0].
    // We make first: "empno > 0 AND mgr > 0"
    // and second: "mgr IS NOT NULL"
    // and ask whether first implies second.
    // It does, so we have no nullable columns.
    return !checker.implies(first, second);
}
 
Example 14
Source File: SortRemoveConstantKeysRule.java    From Bats with Apache License 2.0 5 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Sort sort = call.rel(0);
  final RelMetadataQuery mq = call.getMetadataQuery();
  final RelNode input = sort.getInput();
  final RelOptPredicateList predicates = mq.getPulledUpPredicates(input);
  if (predicates == null) {
    return;
  }

  final RexBuilder rexBuilder = sort.getCluster().getRexBuilder();
  final List<RelFieldCollation> collationsList =
      sort.getCollation().getFieldCollations().stream()
          .filter(fc ->
              !predicates.constantMap.containsKey(
                  rexBuilder.makeInputRef(input, fc.getFieldIndex())))
          .collect(Collectors.toList());

  if (collationsList.size() == sort.collation.getFieldCollations().size()) {
    return;
  }

  // No active collations. Remove the sort completely
  if (collationsList.isEmpty() && sort.offset == null && sort.fetch == null) {
    call.transformTo(input);
    call.getPlanner().setImportance(sort, 0.0);
    return;
  }

  final Sort result =
      sort.copy(sort.getTraitSet(), input, RelCollations.of(collationsList));
  call.transformTo(result);
  call.getPlanner().setImportance(sort, 0.0);
}
 
Example 15
Source File: UnionPullUpConstantsRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Union union = call.rel(0);

  final RexBuilder rexBuilder = union.getCluster().getRexBuilder();
  final RelMetadataQuery mq = call.getMetadataQuery();
  final RelOptPredicateList predicates = mq.getPulledUpPredicates(union);
  if (predicates == null) {
    return;
  }

  final Map<Integer, RexNode> constants = new HashMap<>();
  for (Map.Entry<RexNode, RexNode> e : predicates.constantMap.entrySet()) {
    if (e.getKey() instanceof RexInputRef) {
      constants.put(((RexInputRef) e.getKey()).getIndex(), e.getValue());
    }
  }

  // None of the expressions are constant. Nothing to do.
  if (constants.isEmpty()) {
    return;
  }

  // Create expressions for Project operators before and after the Union
  List<RelDataTypeField> fields = union.getRowType().getFieldList();
  List<RexNode> topChildExprs = new ArrayList<>();
  List<String> topChildExprsFields = new ArrayList<>();
  List<RexNode> refs = new ArrayList<>();
  ImmutableBitSet.Builder refsIndexBuilder = ImmutableBitSet.builder();
  for (RelDataTypeField field : fields) {
    final RexNode constant = constants.get(field.getIndex());
    if (constant != null) {
      topChildExprs.add(constant);
      topChildExprsFields.add(field.getName());
    } else {
      final RexNode expr = rexBuilder.makeInputRef(union, field.getIndex());
      topChildExprs.add(expr);
      topChildExprsFields.add(field.getName());
      refs.add(expr);
      refsIndexBuilder.set(field.getIndex());
    }
  }
  ImmutableBitSet refsIndex = refsIndexBuilder.build();

  // Update top Project positions
  final Mappings.TargetMapping mapping =
      RelOptUtil.permutation(refs, union.getInput(0).getRowType()).inverse();
  topChildExprs = RexUtil.apply(mapping, topChildExprs);

  // Create new Project-Union-Project sequences
  final RelBuilder relBuilder = call.builder();
  for (RelNode input : union.getInputs()) {
    List<Pair<RexNode, String>> newChildExprs = new ArrayList<>();
    for (int j : refsIndex) {
      newChildExprs.add(
          Pair.of(rexBuilder.makeInputRef(input, j),
              input.getRowType().getFieldList().get(j).getName()));
    }
    if (newChildExprs.isEmpty()) {
      // At least a single item in project is required.
      newChildExprs.add(
          Pair.of(topChildExprs.get(0), topChildExprsFields.get(0)));
    }
    // Add the input with project on top
    relBuilder.push(input);
    relBuilder.project(Pair.left(newChildExprs), Pair.right(newChildExprs));
  }
  relBuilder.union(union.all, union.getInputs().size());
  // Create top Project fixing nullability of fields
  relBuilder.project(topChildExprs, topChildExprsFields);
  relBuilder.convert(union.getRowType(), false);

  call.transformTo(relBuilder.build());
}
 
Example 16
Source File: ExchangeRemoveConstantKeysRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final SortExchange sortExchange = call.rel(0);
  final RelMetadataQuery mq = call.getMetadataQuery();
  final RelNode input = sortExchange.getInput();
  final RelOptPredicateList predicates = mq.getPulledUpPredicates(input);
  if (predicates == null) {
    return;
  }

  final Set<Integer> constants = new HashSet<>();
  predicates.constantMap.keySet().forEach(key -> {
    if (key instanceof RexInputRef) {
      constants.add(((RexInputRef) key).getIndex());
    }
  });

  if (constants.isEmpty()) {
    return;
  }

  List<Integer> distributionKeys = new ArrayList<>();
  boolean distributionSimplified = false;
  boolean hashDistribution = sortExchange.getDistribution().getType()
      == RelDistribution.Type.HASH_DISTRIBUTED;
  if (hashDistribution) {
    distributionKeys = simplifyDistributionKeys(
        sortExchange.getDistribution(), constants);
    distributionSimplified =
        distributionKeys.size() != sortExchange.getDistribution().getKeys()
            .size();
  }

  final List<RelFieldCollation> fieldCollations = sortExchange
      .getCollation().getFieldCollations().stream().filter(
          fc -> !constants.contains(fc.getFieldIndex()))
       .collect(Collectors.toList());

  boolean collationSimplified =
       fieldCollations.size() != sortExchange.getCollation()
           .getFieldCollations().size();
  if (distributionSimplified
       || collationSimplified) {
    RelDistribution distribution = distributionSimplified
        ? (distributionKeys.isEmpty()
            ? RelDistributions.SINGLETON
            : RelDistributions.hash(distributionKeys))
        : sortExchange.getDistribution();
    RelCollation collation = collationSimplified
        ? RelCollations.of(fieldCollations)
        : sortExchange.getCollation();

    call.transformTo(call.builder()
        .push(sortExchange.getInput())
        .sortExchange(distribution, collation)
        .build());
    call.getPlanner().prune(sortExchange);
  }
}
 
Example 17
Source File: RelOptUtil.java    From calcite with Apache License 2.0 4 votes vote down vote up
/**
 * Determines whether any of the fields in a given relational expression may
 * contain null values, taking into account constraints on the field types and
 * also deduced predicates.
 *
 * <p>The method is cautious: It may sometimes return {@code true} when the
 * actual answer is {@code false}. In particular, it does this when there
 * is no executor, or the executor is not a sub-class of
 * {@link RexExecutorImpl}.
 */
private static boolean containsNullableFields(RelNode r) {
  final RexBuilder rexBuilder = r.getCluster().getRexBuilder();
  final RelDataType rowType = r.getRowType();
  final List<RexNode> list = new ArrayList<>();
  final RelMetadataQuery mq = r.getCluster().getMetadataQuery();
  for (RelDataTypeField field : rowType.getFieldList()) {
    if (field.getType().isNullable()) {
      list.add(
          rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL,
              rexBuilder.makeInputRef(field.getType(), field.getIndex())));
    }
  }
  if (list.isEmpty()) {
    // All columns are declared NOT NULL.
    return false;
  }
  final RelOptPredicateList predicates = mq.getPulledUpPredicates(r);
  if (predicates.pulledUpPredicates.isEmpty()) {
    // We have no predicates, so cannot deduce that any of the fields
    // declared NULL are really NOT NULL.
    return true;
  }
  final RexExecutor executor = r.getCluster().getPlanner().getExecutor();
  if (!(executor instanceof RexExecutorImpl)) {
    // Cannot proceed without an executor.
    return true;
  }
  final RexImplicationChecker checker =
      new RexImplicationChecker(rexBuilder, executor,
          rowType);
  final RexNode first =
      RexUtil.composeConjunction(rexBuilder, predicates.pulledUpPredicates);
  final RexNode second = RexUtil.composeConjunction(rexBuilder, list);
  // Suppose we have EMP(empno INT NOT NULL, mgr INT),
  // and predicates [empno > 0, mgr > 0].
  // We make first: "empno > 0 AND mgr > 0"
  // and second: "mgr IS NOT NULL"
  // and ask whether first implies second.
  // It does, so we have no nullable columns.
  return !checker.implies(first, second);
}
 
Example 18
Source File: ReduceExpressionsUtil.java    From dremio-oss with Apache License 2.0 4 votes vote down vote up
public static boolean numReducibleExprs(Filter filter) {
  final List<RexNode> expList = Lists.newArrayList(filter.getCondition());
  final RelMetadataQuery mq = filter.getCluster().getMetadataQuery();
  final RelOptPredicateList predicates = mq.getPulledUpPredicates(filter.getInput());
  return reduceExpressions(filter, expList, predicates);
}
 
Example 19
Source File: UnionPullUpConstantsRule.java    From Bats with Apache License 2.0 4 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Union union = call.rel(0);

  final int count = union.getRowType().getFieldCount();
  if (count == 1) {
    // No room for optimization since we cannot create an empty Project
    // operator. If we created a Project with one column, this rule would
    // cycle.
    return;
  }

  final RexBuilder rexBuilder = union.getCluster().getRexBuilder();
  final RelMetadataQuery mq = call.getMetadataQuery();
  final RelOptPredicateList predicates = mq.getPulledUpPredicates(union);
  if (predicates == null) {
    return;
  }

  final Map<Integer, RexNode> constants = new HashMap<>();
  for (Map.Entry<RexNode, RexNode> e : predicates.constantMap.entrySet()) {
    if (e.getKey() instanceof RexInputRef) {
      constants.put(((RexInputRef) e.getKey()).getIndex(), e.getValue());
    }
  }

  // None of the expressions are constant. Nothing to do.
  if (constants.isEmpty()) {
    return;
  }

  // Create expressions for Project operators before and after the Union
  List<RelDataTypeField> fields = union.getRowType().getFieldList();
  List<RexNode> topChildExprs = new ArrayList<>();
  List<String> topChildExprsFields = new ArrayList<>();
  List<RexNode> refs = new ArrayList<>();
  ImmutableBitSet.Builder refsIndexBuilder = ImmutableBitSet.builder();
  for (RelDataTypeField field : fields) {
    final RexNode constant = constants.get(field.getIndex());
    if (constant != null) {
      topChildExprs.add(constant);
      topChildExprsFields.add(field.getName());
    } else {
      final RexNode expr = rexBuilder.makeInputRef(union, field.getIndex());
      topChildExprs.add(expr);
      topChildExprsFields.add(field.getName());
      refs.add(expr);
      refsIndexBuilder.set(field.getIndex());
    }
  }
  ImmutableBitSet refsIndex = refsIndexBuilder.build();

  // Update top Project positions
  final Mappings.TargetMapping mapping =
      RelOptUtil.permutation(refs, union.getInput(0).getRowType()).inverse();
  topChildExprs = ImmutableList.copyOf(RexUtil.apply(mapping, topChildExprs));

  // Create new Project-Union-Project sequences
  final RelBuilder relBuilder = call.builder();
  for (RelNode input : union.getInputs()) {
    List<Pair<RexNode, String>> newChildExprs = new ArrayList<>();
    for (int j : refsIndex) {
      newChildExprs.add(
          Pair.of(rexBuilder.makeInputRef(input, j),
              input.getRowType().getFieldList().get(j).getName()));
    }
    if (newChildExprs.isEmpty()) {
      // At least a single item in project is required.
      newChildExprs.add(
          Pair.of(topChildExprs.get(0), topChildExprsFields.get(0)));
    }
    // Add the input with project on top
    relBuilder.push(input);
    relBuilder.project(Pair.left(newChildExprs), Pair.right(newChildExprs));
  }
  relBuilder.union(union.all, union.getInputs().size());
  // Create top Project fixing nullability of fields
  relBuilder.project(topChildExprs, topChildExprsFields);
  relBuilder.convert(union.getRowType(), false);

  call.transformTo(relBuilder.build());
}