Java Code Examples for org.apache.calcite.util.ImmutableBitSet#cardinality()

The following examples show how to use org.apache.calcite.util.ImmutableBitSet#cardinality() . 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: PrelUtil.java    From dremio-oss with Apache License 2.0 6 votes vote down vote up
public static RelNode addPartialProjectOnJoin(JoinPrel join, ImmutableBitSet projected) {
  if ((projected != null) && ((projected.cardinality() != 0) || (projected.cardinality() != join.getRowType().getFieldCount()))) {
    List<RelDataTypeField> fields = join.getRowType().getFieldList();
    List<RelDataType> dataTypes = new ArrayList<>();
    List<String> fieldNames = new ArrayList<>();
    List<RexNode> exprs = new ArrayList<>();
    for (int i = 0; i < fields.size(); i++) {
      if (projected.get(i)) {
        RelDataTypeField field = fields.get(i);
        dataTypes.add(field.getType());
        fieldNames.add(field.getName());
        exprs.add(join.getCluster().getRexBuilder().makeInputRef(field.getType(), i));
      }
    }
    RelDataType rowType = join.getCluster().getTypeFactory().createStructType(dataTypes, fieldNames);
    return ProjectPrel.create(join.getCluster(), join.getTraitSet(), join, exprs, rowType);
  }
  return join;
}
 
Example 2
Source File: RelMdPredicates.java    From Bats with Apache License 2.0 5 votes vote down vote up
Iterable<Mapping> mappings(final RexNode predicate) {
  final ImmutableBitSet fields = exprFields.get(predicate);
  if (fields.cardinality() == 0) {
    return Collections.emptyList();
  }
  return () -> new ExprsItr(fields);
}
 
Example 3
Source File: ProjectWindowTransposeRule.java    From calcite with Apache License 2.0 5 votes vote down vote up
private int getAdjustedIndex(final int initIndex,
    final ImmutableBitSet beReferred, final int windowInputColumn) {
  if (initIndex >= windowInputColumn) {
    return beReferred.cardinality() + (initIndex - windowInputColumn);
  } else {
    return beReferred.get(0, initIndex).cardinality();
  }
}
 
Example 4
Source File: RelMdPredicates.java    From calcite with Apache License 2.0 5 votes vote down vote up
ExprsItr(ImmutableBitSet fields) {
  nextMapping = null;
  columns = new int[fields.cardinality()];
  columnSets = new BitSet[fields.cardinality()];
  iterationIdx = new int[fields.cardinality()];
  for (int j = 0, i = fields.nextSetBit(0); i >= 0; i = fields
      .nextSetBit(i + 1), j++) {
    columns[j] = i;
    columnSets[j] = equivalence.get(i);
    iterationIdx[j] = 0;
  }
  firstCall = true;
}
 
Example 5
Source File: RelMdPredicates.java    From calcite with Apache License 2.0 5 votes vote down vote up
Iterable<Mapping> mappings(final RexNode predicate) {
  final ImmutableBitSet fields = exprFields.get(predicate);
  if (fields.cardinality() == 0) {
    return Collections.emptyList();
  }
  return () -> new ExprsItr(fields);
}
 
Example 6
Source File: DremioFieldTrimmer.java    From dremio-oss with Apache License 2.0 5 votes vote down vote up
/**
 * Variant of {@link #trimFields(RelNode, ImmutableBitSet, Set)} for {@link ScanCrel}.
 */
