private Optional < PlanNode > coalesceWithNullAggregation ( AggregationNode aggregationNode , PlanNode outerJoin , SymbolAllocator symbolAllocator , PlanNodeIdAllocator idAllocator , Lookup lookup ) { // Create an aggregation node over a row of nulls. Optional < MappedAggregationInfo > aggregationOverNullInfoResultNode = createAggregationOverNull ( aggregationNode , symbolAllocator , idAllocator , lookup ) ; if ( ! aggregationOverNullInfoResultNode . isPresent ( ) ) { return Optional . empty ( ) ; } MappedAggregationInfo aggregationOverNullInfo = aggregationOverNullInfoResultNode . get ( ) ; AggregationNode aggregationOverNull = aggregationOverNullInfo . getAggregation ( ) ; Map < Symbol , Symbol > sourceAggregationToOverNullMapping = aggregationOverNullInfo . getSymbolMapping ( ) ; // Do a cross join with the aggregation over null JoinNode crossJoin = new JoinNode ( idAllocator . getNextId ( ) , JoinNode . Type . INNER , outerJoin , aggregationOverNull , ImmutableList . of ( ) , ImmutableList . < Symbol > builder ( ) . addAll ( outerJoin . getOutputSymbols ( ) ) . addAll ( aggregationOverNull . getOutputSymbols ( ) ) . build ( ) , Optional . empty ( ) , Optional . empty ( ) , Optional . empty ( ) , Optional . empty ( ) ) ; // Add coalesce expressions for all aggregation functions Assignments . Builder assignmentsBuilder = Assignments . builder ( ) ; for ( Symbol symbol : outerJoin . getOutputSymbols ( ) ) { if ( aggregationNode . getAggregations ( ) . containsKey ( symbol ) ) { assignmentsBuilder . put ( symbol , new CoalesceExpression ( symbol . toSymbolReference ( ) , sourceAggregationToOverNullMapping . get ( symbol ) . toSymbolReference ( ) ) ) ; } else { assignmentsBuilder . put ( symbol , symbol . toSymbolReference ( ) ) ; } } return Optional . of ( new ProjectNode ( idAllocator . getNextId ( ) , crossJoin , assignmentsBuilder . build ( ) ) ) ; }
null row .