use of org.apache.calcite.rel.core.CorrelationId in project calcite by apache.
the class EnumerableBatchNestedLoopJoinRule method onMatch.
@Override
public void onMatch(RelOptRuleCall call) {
final Join join = call.rel(0);
final int leftFieldCount = join.getLeft().getRowType().getFieldCount();
final RelOptCluster cluster = join.getCluster();
final RexBuilder rexBuilder = cluster.getRexBuilder();
final RelBuilder relBuilder = call.builder();
final Set<CorrelationId> correlationIds = new HashSet<>();
final List<RexNode> corrVarList = new ArrayList<>();
final int batchSize = config.batchSize();
for (int i = 0; i < batchSize; i++) {
CorrelationId correlationId = cluster.createCorrel();
correlationIds.add(correlationId);
corrVarList.add(rexBuilder.makeCorrel(join.getLeft().getRowType(), correlationId));
}
final RexNode corrVar0 = corrVarList.get(0);
final ImmutableBitSet.Builder requiredColumns = ImmutableBitSet.builder();
// Generate first condition
final RexNode condition = join.getCondition().accept(new RexShuttle() {
@Override
public RexNode visitInputRef(RexInputRef input) {
int field = input.getIndex();
if (field >= leftFieldCount) {
return rexBuilder.makeInputRef(input.getType(), input.getIndex() - leftFieldCount);
}
requiredColumns.set(field);
return rexBuilder.makeFieldAccess(corrVar0, field);
}
});
final List<RexNode> conditionList = new ArrayList<>();
conditionList.add(condition);
// Add batchSize-1 other conditions
for (int i = 1; i < batchSize; i++) {
final int corrIndex = i;
final RexNode condition2 = condition.accept(new RexShuttle() {
@Override
public RexNode visitCorrelVariable(RexCorrelVariable variable) {
return variable.equals(corrVar0) ? corrVarList.get(corrIndex) : variable;
}
});
conditionList.add(condition2);
}
// Push a filter with batchSize disjunctions
relBuilder.push(join.getRight()).filter(relBuilder.or(conditionList));
final RelNode right = relBuilder.build();
call.transformTo(EnumerableBatchNestedLoopJoin.create(convert(join.getLeft(), join.getLeft().getTraitSet().replace(EnumerableConvention.INSTANCE)), convert(right, right.getTraitSet().replace(EnumerableConvention.INSTANCE)), join.getCondition(), requiredColumns.build(), correlationIds, join.getJoinType()));
}
use of org.apache.calcite.rel.core.CorrelationId in project calcite by apache.
the class RelOptUtil method getVariablesSetAndUsed.
// to be removed before 2.0
@Deprecated
@SuppressWarnings("MixedMutabilityReturnType")
public static List<CorrelationId> getVariablesSetAndUsed(RelNode rel0, RelNode rel1) {
Set<CorrelationId> set = getVariablesSet(rel0);
if (set.size() == 0) {
return ImmutableList.of();
}
Set<CorrelationId> used = getVariablesUsed(rel1);
if (used.size() == 0) {
return ImmutableList.of();
}
final List<CorrelationId> result = new ArrayList<>();
for (CorrelationId s : set) {
if (used.contains(s) && !result.contains(s)) {
result.add(s);
}
}
return result;
}
use of org.apache.calcite.rel.core.CorrelationId in project calcite by apache.
the class JoinToCorrelateRule method onMatch.
@Override
public void onMatch(RelOptRuleCall call) {
assert matches(call);
final Join join = call.rel(0);
RelNode right = join.getRight();
final RelNode left = join.getLeft();
final int leftFieldCount = left.getRowType().getFieldCount();
final RelOptCluster cluster = join.getCluster();
final RexBuilder rexBuilder = cluster.getRexBuilder();
final RelBuilder relBuilder = call.builder();
final CorrelationId correlationId = cluster.createCorrel();
final RexNode corrVar = rexBuilder.makeCorrel(left.getRowType(), correlationId);
final ImmutableBitSet.Builder requiredColumns = ImmutableBitSet.builder();
// Replace all references of left input with FieldAccess(corrVar, field)
final RexNode joinCondition = join.getCondition().accept(new RexShuttle() {
@Override
public RexNode visitInputRef(RexInputRef input) {
int field = input.getIndex();
if (field >= leftFieldCount) {
return rexBuilder.makeInputRef(input.getType(), input.getIndex() - leftFieldCount);
}
requiredColumns.set(field);
return rexBuilder.makeFieldAccess(corrVar, field);
}
});
relBuilder.push(right).filter(joinCondition);
RelNode newRel = LogicalCorrelate.create(left, relBuilder.build(), join.getHints(), correlationId, requiredColumns.build(), join.getJoinType());
call.transformTo(newRel);
}
use of org.apache.calcite.rel.core.CorrelationId in project calcite by apache.
the class SubQueryRemoveRule method rewriteIn.
/**
* Rewrites an IN RexSubQuery into a {@link Join}.
*
* @param e IN sub-query to rewrite
* @param variablesSet A set of variables used by a relational
* expression of the specified RexSubQuery
* @param logic Logic for evaluating
* @param builder Builder
* @param offset Offset to shift {@link RexInputRef}
*
* @return Expression that may be used to replace the RexSubQuery
*/
private static RexNode rewriteIn(RexSubQuery e, Set<CorrelationId> variablesSet, RelOptUtil.Logic logic, RelBuilder builder, int offset) {
// Most general case, where the left and right keys might have nulls, and
// caller requires 3-valued logic return.
//
// select e.deptno, e.deptno in (select deptno from emp)
// from emp as e
//
// becomes
//
// select e.deptno,
// case
// when ct.c = 0 then false
// when e.deptno is null then null
// when dt.i is not null then true
// when ct.ck < ct.c then null
// else false
// end
// from emp as e
// left join (
// (select count(*) as c, count(deptno) as ck from emp) as ct
// cross join (select distinct deptno, true as i from emp)) as dt
// on e.deptno = dt.deptno
//
// If keys are not null we can remove "ct" and simplify to
//
// select e.deptno,
// case
// when dt.i is not null then true
// else false
// end
// from emp as e
// left join (select distinct deptno, true as i from emp) as dt
// on e.deptno = dt.deptno
//
// We could further simplify to
//
// select e.deptno,
// dt.i is not null
// from emp as e
// left join (select distinct deptno, true as i from emp) as dt
// on e.deptno = dt.deptno
//
// but have not yet.
//
// If the logic is TRUE we can just kill the record if the condition
// evaluates to FALSE or UNKNOWN. Thus the query simplifies to an inner
// join:
//
// select e.deptno,
// true
// from emp as e
// inner join (select distinct deptno from emp) as dt
// on e.deptno = dt.deptno
//
builder.push(e.rel);
final List<RexNode> fields = new ArrayList<>(builder.fields());
// for the case when IN has only literal operands, it may be handled
// in the simpler way:
//
// select e.deptno, 123456 in (select deptno from emp)
// from emp as e
//
// becomes
//
// select e.deptno,
// case
// when dt.c IS NULL THEN FALSE
// when e.deptno IS NULL THEN NULL
// when dt.cs IS FALSE THEN NULL
// when dt.cs IS NOT NULL THEN TRUE
// else false
// end
// from emp AS e
// cross join (
// select distinct deptno is not null as cs, count(*) as c
// from emp
// where deptno = 123456 or deptno is null or e.deptno is null
// order by cs desc limit 1) as dt
//
boolean allLiterals = RexUtil.allLiterals(e.getOperands());
final List<RexNode> expressionOperands = new ArrayList<>(e.getOperands());
final List<RexNode> keyIsNulls = e.getOperands().stream().filter(operand -> operand.getType().isNullable()).map(builder::isNull).collect(Collectors.toList());
final RexLiteral trueLiteral = builder.literal(true);
final RexLiteral falseLiteral = builder.literal(false);
final RexLiteral unknownLiteral = builder.getRexBuilder().makeNullLiteral(trueLiteral.getType());
if (allLiterals) {
final List<RexNode> conditions = Pair.zip(expressionOperands, fields).stream().map(pair -> builder.equals(pair.left, pair.right)).collect(Collectors.toList());
switch(logic) {
case TRUE:
case TRUE_FALSE:
builder.filter(conditions);
builder.project(builder.alias(trueLiteral, "cs"));
builder.distinct();
break;
default:
List<RexNode> isNullOperands = fields.stream().map(builder::isNull).collect(Collectors.toList());
// uses keyIsNulls conditions in the filter to avoid empty results
isNullOperands.addAll(keyIsNulls);
builder.filter(builder.or(builder.and(conditions), builder.or(isNullOperands)));
RexNode project = builder.and(fields.stream().map(builder::isNotNull).collect(Collectors.toList()));
builder.project(builder.alias(project, "cs"));
if (variablesSet.isEmpty()) {
builder.aggregate(builder.groupKey(builder.field("cs")), builder.count(false, "c"));
// sorts input with desc order since we are interested
// only in the case when one of the values is true.
// When true value is absent then we are interested
// only in false value.
builder.sortLimit(0, 1, ImmutableList.of(builder.desc(builder.field("cs"))));
} else {
builder.distinct();
}
}
// clears expressionOperands and fields lists since
// all expressions were used in the filter
expressionOperands.clear();
fields.clear();
} else {
switch(logic) {
case TRUE:
builder.aggregate(builder.groupKey(fields));
break;
case TRUE_FALSE_UNKNOWN:
case UNKNOWN_AS_TRUE:
// Builds the cross join
builder.aggregate(builder.groupKey(), builder.count(false, "c"), builder.count(builder.fields()).as("ck"));
builder.as("ct");
if (!variablesSet.isEmpty()) {
builder.join(JoinRelType.LEFT, trueLiteral, variablesSet);
} else {
builder.join(JoinRelType.INNER, trueLiteral, variablesSet);
}
offset += 2;
builder.push(e.rel);
// fall through
default:
fields.add(builder.alias(trueLiteral, "i"));
builder.project(fields);
builder.distinct();
}
}
builder.as("dt");
int refOffset = offset;
final List<RexNode> conditions = Pair.zip(expressionOperands, builder.fields()).stream().map(pair -> builder.equals(pair.left, RexUtil.shift(pair.right, refOffset))).collect(Collectors.toList());
switch(logic) {
case TRUE:
builder.join(JoinRelType.INNER, builder.and(conditions), variablesSet);
return trueLiteral;
default:
break;
}
// Now the left join
builder.join(JoinRelType.LEFT, builder.and(conditions), variablesSet);
final ImmutableList.Builder<RexNode> operands = ImmutableList.builder();
RexLiteral b = trueLiteral;
switch(logic) {
case TRUE_FALSE_UNKNOWN:
b = unknownLiteral;
// fall through
case UNKNOWN_AS_TRUE:
if (allLiterals) {
// for the case of non-correlated sub-queries
if (variablesSet.isEmpty()) {
operands.add(builder.isNull(builder.field("c")), falseLiteral);
}
operands.add(builder.equals(builder.field("cs"), falseLiteral), b);
} else {
operands.add(builder.equals(builder.field("ct", "c"), builder.literal(0)), falseLiteral);
}
break;
default:
break;
}
if (!keyIsNulls.isEmpty()) {
operands.add(builder.or(keyIsNulls), unknownLiteral);
}
if (allLiterals) {
operands.add(builder.isNotNull(builder.field("cs")), trueLiteral);
} else {
operands.add(builder.isNotNull(last(builder.fields())), trueLiteral);
}
if (!allLiterals) {
switch(logic) {
case TRUE_FALSE_UNKNOWN:
case UNKNOWN_AS_TRUE:
operands.add(builder.lessThan(builder.field("ct", "ck"), builder.field("ct", "c")), b);
break;
default:
break;
}
}
operands.add(falseLiteral);
return builder.call(SqlStdOperatorTable.CASE, operands.build());
}
use of org.apache.calcite.rel.core.CorrelationId in project calcite by apache.
the class SubQueryRemoveRule method matchJoin.
private static void matchJoin(SubQueryRemoveRule rule, RelOptRuleCall call) {
final Join join = call.rel(0);
final RelBuilder builder = call.builder();
final RexSubQuery e = RexUtil.SubQueryFinder.find(join.getCondition());
assert e != null;
final RelOptUtil.Logic logic = LogicVisitor.find(RelOptUtil.Logic.TRUE, ImmutableList.of(join.getCondition()), e);
builder.push(join.getLeft());
builder.push(join.getRight());
final int fieldCount = join.getRowType().getFieldCount();
final Set<CorrelationId> variablesSet = RelOptUtil.getVariablesUsed(e.rel);
final RexNode target = rule.apply(e, variablesSet, logic, builder, 2, fieldCount);
final RexShuttle shuttle = new ReplaceSubQueryShuttle(e, target);
builder.join(join.getJoinType(), shuttle.apply(join.getCondition()));
builder.project(fields(builder, join.getRowType().getFieldCount()));
call.transformTo(builder.build());
}
Aggregations