@SuppressWarnings("unused")
public TrimResult trimFields(
    ScanCrel crel,
    ImmutableBitSet fieldsUsed,
    Set<RelDataTypeField> extraFields) {

  if(fieldsUsed.cardinality() == crel.getRowType().getFieldCount()) {
    return result(crel, Mappings.createIdentity(crel.getRowType().getFieldCount()));
  }

  if(fieldsUsed.cardinality() == 0) {
    // do something similar to dummy project but avoid using a scan field. This ensures the scan
    // does a skipAll operation rather than projectin a useless column.
    final RelOptCluster cluster = crel.getCluster();
    final Mapping mapping = Mappings.create(MappingType.INVERSE_SURJECTION, crel.getRowType().getFieldCount(), 1);
    final RexLiteral expr = cluster.getRexBuilder().makeExactLiteral(BigDecimal.ZERO);
    builder.push(crel);
    builder.project(ImmutableList.<RexNode>of(expr), ImmutableList.of("DUMMY"));
    return result(builder.build(), mapping);
  }

  final List<SchemaPath> paths = new ArrayList<>();
  final Mapping m = Mappings.create(MappingType.PARTIAL_FUNCTION, crel.getRowType().getFieldCount(), fieldsUsed.cardinality());
  int index = 0;
  for(int i : fieldsUsed) {
    paths.add(SchemaPath.getSimplePath(crel.getRowType().getFieldList().get(i).getName()));
    m.set(i, index);
    index++;
  }

  ScanCrel newCrel = crel.cloneWithProject(paths);
  return result(newCrel, m);
}
 
Example 7
Source File: RexToExpr.java    From dremio-oss with Apache License 2.0 5 votes vote down vote up
public static List<NamedExpression> aggsToExpr(
    RelDataType rowType, RelNode input, ImmutableBitSet groupSet, List<AggregateCall> aggCalls) {
  final List<String> fields = rowType.getFieldNames();
  final List<String> childFields = input.getRowType().getFieldNames();
  final List<NamedExpression> aggExprs = Lists.newArrayList();
  for (Ord<AggregateCall> aggCall : Ord.zip(aggCalls)) {
    int aggExprOrdinal = groupSet.cardinality() + aggCall.i;
    FieldReference ref = FieldReference.getWithQuotedRef(fields.get(aggExprOrdinal));
    LogicalExpression expr = toExpr(aggCall.e, childFields);
    NamedExpression ne = new NamedExpression(expr, ref);
    aggExprs.add(ne);
  }
  return aggExprs;
}
 
Example 8
Source File: RelFieldTrimmer.java    From Bats with Apache License 2.0 5 votes vote down vote up
/**
 * Variant of {@link #trimFields(RelNode, ImmutableBitSet, Set)} for
 * {@link org.apache.calcite.rel.logical.LogicalFilter}.
 */
public TrimResult trimFields(Filter filter, ImmutableBitSet fieldsUsed, Set<RelDataTypeField> extraFields) {
    final RelDataType rowType = filter.getRowType();
    final int fieldCount = rowType.getFieldCount();
    final RexNode conditionExpr = filter.getCondition();
    final RelNode input = filter.getInput();

    // We use the fields used by the consumer, plus any fields used in the
    // filter.
    final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<>(extraFields);
    RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(inputExtraFields);
    inputFinder.inputBitSet.addAll(fieldsUsed);
    conditionExpr.accept(inputFinder);
    final ImmutableBitSet inputFieldsUsed = inputFinder.inputBitSet.build();

    // Create input with trimmed columns.
    TrimResult trimResult = trimChild(filter, input, inputFieldsUsed, inputExtraFields);
    RelNode newInput = trimResult.left;
    final Mapping inputMapping = trimResult.right;

    // If the input is unchanged, and we need to project all columns,
    // there's nothing we can do.
    if (newInput == input && fieldsUsed.cardinality() == fieldCount) {
        return result(filter, Mappings.createIdentity(fieldCount));
    }

    // Build new project expressions, and populate the mapping.
    final RexVisitor<RexNode> shuttle = new RexPermuteInputsShuttle(inputMapping, newInput);
    RexNode newConditionExpr = conditionExpr.accept(shuttle);

    // Use copy rather than relBuilder so that correlating variables get set.
    relBuilder.push(filter.copy(filter.getTraitSet(), newInput, newConditionExpr));

    // The result has the same mapping as the input gave us. Sometimes we
    // return fields that the consumer didn't ask for, because the filter
    // needs them for its condition.
    return result(relBuilder.build(), inputMapping);
}
 
