use of org.apache.calcite.plan.volcano.RelSubset in project calcite by apache.
the class JoinAssociateRule method onMatch.
// ~ Methods ----------------------------------------------------------------
public void onMatch(final RelOptRuleCall call) {
final Join topJoin = call.rel(0);
final Join bottomJoin = call.rel(1);
final RelNode relA = bottomJoin.getLeft();
final RelNode relB = bottomJoin.getRight();
final RelSubset relC = call.rel(2);
final RelOptCluster cluster = topJoin.getCluster();
final RexBuilder rexBuilder = cluster.getRexBuilder();
if (relC.getConvention() != relA.getConvention()) {
// EnumerableConvention, we're only interested in enumerable subsets.
return;
}
// topJoin
// / \
// bottomJoin C
// / \
// A B
final int aCount = relA.getRowType().getFieldCount();
final int bCount = relB.getRowType().getFieldCount();
final int cCount = relC.getRowType().getFieldCount();
final ImmutableBitSet aBitSet = ImmutableBitSet.range(0, aCount);
final ImmutableBitSet bBitSet = ImmutableBitSet.range(aCount, aCount + bCount);
if (!topJoin.getSystemFieldList().isEmpty()) {
// FIXME Enable this rule for joins with system fields
return;
}
// (Is this too strict?)
if (topJoin.getJoinType() != JoinRelType.INNER || bottomJoin.getJoinType() != JoinRelType.INNER) {
return;
}
// Goal is to transform to
//
// newTopJoin
// / \
// A newBottomJoin
// / \
// B C
// Split the condition of topJoin and bottomJoin into a conjunctions. A
// condition can be pushed down if it does not use columns from A.
final List<RexNode> top = Lists.newArrayList();
final List<RexNode> bottom = Lists.newArrayList();
JoinPushThroughJoinRule.split(topJoin.getCondition(), aBitSet, top, bottom);
JoinPushThroughJoinRule.split(bottomJoin.getCondition(), aBitSet, top, bottom);
// Mapping for moving conditions from topJoin or bottomJoin to
// newBottomJoin.
// target: | B | C |
// source: | A | B | C |
final Mappings.TargetMapping bottomMapping = Mappings.createShiftMapping(aCount + bCount + cCount, 0, aCount, bCount, bCount, aCount + bCount, cCount);
final List<RexNode> newBottomList = Lists.newArrayList();
new RexPermuteInputsShuttle(bottomMapping, relB, relC).visitList(bottom, newBottomList);
RexNode newBottomCondition = RexUtil.composeConjunction(rexBuilder, newBottomList, false);
final Join newBottomJoin = bottomJoin.copy(bottomJoin.getTraitSet(), newBottomCondition, relB, relC, JoinRelType.INNER, false);
// Condition for newTopJoin consists of pieces from bottomJoin and topJoin.
// Field ordinals do not need to be changed.
RexNode newTopCondition = RexUtil.composeConjunction(rexBuilder, top, false);
final Join newTopJoin = topJoin.copy(topJoin.getTraitSet(), newTopCondition, relA, newBottomJoin, JoinRelType.INNER, false);
call.transformTo(newTopJoin);
}
use of org.apache.calcite.plan.volcano.RelSubset in project drill by axbaretto.
the class JoinUtils method isScalarSubquery.
/**
* Utility method to check if a subquery (represented by its root RelNode) is provably scalar. Currently
* only aggregates with no group-by are considered scalar. In the future, this method should be generalized
* to include more cases and reconciled with Calcite's notion of scalar.
* @param root The root RelNode to be examined
* @return True if the root rel or its descendant is scalar, False otherwise
*/
public static boolean isScalarSubquery(RelNode root) {
DrillAggregateRel agg = null;
RelNode currentrel = root;
while (agg == null && currentrel != null) {
if (currentrel instanceof DrillAggregateRel) {
agg = (DrillAggregateRel) currentrel;
} else if (currentrel instanceof RelSubset) {
currentrel = ((RelSubset) currentrel).getBest();
} else if (currentrel.getInputs().size() == 1) {
// If the rel is not an aggregate or RelSubset, but is a single-input rel (could be Project,
// Filter, Sort etc.), check its input
currentrel = currentrel.getInput(0);
} else {
break;
}
}
if (agg != null) {
if (agg.getGroupSet().isEmpty()) {
return true;
}
}
return false;
}
use of org.apache.calcite.plan.volcano.RelSubset in project drill by axbaretto.
the class SubsetTransformer method go.
public boolean go(T n, RelNode candidateSet) throws E {
if (!(candidateSet instanceof RelSubset)) {
return false;
}
boolean transform = false;
Set<RelNode> transformedRels = Sets.newIdentityHashSet();
Set<RelTraitSet> traitSets = Sets.newHashSet();
// 1, get all the target traitsets from candidateSet's rel list,
for (RelNode rel : ((RelSubset) candidateSet).getRelList()) {
if (isPhysical(rel)) {
final RelTraitSet relTraitSet = rel.getTraitSet();
if (!traitSets.contains(relTraitSet)) {
traitSets.add(relTraitSet);
logger.trace("{}.convertChild get traitSet {}", this.getClass().getSimpleName(), relTraitSet);
}
}
}
// 2, convert the candidateSet to targeted taitSets
for (RelTraitSet traitSet : traitSets) {
RelNode newRel = RelOptRule.convert(candidateSet, traitSet.simplify());
if (transformedRels.contains(newRel)) {
continue;
}
transformedRels.add(newRel);
logger.trace("{}.convertChild to convert NODE {} ,AND {}", this.getClass().getSimpleName(), n, newRel);
RelNode out = convertChild(n, newRel);
// RelNode out = convertChild(n, rel);
if (out != null) {
call.transformTo(out);
transform = true;
}
}
return transform;
}
use of org.apache.calcite.plan.volcano.RelSubset in project drill by apache.
the class JoinUtils method isScalarSubquery.
/**
* Utility method to check if a subquery (represented by its root RelNode) is provably scalar. Currently
* only aggregates with no group-by are considered scalar. In the future, this method should be generalized
* to include more cases and reconciled with Calcite's notion of scalar.
* @param root The root RelNode to be examined
* @return True if the root rel or its descendant is scalar, False otherwise
*/
public static boolean isScalarSubquery(RelNode root) {
DrillAggregateRel agg = null;
RelNode currentrel = root;
while (agg == null && currentrel != null) {
if (currentrel instanceof DrillAggregateRel) {
agg = (DrillAggregateRel) currentrel;
} else if (currentrel instanceof RelSubset) {
currentrel = ((RelSubset) currentrel).getBest();
} else if (currentrel instanceof DrillLimitRel) {
// TODO: Improve this check when DRILL-5691 is fixed.
// The problem is that RelMdMaxRowCount currently cannot be used
// due to CALCITE-1048.
Integer fetchValue = ((RexLiteral) ((DrillLimitRel) currentrel).getFetch()).getValueAs(Integer.class);
return fetchValue != null && fetchValue <= 1;
} else if (currentrel.getInputs().size() == 1) {
// If the rel is not an aggregate or RelSubset, but is a single-input rel (could be Project,
// Filter, Sort etc.), check its input
currentrel = currentrel.getInput(0);
} else {
break;
}
}
if (agg != null) {
if (agg.getGroupSet().isEmpty()) {
return true;
}
// with empty call list and literal from project expression in group set.
if (agg.getAggCallList().isEmpty() && agg.getGroupSet().cardinality() == 1) {
ProjectExpressionsCollector expressionsCollector = new ProjectExpressionsCollector();
agg.accept(expressionsCollector);
List<RexNode> projectedExpressions = expressionsCollector.getProjectedExpressions();
return projectedExpressions.size() == 1 && RexUtil.isLiteral(projectedExpressions.get(agg.getGroupSet().nth(0)), true);
}
}
return false;
}
use of org.apache.calcite.plan.volcano.RelSubset in project drill by apache.
the class DrillPushRowKeyJoinToScanRule method isRowKeyColumn.
/* Finds whether the given column reference is for the rowkey col(also known as primary-key col).
* We need to recurse down the operators looking at their references down to the scan
* to figure out whether the reference is a rowkey col. Projections can rearrange the
* incoming columns. We also need to handle HepRelVertex/RelSubset while handling the rels.
*/
private static boolean isRowKeyColumn(int index, RelNode rel) {
RelNode curRel = rel;
int curIndex = index;
while (curRel != null && !(curRel instanceof DrillScanRel)) {
logger.debug("IsRowKeyColumn: Rel={}, RowTypePos={}, RowType={}", curRel.toString(), curIndex, curRel.getRowType().toString());
if (curRel instanceof HepRelVertex) {
curRel = ((HepRelVertex) curRel).getCurrentRel();
} else if (curRel instanceof RelSubset) {
if (((RelSubset) curRel).getBest() != null) {
curRel = ((RelSubset) curRel).getBest();
} else {
curRel = ((RelSubset) curRel).getOriginal();
}
} else {
RelNode child = null;
// before recursing down that child rel.
for (RelNode input : curRel.getInputs()) {
if (input.getRowType().getFieldList().size() <= curIndex) {
curIndex -= input.getRowType().getFieldList().size();
} else {
child = input;
break;
}
}
curRel = child;
}
// Otherwise, the column index is the `RexInputRef` index.
if (curRel != null && curRel instanceof DrillProjectRel) {
List<RexNode> childExprs = curRel.getChildExps();
if (childExprs != null && childExprs.size() > 0) {
if (childExprs.get(curIndex) instanceof RexInputRef) {
curIndex = ((RexInputRef) childExprs.get(curIndex)).getIndex();
} else {
// Currently do not support expressions on rowkey col. So if an expr is present,
// return false
logger.debug("IsRowKeyColumn: ABORT: Primary-key EXPR$={}", childExprs.get(curIndex).toString());
return false;
}
}
}
}
logger.debug("IsRowKeyColumn:Primary-key Col={} ", curRel != null ? curRel.getRowType().getFieldNames().get(curIndex) : "??");
// Get the primary-key col name from the scan and match with the column being referenced.
if (curRel != null && curRel instanceof DrillScanRel) {
if (((DrillScanRel) curRel).getGroupScan() instanceof DbGroupScan) {
DbGroupScan dbGroupScan = (DbGroupScan) ((DrillScanRel) curRel).getGroupScan();
String rowKeyName = dbGroupScan.getRowKeyName();
DbGroupScan restrictedGroupScan = dbGroupScan.getRestrictedScan(((DrillScanRel) curRel).getColumns());
// Also verify this scan supports restricted groupscans(random seeks)
if (restrictedGroupScan != null && curRel.getRowType().getFieldNames().get(curIndex).equalsIgnoreCase(rowKeyName)) {
logger.debug("IsRowKeyColumn: FOUND: Rel={}, RowTypePos={}, RowType={}", curRel.toString(), curIndex, curRel.getRowType().toString());
return true;
}
}
}
logger.debug("IsRowKeyColumn: NOT FOUND");
return false;
}
Aggregations