private Function<Symbol, Optional<Symbol>> translateGroupIdSymbols(GroupIdNode node) { return symbol -> { if (node.getAggregationArguments().contains(symbol)) { return Optional.of(symbol); } if (node.getCommonGroupingColumns().contains(symbol)) { return Optional.of(node.getGroupingColumns().get(symbol)); } return Optional.empty(); }; }
@Override public Void visitGroupId(GroupIdNode node, Void context) { // grouping sets are easier to understand in terms of inputs List<String> inputGroupingSetSymbols = node.getGroupingSets().stream() .map(set -> "(" + Joiner.on(", ").join(set.stream() .map(symbol -> node.getGroupingColumns().get(symbol)) .collect(Collectors.toList())) + ")") .collect(Collectors.toList()); printNode(node, "GroupId", Joiner.on(", ").join(inputGroupingSetSymbols), NODE_COLORS.get(NodeType.AGGREGATE)); return node.getSource().accept(this, context); }
@Override public ActualProperties visitGroupId(GroupIdNode node, List<ActualProperties> inputProperties) { Map<Symbol, Symbol> inputToOutputMappings = new HashMap<>(); for (Map.Entry<Symbol, Symbol> setMapping : node.getGroupingColumns().entrySet()) { if (node.getCommonGroupingColumns().contains(setMapping.getKey())) { // TODO: Add support for translating a property on a single column to multiple columns // when GroupIdNode is copying a single input grouping column into multiple output grouping columns (i.e. aliases), this is basically picking one arbitrarily inputToOutputMappings.putIfAbsent(setMapping.getValue(), setMapping.getKey()); } } // TODO: Add support for translating a property on a single column to multiple columns // this is deliberately placed after the grouping columns, because preserving properties has a bigger perf impact for (Symbol argument : node.getAggregationArguments()) { inputToOutputMappings.putIfAbsent(argument, argument); } return Iterables.getOnlyElement(inputProperties).translate(column -> Optional.ofNullable(inputToOutputMappings.get(column))); }
@Override public StreamProperties visitGroupId(GroupIdNode node, List<StreamProperties> inputProperties) { Map<Symbol, Symbol> inputToOutputMappings = new HashMap<>(); for (Map.Entry<Symbol, Symbol> setMapping : node.getGroupingColumns().entrySet()) { if (node.getCommonGroupingColumns().contains(setMapping.getKey())) { // TODO: Add support for translating a property on a single column to multiple columns // when GroupIdNode is copying a single input grouping column into multiple output grouping columns (i.e. aliases), this is basically picking one arbitrarily inputToOutputMappings.putIfAbsent(setMapping.getValue(), setMapping.getKey()); } } // TODO: Add support for translating a property on a single column to multiple columns // this is deliberately placed after the grouping columns, because preserving properties has a bigger perf impact for (Symbol argument : node.getAggregationArguments()) { inputToOutputMappings.putIfAbsent(argument, argument); } return Iterables.getOnlyElement(inputProperties).translate(column -> Optional.ofNullable(inputToOutputMappings.get(column))); }
@Override public Void visitGroupId(GroupIdNode node, Integer indent) { // grouping sets are easier to understand in terms of inputs List<List<Symbol>> inputGroupingSetSymbols = node.getGroupingSets().stream() .map(set -> set.stream() .map(symbol -> node.getGroupingColumns().get(symbol)) .collect(Collectors.toList())) .collect(Collectors.toList()); print(indent, "- GroupId%s => [%s]", inputGroupingSetSymbols, formatOutputs(node.getOutputSymbols())); printPlanNodesStatsAndCost(indent + 2, node); printStats(indent + 2, node.getId()); for (Map.Entry<Symbol, Symbol> mapping : node.getGroupingColumns().entrySet()) { print(indent + 2, "%s := %s", mapping.getKey(), mapping.getValue()); } return processChildren(node, indent + 1); }
@Override public PlanNode visitGroupId(GroupIdNode node, RewriteContext<Set<Symbol>> context) { ImmutableSet.Builder<Symbol> expectedInputs = ImmutableSet.builder(); List<Symbol> newAggregationArguments = node.getAggregationArguments().stream() .filter(context.get()::contains) .collect(Collectors.toList()); expectedInputs.addAll(newAggregationArguments); ImmutableList.Builder<List<Symbol>> newGroupingSets = ImmutableList.builder(); Map<Symbol, Symbol> newGroupingMapping = new HashMap<>(); for (List<Symbol> groupingSet : node.getGroupingSets()) { ImmutableList.Builder<Symbol> newGroupingSet = ImmutableList.builder(); for (Symbol output : groupingSet) { if (context.get().contains(output)) { newGroupingSet.add(output); newGroupingMapping.putIfAbsent(output, node.getGroupingColumns().get(output)); expectedInputs.add(node.getGroupingColumns().get(output)); } } newGroupingSets.add(newGroupingSet.build()); } PlanNode source = context.rewrite(node.getSource(), expectedInputs.build()); return new GroupIdNode(node.getId(), source, newGroupingSets.build(), newGroupingMapping, newAggregationArguments, node.getGroupIdSymbol()); }
@Override public PlanNode visitGroupId(GroupIdNode node, RewriteContext<Expression> context) { Map<Symbol, SymbolReference> commonGroupingSymbolMapping = node.getGroupingColumns().entrySet().stream() .filter(entry -> node.getCommonGroupingColumns().contains(entry.getKey())) .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().toSymbolReference())); Predicate<Expression> pushdownEligiblePredicate = conjunct -> SymbolsExtractor.extractUnique(conjunct).stream() .allMatch(commonGroupingSymbolMapping.keySet()::contains); Map<Boolean, List<Expression>> conjuncts = extractConjuncts(context.get()).stream().collect(Collectors.partitioningBy(pushdownEligiblePredicate)); // Push down conjuncts from the inherited predicate that apply to common grouping symbols PlanNode rewrittenNode = context.defaultRewrite(node, inlineSymbols(commonGroupingSymbolMapping, combineConjuncts(conjuncts.get(true)))); // All other conjuncts, if any, will be in the filter node. if (!conjuncts.get(false).isEmpty()) { rewrittenNode = new FilterNode(idAllocator.getNextId(), rewrittenNode, combineConjuncts(conjuncts.get(false))); } return rewrittenNode; }
outputTypes.add(source.getTypes().get(source.getLayout().get(node.getGroupingColumns().get(output)))); setMapping.put(newLayout.get(output), source.getLayout().get(node.getGroupingColumns().get(output)));
@Override public PlanNode visitGroupId(GroupIdNode node, RewriteContext<Void> context) { PlanNode source = context.rewrite(node.getSource()); Map<Symbol, Symbol> newGroupingMappings = new HashMap<>(); ImmutableList.Builder<List<Symbol>> newGroupingSets = ImmutableList.builder(); for (List<Symbol> groupingSet : node.getGroupingSets()) { ImmutableList.Builder<Symbol> newGroupingSet = ImmutableList.builder(); for (Symbol output : groupingSet) { newGroupingMappings.putIfAbsent(canonicalize(output), canonicalize(node.getGroupingColumns().get(output))); newGroupingSet.add(canonicalize(output)); } newGroupingSets.add(newGroupingSet.build()); } return new GroupIdNode(node.getId(), source, newGroupingSets.build(), newGroupingMappings, canonicalizeAndDistinct(node.getAggregationArguments()), canonicalize(node.getGroupIdSymbol())); }