Example 9
Source File: RelFieldTrimmer.java    From calcite with Apache License 2.0 5 votes vote down vote up
public TrimResult trimFields(
    Exchange exchange,
    ImmutableBitSet fieldsUsed,
    Set<RelDataTypeField> extraFields) {
  final RelDataType rowType = exchange.getRowType();
  final int fieldCount = rowType.getFieldCount();
  final RelDistribution distribution = exchange.getDistribution();
  final RelNode input = exchange.getInput();

  // We use the fields used by the consumer, plus any fields used as exchange
  // keys.
  final ImmutableBitSet.Builder inputFieldsUsed = fieldsUsed.rebuild();
  for (int keyIndex : distribution.getKeys()) {
    inputFieldsUsed.set(keyIndex);
  }

  // Create input with trimmed columns.
  final Set<RelDataTypeField> inputExtraFields = Collections.emptySet();
  final TrimResult trimResult =
      trimChild(exchange, input, inputFieldsUsed.build(), inputExtraFields);
  final RelNode newInput = trimResult.left;
  final Mapping inputMapping = trimResult.right;

  // If the input is unchanged, and we need to project all columns,
  // there's nothing we can do.
  if (newInput == input
      && inputMapping.isIdentity()
      && fieldsUsed.cardinality() == fieldCount) {
    return result(exchange, Mappings.createIdentity(fieldCount));
  }

  relBuilder.push(newInput);
  final RelDistribution newDistribution = distribution.apply(inputMapping);
  relBuilder.exchange(newDistribution);

  return result(relBuilder.build(), inputMapping);
}
 
Example 10
Source File: ProjectWindowTransposeRule.java    From Bats with Apache License 2.0 5 votes vote down vote up
private int getAdjustedIndex(final int initIndex, final ImmutableBitSet beReferred, final int windowInputColumn) {
    if (initIndex >= windowInputColumn) {
        return beReferred.cardinality() + (initIndex - windowInputColumn);
    } else {
        return beReferred.get(0, initIndex).cardinality();
    }
}
 
Example 11
Source File: RelMdPredicates.java    From Bats with Apache License 2.0 5 votes vote down vote up
ExprsItr(ImmutableBitSet fields) {
  nextMapping = null;
  columns = new int[fields.cardinality()];
  columnSets = new BitSet[fields.cardinality()];
  iterationIdx = new int[fields.cardinality()];
  for (int j = 0, i = fields.nextSetBit(0); i >= 0; i = fields
      .nextSetBit(i + 1), j++) {
    columns[j] = i;
    columnSets[j] = equivalence.get(i);
    iterationIdx[j] = 0;
  }
  firstCall = true;
}
 
Example 12
Source File: LoptMultiJoin.java    From calcite with Apache License 2.0 4 votes vote down vote up
/**
 * Sets weighting for each combination of factors, depending on which join
 * filters reference which factors. Greater weight is given to equality
 * conditions. Also, sets bitmaps indicating which factors are referenced by
 * each factor within join filters that are comparisons.
 */
