presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PlanNodeDecorrelator.java/DecorrelatingVisitor/visitTopN
class DecorrelatingVisitor:        @Override
        public Optional<DecorrelationResult> visitTopN(TopNNode node, Void context)
        {
            if (node.getCount() == 0) {
                return Optional.empty();
            }

            Optional<DecorrelationResult> childDecorrelationResultOptional = node.getSource().accept(this, null);
            if (!childDecorrelationResultOptional.isPresent()) {
                return Optional.empty();
            }

            DecorrelationResult childDecorrelationResult = childDecorrelationResultOptional.get();
            if (childDecorrelationResult.atMostSingleRow) {
                return childDecorrelationResultOptional;
            }

            PlanNode decorrelatedChildNode = childDecorrelationResult.node;
            Set<Symbol> constantSymbols = childDecorrelationResult.getConstantSymbols();
            Optional<OrderingScheme> decorrelatedOrderingScheme = decorrelateOrderingScheme(node.getOrderingScheme(), constantSymbols);

            // no partitioning needed (no symbols to partition by)
            if (childDecorrelationResult.symbolsToPropagate.isEmpty()) {
                return decorrelatedOrderingScheme
                        .map(orderingScheme -> Optional.of(new DecorrelationResult(
                                // ordering symbols are present - return decorrelated TopNNode
                                new TopNNode(node.getId(), decorrelatedChildNode, node.getCount(), orderingScheme, node.getStep()),
                                childDecorrelationResult.symbolsToPropagate,
                                childDecorrelationResult.correlatedPredicates,
                                childDecorrelationResult.correlatedSymbolsMapping,
                                node.getCount() == 1)))
                        .orElseGet(() -> Optional.of(new DecorrelationResult(
                                // no ordering symbols are left - convert to LimitNode
                                new LimitNode(node.getId(), decorrelatedChildNode, node.getCount(), false),
                                childDecorrelationResult.symbolsToPropagate,
                                childDecorrelationResult.correlatedPredicates,
                                childDecorrelationResult.correlatedSymbolsMapping,
                                node.getCount() == 1)));
            }

            if (!constantSymbols.containsAll(childDecorrelationResult.symbolsToPropagate)) {
                return Optional.empty();
            }

            return decorrelatedOrderingScheme
                    .map(orderingScheme -> {
                        // ordering symbols are present - rewrite TopN to TopNRowNumberNode partitioned by constant symbols
                        TopNRowNumberNode topNRowNumberNode = new TopNRowNumberNode(
                                node.getId(),
                                decorrelatedChildNode,
                                new Specification(
                                        ImmutableList.copyOf(childDecorrelationResult.symbolsToPropagate),
                                        Optional.of(orderingScheme)),
                                symbolAllocator.newSymbol("row_number", BIGINT),
                                toIntExact(node.getCount()),
                                false,
                                Optional.empty());

                        return Optional.of(new DecorrelationResult(
                                topNRowNumberNode,
                                childDecorrelationResult.symbolsToPropagate,
                                childDecorrelationResult.correlatedPredicates,
                                childDecorrelationResult.correlatedSymbolsMapping,
                                node.getCount() == 1));
                    })
                    .orElseGet(() -> {
                        // no ordering symbols are left - rewrite TopN to RowNumberNode partitioned by constant symbols
                        RowNumberNode rowNumberNode = new RowNumberNode(
                                node.getId(),
                                decorrelatedChildNode,
                                ImmutableList.copyOf(childDecorrelationResult.symbolsToPropagate),
                                symbolAllocator.newSymbol("row_number", BIGINT),
                                Optional.of(toIntExact(node.getCount())),
                                Optional.empty());

                        return Optional.of(new DecorrelationResult(
                                rowNumberNode,
                                childDecorrelationResult.symbolsToPropagate,
                                childDecorrelationResult.correlatedPredicates,
                                childDecorrelationResult.correlatedSymbolsMapping,
                                node.getCount() == 1));
                    });
        }