Search in sources :

Example 1 with DruidQueryRel

use of org.apache.druid.sql.calcite.rel.DruidQueryRel in project druid by druid-io.

the class DruidUnionDataSourceRule method onMatch.

@Override
public void onMatch(final RelOptRuleCall call) {
    final Union unionRel = call.rel(0);
    final DruidRel<?> firstDruidRel = call.rel(1);
    final DruidQueryRel secondDruidRel = call.rel(2);
    if (firstDruidRel instanceof DruidUnionDataSourceRel) {
        // Unwrap and flatten the inputs to the Union.
        final RelNode newUnionRel = call.builder().pushAll(firstDruidRel.getInputs()).push(secondDruidRel).union(true, firstDruidRel.getInputs().size() + 1).build();
        call.transformTo(DruidUnionDataSourceRel.create((Union) newUnionRel, getColumnNamesIfTableOrUnion(firstDruidRel, plannerContext).get(), firstDruidRel.getPlannerContext()));
    } else {
        // Sanity check.
        if (!(firstDruidRel instanceof DruidQueryRel)) {
            throw new ISE("Expected first rel to be a DruidQueryRel, but it was %s", firstDruidRel.getClass().getName());
        }
        call.transformTo(DruidUnionDataSourceRel.create(unionRel, getColumnNamesIfTableOrUnion(firstDruidRel, plannerContext).get(), firstDruidRel.getPlannerContext()));
    }
}
Also used : RelNode(org.apache.calcite.rel.RelNode) DruidQueryRel(org.apache.druid.sql.calcite.rel.DruidQueryRel) ISE(org.apache.druid.java.util.common.ISE) DruidUnionDataSourceRel(org.apache.druid.sql.calcite.rel.DruidUnionDataSourceRel) Union(org.apache.calcite.rel.core.Union)

Example 2 with DruidQueryRel

use of org.apache.druid.sql.calcite.rel.DruidQueryRel in project druid by druid-io.

the class DruidJoinRule method onMatch.

@Override
public void onMatch(RelOptRuleCall call) {
    final Join join = call.rel(0);
    final DruidRel<?> left = call.rel(1);
    final DruidRel<?> right = call.rel(2);
    final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
    final DruidRel<?> newLeft;
    final DruidRel<?> newRight;
    final Filter leftFilter;
    final List<RexNode> newProjectExprs = new ArrayList<>();
    // Already verified to be present in "matches", so just call "get".
    // Can't be final, because we're going to reassign it up to a couple of times.
    ConditionAnalysis conditionAnalysis = analyzeCondition(join.getCondition(), join.getLeft().getRowType(), right).get();
    final boolean isLeftDirectAccessPossible = enableLeftScanDirect && (left instanceof DruidQueryRel);
    if (left.getPartialDruidQuery().stage() == PartialDruidQuery.Stage.SELECT_PROJECT && (isLeftDirectAccessPossible || left.getPartialDruidQuery().getWhereFilter() == null)) {
        // Swap the left-side projection above the join, so the left side is a simple scan or mapping. This helps us
        // avoid subqueries.
        final RelNode leftScan = left.getPartialDruidQuery().getScan();
        final Project leftProject = left.getPartialDruidQuery().getSelectProject();
        leftFilter = left.getPartialDruidQuery().getWhereFilter();
        // Left-side projection expressions rewritten to be on top of the join.
        newProjectExprs.addAll(leftProject.getProjects());
        newLeft = left.withPartialQuery(PartialDruidQuery.create(leftScan));
        conditionAnalysis = conditionAnalysis.pushThroughLeftProject(leftProject);
    } else {
        // Leave left as-is. Write input refs that do nothing.
        for (int i = 0; i < left.getRowType().getFieldCount(); i++) {
            newProjectExprs.add(rexBuilder.makeInputRef(join.getRowType().getFieldList().get(i).getType(), i));
        }
        newLeft = left;
        leftFilter = null;
    }
    if (right.getPartialDruidQuery().stage() == PartialDruidQuery.Stage.SELECT_PROJECT && right.getPartialDruidQuery().getWhereFilter() == null && !right.getPartialDruidQuery().getSelectProject().isMapping() && conditionAnalysis.onlyUsesMappingsFromRightProject(right.getPartialDruidQuery().getSelectProject())) {
        // Swap the right-side projection above the join, so the right side is a simple scan or mapping. This helps us
        // avoid subqueries.
        final RelNode rightScan = right.getPartialDruidQuery().getScan();
        final Project rightProject = right.getPartialDruidQuery().getSelectProject();
        // Right-side projection expressions rewritten to be on top of the join.
        for (final RexNode rexNode : RexUtil.shift(rightProject.getProjects(), newLeft.getRowType().getFieldCount())) {
            if (join.getJoinType().generatesNullsOnRight()) {
                newProjectExprs.add(makeNullableIfLiteral(rexNode, rexBuilder));
            } else {
                newProjectExprs.add(rexNode);
            }
        }
        newRight = right.withPartialQuery(PartialDruidQuery.create(rightScan));
        conditionAnalysis = conditionAnalysis.pushThroughRightProject(rightProject);
    } else {
        // Leave right as-is. Write input refs that do nothing.
        for (int i = 0; i < right.getRowType().getFieldCount(); i++) {
            newProjectExprs.add(rexBuilder.makeInputRef(join.getRowType().getFieldList().get(left.getRowType().getFieldCount() + i).getType(), newLeft.getRowType().getFieldCount() + i));
        }
        newRight = right;
    }
    // Druid join written on top of the new left and right sides.
    final DruidJoinQueryRel druidJoin = DruidJoinQueryRel.create(join.copy(join.getTraitSet(), conditionAnalysis.getCondition(rexBuilder), newLeft, newRight, join.getJoinType(), join.isSemiJoinDone()), leftFilter, left.getPlannerContext());
    final RelBuilder relBuilder = call.builder().push(druidJoin).project(RexUtil.fixUp(rexBuilder, newProjectExprs, RelOptUtil.getFieldTypeList(druidJoin.getRowType())));
    call.transformTo(relBuilder.build());
}
Also used : DruidJoinQueryRel(org.apache.druid.sql.calcite.rel.DruidJoinQueryRel) RelBuilder(org.apache.calcite.tools.RelBuilder) ArrayList(java.util.ArrayList) Join(org.apache.calcite.rel.core.Join) DruidQueryRel(org.apache.druid.sql.calcite.rel.DruidQueryRel) Project(org.apache.calcite.rel.core.Project) RelNode(org.apache.calcite.rel.RelNode) Filter(org.apache.calcite.rel.core.Filter) RexBuilder(org.apache.calcite.rex.RexBuilder) RexNode(org.apache.calcite.rex.RexNode)