public void setFactorWeights() {
  factorWeights = new int[nJoinFactors][nJoinFactors];
  factorsRefByFactor = new ImmutableBitSet[nJoinFactors];
  for (int i = 0; i < nJoinFactors; i++) {
    factorsRefByFactor[i] = ImmutableBitSet.of();
  }

  for (RexNode joinFilter : allJoinFilters) {
    ImmutableBitSet factorRefs = factorsRefByJoinFilter.get(joinFilter);

    // don't give weights to non-comparison expressions
    if (!(joinFilter instanceof RexCall)) {
      continue;
    }
    if (!joinFilter.isA(SqlKind.COMPARISON)) {
      continue;
    }

    // OR the factors referenced in this join filter into the
    // bitmaps corresponding to each of the factors; however,
    // exclude the bit corresponding to the factor itself
    for (int factor : factorRefs) {
      factorsRefByFactor[factor] =
          factorsRefByFactor[factor]
              .rebuild()
              .addAll(factorRefs)
              .clear(factor)
              .build();
    }

    if (factorRefs.cardinality() == 2) {
      int leftFactor = factorRefs.nextSetBit(0);
      int rightFactor = factorRefs.nextSetBit(leftFactor + 1);

      final RexCall call = (RexCall) joinFilter;
      ImmutableBitSet leftFields = fieldBitmap(call.getOperands().get(0));
      ImmutableBitSet leftBitmap = factorBitmap(leftFields);

      // filter contains only two factor references, one on each
      // side of the operator
      int weight;
      if (leftBitmap.cardinality() == 1) {
        // give higher weight to equi-joins
        switch (joinFilter.getKind()) {
        case EQUALS:
          weight = 3;
          break;
        default:
          weight = 2;
        }
      } else {
        // cross product of two tables
        weight = 1;
      }
      setFactorWeight(weight, leftFactor, rightFactor);
    } else {
      // multiple factor references -- set a weight for each
      // combination of factors referenced within the filter
      final List<Integer> list  = ImmutableIntList.copyOf(factorRefs);
      for (int outer : list) {
        for (int inner : list) {
          if (outer != inner) {
            setFactorWeight(1, outer, inner);
          }
        }
      }
    }
  }
}
 
Example 13
Source File: RelMdDistinctRowCount.java    From dremio-oss with Apache License 2.0 4 votes vote down vote up
private Double getDistinctRowCountFromEstimateRowCount(RelNode rel, RelMetadataQuery mq, ImmutableBitSet groupKey, RexNode predicate) {
  final int groupKeySize = groupKey.cardinality();
  return rel.estimateRowCount(mq) * (1.0 - Math.pow(0.9, groupKeySize)) * RelMdUtil.guessSelectivity(predicate);
}
 
Example 14
Source File: LoptMultiJoin.java    From Bats with Apache License 2.0 4 votes vote down vote up
/**
 * Sets weighting for each combination of factors, depending on which join
 * filters reference which factors. Greater weight is given to equality
 * conditions. Also, sets bitmaps indicating which factors are referenced by
 * each factor within join filters that are comparisons.
 */
public void setFactorWeights() {
  factorWeights = new int[nJoinFactors][nJoinFactors];
  factorsRefByFactor = new ImmutableBitSet[nJoinFactors];
  for (int i = 0; i < nJoinFactors; i++) {
    factorsRefByFactor[i] = ImmutableBitSet.of();
  }

  for (RexNode joinFilter : allJoinFilters) {
    ImmutableBitSet factorRefs = factorsRefByJoinFilter.get(joinFilter);

    // don't give weights to non-comparison expressions
    if (!(joinFilter instanceof RexCall)) {
      continue;
    }
    if (!joinFilter.isA(SqlKind.COMPARISON)) {
      continue;
    }

    // OR the factors referenced in this join filter into the
    // bitmaps corresponding to each of the factors; however,
    // exclude the bit corresponding to the factor itself
    for (int factor : factorRefs) {
      factorsRefByFactor[factor] =
          factorsRefByFactor[factor]
              .rebuild()
              .addAll(factorRefs)
              .clear(factor)
              .build();
    }

    if (factorRefs.cardinality() == 2) {
      int leftFactor = factorRefs.nextSetBit(0);
      int rightFactor = factorRefs.nextSetBit(leftFactor + 1);

      final RexCall call = (RexCall) joinFilter;
      ImmutableBitSet leftFields = fieldBitmap(call.getOperands().get(0));
      ImmutableBitSet leftBitmap = factorBitmap(leftFields);

      // filter contains only two factor references, one on each
      // side of the operator
      int weight;
      if (leftBitmap.cardinality() == 1) {
        // give higher weight to equi-joins
        switch (joinFilter.getKind()) {
        case EQUALS:
          weight = 3;
          break;
        default:
          weight = 2;
        }
      } else {
        // cross product of two tables
        weight = 1;
      }
      setFactorWeight(weight, leftFactor, rightFactor);
    } else {
      // multiple factor references -- set a weight for each
      // combination of factors referenced within the filter
      final List<Integer> list  = ImmutableIntList.copyOf(factorRefs);
      for (int outer : list) {
        for (int inner : list) {
          if (outer != inner) {
            setFactorWeight(1, outer, inner);
          }
        }
      }
    }
  }
}
 
