@Override public ActualProperties visitJoin(JoinNode node, List<ActualProperties> inputProperties) { // TODO: include all equivalent columns in partitioning properties ActualProperties probeProperties = inputProperties.get(0); ActualProperties buildProperties = inputProperties.get(1); switch (node.getType()) { case INNER: return ActualProperties.builderFrom(probeProperties) .constants( ImmutableMap.<Symbol, NullableValue>builder() .putAll(probeProperties.getConstants()) .putAll(buildProperties.getConstants()) .build()) .build(); case LEFT: return ActualProperties.builderFrom(probeProperties) .constants(probeProperties.getConstants()) .build(); case RIGHT: return ActualProperties.builderFrom(buildProperties) .constants(buildProperties.getConstants()) .local(ImmutableList.of()) .build(); case FULL: // We can't say anything about the partitioning scheme because any partition of // a hash-partitioned join can produce nulls in case of a lack of matches return ActualProperties.builder() .global( probeProperties.isSingleNode() ? singleStreamPartition() : arbitraryPartition()) .build(); default: throw new UnsupportedOperationException("Unsupported join type: " + node.getType()); } }
@Override public ActualProperties visitDistinctLimit( DistinctLimitNode node, List<ActualProperties> inputProperties) { ActualProperties properties = Iterables.getOnlyElement(inputProperties); return ActualProperties.builderFrom(properties) .local(LocalProperties.grouped(node.getDistinctSymbols())) .build(); }
@Override public ActualProperties visitSort(SortNode node, List<ActualProperties> inputProperties) { ActualProperties properties = Iterables.getOnlyElement(inputProperties); List<SortingProperty<Symbol>> localProperties = node.getOrderBy() .stream() .map(column -> new SortingProperty<>(column, node.getOrderings().get(column))) .collect(toImmutableList()); return ActualProperties.builderFrom(properties).local(localProperties).build(); }
@Override public ActualProperties visitFilter(FilterNode node, List<ActualProperties> inputProperties) { ActualProperties properties = Iterables.getOnlyElement(inputProperties); DomainTranslator.ExtractionResult decomposedPredicate = DomainTranslator.fromPredicate(metadata, session, node.getPredicate(), types); Map<Symbol, NullableValue> constants = new HashMap<>(properties.getConstants()); constants.putAll( extractFixedValues(decomposedPredicate.getTupleDomain()).orElse(ImmutableMap.of())); return ActualProperties.builderFrom(properties).constants(constants).build(); }
@Override public ActualProperties visitTopNRowNumber( TopNRowNumberNode node, List<ActualProperties> inputProperties) { ActualProperties properties = Iterables.getOnlyElement(inputProperties); ImmutableList.Builder<LocalProperty<Symbol>> localProperties = ImmutableList.builder(); localProperties.add(new GroupingProperty<>(node.getPartitionBy())); for (Symbol column : node.getOrderBy()) { localProperties.add(new SortingProperty<>(column, node.getOrderings().get(column))); } return ActualProperties.builderFrom(properties).local(localProperties.build()).build(); }
@Override public ActualProperties visitIndexJoin( IndexJoinNode node, List<ActualProperties> inputProperties) { // TODO: include all equivalent columns in partitioning properties ActualProperties probeProperties = inputProperties.get(0); ActualProperties indexProperties = inputProperties.get(1); switch (node.getType()) { case INNER: return ActualProperties.builderFrom(probeProperties) .constants( ImmutableMap.<Symbol, NullableValue>builder() .putAll(probeProperties.getConstants()) .putAll(indexProperties.getConstants()) .build()) .build(); case SOURCE_OUTER: return ActualProperties.builderFrom(probeProperties) .constants(probeProperties.getConstants()) .build(); default: throw new UnsupportedOperationException("Unsupported join type: " + node.getType()); } }
@Override public ActualProperties visitProject(ProjectNode node, List<ActualProperties> inputProperties) { ActualProperties properties = Iterables.getOnlyElement(inputProperties); Map<Symbol, Symbol> identities = computeIdentityTranslations(node.getAssignments()); ActualProperties translatedProperties = properties.translate(column -> Optional.ofNullable(identities.get(column))); // Extract additional constants Map<Symbol, NullableValue> constants = new HashMap<>(); for (Map.Entry<Symbol, Expression> assignment : node.getAssignments().entrySet()) { Expression expression = assignment.getValue(); IdentityHashMap<Expression, Type> expressionTypes = getExpressionTypes( session, metadata, parser, types, expression, emptyList() /* parameters already replaced */); Type type = requireNonNull(expressionTypes.get(expression)); ExpressionInterpreter optimizer = ExpressionInterpreter.expressionOptimizer( expression, metadata, session, expressionTypes); // TODO: // We want to use a symbol resolver that looks up in the constants from the input subplan // to take advantage of constant-folding for complex expressions // However, that currently causes errors when those expressions operate on arrays or row // types // ("ROW comparison not supported for fields with null elements", etc) Object value = optimizer.optimize(NoOpSymbolResolver.INSTANCE); if (value instanceof SymbolReference) { Symbol symbol = Symbol.from((SymbolReference) value); NullableValue existingConstantValue = constants.get(symbol); if (existingConstantValue != null) { constants.put(assignment.getKey(), new NullableValue(type, value)); } } else if (!(value instanceof Expression)) { constants.put(assignment.getKey(), new NullableValue(type, value)); } } constants.putAll(translatedProperties.getConstants()); return ActualProperties.builderFrom(translatedProperties).constants(constants).build(); }
@Override public ActualProperties visitAggregation( AggregationNode node, List<ActualProperties> inputProperties) { ActualProperties properties = Iterables.getOnlyElement(inputProperties); ActualProperties translated = properties.translate( symbol -> node.getGroupingKeys().contains(symbol) ? Optional.of(symbol) : Optional.<Symbol>empty()); return ActualProperties.builderFrom(translated) .local(LocalProperties.grouped(node.getGroupingKeys())) .build(); }
@Override public ActualProperties visitWindow(WindowNode node, List<ActualProperties> inputProperties) { ActualProperties properties = Iterables.getOnlyElement(inputProperties); // If the input is completely pre-partitioned and sorted, then the original input properties // will be respected if (ImmutableSet.copyOf(node.getPartitionBy()).equals(node.getPrePartitionedInputs()) && node.getPreSortedOrderPrefix() == node.getOrderBy().size()) { return properties; } ImmutableList.Builder<LocalProperty<Symbol>> localProperties = ImmutableList.builder(); // If the WindowNode has pre-partitioned inputs, then it will not change the order of those // inputs at output, // so we should just propagate those underlying local properties that guarantee the // pre-partitioning. // TODO: come up with a more general form of this operation for other streaming operators if (!node.getPrePartitionedInputs().isEmpty()) { GroupingProperty<Symbol> prePartitionedProperty = new GroupingProperty<>(node.getPrePartitionedInputs()); for (LocalProperty<Symbol> localProperty : properties.getLocalProperties()) { if (!prePartitionedProperty.isSimplifiedBy(localProperty)) { break; } localProperties.add(localProperty); } } if (!node.getPartitionBy().isEmpty()) { localProperties.add(new GroupingProperty<>(node.getPartitionBy())); } for (Symbol column : node.getOrderBy()) { localProperties.add(new SortingProperty<>(column, node.getOrderings().get(column))); } return ActualProperties.builderFrom(properties) .local(LocalProperties.normalizeAndPrune(localProperties.build())) .build(); }