Search in sources :

Example 1 with ProjectNode

use of com.facebook.presto.sql.planner.plan.ProjectNode in project presto by prestodb.

the class TestEffectivePredicateExtractor method testProject.

@Test
public void testProject() throws Exception {
    PlanNode node = new ProjectNode(newId(), filter(baseTableScan, and(equals(AE, BE), equals(BE, CE), lessThan(CE, bigintLiteral(10)))), Assignments.of(D, AE, E, CE));
    Expression effectivePredicate = EffectivePredicateExtractor.extract(node, TYPES);
    // Rewrite in terms of project output symbols
    assertEquals(normalizeConjuncts(effectivePredicate), normalizeConjuncts(lessThan(DE, bigintLiteral(10)), equals(DE, EE)));
}
Also used : PlanNode(com.facebook.presto.sql.planner.plan.PlanNode) ComparisonExpression(com.facebook.presto.sql.tree.ComparisonExpression) Expression(com.facebook.presto.sql.tree.Expression) ProjectNode(com.facebook.presto.sql.planner.plan.ProjectNode) Test(org.testng.annotations.Test)

Example 2 with ProjectNode

use of com.facebook.presto.sql.planner.plan.ProjectNode in project presto by prestodb.

the class TestCountConstantOptimizer method testCountConstantOptimizer.

@Test
public void testCountConstantOptimizer() throws Exception {
    CountConstantOptimizer optimizer = new CountConstantOptimizer();
    PlanNodeIdAllocator planNodeIdAllocator = new PlanNodeIdAllocator();
    Symbol countAggregationSymbol = new Symbol("count");
    Signature countAggregationSignature = new Signature("count", FunctionKind.AGGREGATE, parseTypeSignature(StandardTypes.BIGINT), parseTypeSignature(StandardTypes.BIGINT));
    ImmutableMap<Symbol, FunctionCall> aggregations = ImmutableMap.of(countAggregationSymbol, new FunctionCall(QualifiedName.of("count"), ImmutableList.of(new SymbolReference("expr"))));
    ImmutableMap<Symbol, Signature> functions = ImmutableMap.of(countAggregationSymbol, countAggregationSignature);
    ValuesNode valuesNode = new ValuesNode(planNodeIdAllocator.getNextId(), ImmutableList.of(new Symbol("col")), ImmutableList.of(ImmutableList.of()));
    AggregationNode eligiblePlan = new AggregationNode(planNodeIdAllocator.getNextId(), new ProjectNode(planNodeIdAllocator.getNextId(), valuesNode, Assignments.of(new Symbol("expr"), new LongLiteral("42"))), aggregations, functions, ImmutableMap.of(), ImmutableList.of(ImmutableList.of()), AggregationNode.Step.INTERMEDIATE, Optional.empty(), Optional.empty());
    assertTrue(((AggregationNode) optimizer.optimize(eligiblePlan, TEST_SESSION, ImmutableMap.of(), new SymbolAllocator(), new PlanNodeIdAllocator())).getAggregations().get(countAggregationSymbol).getArguments().isEmpty());
    AggregationNode ineligiblePlan = new AggregationNode(planNodeIdAllocator.getNextId(), new ProjectNode(planNodeIdAllocator.getNextId(), valuesNode, Assignments.of(new Symbol("expr"), new FunctionCall(QualifiedName.of("function"), ImmutableList.of(new Identifier("x"))))), aggregations, functions, ImmutableMap.of(), ImmutableList.of(ImmutableList.of()), AggregationNode.Step.INTERMEDIATE, Optional.empty(), Optional.empty());
    assertFalse(((AggregationNode) optimizer.optimize(ineligiblePlan, TEST_SESSION, ImmutableMap.of(), new SymbolAllocator(), new PlanNodeIdAllocator())).getAggregations().get(countAggregationSymbol).getArguments().isEmpty());
}
Also used : SymbolAllocator(com.facebook.presto.sql.planner.SymbolAllocator) ValuesNode(com.facebook.presto.sql.planner.plan.ValuesNode) LongLiteral(com.facebook.presto.sql.tree.LongLiteral) Symbol(com.facebook.presto.sql.planner.Symbol) SymbolReference(com.facebook.presto.sql.tree.SymbolReference) AggregationNode(com.facebook.presto.sql.planner.plan.AggregationNode) Identifier(com.facebook.presto.sql.tree.Identifier) PlanNodeIdAllocator(com.facebook.presto.sql.planner.PlanNodeIdAllocator) Signature(com.facebook.presto.metadata.Signature) TypeSignature.parseTypeSignature(com.facebook.presto.spi.type.TypeSignature.parseTypeSignature) ProjectNode(com.facebook.presto.sql.planner.plan.ProjectNode) FunctionCall(com.facebook.presto.sql.tree.FunctionCall) Test(org.testng.annotations.Test)