Example 15
Source File: ProfilerImpl.java    From calcite with Apache License 2.0 4 votes vote down vote up
/** Populates {@code spaces} with the next batch.
 * Returns an empty list if done. */
List<Space> nextBatch(int pass) {
  final List<Space> spaces = new ArrayList<>();
loop:
  for (;;) {
    if (spaces.size() >= combinationsPerPass) {
      // We have enough for the next pass.
      return spaces;
    }
    // First, see if there is a space we did have room for last pass.
    final ImmutableBitSet ordinals = spaceQueue.poll();
    if (ordinals != null) {
      final Space space = new Space(this, ordinals, toColumns(ordinals));
      spaces.add(space);
      if (ordinals.cardinality() == 1) {
        singletonSpaces.set(ordinals.nth(0), space);
      }
    } else {
      // Next, take a space that was done last time, generate its
      // successors, and add the interesting ones to the space queue.
      for (;;) {
        final Space doneSpace = doneQueue.poll();
        if (doneSpace == null) {
          // There are no more done spaces. We're done.
          return spaces;
        }
        if (doneSpace.columnOrdinals.cardinality() > 4) {
          // Do not generate successors for groups with lots of columns,
          // probably initial groups
          continue;
        }
        for (Column column : columns) {
          if (!doneSpace.columnOrdinals.get(column.ordinal)) {
            if (pass == 0
                || doneSpace.columnOrdinals.cardinality() == 0
                || !containsKey(
                    doneSpace.columnOrdinals.set(column.ordinal))
                && predicate.test(Pair.of(doneSpace, column))) {
              final ImmutableBitSet nextOrdinals =
                  doneSpace.columnOrdinals.set(column.ordinal);
              if (resultSet.add(nextOrdinals)) {
                spaceQueue.add(nextOrdinals);
              }
            }
          }
        }
        // We've converted at a space into at least one interesting
        // successor.
        if (!spaceQueue.isEmpty()) {
          continue loop;
        }
      }
    }
  }
}
 
Example 16
Source File: AggregateMergeRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Aggregate topAgg = call.rel(0);
  final Aggregate bottomAgg = call.rel(1);
  if (topAgg.getGroupCount() > bottomAgg.getGroupCount()) {
    return;
  }

  final ImmutableBitSet bottomGroupSet = bottomAgg.getGroupSet();
  final Map<Integer, Integer> map = new HashMap<>();
  bottomGroupSet.forEach(v -> map.put(map.size(), v));
  for (int k : topAgg.getGroupSet()) {
    if (!map.containsKey(k)) {
      return;
    }
  }

  // top aggregate keys must be subset of lower aggregate keys
  final ImmutableBitSet topGroupSet = topAgg.getGroupSet().permute(map);
  if (!bottomGroupSet.contains(topGroupSet)) {
    return;
  }

  boolean hasEmptyGroup = topAgg.getGroupSets()
      .stream().anyMatch(n -> n.isEmpty());

  final List<AggregateCall> finalCalls = new ArrayList<>();
  for (AggregateCall topCall : topAgg.getAggCallList()) {
    if (!isAggregateSupported(topCall)
        || topCall.getArgList().size() == 0) {
      return;
    }
    // Make sure top aggregate argument refers to one of the aggregate
    int bottomIndex = topCall.getArgList().get(0) - bottomGroupSet.cardinality();
    if (bottomIndex >= bottomAgg.getAggCallList().size()
        || bottomIndex < 0) {
      return;
    }
    AggregateCall bottomCall = bottomAgg.getAggCallList().get(bottomIndex);
    // Should not merge if top agg with empty group keys and the lower agg
    // function is COUNT, because in case of empty input for lower agg,
    // the result is empty, if we merge them, we end up with 1 result with
    // 0, which is wrong.
    if (!isAggregateSupported(bottomCall)
        || (bottomCall.getAggregation() == SqlStdOperatorTable.COUNT
             && hasEmptyGroup)) {
      return;
    }
    SqlSplittableAggFunction splitter = Objects.requireNonNull(
        bottomCall.getAggregation().unwrap(SqlSplittableAggFunction.class));
    AggregateCall finalCall = splitter.merge(topCall, bottomCall);
    // fail to merge the aggregate call, bail out
    if (finalCall == null) {
      return;
    }
    finalCalls.add(finalCall);
  }

  // re-map grouping sets
  ImmutableList<ImmutableBitSet> newGroupingSets = null;
  if (topAgg.getGroupType() != Group.SIMPLE) {
    newGroupingSets =
        ImmutableBitSet.ORDERING.immutableSortedCopy(
            ImmutableBitSet.permute(topAgg.getGroupSets(), map));
  }

  final Aggregate finalAgg =
      topAgg.copy(topAgg.getTraitSet(), bottomAgg.getInput(), topGroupSet,
          newGroupingSets, finalCalls);
  call.transformTo(finalAgg);
}
 
