Search in sources :

Example 1 with RexSlot

use of org.apache.calcite.rex.RexSlot 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 2 with RexSlot

use of org.apache.calcite.rex.RexSlot in project calcite by apache.

the class SplunkPushDownRule method appendSearchString.

/**
 * Appends a search string.
 *
 * @param toAppend Search string to append
 * @param splunkRel Relational expression
 * @param topProj Top projection
 * @param bottomProj Bottom projection
 */
protected RelNode appendSearchString(String toAppend, SplunkTableScan splunkRel, LogicalProject topProj, LogicalProject bottomProj, RelDataType topRow, RelDataType bottomRow) {
    final RelOptCluster cluster = splunkRel.getCluster();
    StringBuilder updateSearchStr = new StringBuilder(splunkRel.search);
    if (!toAppend.isEmpty()) {
        updateSearchStr.append(" ").append(toAppend);
    }
    List<RelDataTypeField> bottomFields = bottomRow == null ? null : bottomRow.getFieldList();
    List<RelDataTypeField> topFields = topRow == null ? null : topRow.getFieldList();
    if (bottomFields == null) {
        bottomFields = splunkRel.getRowType().getFieldList();
    }
    // handle bottom projection (ie choose a subset of the table fields)
    if (bottomProj != null) {
        List<RelDataTypeField> tmp = new ArrayList<RelDataTypeField>();
        List<RelDataTypeField> dRow = bottomProj.getRowType().getFieldList();
        for (RexNode rn : bottomProj.getProjects()) {
            RelDataTypeField rdtf;
            if (rn instanceof RexSlot) {
                RexSlot rs = (RexSlot) rn;
                rdtf = bottomFields.get(rs.getIndex());
            } else {
                rdtf = dRow.get(tmp.size());
            }
            tmp.add(rdtf);
        }
        bottomFields = tmp;
    }
    // field renaming: to -> from
    List<Pair<String, String>> renames = new LinkedList<Pair<String, String>>();
    // handle top projection (ie reordering and renaming)
    List<RelDataTypeField> newFields = bottomFields;
    if (topProj != null) {
        LOGGER.debug("topProj: {}", String.valueOf(topProj.getPermutation()));
        newFields = new ArrayList<RelDataTypeField>();
        int i = 0;
        for (RexNode rn : topProj.getProjects()) {
            RexInputRef rif = (RexInputRef) rn;
            RelDataTypeField field = bottomFields.get(rif.getIndex());
            if (!bottomFields.get(rif.getIndex()).getName().equals(topFields.get(i).getName())) {
                renames.add(Pair.of(bottomFields.get(rif.getIndex()).getName(), topFields.get(i).getName()));
                field = topFields.get(i);
            }
            newFields.add(field);
        }
    }
    if (!renames.isEmpty()) {
        updateSearchStr.append("| rename ");
        for (Pair<String, String> p : renames) {
            updateSearchStr.append(p.left).append(" AS ").append(p.right).append(" ");
        }
    }
    RelDataType resultType = cluster.getTypeFactory().createStructType(newFields);
    String searchWithFilter = updateSearchStr.toString();
    RelNode rel = new SplunkTableScan(cluster, splunkRel.getTable(), splunkRel.splunkTable, searchWithFilter, splunkRel.earliest, splunkRel.latest, resultType.getFieldNames());
    LOGGER.debug("end of appendSearchString fieldNames: {}", rel.getRowType().getFieldNames());
    return rel;
}
Also used : RelOptCluster(org.apache.calcite.plan.RelOptCluster) ArrayList(java.util.ArrayList) RelDataType(org.apache.calcite.rel.type.RelDataType) NlsString(org.apache.calcite.util.NlsString) LinkedList(java.util.LinkedList) RelDataTypeField(org.apache.calcite.rel.type.RelDataTypeField) RelNode(org.apache.calcite.rel.RelNode) RexSlot(org.apache.calcite.rex.RexSlot) RexInputRef(org.apache.calcite.rex.RexInputRef) RexNode(org.apache.calcite.rex.RexNode) Pair(org.apache.calcite.util.Pair)

Aggregations

ArrayList (java.util.ArrayList)2 RexInputRef (org.apache.calcite.rex.RexInputRef)2 RexNode (org.apache.calcite.rex.RexNode)2 RexSlot (org.apache.calcite.rex.RexSlot)2 HashSet (java.util.HashSet)1 LinkedList (java.util.LinkedList)1 RelOptCluster (org.apache.calcite.plan.RelOptCluster)1 RelNode (org.apache.calcite.rel.RelNode)1 RelDataType (org.apache.calcite.rel.type.RelDataType)1 RelDataTypeField (org.apache.calcite.rel.type.RelDataTypeField)1 RexCall (org.apache.calcite.rex.RexCall)1 RexLiteral (org.apache.calcite.rex.RexLiteral)1 NlsString (org.apache.calcite.util.NlsString)1 Pair (org.apache.calcite.util.Pair)1 Pair (org.apache.druid.java.util.common.Pair)1 LookupDataSource (org.apache.druid.query.LookupDataSource)1 DruidQueryRel (org.apache.druid.sql.calcite.rel.DruidQueryRel)1