Example 3 with ProjectNode

use of com.facebook.presto.sql.planner.plan.ProjectNode in project presto by prestodb.

the class TestVerifyOnlyOneOutputNode method testValidateFailed.

@Test(expectedExceptions = IllegalStateException.class)
public void testValidateFailed() throws Exception {
    // random plan with 2 output nodes
    PlanNode root = new OutputNode(idAllocator.getNextId(), new ExplainAnalyzeNode(idAllocator.getNextId(), new OutputNode(idAllocator.getNextId(), new ProjectNode(idAllocator.getNextId(), new ValuesNode(idAllocator.getNextId(), ImmutableList.of(), ImmutableList.of()), Assignments.of()), ImmutableList.of(), ImmutableList.of()), new Symbol("a")), ImmutableList.of(), ImmutableList.of());
    new VerifyOnlyOneOutputNode().validate(root, null, null, null, null);
}
Also used : ValuesNode(com.facebook.presto.sql.planner.plan.ValuesNode) OutputNode(com.facebook.presto.sql.planner.plan.OutputNode) PlanNode(com.facebook.presto.sql.planner.plan.PlanNode) ExplainAnalyzeNode(com.facebook.presto.sql.planner.plan.ExplainAnalyzeNode) Symbol(com.facebook.presto.sql.planner.Symbol) ProjectNode(com.facebook.presto.sql.planner.plan.ProjectNode) Test(org.testng.annotations.Test)

Example 4 with ProjectNode

use of com.facebook.presto.sql.planner.plan.ProjectNode in project presto by prestodb.

the class LogicalPlanner method createInsertPlan.

