use of org.apache.calcite.util.ImmutableBitSet in project calcite by apache.
the class RelOptUtil method splitJoinCondition.
private static void splitJoinCondition(List<RelDataTypeField> sysFieldList, List<RelNode> inputs, RexNode condition, List<List<RexNode>> joinKeys, @Nullable List<Integer> filterNulls, @Nullable List<SqlOperator> rangeOp, List<RexNode> nonEquiList) {
final int sysFieldCount = sysFieldList.size();
final RelOptCluster cluster = inputs.get(0).getCluster();
final RexBuilder rexBuilder = cluster.getRexBuilder();
final RelDataTypeFactory typeFactory = cluster.getTypeFactory();
final ImmutableBitSet[] inputsRange = new ImmutableBitSet[inputs.size()];
int totalFieldCount = 0;
for (int i = 0; i < inputs.size(); i++) {
final int firstField = totalFieldCount + sysFieldCount;
totalFieldCount = firstField + inputs.get(i).getRowType().getFieldCount();
inputsRange[i] = ImmutableBitSet.range(firstField, totalFieldCount);
}
// adjustment array
int[] adjustments = new int[totalFieldCount];
for (int i = 0; i < inputs.size(); i++) {
final int adjustment = inputsRange[i].nextSetBit(0);
for (int j = adjustment; j < inputsRange[i].length(); j++) {
adjustments[j] = -adjustment;
}
}
if (condition.getKind() == SqlKind.AND) {
for (RexNode operand : ((RexCall) condition).getOperands()) {
splitJoinCondition(sysFieldList, inputs, operand, joinKeys, filterNulls, rangeOp, nonEquiList);
}
return;
}
if (condition instanceof RexCall) {
RexNode leftKey = null;
RexNode rightKey = null;
int leftInput = 0;
int rightInput = 0;
List<RelDataTypeField> leftFields = null;
List<RelDataTypeField> rightFields = null;
boolean reverse = false;
final RexCall call = collapseExpandedIsNotDistinctFromExpr((RexCall) condition, rexBuilder);
SqlKind kind = call.getKind();
// Only consider range operators if we haven't already seen one
if ((kind == SqlKind.EQUALS) || (filterNulls != null && kind == SqlKind.IS_NOT_DISTINCT_FROM) || (rangeOp != null && rangeOp.isEmpty() && (kind == SqlKind.GREATER_THAN || kind == SqlKind.GREATER_THAN_OR_EQUAL || kind == SqlKind.LESS_THAN || kind == SqlKind.LESS_THAN_OR_EQUAL))) {
final List<RexNode> operands = call.getOperands();
RexNode op0 = operands.get(0);
RexNode op1 = operands.get(1);
final ImmutableBitSet projRefs0 = InputFinder.bits(op0);
final ImmutableBitSet projRefs1 = InputFinder.bits(op1);
boolean foundBothInputs = false;
for (int i = 0; i < inputs.size() && !foundBothInputs; i++) {
if (projRefs0.intersects(inputsRange[i]) && projRefs0.union(inputsRange[i]).equals(inputsRange[i])) {
if (leftKey == null) {
leftKey = op0;
leftInput = i;
leftFields = inputs.get(leftInput).getRowType().getFieldList();
} else {
rightKey = op0;
rightInput = i;
rightFields = inputs.get(rightInput).getRowType().getFieldList();
reverse = true;
foundBothInputs = true;
}
} else if (projRefs1.intersects(inputsRange[i]) && projRefs1.union(inputsRange[i]).equals(inputsRange[i])) {
if (leftKey == null) {
leftKey = op1;
leftInput = i;
leftFields = inputs.get(leftInput).getRowType().getFieldList();
} else {
rightKey = op1;
rightInput = i;
rightFields = inputs.get(rightInput).getRowType().getFieldList();
foundBothInputs = true;
}
}
}
if ((leftKey != null) && (rightKey != null)) {
// replace right Key input ref
rightKey = rightKey.accept(new RelOptUtil.RexInputConverter(rexBuilder, rightFields, rightFields, adjustments));
// left key only needs to be adjusted if there are system
// fields, but do it for uniformity
leftKey = leftKey.accept(new RelOptUtil.RexInputConverter(rexBuilder, leftFields, leftFields, adjustments));
RelDataType leftKeyType = leftKey.getType();
RelDataType rightKeyType = rightKey.getType();
if (leftKeyType != rightKeyType) {
// perform casting
RelDataType targetKeyType = typeFactory.leastRestrictive(ImmutableList.of(leftKeyType, rightKeyType));
if (targetKeyType == null) {
throw new AssertionError("Cannot find common type for join keys " + leftKey + " (type " + leftKeyType + ") and " + rightKey + " (type " + rightKeyType + ")");
}
if (leftKeyType != targetKeyType) {
leftKey = rexBuilder.makeCast(targetKeyType, leftKey);
}
if (rightKeyType != targetKeyType) {
rightKey = rexBuilder.makeCast(targetKeyType, rightKey);
}
}
}
}
if ((rangeOp == null) && ((leftKey == null) || (rightKey == null))) {
// no equality join keys found yet:
// try transforming the condition to
// equality "join" conditions, e.g.
// f(LHS) > 0 ===> ( f(LHS) > 0 ) = TRUE,
// and make the RHS produce TRUE, but only if we're strictly
// looking for equi-joins
final ImmutableBitSet projRefs = InputFinder.bits(condition);
leftKey = null;
rightKey = null;
boolean foundInput = false;
for (int i = 0; i < inputs.size() && !foundInput; i++) {
if (inputsRange[i].contains(projRefs)) {
leftInput = i;
leftFields = inputs.get(leftInput).getRowType().getFieldList();
leftKey = condition.accept(new RelOptUtil.RexInputConverter(rexBuilder, leftFields, leftFields, adjustments));
rightKey = rexBuilder.makeLiteral(true);
// effectively performing an equality comparison
kind = SqlKind.EQUALS;
foundInput = true;
}
}
}
if ((leftKey != null) && (rightKey != null)) {
// found suitable join keys
// add them to key list, ensuring that if there is a
// non-equi join predicate, it appears at the end of the
// key list; also mark the null filtering property
addJoinKey(joinKeys.get(leftInput), leftKey, (rangeOp != null) && !rangeOp.isEmpty());
addJoinKey(joinKeys.get(rightInput), rightKey, (rangeOp != null) && !rangeOp.isEmpty());
if (filterNulls != null && kind == SqlKind.EQUALS) {
// nulls are considered not matching for equality comparison
// add the position of the most recently inserted key
filterNulls.add(joinKeys.get(leftInput).size() - 1);
}
if (rangeOp != null && kind != SqlKind.EQUALS && kind != SqlKind.IS_DISTINCT_FROM) {
SqlOperator op = call.getOperator();
if (reverse) {
op = requireNonNull(op.reverse());
}
rangeOp.add(op);
}
return;
}
// else fall through and add this condition as nonEqui condition
}
// The operator is not of RexCall type
// So we fail. Fall through.
// Add this condition to the list of non-equi-join conditions.
nonEquiList.add(condition);
}
use of org.apache.calcite.util.ImmutableBitSet in project calcite by apache.
the class RelOptUtil method classifyFilters.
/**
* Classifies filters according to where they should be processed. They
* either stay where they are, are pushed to the join (if they originated
* from above the join), or are pushed to one of the children. Filters that
* are pushed are added to list passed in as input parameters.
*
* @param joinRel join node
* @param filters filters to be classified
* @param pushInto whether filters can be pushed into the join
* @param pushLeft true if filters can be pushed to the left
* @param pushRight true if filters can be pushed to the right
* @param joinFilters list of filters to push to the join
* @param leftFilters list of filters to push to the left child
* @param rightFilters list of filters to push to the right child
* @return whether at least one filter was pushed
*/
public static boolean classifyFilters(RelNode joinRel, List<RexNode> filters, boolean pushInto, boolean pushLeft, boolean pushRight, List<RexNode> joinFilters, List<RexNode> leftFilters, List<RexNode> rightFilters) {
RexBuilder rexBuilder = joinRel.getCluster().getRexBuilder();
List<RelDataTypeField> joinFields = joinRel.getRowType().getFieldList();
// joinRel.getSystemFieldList().size();
final int nSysFields = 0;
final List<RelDataTypeField> leftFields = joinRel.getInputs().get(0).getRowType().getFieldList();
final int nFieldsLeft = leftFields.size();
final List<RelDataTypeField> rightFields = joinRel.getInputs().get(1).getRowType().getFieldList();
final int nFieldsRight = rightFields.size();
final int nTotalFields = nFieldsLeft + nFieldsRight;
// set the reference bitmaps for the left and right children
ImmutableBitSet leftBitmap = ImmutableBitSet.range(nSysFields, nSysFields + nFieldsLeft);
ImmutableBitSet rightBitmap = ImmutableBitSet.range(nSysFields + nFieldsLeft, nTotalFields);
final List<RexNode> filtersToRemove = new ArrayList<>();
for (RexNode filter : filters) {
final InputFinder inputFinder = InputFinder.analyze(filter);
final ImmutableBitSet inputBits = inputFinder.build();
if (pushLeft && leftBitmap.contains(inputBits)) {
// ignore filters that always evaluate to true
if (!filter.isAlwaysTrue()) {
// adjust the field references in the filter to reflect
// that fields in the left now shift over by the number
// of system fields
final RexNode shiftedFilter = shiftFilter(nSysFields, nSysFields + nFieldsLeft, -nSysFields, rexBuilder, joinFields, nTotalFields, leftFields, filter);
leftFilters.add(shiftedFilter);
}
filtersToRemove.add(filter);
} else if (pushRight && rightBitmap.contains(inputBits)) {
if (!filter.isAlwaysTrue()) {
// adjust the field references in the filter to reflect
// that fields in the right now shift over to the left
final RexNode shiftedFilter = shiftFilter(nSysFields + nFieldsLeft, nTotalFields, -(nSysFields + nFieldsLeft), rexBuilder, joinFields, nTotalFields, rightFields, filter);
rightFilters.add(shiftedFilter);
}
filtersToRemove.add(filter);
} else {
// If the filter can't be pushed to either child, we may push them into the join
if (pushInto) {
if (!joinFilters.contains(filter)) {
joinFilters.add(filter);
}
filtersToRemove.add(filter);
}
}
}
// Remove filters after the loop, to prevent concurrent modification.
if (!filtersToRemove.isEmpty()) {
filters.removeAll(filtersToRemove);
}
// Did anything change?
return !filtersToRemove.isEmpty();
}
use of org.apache.calcite.util.ImmutableBitSet in project calcite by apache.
the class RelOptUtil method pushDownEqualJoinConditions.
/**
* Pushes down parts of a join condition.
*
* <p>For example, given
* "emp JOIN dept ON emp.deptno + 1 = dept.deptno", adds a project above
* "emp" that computes the expression
* "emp.deptno + 1". The resulting join condition is a simple combination
* of AND, equals, and input fields.
*/
private static RexNode pushDownEqualJoinConditions(RexNode condition, int leftCount, int rightCount, List<RexNode> extraLeftExprs, List<RexNode> extraRightExprs, RexBuilder builder) {
// Normalize the condition first
RexNode node = (condition instanceof RexCall) ? collapseExpandedIsNotDistinctFromExpr((RexCall) condition, builder) : condition;
switch(node.getKind()) {
case EQUALS:
case IS_NOT_DISTINCT_FROM:
final RexCall call0 = (RexCall) node;
final RexNode leftRex = call0.getOperands().get(0);
final RexNode rightRex = call0.getOperands().get(1);
final ImmutableBitSet leftBits = RelOptUtil.InputFinder.bits(leftRex);
final ImmutableBitSet rightBits = RelOptUtil.InputFinder.bits(rightRex);
final int pivot = leftCount + extraLeftExprs.size();
Side lside = Side.of(leftBits, pivot);
Side rside = Side.of(rightBits, pivot);
if (!lside.opposite(rside)) {
return call0;
}
// fall through
case AND:
final RexCall call = (RexCall) node;
final List<RexNode> list = new ArrayList<>();
List<RexNode> operands = Lists.newArrayList(call.getOperands());
for (int i = 0; i < operands.size(); i++) {
RexNode operand = operands.get(i);
if (operand instanceof RexCall) {
operand = collapseExpandedIsNotDistinctFromExpr((RexCall) operand, builder);
}
if (node.getKind() == SqlKind.AND && operand.getKind() != SqlKind.EQUALS && operand.getKind() != SqlKind.IS_NOT_DISTINCT_FROM) {
// one of the join condition is neither EQ nor INDF
list.add(operand);
} else {
final int left2 = leftCount + extraLeftExprs.size();
final RexNode e = pushDownEqualJoinConditions(operand, leftCount, rightCount, extraLeftExprs, extraRightExprs, builder);
if (!e.equals(operand)) {
final List<RexNode> remainingOperands = Util.skip(operands, i + 1);
final int left3 = leftCount + extraLeftExprs.size();
fix(remainingOperands, left2, left3);
fix(list, left2, left3);
}
list.add(e);
}
}
if (!list.equals(call.getOperands())) {
return call.clone(call.getType(), list);
}
return call;
case OR:
case INPUT_REF:
case LITERAL:
case NOT:
return node;
default:
final ImmutableBitSet bits = RelOptUtil.InputFinder.bits(node);
final int mid = leftCount + extraLeftExprs.size();
switch(Side.of(bits, mid)) {
case LEFT:
fix(extraRightExprs, mid, mid + 1);
extraLeftExprs.add(node);
return new RexInputRef(mid, node.getType());
case RIGHT:
final int index2 = mid + rightCount + extraRightExprs.size();
extraRightExprs.add(node);
return new RexInputRef(index2, node.getType());
case BOTH:
case EMPTY:
default:
return node;
}
}
}
use of org.apache.calcite.util.ImmutableBitSet in project calcite by apache.
the class RelOptUtil method simplifyJoin.
/**
* Simplifies outer joins if filter above would reject nulls.
*
* @param joinRel Join
* @param aboveFilters Filters from above
* @param joinType Join type, can not be inner join
*/
public static JoinRelType simplifyJoin(RelNode joinRel, ImmutableList<RexNode> aboveFilters, JoinRelType joinType) {
// No need to simplify if the join only outputs left side.
if (!joinType.projectsRight()) {
return joinType;
}
final int nTotalFields = joinRel.getRowType().getFieldCount();
final int nSysFields = 0;
final int nFieldsLeft = joinRel.getInputs().get(0).getRowType().getFieldCount();
final int nFieldsRight = joinRel.getInputs().get(1).getRowType().getFieldCount();
assert nTotalFields == nSysFields + nFieldsLeft + nFieldsRight;
// set the reference bitmaps for the left and right children
ImmutableBitSet leftBitmap = ImmutableBitSet.range(nSysFields, nSysFields + nFieldsLeft);
ImmutableBitSet rightBitmap = ImmutableBitSet.range(nSysFields + nFieldsLeft, nTotalFields);
for (RexNode filter : aboveFilters) {
if (joinType.generatesNullsOnLeft() && Strong.isNotTrue(filter, leftBitmap)) {
joinType = joinType.cancelNullsOnLeft();
}
if (joinType.generatesNullsOnRight() && Strong.isNotTrue(filter, rightBitmap)) {
joinType = joinType.cancelNullsOnRight();
}
if (!joinType.isOuterJoin()) {
break;
}
}
return joinType;
}
use of org.apache.calcite.util.ImmutableBitSet in project calcite by apache.
the class RelOptUtil method correlationColumns.
/**
* Finds which columns of a correlation variable are used within a
* relational expression.
*/
public static ImmutableBitSet correlationColumns(CorrelationId id, RelNode rel) {
final CorrelationCollector collector = new CorrelationCollector();
rel.accept(collector);
final ImmutableBitSet.Builder builder = ImmutableBitSet.builder();
for (int field : collector.vuv.variableFields.get(id)) {
if (field >= 0) {
builder.set(field);
}
}
return builder.build();
}
Aggregations