Search in sources :

Example 1 with JoinUsing

use of com.facebook.presto.sql.tree.JoinUsing in project presto by prestodb.

the class AstBuilder method visitJoinRelation.

// *************** from clause *****************
@Override
public Node visitJoinRelation(SqlBaseParser.JoinRelationContext context) {
    Relation left = (Relation) visit(context.left);
    Relation right;
    if (context.CROSS() != null) {
        right = (Relation) visit(context.right);
        return new Join(getLocation(context), Join.Type.CROSS, left, right, Optional.empty());
    }
    JoinCriteria criteria;
    if (context.NATURAL() != null) {
        right = (Relation) visit(context.right);
        criteria = new NaturalJoin();
    } else {
        right = (Relation) visit(context.rightRelation);
        if (context.joinCriteria().ON() != null) {
            criteria = new JoinOn((Expression) visit(context.joinCriteria().booleanExpression()));
        } else if (context.joinCriteria().USING() != null) {
            criteria = new JoinUsing(visit(context.joinCriteria().identifier(), Identifier.class));
        } else {
            throw new IllegalArgumentException("Unsupported join criteria");
        }
    }
    Join.Type joinType;
    if (context.joinType().LEFT() != null) {
        joinType = Join.Type.LEFT;
    } else if (context.joinType().RIGHT() != null) {
        joinType = Join.Type.RIGHT;
    } else if (context.joinType().FULL() != null) {
        joinType = Join.Type.FULL;
    } else {
        joinType = Join.Type.INNER;
    }
    return new Join(getLocation(context), joinType, left, right, Optional.of(criteria));
}
Also used : AliasedRelation(com.facebook.presto.sql.tree.AliasedRelation) SampledRelation(com.facebook.presto.sql.tree.SampledRelation) Relation(com.facebook.presto.sql.tree.Relation) Identifier(com.facebook.presto.sql.tree.Identifier) SubqueryExpression(com.facebook.presto.sql.tree.SubqueryExpression) SubscriptExpression(com.facebook.presto.sql.tree.SubscriptExpression) LogicalBinaryExpression(com.facebook.presto.sql.tree.LogicalBinaryExpression) SearchedCaseExpression(com.facebook.presto.sql.tree.SearchedCaseExpression) CoalesceExpression(com.facebook.presto.sql.tree.CoalesceExpression) SimpleCaseExpression(com.facebook.presto.sql.tree.SimpleCaseExpression) NotExpression(com.facebook.presto.sql.tree.NotExpression) LambdaExpression(com.facebook.presto.sql.tree.LambdaExpression) IfExpression(com.facebook.presto.sql.tree.IfExpression) BindExpression(com.facebook.presto.sql.tree.BindExpression) QuantifiedComparisonExpression(com.facebook.presto.sql.tree.QuantifiedComparisonExpression) InListExpression(com.facebook.presto.sql.tree.InListExpression) TryExpression(com.facebook.presto.sql.tree.TryExpression) ArithmeticUnaryExpression(com.facebook.presto.sql.tree.ArithmeticUnaryExpression) DereferenceExpression(com.facebook.presto.sql.tree.DereferenceExpression) NullIfExpression(com.facebook.presto.sql.tree.NullIfExpression) ComparisonExpression(com.facebook.presto.sql.tree.ComparisonExpression) Expression(com.facebook.presto.sql.tree.Expression) ArithmeticBinaryExpression(com.facebook.presto.sql.tree.ArithmeticBinaryExpression) JoinCriteria(com.facebook.presto.sql.tree.JoinCriteria) NaturalJoin(com.facebook.presto.sql.tree.NaturalJoin) Join(com.facebook.presto.sql.tree.Join) JoinUsing(com.facebook.presto.sql.tree.JoinUsing) NaturalJoin(com.facebook.presto.sql.tree.NaturalJoin) JoinOn(com.facebook.presto.sql.tree.JoinOn)

Example 2 with JoinUsing

use of com.facebook.presto.sql.tree.JoinUsing in project presto by prestodb.