private RelationPlan createInsertPlan(Analysis analysis, Insert insertStatement) {
    Analysis.Insert insert = analysis.getInsert().get();
    TableMetadata tableMetadata = metadata.getTableMetadata(session, insert.getTarget());
    List<ColumnMetadata> visibleTableColumns = tableMetadata.getColumns().stream().filter(column -> !column.isHidden()).collect(toImmutableList());
    List<String> visibleTableColumnNames = visibleTableColumns.stream().map(ColumnMetadata::getName).collect(toImmutableList());
    RelationPlan plan = createRelationPlan(analysis, insertStatement.getQuery());
    Map<String, ColumnHandle> columns = metadata.getColumnHandles(session, insert.getTarget());
    Assignments.Builder assignments = Assignments.builder();
    for (ColumnMetadata column : tableMetadata.getColumns()) {
        if (column.isHidden()) {
            continue;
        }
        Symbol output = symbolAllocator.newSymbol(column.getName(), column.getType());
        int index = insert.getColumns().indexOf(columns.get(column.getName()));
        if (index < 0) {
            assignments.put(output, new NullLiteral());
        } else {
            Symbol input = plan.getSymbol(index);
            Type tableType = column.getType();
            Type queryType = symbolAllocator.getTypes().get(input);
            if (queryType.equals(tableType) || metadata.getTypeManager().isTypeOnlyCoercion(queryType, tableType)) {
                assignments.put(output, input.toSymbolReference());
            } else {
                Expression cast = new Cast(input.toSymbolReference(), tableType.getTypeSignature().toString());
                assignments.put(output, cast);
            }
        }
    }
    ProjectNode projectNode = new ProjectNode(idAllocator.getNextId(), plan.getRoot(), assignments.build());
    List<Field> fields = visibleTableColumns.stream().map(column -> Field.newUnqualified(column.getName(), column.getType())).collect(toImmutableList());
    Scope scope = Scope.builder().withRelationType(new RelationType(fields)).build();
    plan = new RelationPlan(projectNode, scope, projectNode.getOutputSymbols());
    Optional<NewTableLayout> newTableLayout = metadata.getInsertLayout(session, insert.getTarget());
    return createTableWriterPlan(analysis, plan, new InsertReference(insert.getTarget()), visibleTableColumnNames, newTableLayout);
}
Also used : QualifiedObjectName(com.facebook.presto.metadata.QualifiedObjectName) TableMetadata(com.facebook.presto.metadata.TableMetadata) ExplainAnalyzeNode(com.facebook.presto.sql.planner.plan.ExplainAnalyzeNode) NOT_FOUND(com.facebook.presto.spi.StandardErrorCode.NOT_FOUND) Field(com.facebook.presto.sql.analyzer.Field) TableWriterNode(com.facebook.presto.sql.planner.plan.TableWriterNode) ValuesNode(com.facebook.presto.sql.planner.plan.ValuesNode) BIGINT(com.facebook.presto.spi.type.BigintType.BIGINT) Delete(com.facebook.presto.sql.tree.Delete) Map(java.util.Map) ImmutableCollectors.toImmutableList(com.facebook.presto.util.ImmutableCollectors.toImmutableList) Assignments(com.facebook.presto.sql.planner.plan.Assignments) OutputNode(com.facebook.presto.sql.planner.plan.OutputNode) ImmutableSet(com.google.common.collect.ImmutableSet) Query(com.facebook.presto.sql.tree.Query) Explain(com.facebook.presto.sql.tree.Explain) String.format(java.lang.String.format) SqlParser(com.facebook.presto.sql.parser.SqlParser) Preconditions.checkState(com.google.common.base.Preconditions.checkState) NullLiteral(com.facebook.presto.sql.tree.NullLiteral) List(java.util.List) ColumnMetadata(com.facebook.presto.spi.ColumnMetadata) NOT_SUPPORTED(com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED) Analysis(com.facebook.presto.sql.analyzer.Analysis) Optional(java.util.Optional) ConnectorId(com.facebook.presto.connector.ConnectorId) LimitNode(com.facebook.presto.sql.planner.plan.LimitNode) PlanOptimizer(com.facebook.presto.sql.planner.optimizations.PlanOptimizer) LambdaArgumentDeclaration(com.facebook.presto.sql.tree.LambdaArgumentDeclaration) NewTableLayout(com.facebook.presto.metadata.NewTableLayout) WriterTarget(com.facebook.presto.sql.planner.plan.TableWriterNode.WriterTarget) PrestoException(com.facebook.presto.spi.PrestoException) ArrayList(java.util.ArrayList) ImmutableList(com.google.common.collect.ImmutableList) Type(com.facebook.presto.spi.type.Type) PlanNode(com.facebook.presto.sql.planner.plan.PlanNode) Objects.requireNonNull(java.util.Objects.requireNonNull) Cast(com.facebook.presto.sql.tree.Cast) VARBINARY(com.facebook.presto.spi.type.VarbinaryType.VARBINARY) ConnectorTableMetadata(com.facebook.presto.spi.ConnectorTableMetadata) PlanSanityChecker(com.facebook.presto.sql.planner.sanity.PlanSanityChecker) CreateTableAsSelect(com.facebook.presto.sql.tree.CreateTableAsSelect) Session(com.facebook.presto.Session) InsertReference(com.facebook.presto.sql.planner.plan.TableWriterNode.InsertReference) RelationType(com.facebook.presto.sql.analyzer.RelationType) DeleteNode(com.facebook.presto.sql.planner.plan.DeleteNode) Insert(com.facebook.presto.sql.tree.Insert) Scope(com.facebook.presto.sql.analyzer.Scope) ProjectNode(com.facebook.presto.sql.planner.plan.ProjectNode) Expression(com.facebook.presto.sql.tree.Expression) ColumnHandle(com.facebook.presto.spi.ColumnHandle) CreateName(com.facebook.presto.sql.planner.plan.TableWriterNode.CreateName) IdentityLinkedHashMap(com.facebook.presto.util.maps.IdentityLinkedHashMap) TableFinishNode(com.facebook.presto.sql.planner.plan.TableFinishNode) Metadata(com.facebook.presto.metadata.Metadata) Statement(com.facebook.presto.sql.tree.Statement) Cast(com.facebook.presto.sql.tree.Cast) ColumnMetadata(com.facebook.presto.spi.ColumnMetadata) NewTableLayout(com.facebook.presto.metadata.NewTableLayout) Assignments(com.facebook.presto.sql.planner.plan.Assignments) InsertReference(com.facebook.presto.sql.planner.plan.TableWriterNode.InsertReference) Field(com.facebook.presto.sql.analyzer.Field) RelationType(com.facebook.presto.sql.analyzer.RelationType) TableMetadata(com.facebook.presto.metadata.TableMetadata) ConnectorTableMetadata(com.facebook.presto.spi.ConnectorTableMetadata) ColumnHandle(com.facebook.presto.spi.ColumnHandle) Type(com.facebook.presto.spi.type.Type) RelationType(com.facebook.presto.sql.analyzer.RelationType) Scope(com.facebook.presto.sql.analyzer.Scope) Expression(com.facebook.presto.sql.tree.Expression) Analysis(com.facebook.presto.sql.analyzer.Analysis) ProjectNode(com.facebook.presto.sql.planner.plan.ProjectNode) NullLiteral(com.facebook.presto.sql.tree.NullLiteral)