Example 17
Source File: RelMdColumnUniqueness.java    From calcite with Apache License 2.0 4 votes vote down vote up
public Boolean areColumnsUnique(Join rel, RelMetadataQuery mq,
    ImmutableBitSet columns, boolean ignoreNulls) {
  columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq);
  if (columns.cardinality() == 0) {
    return false;
  }

  final RelNode left = rel.getLeft();
  final RelNode right = rel.getRight();

  // Semi or anti join should ignore uniqueness of the right input.
  if (!rel.getJoinType().projectsRight()) {
    return mq.areColumnsUnique(left, columns, ignoreNulls);
  }

  // Divide up the input column mask into column masks for the left and
  // right sides of the join
  final Pair<ImmutableBitSet, ImmutableBitSet> leftAndRightColumns =
      splitLeftAndRightColumns(rel.getLeft().getRowType().getFieldCount(),
          columns);
  final ImmutableBitSet leftColumns = leftAndRightColumns.left;
  final ImmutableBitSet rightColumns = leftAndRightColumns.right;

  // for FULL OUTER JOIN if columns contain column from both inputs it is not
  // guaranteed that the result will be unique
  if (!ignoreNulls && rel.getJoinType() == JoinRelType.FULL
      && leftColumns.cardinality() > 0 && rightColumns.cardinality() > 0) {
    return false;
  }

  // If the original column mask contains columns from both the left and
  // right hand side, then the columns are unique if and only if they're
  // unique for their respective join inputs
  Boolean leftUnique = mq.areColumnsUnique(left, leftColumns, ignoreNulls);
  Boolean rightUnique = mq.areColumnsUnique(right, rightColumns, ignoreNulls);
  if ((leftColumns.cardinality() > 0)
      && (rightColumns.cardinality() > 0)) {
    if ((leftUnique == null) || (rightUnique == null)) {
      return null;
    } else {
      return leftUnique && rightUnique;
    }
  }

  // If we're only trying to determine uniqueness for columns that
  // originate from one join input, then determine if the equijoin
  // columns from the other join input are unique.  If they are, then
  // the columns are unique for the entire join if they're unique for
  // the corresponding join input, provided that input is not null
  // generating.
  final JoinInfo joinInfo = rel.analyzeCondition();
  if (leftColumns.cardinality() > 0) {
    if (rel.getJoinType().generatesNullsOnLeft()) {
      return false;
    }
    Boolean rightJoinColsUnique =
        mq.areColumnsUnique(right, joinInfo.rightSet(), ignoreNulls);
    if ((rightJoinColsUnique == null) || (leftUnique == null)) {
      return null;
    }
    return rightJoinColsUnique && leftUnique;
  } else if (rightColumns.cardinality() > 0) {
    if (rel.getJoinType().generatesNullsOnRight()) {
      return false;
    }
    Boolean leftJoinColsUnique =
        mq.areColumnsUnique(left, joinInfo.leftSet(), ignoreNulls);
    if ((leftJoinColsUnique == null) || (rightUnique == null)) {
      return null;
    }
    return leftJoinColsUnique && rightUnique;
  }

  throw new AssertionError();
}
 
