private PlanBuilder appendSemiJoin(PlanBuilder subPlan, InPredicate inPredicate) { TranslationMap translations = new TranslationMap(subPlan.getRelationPlan(), analysis); translations.copyMappingsFrom(subPlan.getTranslations()); subPlan = appendProjections(subPlan, ImmutableList.of(inPredicate.getValue())); Symbol sourceJoinSymbol = subPlan.translate(inPredicate.getValue()); checkState(inPredicate.getValueList() instanceof SubqueryExpression); SubqueryExpression subqueryExpression = (SubqueryExpression) inPredicate.getValueList(); RelationPlanner relationPlanner = new RelationPlanner(analysis, symbolAllocator, idAllocator, metadata, session); RelationPlan valueListRelation = relationPlanner.process(subqueryExpression.getQuery(), null); Symbol filteringSourceJoinSymbol = Iterables.getOnlyElement(valueListRelation.getRoot().getOutputSymbols()); Symbol semiJoinOutputSymbol = symbolAllocator.newSymbol("semijoinresult", BOOLEAN); translations.put(inPredicate, semiJoinOutputSymbol); return new PlanBuilder( translations, new SemiJoinNode( idAllocator.getNextId(), subPlan.getRoot(), valueListRelation.getRoot(), sourceJoinSymbol, filteringSourceJoinSymbol, semiJoinOutputSymbol, Optional.empty(), Optional.empty()), subPlan.getSampleWeight()); }
private PlanBuilder appendProjections(PlanBuilder subPlan, Iterable<Expression> expressions) { TranslationMap translations = new TranslationMap(subPlan.getRelationPlan(), analysis); // Carry over the translations from the source because we are appending projections translations.copyMappingsFrom(subPlan.getTranslations()); ImmutableMap.Builder<Symbol, Expression> projections = ImmutableMap.builder(); // add an identity projection for underlying plan for (Symbol symbol : subPlan.getRoot().getOutputSymbols()) { Expression expression = new QualifiedNameReference(symbol.toQualifiedName()); projections.put(symbol, expression); } ImmutableMap.Builder<Symbol, Expression> newTranslations = ImmutableMap.builder(); for (Expression expression : expressions) { Symbol symbol = symbolAllocator.newSymbol(expression, analysis.getType(expression)); // TODO: CHECK IF THE REWRITE OF A SEMI JOINED EXPRESSION WILL WORK!!!!!!! projections.put(symbol, translations.rewrite(expression)); newTranslations.put(symbol, expression); } // Now append the new translations into the TranslationMap for (Map.Entry<Symbol, Expression> entry : newTranslations.build().entrySet()) { translations.put(entry.getValue(), entry.getKey()); } return new PlanBuilder( translations, new ProjectNode(idAllocator.getNextId(), subPlan.getRoot(), projections.build()), subPlan.getSampleWeight()); }
@Override protected RelationPlan visitQuerySpecification(QuerySpecification node, Void context) { PlanBuilder subPlan = new QueryPlanner(analysis, symbolAllocator, idAllocator, metadata, session) .process(node, null); ImmutableList.Builder<Symbol> outputSymbols = ImmutableList.builder(); for (FieldOrExpression fieldOrExpression : analysis.getOutputExpressions(node)) { outputSymbols.add(subPlan.translate(fieldOrExpression)); } return new RelationPlan( subPlan.getRoot(), analysis.getOutputDescriptor(node), outputSymbols.build(), subPlan.getSampleWeight()); }
@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); // Convert CROSS JOIN UNNEST to an UnnestNode if (node.getRight() instanceof Unnest || (node.getRight() instanceof AliasedRelation && ((AliasedRelation) node.getRight()).getRelation() instanceof Unnest)) { Unnest unnest; if (node.getRight() instanceof AliasedRelation) { unnest = (Unnest) ((AliasedRelation) node.getRight()).getRelation(); } else { unnest = (Unnest) node.getRight(); } if (node.getType() != Join.Type.CROSS && node.getType() != Join.Type.IMPLICIT) { throw new SemanticException( NOT_SUPPORTED, unnest, "UNNEST only supported on the right side of CROSS JOIN"); } return planCrossJoinUnnest(leftPlan, node, unnest); } RelationPlan rightPlan = process(node.getRight(), context); PlanBuilder leftPlanBuilder = initializePlanBuilder(leftPlan); PlanBuilder rightPlanBuilder = initializePlanBuilder(rightPlan); TupleDescriptor outputDescriptor = analysis.getOutputDescriptor(node); // NOTE: symbols must be in the same order as the outputDescriptor List<Symbol> outputSymbols = ImmutableList.<Symbol>builder() .addAll(leftPlan.getOutputSymbols()) .addAll(rightPlan.getOutputSymbols()) .build(); ImmutableList.Builder<JoinNode.EquiJoinClause> equiClauses = ImmutableList.builder(); Expression postInnerJoinCriteria = new BooleanLiteral("TRUE"); if (node.getType() != Join.Type.CROSS && node.getType() != Join.Type.IMPLICIT) { Expression criteria = analysis.getJoinCriteria(node); TupleDescriptor left = analysis.getOutputDescriptor(node.getLeft()); TupleDescriptor right = analysis.getOutputDescriptor(node.getRight()); List<Expression> leftExpressions = new ArrayList<>(); List<Expression> rightExpressions = new ArrayList<>(); List<ComparisonExpression.Type> comparisonTypes = new ArrayList<>(); for (Expression conjunct : ExpressionUtils.extractConjuncts(criteria)) { if (!(conjunct instanceof ComparisonExpression)) { throw new SemanticException( NOT_SUPPORTED, node, "Unsupported non-equi join form: %s", conjunct); } ComparisonExpression comparison = (ComparisonExpression) conjunct; ComparisonExpression.Type comparisonType = comparison.getType(); if (comparison.getType() != EQUAL && node.getType() != INNER) { throw new SemanticException( NOT_SUPPORTED, node, "Non-equi joins only supported for inner join: %s", conjunct); } Set<QualifiedName> firstDependencies = DependencyExtractor.extractNames(comparison.getLeft()); Set<QualifiedName> secondDependencies = DependencyExtractor.extractNames(comparison.getRight()); Expression leftExpression; Expression rightExpression; if (Iterables.all(firstDependencies, left.canResolvePredicate()) && Iterables.all(secondDependencies, right.canResolvePredicate())) { leftExpression = comparison.getLeft(); rightExpression = comparison.getRight(); } else if (Iterables.all(firstDependencies, right.canResolvePredicate()) && Iterables.all(secondDependencies, left.canResolvePredicate())) { leftExpression = comparison.getRight(); rightExpression = comparison.getLeft(); comparisonType = flipComparison(comparisonType); } else { // must have a complex expression that involves both tuples on one side of the comparison // expression (e.g., coalesce(left.x, right.x) = 1) throw new SemanticException( NOT_SUPPORTED, node, "Unsupported non-equi join form: %s", conjunct); } leftExpressions.add(leftExpression); rightExpressions.add(rightExpression); comparisonTypes.add(comparisonType); } Analysis.JoinInPredicates joinInPredicates = analysis.getJoinInPredicates(node); // Add semi joins if necessary if (joinInPredicates != null) { leftPlanBuilder = appendSemiJoins(leftPlanBuilder, joinInPredicates.getLeftInPredicates()); rightPlanBuilder = appendSemiJoins(rightPlanBuilder, joinInPredicates.getRightInPredicates()); } // Add projections for join criteria leftPlanBuilder = appendProjections(leftPlanBuilder, leftExpressions); rightPlanBuilder = appendProjections(rightPlanBuilder, rightExpressions); List<Expression> postInnerJoinComparisons = new ArrayList<>(); for (int i = 0; i < comparisonTypes.size(); i++) { Symbol leftSymbol = leftPlanBuilder.translate(leftExpressions.get(i)); Symbol rightSymbol = rightPlanBuilder.translate(rightExpressions.get(i)); equiClauses.add(new JoinNode.EquiJoinClause(leftSymbol, rightSymbol)); Expression leftExpression = leftPlanBuilder.rewrite(leftExpressions.get(i)); Expression rightExpression = rightPlanBuilder.rewrite(rightExpressions.get(i)); postInnerJoinComparisons.add( new ComparisonExpression(comparisonTypes.get(i), leftExpression, rightExpression)); } postInnerJoinCriteria = ExpressionUtils.and(postInnerJoinComparisons); } PlanNode root; if (node.getType() == INNER) { root = new JoinNode( idAllocator.getNextId(), JoinNode.Type.CROSS, leftPlanBuilder.getRoot(), rightPlanBuilder.getRoot(), ImmutableList.<JoinNode.EquiJoinClause>of(), Optional.empty(), Optional.empty()); root = new FilterNode(idAllocator.getNextId(), root, postInnerJoinCriteria); } else { root = new JoinNode( idAllocator.getNextId(), JoinNode.Type.typeConvert(node.getType()), leftPlanBuilder.getRoot(), rightPlanBuilder.getRoot(), equiClauses.build(), Optional.empty(), Optional.empty()); } Optional<Symbol> sampleWeight = Optional.empty(); if (leftPlanBuilder.getSampleWeight().isPresent() || rightPlanBuilder.getSampleWeight().isPresent()) { Expression expression = new ArithmeticBinaryExpression( ArithmeticBinaryExpression.Type.MULTIPLY, oneIfNull(leftPlanBuilder.getSampleWeight()), oneIfNull(rightPlanBuilder.getSampleWeight())); sampleWeight = Optional.of(symbolAllocator.newSymbol(expression, BIGINT)); ImmutableMap.Builder<Symbol, Expression> projections = ImmutableMap.builder(); projections.put(sampleWeight.get(), expression); for (Symbol symbol : root.getOutputSymbols()) { projections.put(symbol, new QualifiedNameReference(symbol.toQualifiedName())); } root = new ProjectNode(idAllocator.getNextId(), root, projections.build()); } return new RelationPlan(root, outputDescriptor, outputSymbols, sampleWeight); }