Example 3 with DruidQueryRel

use of org.apache.druid.sql.calcite.rel.DruidQueryRel in project druid by druid-io.

the class DruidJoinRule method analyzeCondition.

/**
 * If this condition is an AND of some combination of (1) literals; (2) equality conditions of the form
 * {@code f(LeftRel) = RightColumn}, then return a {@link ConditionAnalysis}.
 */
private Optional<ConditionAnalysis> analyzeCondition(final RexNode condition, final RelDataType leftRowType, DruidRel<?> right) {
    final List<RexNode> subConditions = decomposeAnd(condition);
    final List<Pair<RexNode, RexInputRef>> equalitySubConditions = new ArrayList<>();
    final List<RexLiteral> literalSubConditions = new ArrayList<>();
    final int numLeftFields = leftRowType.getFieldCount();
    final Set<RexInputRef> rightColumns = new HashSet<>();
    for (RexNode subCondition : subConditions) {
        if (RexUtil.isLiteral(subCondition, true)) {
            if (subCondition.isA(SqlKind.CAST)) {
                // This is CAST(literal) which is always OK.
                // We know that this is CAST(literal) as it passed the check from RexUtil.isLiteral
                RexCall call = (RexCall) subCondition;
                // are different, then skipping the cast might change the meaning of the subcondition.
                if (call.getType().getSqlTypeName().equals(call.getOperands().get(0).getType().getSqlTypeName())) {
                    // If the types are the same, unwrap the cast and use the underlying literal.
                    literalSubConditions.add((RexLiteral) call.getOperands().get(0));
                } else {
                    // If the types are not the same, return Optional.empty() indicating the condition is not supported.
                    return Optional.empty();
                }
            } else {
                // Literals are always OK.
                literalSubConditions.add((RexLiteral) subCondition);
            }
            continue;
        }
        if (!subCondition.isA(SqlKind.EQUALS)) {
            // If it's not EQUALS, it's not supported.
            plannerContext.setPlanningError("SQL requires a join with '%s' condition that is not supported.", subCondition.getKind());
            return Optional.empty();
        }
        final List<RexNode> operands = ((RexCall) subCondition).getOperands();
        Preconditions.checkState(operands.size() == 2, "Expected 2 operands, got[%,d]", operands.size());
        if (isLeftExpression(operands.get(0), numLeftFields) && isRightInputRef(operands.get(1), numLeftFields)) {
            equalitySubConditions.add(Pair.of(operands.get(0), (RexInputRef) operands.get(1)));
            rightColumns.add((RexInputRef) operands.get(1));
        } else if (isRightInputRef(operands.get(0), numLeftFields) && isLeftExpression(operands.get(1), numLeftFields)) {
            equalitySubConditions.add(Pair.of(operands.get(1), (RexInputRef) operands.get(0)));
            rightColumns.add((RexInputRef) operands.get(0));
        } else {
            // Cannot handle this condition.
            plannerContext.setPlanningError("SQL is resulting in a join that has unsupported operand types.");
            return Optional.empty();
        }
    }
    // thereby allowing join conditions on both k and v columns of the lookup
    if (right != null && !DruidJoinQueryRel.computeRightRequiresSubquery(DruidJoinQueryRel.getSomeDruidChild(right)) && right instanceof DruidQueryRel) {
        DruidQueryRel druidQueryRel = (DruidQueryRel) right;
        if (druidQueryRel.getDruidTable().getDataSource() instanceof LookupDataSource) {
            long distinctRightColumns = rightColumns.stream().map(RexSlot::getIndex).distinct().count();
            if (distinctRightColumns > 1) {
                // it means that the join's right side is lookup and the join condition contains both key and value columns of lookup.
                // currently, the lookup datasource in the native engine doesn't support using value column in the join condition.
                plannerContext.setPlanningError("SQL is resulting in a join involving lookup where value column is used in the condition.");
                return Optional.empty();
            }
        }
    }
    return Optional.of(new ConditionAnalysis(numLeftFields, equalitySubConditions, literalSubConditions));
}
Also used : RexLiteral(org.apache.calcite.rex.RexLiteral) ArrayList(java.util.ArrayList) DruidQueryRel(org.apache.druid.sql.calcite.rel.DruidQueryRel) RexCall(org.apache.calcite.rex.RexCall) LookupDataSource(org.apache.druid.query.LookupDataSource) RexInputRef(org.apache.calcite.rex.RexInputRef) RexSlot(org.apache.calcite.rex.RexSlot) RexNode(org.apache.calcite.rex.RexNode) Pair(org.apache.druid.java.util.common.Pair) HashSet(java.util.HashSet)