Example 18
Source File: MaterializedViewAggregateRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
@Override protected RelNode createUnion(RelBuilder relBuilder, RexBuilder rexBuilder,
    RelNode topProject, RelNode unionInputQuery, RelNode unionInputView) {
  // Union
  relBuilder.push(unionInputQuery);
  relBuilder.push(unionInputView);
  relBuilder.union(true);
  List<RexNode> exprList = new ArrayList<>(relBuilder.peek().getRowType().getFieldCount());
  List<String> nameList = new ArrayList<>(relBuilder.peek().getRowType().getFieldCount());
  for (int i = 0; i < relBuilder.peek().getRowType().getFieldCount(); i++) {
    // We can take unionInputQuery as it is query based.
    RelDataTypeField field = unionInputQuery.getRowType().getFieldList().get(i);
    exprList.add(
        rexBuilder.ensureType(
            field.getType(),
            rexBuilder.makeInputRef(relBuilder.peek(), i),
            true));
    nameList.add(field.getName());
  }
  relBuilder.project(exprList, nameList);
  // Rollup aggregate
  Aggregate aggregate = (Aggregate) unionInputQuery;
  final ImmutableBitSet groupSet = ImmutableBitSet.range(aggregate.getGroupCount());
  final List<AggCall> aggregateCalls = new ArrayList<>();
  for (int i = 0; i < aggregate.getAggCallList().size(); i++) {
    AggregateCall aggCall = aggregate.getAggCallList().get(i);
    if (aggCall.isDistinct()) {
      // Cannot ROLLUP distinct
      return null;
    }
    SqlAggFunction rollupAgg =
        getRollup(aggCall.getAggregation());
    if (rollupAgg == null) {
      // Cannot rollup this aggregate, bail out
      return null;
    }
    final RexInputRef operand =
        rexBuilder.makeInputRef(relBuilder.peek(),
            aggregate.getGroupCount() + i);
    aggregateCalls.add(
        relBuilder.aggregateCall(rollupAgg, operand)
            .distinct(aggCall.isDistinct())
            .approximate(aggCall.isApproximate())
            .as(aggCall.name));
  }
  RelNode prevNode = relBuilder.peek();
  RelNode result = relBuilder
      .aggregate(relBuilder.groupKey(groupSet), aggregateCalls)
      .build();
  if (prevNode == result && groupSet.cardinality() != result.getRowType().getFieldCount()) {
    // Aggregate was not inserted but we need to prune columns
    result = relBuilder
        .push(result)
        .project(relBuilder.fields(groupSet))
        .build();
  }
  if (topProject != null) {
    // Top project
    return topProject.copy(topProject.getTraitSet(), ImmutableList.of(result));
  }
  // Result
  return result;
}
 
