@Override public PlanNode visitSpatialJoin(SpatialJoinNode node, RewriteContext<Void> context) { PlanNode left = context.rewrite(node.getLeft()); PlanNode right = context.rewrite(node.getRight()); return new SpatialJoinNode(node.getId(), node.getType(), left, right, canonicalizeAndDistinct(node.getOutputSymbols()), canonicalize(node.getFilter()), canonicalize(node.getLeftPartitionSymbol()), canonicalize(node.getRightPartitionSymbol()), node.getKdbTree()); }
@Override public Void visitSpatialJoin(SpatialJoinNode node, Integer indent) { print(indent, "- %s[%s] => [%s]", node.getType().getJoinLabel(), node.getFilter(), formatOutputs(node.getOutputSymbols())); print(indent + 2, "Distribution: %s", node.getDistributionType()); printPlanNodesStatsAndCost(indent + 2, node); printStats(indent + 2, node.getId()); node.getLeft().accept(this, indent + 1); node.getRight().accept(this, indent + 1); return null; }
@Override public PlanNodeCostEstimate visitSpatialJoin(SpatialJoinNode node, Void context) { return calculateJoinCost( node, node.getLeft(), node.getRight(), node.getDistributionType() == SpatialJoinNode.DistributionType.REPLICATED); }
@Override public PlanNode replaceChildren(List<PlanNode> newChildren) { checkArgument(newChildren.size() == 2, "expected newChildren to contain 2 nodes"); return new SpatialJoinNode(getId(), type, newChildren.get(0), newChildren.get(1), outputSymbols, filter, leftPartitionSymbol, rightPartitionSymbol, kdbTree); } }
@Override protected Optional<PlanNodeStatsEstimate> doCalculate(SpatialJoinNode node, StatsProvider sourceStats, Lookup lookup, Session session, TypeProvider types) { PlanNodeStatsEstimate leftStats = sourceStats.getStats(node.getLeft()); PlanNodeStatsEstimate rightStats = sourceStats.getStats(node.getRight()); PlanNodeStatsEstimate crossJoinStats = crossJoinStats(node, leftStats, rightStats); switch (node.getType()) { case INNER: return Optional.of(statsCalculator.filterStats(crossJoinStats, node.getFilter(), session, types)); case LEFT: return Optional.of(PlanNodeStatsEstimate.unknown()); default: throw new IllegalArgumentException("Unknown spatial join type: " + node.getType()); } }
@Override public PlanWithProperties visitSpatialJoin(SpatialJoinNode node, PreferredProperties preferredProperties) SpatialJoinNode.DistributionType distributionType = node.getDistributionType(); PlanWithProperties left = node.getLeft().accept(this, PreferredProperties.any()); PlanWithProperties right = node.getRight().accept(this, PreferredProperties.any()); partitionedExchange(idAllocator.getNextId(), REMOTE, left.getNode(), ImmutableList.of(node.getLeftPartitionSymbol().get()), Optional.empty()), left.getProperties()); right = withDerivedProperties( partitionedExchange(idAllocator.getNextId(), REMOTE, right.getNode(), ImmutableList.of(node.getRightPartitionSymbol().get()), Optional.empty()), right.getProperties()); PlanNode newJoinNode = node.replaceChildren(ImmutableList.of(left.getNode(), right.getNode())); return new PlanWithProperties(newJoinNode, deriveProperties(newJoinNode, ImmutableList.of(left.getProperties(), right.getProperties())));
@Override public Void visitSpatialJoin(SpatialJoinNode node, Set<Symbol> boundSymbols) { node.getLeft().accept(this, boundSymbols); node.getRight().accept(this, boundSymbols); Set<Symbol> leftInputs = createInputs(node.getLeft(), boundSymbols); Set<Symbol> rightInputs = createInputs(node.getRight(), boundSymbols); Set<Symbol> allInputs = ImmutableSet.<Symbol>builder() .addAll(leftInputs) .addAll(rightInputs) .build(); Set<Symbol> predicateSymbols = SymbolsExtractor.extractUnique(node.getFilter()); checkArgument( allInputs.containsAll(predicateSymbols), "Symbol from filter (%s) not in sources (%s)", predicateSymbols, allInputs); checkLeftOutputSymbolsBeforeRight(node.getLeft().getOutputSymbols(), node.getOutputSymbols()); return null; }
@Override public Expression visitSpatialJoin(SpatialJoinNode node, Void context) { Expression leftPredicate = node.getLeft().accept(this, context); Expression rightPredicate = node.getRight().accept(this, context); switch (node.getType()) { case INNER: return combineConjuncts(ImmutableList.<Expression>builder() .add(pullExpressionThroughSymbols(leftPredicate, node.getOutputSymbols())) .add(pullExpressionThroughSymbols(rightPredicate, node.getOutputSymbols())) .build()); case LEFT: return combineConjuncts(ImmutableList.<Expression>builder() .add(pullExpressionThroughSymbols(leftPredicate, node.getOutputSymbols())) .addAll(pullNullableConjunctsThroughOuterJoin(extractConjuncts(rightPredicate), node.getOutputSymbols(), node.getRight().getOutputSymbols()::contains)) .build()); default: throw new IllegalArgumentException("Unsupported spatial join type: " + node.getType()); } }
private OperatorFactory createSpatialLookupJoin(SpatialJoinNode node, PlanNode probeNode, PhysicalOperation probeSource, Symbol probeSymbol, PagesSpatialIndexFactory pagesSpatialIndexFactory, LocalExecutionPlanContext context) { List<Type> probeTypes = probeSource.getTypes(); List<Symbol> probeOutputSymbols = node.getOutputSymbols().stream() .filter(symbol -> probeNode.getOutputSymbols().contains(symbol)) .collect(toImmutableList()); List<Integer> probeOutputChannels = ImmutableList.copyOf(getChannelsForSymbols(probeOutputSymbols, probeSource.getLayout())); Function<Symbol, Integer> probeChannelGetter = channelGetter(probeSource); int probeChannel = probeChannelGetter.apply(probeSymbol); Optional<Integer> partitionChannel = node.getLeftPartitionSymbol().map(probeChannelGetter::apply); return new SpatialJoinOperatorFactory( context.getNextOperatorId(), node.getId(), node.getType(), probeTypes, probeOutputChannels, probeChannel, partitionChannel, pagesSpatialIndexFactory); }
@Override public StreamProperties visitSpatialJoin(SpatialJoinNode node, List<StreamProperties> inputProperties) { StreamProperties leftProperties = inputProperties.get(0); switch (node.getType()) { case INNER: case LEFT: return leftProperties.translate(column -> PropertyDerivations.filterIfMissing(node.getOutputSymbols(), column)); default: throw new IllegalArgumentException("Unsupported spatial join type: " + node.getType()); } }
@Override public MatchResult detailMatches(PlanNode node, StatsProvider stats, Session session, Metadata metadata, SymbolAliases symbolAliases) { checkState(shapeMatches(node), "Plan testing framework error: shapeMatches returned false in detailMatches in %s", this.getClass().getName()); SpatialJoinNode joinNode = (SpatialJoinNode) node; if (!new ExpressionVerifier(symbolAliases).process(joinNode.getFilter(), filter)) { return NO_MATCH; } if (!joinNode.getKdbTree().equals(kdbTree)) { return NO_MATCH; } return match(); }
@Override public PhysicalOperation visitSpatialJoin(SpatialJoinNode node, LocalExecutionPlanContext context) { Expression filterExpression = node.getFilter(); List<FunctionCall> spatialFunctions = extractSupportedSpatialFunctions(filterExpression); for (FunctionCall spatialFunction : spatialFunctions) { Optional<PhysicalOperation> operation = tryCreateSpatialJoin(context, node, removeExpressionFromFilter(filterExpression, spatialFunction), spatialFunction, Optional.empty(), Optional.empty()); if (operation.isPresent()) { return operation.get(); } } List<ComparisonExpression> spatialComparisons = extractSupportedSpatialComparisons(filterExpression); for (ComparisonExpression spatialComparison : spatialComparisons) { if (spatialComparison.getOperator() == LESS_THAN || spatialComparison.getOperator() == LESS_THAN_OR_EQUAL) { // ST_Distance(a, b) <= r Expression radius = spatialComparison.getRight(); if (radius instanceof SymbolReference && getSymbolReferences(node.getRight().getOutputSymbols()).contains(radius)) { FunctionCall spatialFunction = (FunctionCall) spatialComparison.getLeft(); Optional<PhysicalOperation> operation = tryCreateSpatialJoin(context, node, removeExpressionFromFilter(filterExpression, spatialComparison), spatialFunction, Optional.of(radius), Optional.of(spatialComparison.getOperator())); if (operation.isPresent()) { return operation.get(); } } } } throw new VerifyException("No valid spatial relationship found for spatial join"); }
@Override public boolean shapeMatches(PlanNode node) { if (!(node instanceof SpatialJoinNode)) { return false; } SpatialJoinNode joinNode = (SpatialJoinNode) node; return joinNode.getType() == type; }
@VisibleForTesting public static final class ExtractSpatialLeftJoin implements Rule<JoinNode> { private static final Pattern<JoinNode> PATTERN = join().matching(node -> node.getCriteria().isEmpty() && node.getFilter().isPresent() && node.getType() == LEFT); private final Metadata metadata; private final SplitManager splitManager; private final PageSourceManager pageSourceManager; public ExtractSpatialLeftJoin(Metadata metadata, SplitManager splitManager, PageSourceManager pageSourceManager) { this.metadata = requireNonNull(metadata, "metadata is null"); this.splitManager = requireNonNull(splitManager, "splitManager is null"); this.pageSourceManager = requireNonNull(pageSourceManager, "pageSourceManager is null"); } @Override public boolean isEnabled(Session session) { return isSpatialJoinEnabled(session); } @Override public Pattern<JoinNode> getPattern() { return PATTERN; } @Override
List<Symbol> outputSymbols = node.getOutputSymbols(); for (int i = 0; i < outputSymbols.size(); i++) { Symbol symbol = outputSymbols.get(i);
@Override public PlanWithProperties visitSpatialJoin(SpatialJoinNode node, PreferredProperties preferredProperties) SpatialJoinNode.DistributionType distributionType = node.getDistributionType(); PlanWithProperties left = node.getLeft().accept(this, PreferredProperties.any()); PlanWithProperties right = node.getRight().accept(this, PreferredProperties.any()); partitionedExchange(idAllocator.getNextId(), REMOTE, left.getNode(), ImmutableList.of(node.getLeftPartitionSymbol().get()), Optional.empty()), left.getProperties()); right = withDerivedProperties( partitionedExchange(idAllocator.getNextId(), REMOTE, right.getNode(), ImmutableList.of(node.getRightPartitionSymbol().get()), Optional.empty()), right.getProperties()); PlanNode newJoinNode = node.replaceChildren(ImmutableList.of(left.getNode(), right.getNode())); return new PlanWithProperties(newJoinNode, deriveProperties(newJoinNode, ImmutableList.of(left.getProperties(), right.getProperties())));
@Override public Void visitSpatialJoin(SpatialJoinNode node, Set<Symbol> boundSymbols) { node.getLeft().accept(this, boundSymbols); node.getRight().accept(this, boundSymbols); Set<Symbol> leftInputs = createInputs(node.getLeft(), boundSymbols); Set<Symbol> rightInputs = createInputs(node.getRight(), boundSymbols); Set<Symbol> allInputs = ImmutableSet.<Symbol>builder() .addAll(leftInputs) .addAll(rightInputs) .build(); Set<Symbol> predicateSymbols = SymbolsExtractor.extractUnique(node.getFilter()); checkArgument( allInputs.containsAll(predicateSymbols), "Symbol from filter (%s) not in sources (%s)", predicateSymbols, allInputs); checkLeftOutputSymbolsBeforeRight(node.getLeft().getOutputSymbols(), node.getOutputSymbols()); return null; }