Example 5 with ProjectNode

use of com.facebook.presto.sql.planner.plan.ProjectNode in project presto by prestodb.

the class QueryPlanner method aggregate.

private PlanBuilder aggregate(PlanBuilder subPlan, QuerySpecification node) {
    List<List<Expression>> groupingSets = analysis.getGroupingSets(node);
    if (groupingSets.isEmpty()) {
        return subPlan;
    }
    // 1. Pre-project all scalar inputs (arguments and non-trivial group by expressions)
    Set<Expression> distinctGroupingColumns = groupingSets.stream().flatMap(Collection::stream).collect(toImmutableSet());
    ImmutableList.Builder<Expression> arguments = ImmutableList.builder();
    analysis.getAggregates(node).stream().map(FunctionCall::getArguments).flatMap(List::stream).forEach(arguments::add);
    // filter expressions need to be projected first
    analysis.getAggregates(node).stream().map(FunctionCall::getFilter).filter(Optional::isPresent).map(Optional::get).forEach(arguments::add);
    Iterable<Expression> inputs = Iterables.concat(distinctGroupingColumns, arguments.build());
    subPlan = handleSubqueries(subPlan, node, inputs);
    if (!Iterables.isEmpty(inputs)) {
        // avoid an empty projection if the only aggregation is COUNT (which has no arguments)
        subPlan = project(subPlan, inputs);
    }
    // 2. Aggregate
    // 2.a. Rewrite aggregate arguments
    TranslationMap argumentTranslations = new TranslationMap(subPlan.getRelationPlan(), analysis, lambdaDeclarationToSymbolMap);
    ImmutableMap.Builder<Symbol, Symbol> argumentMappingBuilder = ImmutableMap.builder();
    for (Expression argument : arguments.build()) {
        Expression parametersReplaced = ExpressionTreeRewriter.rewriteWith(new ParameterRewriter(analysis.getParameters(), analysis), argument);
        argumentTranslations.addIntermediateMapping(argument, parametersReplaced);
        Symbol input = subPlan.translate(parametersReplaced);
        if (!argumentTranslations.containsSymbol(parametersReplaced)) {
            Symbol output = symbolAllocator.newSymbol(parametersReplaced, analysis.getTypeWithCoercions(parametersReplaced), "arg");
            argumentMappingBuilder.put(output, input);
            argumentTranslations.put(parametersReplaced, output);
        }
    }
    Map<Symbol, Symbol> argumentMappings = argumentMappingBuilder.build();
    // 2.b. Rewrite grouping columns
    TranslationMap groupingTranslations = new TranslationMap(subPlan.getRelationPlan(), analysis, lambdaDeclarationToSymbolMap);
    Map<Symbol, Symbol> groupingSetMappings = new HashMap<>();
    List<List<Symbol>> groupingSymbols = new ArrayList<>();
    for (List<Expression> groupingSet : groupingSets) {
        ImmutableList.Builder<Symbol> symbols = ImmutableList.builder();
        for (Expression expression : groupingSet) {
            Expression parametersReplaced = ExpressionTreeRewriter.rewriteWith(new ParameterRewriter(analysis.getParameters(), analysis), expression);
            groupingTranslations.addIntermediateMapping(expression, parametersReplaced);
            Symbol input = subPlan.translate(expression);
            Symbol output;
            if (!groupingTranslations.containsSymbol(parametersReplaced)) {
                output = symbolAllocator.newSymbol(parametersReplaced, analysis.getTypeWithCoercions(expression), "gid");
                groupingTranslations.put(parametersReplaced, output);
            } else {
                output = groupingTranslations.get(parametersReplaced);
            }
            groupingSetMappings.put(output, input);
            symbols.add(output);
        }
        groupingSymbols.add(symbols.build());
    }
    // 2.c. Generate GroupIdNode (multiple grouping sets) or ProjectNode (single grouping set)
    Optional<Symbol> groupIdSymbol = Optional.empty();
    if (groupingSets.size() > 1) {
        groupIdSymbol = Optional.of(symbolAllocator.newSymbol("groupId", BIGINT));
        GroupIdNode groupId = new GroupIdNode(idAllocator.getNextId(), subPlan.getRoot(), groupingSymbols, groupingSetMappings, argumentMappings, groupIdSymbol.get());
        subPlan = new PlanBuilder(groupingTranslations, groupId, analysis.getParameters());
    } else {
        Assignments.Builder assignments = Assignments.builder();
        for (Symbol output : argumentMappings.keySet()) {
            assignments.put(output, argumentMappings.get(output).toSymbolReference());
        }
        for (Symbol output : groupingSetMappings.keySet()) {
            assignments.put(output, groupingSetMappings.get(output).toSymbolReference());
        }
        ProjectNode project = new ProjectNode(idAllocator.getNextId(), subPlan.getRoot(), assignments.build());
        subPlan = new PlanBuilder(groupingTranslations, project, analysis.getParameters());
    }
    TranslationMap aggregationTranslations = new TranslationMap(subPlan.getRelationPlan(), analysis, lambdaDeclarationToSymbolMap);
    aggregationTranslations.copyMappingsFrom(groupingTranslations);
    // 2.d. Rewrite aggregates
    ImmutableMap.Builder<Symbol, FunctionCall> aggregationAssignments = ImmutableMap.builder();
    ImmutableMap.Builder<Symbol, Signature> functions = ImmutableMap.builder();
    boolean needPostProjectionCoercion = false;
    for (FunctionCall aggregate : analysis.getAggregates(node)) {
        Expression parametersReplaced = ExpressionTreeRewriter.rewriteWith(new ParameterRewriter(analysis.getParameters(), analysis), aggregate);
        aggregationTranslations.addIntermediateMapping(aggregate, parametersReplaced);
        Expression rewritten = argumentTranslations.rewrite(parametersReplaced);
        Symbol newSymbol = symbolAllocator.newSymbol(rewritten, analysis.getType(aggregate));
        // Therefore we can end up with this implicit cast, and have to move it into a post-projection
        if (rewritten instanceof Cast) {
            rewritten = ((Cast) rewritten).getExpression();
            needPostProjectionCoercion = true;
        }
        aggregationAssignments.put(newSymbol, (FunctionCall) rewritten);
        aggregationTranslations.put(parametersReplaced, newSymbol);
        functions.put(newSymbol, analysis.getFunctionSignature(aggregate));
    }
    // 2.e. Mark distinct rows for each aggregate that has DISTINCT
    // Map from aggregate function arguments to marker symbols, so that we can reuse the markers, if two aggregates have the same argument
    Map<Set<Expression>, Symbol> argumentMarkers = new HashMap<>();
    // Map from aggregate functions to marker symbols
    Map<Symbol, Symbol> masks = new HashMap<>();
    for (FunctionCall aggregate : Iterables.filter(analysis.getAggregates(node), FunctionCall::isDistinct)) {
        Set<Expression> args = ImmutableSet.copyOf(aggregate.getArguments());
        Symbol marker = argumentMarkers.get(args);
        Symbol aggregateSymbol = aggregationTranslations.get(aggregate);
        if (marker == null) {
            if (args.size() == 1) {
                marker = symbolAllocator.newSymbol(getOnlyElement(args), BOOLEAN, "distinct");
            } else {
                marker = symbolAllocator.newSymbol(aggregateSymbol.getName(), BOOLEAN, "distinct");
            }
            argumentMarkers.put(args, marker);
        }
        masks.put(aggregateSymbol, marker);
    }
    for (Map.Entry<Set<Expression>, Symbol> entry : argumentMarkers.entrySet()) {
        ImmutableList.Builder<Symbol> builder = ImmutableList.builder();
        builder.addAll(groupingSymbols.stream().flatMap(Collection::stream).distinct().collect(Collectors.toList()));
        groupIdSymbol.ifPresent(builder::add);
        for (Expression expression : entry.getKey()) {
            builder.add(argumentTranslations.get(expression));
        }
        subPlan = subPlan.withNewRoot(new MarkDistinctNode(idAllocator.getNextId(), subPlan.getRoot(), entry.getValue(), builder.build(), Optional.empty()));
    }
    AggregationNode aggregationNode = new AggregationNode(idAllocator.getNextId(), subPlan.getRoot(), aggregationAssignments.build(), functions.build(), masks, groupingSymbols, AggregationNode.Step.SINGLE, Optional.empty(), groupIdSymbol);
    subPlan = new PlanBuilder(aggregationTranslations, aggregationNode, analysis.getParameters());
    // TODO: this is a hack, we should change type coercions to coerce the inputs to functions/operators instead of coercing the output
    if (needPostProjectionCoercion) {
        return explicitCoercionFields(subPlan, distinctGroupingColumns, analysis.getAggregates(node));
    }
    return subPlan;
}
Also used : Cast(com.facebook.presto.sql.tree.Cast) ImmutableCollectors.toImmutableSet(com.facebook.presto.util.ImmutableCollectors.toImmutableSet) ImmutableSet(com.google.common.collect.ImmutableSet) Set(java.util.Set) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) IdentityLinkedHashMap(com.facebook.presto.util.maps.IdentityLinkedHashMap) ImmutableList(com.google.common.collect.ImmutableList) ArrayList(java.util.ArrayList) Assignments(com.facebook.presto.sql.planner.plan.Assignments) GroupIdNode(com.facebook.presto.sql.planner.plan.GroupIdNode) List(java.util.List) ArrayList(java.util.ArrayList) ImmutableList(com.google.common.collect.ImmutableList) FunctionCall(com.facebook.presto.sql.tree.FunctionCall) MarkDistinctNode(com.facebook.presto.sql.planner.plan.MarkDistinctNode) Optional(java.util.Optional) AggregationNode(com.facebook.presto.sql.planner.plan.AggregationNode) ImmutableMap(com.google.common.collect.ImmutableMap) Expression(com.facebook.presto.sql.tree.Expression) Signature(com.facebook.presto.metadata.Signature) ProjectNode(com.facebook.presto.sql.planner.plan.ProjectNode) Map(java.util.Map) ImmutableMap(com.google.common.collect.ImmutableMap) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) IdentityLinkedHashMap(com.facebook.presto.util.maps.IdentityLinkedHashMap)