the class RelationPlanner method visitJoin.

@Override
protected RelationPlan visitJoin(Join node, Void context) {
    // TODO: translate the RIGHT join into a mirrored LEFT join when we refactor (@martint)
    RelationPlan leftPlan = process(node.getLeft(), context);
    Optional<Unnest> unnest = getUnnest(node.getRight());
    if (unnest.isPresent()) {
        if (node.getType() != Join.Type.CROSS && node.getType() != Join.Type.IMPLICIT) {
            throw notSupportedException(unnest.get(), "UNNEST on other than the right side of CROSS JOIN");
        }
        return planCrossJoinUnnest(leftPlan, node, unnest.get());
    }
    Optional<Lateral> lateral = getLateral(node.getRight());
    if (lateral.isPresent()) {
        if (node.getType() != Join.Type.CROSS && node.getType() != Join.Type.IMPLICIT) {
            throw notSupportedException(lateral.get(), "LATERAL on other than the right side of CROSS JOIN");
        }
        return planLateralJoin(node, leftPlan, lateral.get());
    }
    RelationPlan rightPlan = process(node.getRight(), context);
    if (node.getCriteria().isPresent() && node.getCriteria().get() instanceof JoinUsing) {
        return planJoinUsing(node, leftPlan, rightPlan);
    }
    PlanBuilder leftPlanBuilder = initializePlanBuilder(leftPlan);
    PlanBuilder rightPlanBuilder = initializePlanBuilder(rightPlan);
    // NOTE: variables must be in the same order as the outputDescriptor
    List<VariableReferenceExpression> outputs = ImmutableList.<VariableReferenceExpression>builder().addAll(leftPlan.getFieldMappings()).addAll(rightPlan.getFieldMappings()).build();
    ImmutableList.Builder<JoinNode.EquiJoinClause> equiClauses = ImmutableList.builder();
    List<Expression> complexJoinExpressions = new ArrayList<>();
    List<Expression> postInnerJoinConditions = new ArrayList<>();
    if (node.getType() != Join.Type.CROSS && node.getType() != Join.Type.IMPLICIT) {
        Expression criteria = analysis.getJoinCriteria(node);
        RelationType left = analysis.getOutputDescriptor(node.getLeft());
        RelationType right = analysis.getOutputDescriptor(node.getRight());
        List<Expression> leftComparisonExpressions = new ArrayList<>();
        List<Expression> rightComparisonExpressions = new ArrayList<>();
        List<ComparisonExpression.Operator> joinConditionComparisonOperators = new ArrayList<>();
        for (Expression conjunct : ExpressionUtils.extractConjuncts(criteria)) {
            conjunct = ExpressionUtils.normalize(conjunct);
            if (!isEqualComparisonExpression(conjunct) && node.getType() != INNER) {
                complexJoinExpressions.add(conjunct);
                continue;
            }
            Set<QualifiedName> dependencies = VariablesExtractor.extractNames(conjunct, analysis.getColumnReferences());
            if (dependencies.stream().allMatch(left::canResolve) || dependencies.stream().allMatch(right::canResolve)) {
                // If the conjunct can be evaluated entirely with the inputs on either side of the join, add
                // it to the list complex expressions and let the optimizers figure out how to push it down later.
                complexJoinExpressions.add(conjunct);
            } else if (conjunct instanceof ComparisonExpression) {
                Expression firstExpression = ((ComparisonExpression) conjunct).getLeft();
                Expression secondExpression = ((ComparisonExpression) conjunct).getRight();
                ComparisonExpression.Operator comparisonOperator = ((ComparisonExpression) conjunct).getOperator();
                Set<QualifiedName> firstDependencies = VariablesExtractor.extractNames(firstExpression, analysis.getColumnReferences());
                Set<QualifiedName> secondDependencies = VariablesExtractor.extractNames(secondExpression, analysis.getColumnReferences());
                if (firstDependencies.stream().allMatch(left::canResolve) && secondDependencies.stream().allMatch(right::canResolve)) {
                    leftComparisonExpressions.add(firstExpression);
                    rightComparisonExpressions.add(secondExpression);
                    addNullFilters(complexJoinExpressions, node.getType(), firstExpression, secondExpression);
                    joinConditionComparisonOperators.add(comparisonOperator);
                } else if (firstDependencies.stream().allMatch(right::canResolve) && secondDependencies.stream().allMatch(left::canResolve)) {
                    leftComparisonExpressions.add(secondExpression);
                    rightComparisonExpressions.add(firstExpression);
                    addNullFilters(complexJoinExpressions, node.getType(), secondExpression, firstExpression);
                    joinConditionComparisonOperators.add(comparisonOperator.flip());
                } else {
                    // the case when we mix variables from both left and right join side on either side of condition.
                    complexJoinExpressions.add(conjunct);
                }
            } else {
                complexJoinExpressions.add(conjunct);
            }
        }
        leftPlanBuilder = subqueryPlanner.handleSubqueries(leftPlanBuilder, leftComparisonExpressions, node);
        rightPlanBuilder = subqueryPlanner.handleSubqueries(rightPlanBuilder, rightComparisonExpressions, node);
        // Add projections for join criteria
        leftPlanBuilder = leftPlanBuilder.appendProjections(leftComparisonExpressions, variableAllocator, idAllocator);
        rightPlanBuilder = rightPlanBuilder.appendProjections(rightComparisonExpressions, variableAllocator, idAllocator);
        for (int i = 0; i < leftComparisonExpressions.size(); i++) {
            if (joinConditionComparisonOperators.get(i) == ComparisonExpression.Operator.EQUAL) {
                VariableReferenceExpression leftVariable = leftPlanBuilder.translateToVariable(leftComparisonExpressions.get(i));
                VariableReferenceExpression righVariable = rightPlanBuilder.translateToVariable(rightComparisonExpressions.get(i));
                equiClauses.add(new JoinNode.EquiJoinClause(leftVariable, righVariable));
            } else {
                Expression leftExpression = leftPlanBuilder.rewrite(leftComparisonExpressions.get(i));
                Expression rightExpression = rightPlanBuilder.rewrite(rightComparisonExpressions.get(i));
                postInnerJoinConditions.add(new ComparisonExpression(joinConditionComparisonOperators.get(i), leftExpression, rightExpression));
            }
        }
    }
    PlanNode root = new JoinNode(getSourceLocation(node), idAllocator.getNextId(), JoinNodeUtils.typeConvert(node.getType()), leftPlanBuilder.getRoot(), rightPlanBuilder.getRoot(), equiClauses.build(), ImmutableList.<VariableReferenceExpression>builder().addAll(leftPlanBuilder.getRoot().getOutputVariables()).addAll(rightPlanBuilder.getRoot().getOutputVariables()).build(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), ImmutableMap.of());
    if (node.getType() != INNER) {
        for (Expression complexExpression : complexJoinExpressions) {
            Set<InPredicate> inPredicates = subqueryPlanner.collectInPredicateSubqueries(complexExpression, node);
            if (!inPredicates.isEmpty()) {
                InPredicate inPredicate = Iterables.getLast(inPredicates);
                throw notSupportedException(inPredicate, "IN with subquery predicate in join condition");
            }
        }
        // subqueries can be applied only to one side of join - left side is selected in arbitrary way
        leftPlanBuilder = subqueryPlanner.handleUncorrelatedSubqueries(leftPlanBuilder, complexJoinExpressions, node);
    }
    RelationPlan intermediateRootRelationPlan = new RelationPlan(root, analysis.getScope(node), outputs);
    TranslationMap translationMap = new TranslationMap(intermediateRootRelationPlan, analysis, lambdaDeclarationToVariableMap);
    translationMap.setFieldMappings(outputs);
    translationMap.putExpressionMappingsFrom(leftPlanBuilder.getTranslations());
    translationMap.putExpressionMappingsFrom(rightPlanBuilder.getTranslations());
    if (node.getType() != INNER && !complexJoinExpressions.isEmpty()) {
        Expression joinedFilterCondition = ExpressionUtils.and(complexJoinExpressions);
        Expression rewrittenFilterCondition = translationMap.rewrite(joinedFilterCondition);
        root = new JoinNode(getSourceLocation(node), idAllocator.getNextId(), JoinNodeUtils.typeConvert(node.getType()), leftPlanBuilder.getRoot(), rightPlanBuilder.getRoot(), equiClauses.build(), ImmutableList.<VariableReferenceExpression>builder().addAll(leftPlanBuilder.getRoot().getOutputVariables()).addAll(rightPlanBuilder.getRoot().getOutputVariables()).build(), Optional.of(castToRowExpression(rewrittenFilterCondition)), Optional.empty(), Optional.empty(), Optional.empty(), ImmutableMap.of());
    }
    if (node.getType() == INNER) {
        // rewrite all the other conditions using output variables from left + right plan node.
        PlanBuilder rootPlanBuilder = new PlanBuilder(translationMap, root);
        rootPlanBuilder = subqueryPlanner.handleSubqueries(rootPlanBuilder, complexJoinExpressions, node);
        for (Expression expression : complexJoinExpressions) {
            postInnerJoinConditions.add(rootPlanBuilder.rewrite(expression));
        }
        root = rootPlanBuilder.getRoot();
        Expression postInnerJoinCriteria;
        if (!postInnerJoinConditions.isEmpty()) {
            postInnerJoinCriteria = ExpressionUtils.and(postInnerJoinConditions);
            root = new FilterNode(getSourceLocation(postInnerJoinCriteria), idAllocator.getNextId(), root, castToRowExpression(postInnerJoinCriteria));
        }
    }
    return new RelationPlan(root, analysis.getScope(node), outputs);
}
Also used : AggregationNode.singleGroupingSet(com.facebook.presto.spi.plan.AggregationNode.singleGroupingSet) Set(java.util.Set) ImmutableList.toImmutableList(com.google.common.collect.ImmutableList.toImmutableList) ImmutableList(com.google.common.collect.ImmutableList) FilterNode(com.facebook.presto.spi.plan.FilterNode) ArrayList(java.util.ArrayList) PlanNode(com.facebook.presto.spi.plan.PlanNode) RelationType(com.facebook.presto.sql.analyzer.RelationType) Unnest(com.facebook.presto.sql.tree.Unnest) Lateral(com.facebook.presto.sql.tree.Lateral) LateralJoinNode(com.facebook.presto.sql.planner.plan.LateralJoinNode) JoinNode(com.facebook.presto.sql.planner.plan.JoinNode) QualifiedName(com.facebook.presto.sql.tree.QualifiedName) JoinUsing(com.facebook.presto.sql.tree.JoinUsing) InPredicate(com.facebook.presto.sql.tree.InPredicate) ComparisonExpression(com.facebook.presto.sql.tree.ComparisonExpression) ExpressionTreeUtils.isEqualComparisonExpression(com.facebook.presto.sql.analyzer.ExpressionTreeUtils.isEqualComparisonExpression) CoalesceExpression(com.facebook.presto.sql.tree.CoalesceExpression) OriginalExpressionUtils.castToRowExpression(com.facebook.presto.sql.relational.OriginalExpressionUtils.castToRowExpression) VariableReferenceExpression(com.facebook.presto.spi.relation.VariableReferenceExpression) RowExpression(com.facebook.presto.spi.relation.RowExpression) DereferenceExpression(com.facebook.presto.sql.tree.DereferenceExpression) ComparisonExpression(com.facebook.presto.sql.tree.ComparisonExpression) ExpressionTreeUtils.isEqualComparisonExpression(com.facebook.presto.sql.analyzer.ExpressionTreeUtils.isEqualComparisonExpression) Expression(com.facebook.presto.sql.tree.Expression) VariableReferenceExpression(com.facebook.presto.spi.relation.VariableReferenceExpression)

