private static boolean canReplaceWithRowNumber(WindowNode node) { return canOptimizeWindowFunction(node) && !node.getOrderingScheme().isPresent(); }
public static boolean dependsOn(WindowNode parent, WindowNode child) { return parent.getPartitionBy().stream().anyMatch(child.getCreatedSymbols()::contains) || (parent.getOrderingScheme().isPresent() && parent.getOrderingScheme().get().getOrderBy().stream().anyMatch(child.getCreatedSymbols()::contains)) || parent.getWindowFunctions().values().stream() .map(WindowNode.Function::getFunctionCall) .map(SymbolsExtractor::extractUnique) .flatMap(Collection::stream) .anyMatch(child.getCreatedSymbols()::contains); } }
private static int compareOrderBy(WindowNode o1, WindowNode o2) if (!o1.getOrderingScheme().isPresent() && !o2.getOrderingScheme().isPresent()) { return 0; else if (o1.getOrderingScheme().isPresent() && !o2.getOrderingScheme().isPresent()) { return 1; else if (!o1.getOrderingScheme().isPresent() && o2.getOrderingScheme().isPresent()) { return -1; OrderingScheme o1OrderingScheme = o1.getOrderingScheme().get(); OrderingScheme o2OrderingScheme = o2.getOrderingScheme().get(); Iterator<Symbol> iterator1 = o1OrderingScheme.getOrderBy().iterator(); Iterator<Symbol> iterator2 = o2OrderingScheme.getOrderBy().iterator();
@Override public Void visitWindow(WindowNode node, Void context) { printNode(node, "Window", format("partition by = %s|order by = %s", Joiner.on(", ").join(node.getPartitionBy()), node.getOrderingScheme() .map(orderingScheme -> Joiner.on(", ").join(orderingScheme.getOrderBy())) .orElse("")), NODE_COLORS.get(NodeType.WINDOW)); return node.getSource().accept(this, context); }
if (node.getOrderingScheme().isPresent()) { OrderingScheme orderingScheme = node.getOrderingScheme().get(); args.add(format("order by (%s)", Stream.concat( orderingScheme.getOrderBy().stream()
@Override public PlanNode visitWindow(WindowNode node, RewriteContext<Context> context) { if (!node.getWindowFunctions().values().stream() .map(function -> function.getFunctionCall().getName()) .allMatch(metadata.getFunctionRegistry()::isAggregationFunction)) { return node; } // Don't need this restriction if we can prove that all order by symbols are deterministically produced if (node.getOrderingScheme().isPresent()) { return node; } // Only RANGE frame type currently supported for aggregation functions because it guarantees the // same value for each peer group. // ROWS frame type requires the ordering to be fully deterministic (e.g. deterministically sorted on all columns) if (node.getFrames().stream().map(WindowNode.Frame::getType).anyMatch(type -> type != WindowFrame.Type.RANGE)) { // TODO: extract frames of type RANGE and allow optimization on them return node; } // Lookup symbols can only be passed through if they are part of the partitioning Set<Symbol> partitionByLookupSymbols = context.get().getLookupSymbols().stream() .filter(node.getPartitionBy()::contains) .collect(toImmutableSet()); if (partitionByLookupSymbols.isEmpty()) { return node; } return context.defaultRewrite(node, new Context(partitionByLookupSymbols, context.get().getSuccess())); }
.addAll(node.getPartitionBy()); node.getOrderingScheme().ifPresent(orderingScheme -> orderingScheme.getOrderBy() .forEach(expectedInputs::add));
@Override public Void visitWindow(WindowNode node, Set<Symbol> boundSymbols) { PlanNode source = node.getSource(); source.accept(this, boundSymbols); // visit child Set<Symbol> inputs = createInputs(source, boundSymbols); checkDependencies(inputs, node.getPartitionBy(), "Invalid node. Partition by symbols (%s) not in source plan output (%s)", node.getPartitionBy(), node.getSource().getOutputSymbols()); if (node.getOrderingScheme().isPresent()) { checkDependencies( inputs, node.getOrderingScheme().get().getOrderBy(), "Invalid node. Order by symbols (%s) not in source plan output (%s)", node.getOrderingScheme().get().getOrderBy(), node.getSource().getOutputSymbols()); } ImmutableList.Builder<Symbol> bounds = ImmutableList.builder(); for (WindowNode.Frame frame : node.getFrames()) { if (frame.getStartValue().isPresent()) { bounds.add(frame.getStartValue().get()); } if (frame.getEndValue().isPresent()) { bounds.add(frame.getEndValue().get()); } } checkDependencies(inputs, bounds.build(), "Invalid node. Frame bounds (%s) not in source plan output (%s)", bounds.build(), node.getSource().getOutputSymbols()); for (WindowNode.Function function : node.getWindowFunctions().values()) { Set<Symbol> dependencies = SymbolsExtractor.extractUnique(function.getFunctionCall()); checkDependencies(inputs, dependencies, "Invalid node. Window function dependencies (%s) not in source plan output (%s)", dependencies, node.getSource().getOutputSymbols()); } return null; }
.addAll(windowNode.getPartitionBy()); windowNode.getOrderingScheme().ifPresent( orderingScheme -> orderingScheme .getOrderBy()
Optional<OrderingScheme> orderingScheme = node.getOrderingScheme(); if (ImmutableSet.copyOf(node.getPartitionBy()).equals(node.getPrePartitionedInputs()) && (!orderingScheme.isPresent() || node.getPreSortedOrderPrefix() == orderingScheme.get().getOrderBy().size())) {
desiredProperties.add(new GroupingProperty<>(node.getPartitionBy())); node.getOrderingScheme().ifPresent(orderingScheme -> orderingScheme.getOrderBy().stream() .map(symbol -> new SortingProperty<>(symbol, orderingScheme.getOrdering(symbol)))
@Override public PlanNode visitLimit(LimitNode node, RewriteContext<Void> context) { // Operators can handle MAX_VALUE rows per page, so do not optimize if count is greater than this value if (node.getCount() > Integer.MAX_VALUE) { return context.defaultRewrite(node); } PlanNode source = context.rewrite(node.getSource()); int limit = toIntExact(node.getCount()); if (source instanceof RowNumberNode) { RowNumberNode rowNumberNode = mergeLimit(((RowNumberNode) source), limit); if (rowNumberNode.getPartitionBy().isEmpty()) { return rowNumberNode; } source = rowNumberNode; } else if (source instanceof WindowNode && canOptimizeWindowFunction((WindowNode) source) && isOptimizeTopNRowNumber(session)) { WindowNode windowNode = (WindowNode) source; // verify that unordered row_number window functions are replaced by RowNumberNode verify(windowNode.getOrderingScheme().isPresent()); TopNRowNumberNode topNRowNumberNode = convertToTopNRowNumber(windowNode, limit); if (windowNode.getPartitionBy().isEmpty()) { return topNRowNumberNode; } source = topNRowNumberNode; } return replaceChildren(node, ImmutableList.of(source)); }
@Override public PlanWithProperties visitWindow(WindowNode node, PreferredProperties preferredProperties) { List<LocalProperty<Symbol>> desiredProperties = new ArrayList<>(); if (!node.getPartitionBy().isEmpty()) { desiredProperties.add(new GroupingProperty<>(node.getPartitionBy())); } node.getOrderingScheme().ifPresent(orderingScheme -> orderingScheme.getOrderBy().stream() .map(symbol -> new SortingProperty<>(symbol, orderingScheme.getOrdering(symbol))) .forEach(desiredProperties::add)); PlanWithProperties child = planChild( node, PreferredProperties.partitionedWithLocal(ImmutableSet.copyOf(node.getPartitionBy()), desiredProperties) .mergeWithParent(preferredProperties)); if (!child.getProperties().isStreamPartitionedOn(node.getPartitionBy()) && !child.getProperties().isNodePartitionedOn(node.getPartitionBy())) { if (node.getPartitionBy().isEmpty()) { child = withDerivedProperties( gatheringExchange(idAllocator.getNextId(), REMOTE, child.getNode()), child.getProperties()); } else { child = withDerivedProperties( partitionedExchange(idAllocator.getNextId(), REMOTE, child.getNode(), node.getPartitionBy(), node.getHashSymbol()), child.getProperties()); } } return rebaseAndDeriveProperties(node, child); }