Aggregations

ProjectNode (com.facebook.presto.sql.planner.plan.ProjectNode)21 Expression (com.facebook.presto.sql.tree.Expression)14 Assignments (com.facebook.presto.sql.planner.plan.Assignments)11 PlanNode (com.facebook.presto.sql.planner.plan.PlanNode)11 Symbol (com.facebook.presto.sql.planner.Symbol)7 Cast (com.facebook.presto.sql.tree.Cast)7 Test (org.testng.annotations.Test)7 AggregationNode (com.facebook.presto.sql.planner.plan.AggregationNode)5 ValuesNode (com.facebook.presto.sql.planner.plan.ValuesNode)5 ImmutableList (com.google.common.collect.ImmutableList)5 List (java.util.List)5 ComparisonExpression (com.facebook.presto.sql.tree.ComparisonExpression)4 FunctionCall (com.facebook.presto.sql.tree.FunctionCall)4 ImmutableMap (com.google.common.collect.ImmutableMap)4 Map (java.util.Map)4 Signature (com.facebook.presto.metadata.Signature)3 Type (com.facebook.presto.spi.type.Type)3 Field (com.facebook.presto.sql.analyzer.Field)3 RelationType (com.facebook.presto.sql.analyzer.RelationType)3 LimitNode (com.facebook.presto.sql.planner.plan.LimitNode)3