Example 3 with JoinUsing

use of com.facebook.presto.sql.tree.JoinUsing in project presto by prestodb.

the class RelationPlanner method planJoinUsing.

private RelationPlan planJoinUsing(Join node, RelationPlan left, RelationPlan right) {
    /* Given: l JOIN r USING (k1, ..., kn)

           produces:

            - project
                    coalesce(l.k1, r.k1)
                    ...,
                    coalesce(l.kn, r.kn)
                    l.v1,
                    ...,
                    l.vn,
                    r.v1,
                    ...,
                    r.vn
              - join (l.k1 = r.k1 and ... l.kn = r.kn)
                    - project
                        cast(l.k1 as commonType(l.k1, r.k1))
                        ...
                    - project
                        cast(rl.k1 as commonType(l.k1, r.k1))

            If casts are redundant (due to column type and common type being equal),
            they will be removed by optimization passes.
        */
    List<Identifier> joinColumns = ((JoinUsing) node.getCriteria().get()).getColumns();
    Analysis.JoinUsingAnalysis joinAnalysis = analysis.getJoinUsing(node);
    ImmutableList.Builder<JoinNode.EquiJoinClause> clauses = ImmutableList.builder();
    Map<Identifier, VariableReferenceExpression> leftJoinColumns = new HashMap<>();
    Map<Identifier, VariableReferenceExpression> rightJoinColumns = new HashMap<>();
    Assignments.Builder leftCoercions = Assignments.builder();
    Assignments.Builder rightCoercions = Assignments.builder();
    leftCoercions.putAll(identitiesAsSymbolReferences(left.getRoot().getOutputVariables()));
    rightCoercions.putAll(identitiesAsSymbolReferences(right.getRoot().getOutputVariables()));
    for (int i = 0; i < joinColumns.size(); i++) {
        Identifier identifier = joinColumns.get(i);
        Type type = analysis.getType(identifier);
        // compute the coercion for the field on the left to the common supertype of left & right
        VariableReferenceExpression leftOutput = variableAllocator.newVariable(identifier, type);
        int leftField = joinAnalysis.getLeftJoinFields().get(i);
        leftCoercions.put(leftOutput, castToRowExpression(new Cast(identifier.getLocation(), createSymbolReference(left.getVariable(leftField)), type.getTypeSignature().toString(), false, metadata.getFunctionAndTypeManager().isTypeOnlyCoercion(left.getDescriptor().getFieldByIndex(leftField).getType(), type))));
        leftJoinColumns.put(identifier, leftOutput);
        // compute the coercion for the field on the right to the common supertype of left & right
        VariableReferenceExpression rightOutput = variableAllocator.newVariable(identifier, type);
        int rightField = joinAnalysis.getRightJoinFields().get(i);
        rightCoercions.put(rightOutput, castToRowExpression(new Cast(identifier.getLocation(), createSymbolReference(right.getVariable(rightField)), type.getTypeSignature().toString(), false, metadata.getFunctionAndTypeManager().isTypeOnlyCoercion(right.getDescriptor().getFieldByIndex(rightField).getType(), type))));
        rightJoinColumns.put(identifier, rightOutput);
        clauses.add(new JoinNode.EquiJoinClause(leftOutput, rightOutput));
    }
    ProjectNode leftCoercion = new ProjectNode(idAllocator.getNextId(), left.getRoot(), leftCoercions.build());
    ProjectNode rightCoercion = new ProjectNode(idAllocator.getNextId(), right.getRoot(), rightCoercions.build());
    JoinNode join = new JoinNode(getSourceLocation(node), idAllocator.getNextId(), JoinNodeUtils.typeConvert(node.getType()), leftCoercion, rightCoercion, clauses.build(), ImmutableList.<VariableReferenceExpression>builder().addAll(leftCoercion.getOutputVariables()).addAll(rightCoercion.getOutputVariables()).build(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), ImmutableMap.of());
    // Add a projection to produce the outputs of the columns in the USING clause,
    // which are defined as coalesce(l.k, r.k)
    Assignments.Builder assignments = Assignments.builder();
    ImmutableList.Builder<VariableReferenceExpression> outputs = ImmutableList.builder();
    for (Identifier column : joinColumns) {
        VariableReferenceExpression output = variableAllocator.newVariable(column, analysis.getType(column));
        outputs.add(output);
        assignments.put(output, castToRowExpression(new CoalesceExpression(column.getLocation(), createSymbolReference(leftJoinColumns.get(column)), createSymbolReference(rightJoinColumns.get(column)))));
    }
    for (int field : joinAnalysis.getOtherLeftFields()) {
        VariableReferenceExpression variable = left.getFieldMappings().get(field);
        outputs.add(variable);
        assignments.put(variable, castToRowExpression(createSymbolReference(variable)));
    }
    for (int field : joinAnalysis.getOtherRightFields()) {
        VariableReferenceExpression variable = right.getFieldMappings().get(field);
        outputs.add(variable);
        assignments.put(variable, castToRowExpression(createSymbolReference(variable)));
    }
    return new RelationPlan(new ProjectNode(idAllocator.getNextId(), join, assignments.build()), analysis.getScope(node), outputs.build());
}
Also used : Cast(com.facebook.presto.sql.tree.Cast) LinkedHashMap(java.util.LinkedHashMap) HashMap(java.util.HashMap) ImmutableList.toImmutableList(com.google.common.collect.ImmutableList.toImmutableList) ImmutableList(com.google.common.collect.ImmutableList) LateralJoinNode(com.facebook.presto.sql.planner.plan.LateralJoinNode) JoinNode(com.facebook.presto.sql.planner.plan.JoinNode) Assignments(com.facebook.presto.spi.plan.Assignments) JoinUsing(com.facebook.presto.sql.tree.JoinUsing) TypeUtils.isEnumType(com.facebook.presto.common.type.TypeUtils.isEnumType) ArrayType(com.facebook.presto.common.type.ArrayType) RowType(com.facebook.presto.common.type.RowType) MapType(com.facebook.presto.common.type.MapType) Type(com.facebook.presto.common.type.Type) RelationType(com.facebook.presto.sql.analyzer.RelationType) Identifier(com.facebook.presto.sql.tree.Identifier) Analysis(com.facebook.presto.sql.analyzer.Analysis) VariableReferenceExpression(com.facebook.presto.spi.relation.VariableReferenceExpression) ProjectNode(com.facebook.presto.spi.plan.ProjectNode) CoalesceExpression(com.facebook.presto.sql.tree.CoalesceExpression)