Example 19
Source File: RelMdColumnUniqueness.java    From Bats with Apache License 2.0 4 votes vote down vote up
public Boolean areColumnsUnique(Join rel, RelMetadataQuery mq,
    ImmutableBitSet columns, boolean ignoreNulls) {
  if (columns.cardinality() == 0) {
    return false;
  }

  final RelNode left = rel.getLeft();
  final RelNode right = rel.getRight();

  // Divide up the input column mask into column masks for the left and
  // right sides of the join
  final Pair<ImmutableBitSet, ImmutableBitSet> leftAndRightColumns =
      splitLeftAndRightColumns(rel.getLeft().getRowType().getFieldCount(),
          columns);
  final ImmutableBitSet leftColumns = leftAndRightColumns.left;
  final ImmutableBitSet rightColumns = leftAndRightColumns.right;

  // If the original column mask contains columns from both the left and
  // right hand side, then the columns are unique if and only if they're
  // unique for their respective join inputs
  Boolean leftUnique = mq.areColumnsUnique(left, leftColumns, ignoreNulls);
  Boolean rightUnique = mq.areColumnsUnique(right, rightColumns, ignoreNulls);
  if ((leftColumns.cardinality() > 0)
      && (rightColumns.cardinality() > 0)) {
    if ((leftUnique == null) || (rightUnique == null)) {
      return null;
    } else {
      return leftUnique && rightUnique;
    }
  }

  // If we're only trying to determine uniqueness for columns that
  // originate from one join input, then determine if the equijoin
  // columns from the other join input are unique.  If they are, then
  // the columns are unique for the entire join if they're unique for
  // the corresponding join input, provided that input is not null
  // generating.
  final JoinInfo joinInfo = rel.analyzeCondition();
  if (leftColumns.cardinality() > 0) {
    if (rel.getJoinType().generatesNullsOnLeft()) {
      return false;
    }
    Boolean rightJoinColsUnique =
        mq.areColumnsUnique(right, joinInfo.rightSet(), ignoreNulls);
    if ((rightJoinColsUnique == null) || (leftUnique == null)) {
      return null;
    }
    return rightJoinColsUnique && leftUnique;
  } else if (rightColumns.cardinality() > 0) {
    if (rel.getJoinType().generatesNullsOnRight()) {
      return false;
    }
    Boolean leftJoinColsUnique =
        mq.areColumnsUnique(left, joinInfo.leftSet(), ignoreNulls);
    if ((leftJoinColsUnique == null) || (rightUnique == null)) {
      return null;
    }
    return leftJoinColsUnique && rightUnique;
  }

  throw new AssertionError();
}
 
Example 20
Source File: LoptOptimizeJoinRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
/**
 * Locates pairs of joins that are self-joins where the join can be removed
 * because the join condition between the two factors is an equality join on
 * unique keys.
 *
 * @param multiJoin join factors being optimized
 */
private void findRemovableSelfJoins(RelMetadataQuery mq, LoptMultiJoin multiJoin) {
  // Candidates for self-joins must be simple factors
  Map<Integer, RelOptTable> simpleFactors = getSimpleFactors(mq, multiJoin);

  // See if a simple factor is repeated and therefore potentially is
  // part of a self-join.  Restrict each factor to at most one
  // self-join.
  final List<RelOptTable> repeatedTables = new ArrayList<>();
  final TreeSet<Integer> sortedFactors = new TreeSet<>();
  sortedFactors.addAll(simpleFactors.keySet());
  final Map<Integer, Integer> selfJoinPairs = new HashMap<>();
  Integer [] factors =
      sortedFactors.toArray(new Integer[0]);
  for (int i = 0; i < factors.length; i++) {
    if (repeatedTables.contains(simpleFactors.get(factors[i]))) {
      continue;
    }
    for (int j = i + 1; j < factors.length; j++) {
      int leftFactor = factors[i];
      int rightFactor = factors[j];
      if (simpleFactors.get(leftFactor).getQualifiedName().equals(
          simpleFactors.get(rightFactor).getQualifiedName())) {
        selfJoinPairs.put(leftFactor, rightFactor);
        repeatedTables.add(simpleFactors.get(leftFactor));
        break;
      }
    }
  }

  // From the candidate self-join pairs, determine if there is
  // the appropriate join condition between the two factors that will
  // allow the join to be removed.
  for (Integer factor1 : selfJoinPairs.keySet()) {
    final int factor2 = selfJoinPairs.get(factor1);
    final List<RexNode> selfJoinFilters = new ArrayList<>();
    for (RexNode filter : multiJoin.getJoinFilters()) {
      ImmutableBitSet joinFactors =
          multiJoin.getFactorsRefByJoinFilter(filter);
      if ((joinFactors.cardinality() == 2)
          && joinFactors.get(factor1)
          && joinFactors.get(factor2)) {
        selfJoinFilters.add(filter);
      }
    }
    if ((selfJoinFilters.size() > 0)
        && isSelfJoinFilterUnique(
          mq,
          multiJoin,
          factor1,
          factor2,
          selfJoinFilters)) {
      multiJoin.addRemovableSelfJoinPair(factor1, factor2);
    }
  }
}