private SubPlanBuilder addDistributedAggregation(
        SubPlanBuilder plan,
        Map<Symbol, FunctionCall> aggregations,
        Map<Symbol, Signature> functions,
        Map<Symbol, Symbol> masks,
        List<Symbol> groupBy,
        Optional<Symbol> sampleWeight,
        double confidence) {
      Map<Symbol, FunctionCall> finalCalls = new HashMap<>();
      Map<Symbol, FunctionCall> intermediateCalls = new HashMap<>();
      Map<Symbol, Signature> intermediateFunctions = new HashMap<>();
      Map<Symbol, Symbol> intermediateMask = new HashMap<>();
      for (Map.Entry<Symbol, FunctionCall> entry : aggregations.entrySet()) {
        Signature signature = functions.get(entry.getKey());
        FunctionInfo function = metadata.getFunction(signature);

        Symbol intermediateSymbol =
            allocator.newSymbol(function.getName().getSuffix(), function.getIntermediateType());
        intermediateCalls.put(intermediateSymbol, entry.getValue());
        intermediateFunctions.put(intermediateSymbol, signature);
        if (masks.containsKey(entry.getKey())) {
          intermediateMask.put(intermediateSymbol, masks.get(entry.getKey()));
        }

        // rewrite final aggregation in terms of intermediate function
        finalCalls.put(
            entry.getKey(),
            new FunctionCall(
                function.getName(),
                ImmutableList.<Expression>of(
                    new QualifiedNameReference(intermediateSymbol.toQualifiedName()))));
      }

      // create partial aggregation plan
      AggregationNode partialAggregation =
          new AggregationNode(
              idAllocator.getNextId(),
              plan.getRoot(),
              groupBy,
              intermediateCalls,
              intermediateFunctions,
              intermediateMask,
              PARTIAL,
              sampleWeight,
              confidence);
      plan.setRoot(
          new SinkNode(
              idAllocator.getNextId(), partialAggregation, partialAggregation.getOutputSymbols()));

      // create final aggregation plan
      ExchangeNode source =
          new ExchangeNode(
              idAllocator.getNextId(), plan.getId(), plan.getRoot().getOutputSymbols());
      AggregationNode finalAggregation =
          new AggregationNode(
              idAllocator.getNextId(),
              source,
              groupBy,
              finalCalls,
              functions,
              ImmutableMap.<Symbol, Symbol>of(),
              FINAL,
              Optional.<Symbol>absent(),
              confidence);

      if (groupBy.isEmpty()) {
        plan = createSingleNodePlan(finalAggregation).addChild(plan.build());
      } else {
        plan.setHashOutputPartitioning(groupBy);
        plan = createFixedDistributionPlan(finalAggregation).addChild(plan.build());
      }
      return plan;
    }
  private InternalTable buildPartitions(
      Session session, String catalogName, Map<String, SerializableNativeValue> filters) {
    QualifiedTableName tableName = extractQualifiedTableName(catalogName, filters);

    InternalTable.Builder table =
        InternalTable.builder(informationSchemaTableColumns(TABLE_INTERNAL_PARTITIONS));

    Optional<TableHandle> tableHandle = metadata.getTableHandle(session, tableName);
    checkArgument(tableHandle.isPresent(), "Table %s does not exist", tableName);
    Map<ColumnHandle, String> columnHandles =
        ImmutableBiMap.copyOf(metadata.getColumnHandles(session, tableHandle.get())).inverse();

    List<TableLayoutResult> layouts =
        metadata.getLayouts(
            session, tableHandle.get(), Constraint.<ColumnHandle>alwaysTrue(), Optional.empty());

    if (layouts.size() == 1) {
      TableLayout layout = Iterables.getOnlyElement(layouts).getLayout();

      layout
          .getDiscretePredicates()
          .ifPresent(
              domains -> {
                int partitionNumber = 1;
                for (TupleDomain<ColumnHandle> domain : domains) {
                  for (Entry<ColumnHandle, SerializableNativeValue> entry :
                      domain.extractNullableFixedValues().entrySet()) {
                    ColumnHandle columnHandle = entry.getKey();
                    String columnName = columnHandles.get(columnHandle);
                    String value = null;
                    if (entry.getValue().getValue() != null) {
                      ColumnMetadata columnMetadata =
                          metadata.getColumnMetadata(session, tableHandle.get(), columnHandle);
                      try {
                        FunctionInfo operator =
                            metadata
                                .getFunctionRegistry()
                                .getCoercion(columnMetadata.getType(), VARCHAR);
                        value =
                            ((Slice)
                                    operator
                                        .getMethodHandle()
                                        .invokeWithArguments(entry.getValue().getValue()))
                                .toStringUtf8();
                      } catch (OperatorNotFoundException e) {
                        value = "<UNREPRESENTABLE VALUE>";
                      } catch (Throwable throwable) {
                        throw Throwables.propagate(throwable);
                      }
                    }
                    table.add(
                        catalogName,
                        tableName.getSchemaName(),
                        tableName.getTableName(),
                        partitionNumber,
                        columnName,
                        value);
                  }
                  partitionNumber++;
                }
              });
    }
    return table.build();
  }
 /** Generates a function call with null handling, automatic binding of session parameter, etc. */
 public ByteCodeNode generateCall(FunctionInfo function, List<ByteCodeNode> arguments) {
   Binding binding = callSiteBinder.bind(function.getMethodHandle());
   return generateInvocation(context, function, arguments, binding);
 }