Aggregations

CoalesceExpression (com.facebook.presto.sql.tree.CoalesceExpression)3 JoinUsing (com.facebook.presto.sql.tree.JoinUsing)3 VariableReferenceExpression (com.facebook.presto.spi.relation.VariableReferenceExpression)2 RelationType (com.facebook.presto.sql.analyzer.RelationType)2 JoinNode (com.facebook.presto.sql.planner.plan.JoinNode)2 LateralJoinNode (com.facebook.presto.sql.planner.plan.LateralJoinNode)2 ComparisonExpression (com.facebook.presto.sql.tree.ComparisonExpression)2 DereferenceExpression (com.facebook.presto.sql.tree.DereferenceExpression)2 Expression (com.facebook.presto.sql.tree.Expression)2 Identifier (com.facebook.presto.sql.tree.Identifier)2 ImmutableList (com.google.common.collect.ImmutableList)2 ImmutableList.toImmutableList (com.google.common.collect.ImmutableList.toImmutableList)2 ArrayType (com.facebook.presto.common.type.ArrayType)1 MapType (com.facebook.presto.common.type.MapType)1 RowType (com.facebook.presto.common.type.RowType)1 Type (com.facebook.presto.common.type.Type)1 TypeUtils.isEnumType (com.facebook.presto.common.type.TypeUtils.isEnumType)1 AggregationNode.singleGroupingSet (com.facebook.presto.spi.plan.AggregationNode.singleGroupingSet)1 Assignments (com.facebook.presto.spi.plan.Assignments)1 FilterNode (com.facebook.presto.spi.plan.FilterNode)1