Example 4 with DruidQueryRel

use of org.apache.druid.sql.calcite.rel.DruidQueryRel in project druid by druid-io.

the class DruidUnionDataSourceRule method matches.

@Override
public boolean matches(RelOptRuleCall call) {
    final Union unionRel = call.rel(0);
    final DruidRel<?> firstDruidRel = call.rel(1);
    final DruidQueryRel secondDruidRel = call.rel(2);
    return isCompatible(unionRel, firstDruidRel, secondDruidRel, plannerContext);
}
Also used : DruidQueryRel(org.apache.druid.sql.calcite.rel.DruidQueryRel) Union(org.apache.calcite.rel.core.Union)

Aggregations

DruidQueryRel (org.apache.druid.sql.calcite.rel.DruidQueryRel)4 ArrayList (java.util.ArrayList)2 RelNode (org.apache.calcite.rel.RelNode)2 Union (org.apache.calcite.rel.core.Union)2 RexNode (org.apache.calcite.rex.RexNode)2 HashSet (java.util.HashSet)1 Filter (org.apache.calcite.rel.core.Filter)1 Join (org.apache.calcite.rel.core.Join)1 Project (org.apache.calcite.rel.core.Project)1 RexBuilder (org.apache.calcite.rex.RexBuilder)1 RexCall (org.apache.calcite.rex.RexCall)1 RexInputRef (org.apache.calcite.rex.RexInputRef)1 RexLiteral (org.apache.calcite.rex.RexLiteral)1 RexSlot (org.apache.calcite.rex.RexSlot)1 RelBuilder (org.apache.calcite.tools.RelBuilder)1 ISE (org.apache.druid.java.util.common.ISE)1 Pair (org.apache.druid.java.util.common.Pair)1 LookupDataSource (org.apache.druid.query.LookupDataSource)1 DruidJoinQueryRel (org.apache.druid.sql.calcite.rel.DruidJoinQueryRel)1 DruidUnionDataSourceRel (org.apache.druid.sql.calcite.rel.DruidUnionDataSourceRel)1