Search in sources :

Example 1 with EnforceSingleRowNode

use of io.trino.sql.planner.plan.EnforceSingleRowNode in project trino by trinodb.

the class SubqueryPlanner method planScalarSubquery.

private PlanBuilder planScalarSubquery(PlanBuilder subPlan, Cluster<SubqueryExpression> cluster) {
    // Plan one of the predicates from the cluster
    SubqueryExpression scalarSubquery = cluster.getRepresentative();
    RelationPlan relationPlan = planSubquery(scalarSubquery, subPlan.getTranslations());
    PlanBuilder subqueryPlan = newPlanBuilder(relationPlan, analysis, lambdaDeclarationToSymbolMap);
    PlanNode root = new EnforceSingleRowNode(idAllocator.getNextId(), subqueryPlan.getRoot());
    Type type = analysis.getType(scalarSubquery);
    RelationType descriptor = relationPlan.getDescriptor();
    List<Symbol> fieldMappings = relationPlan.getFieldMappings();
    Symbol column;
    if (descriptor.getVisibleFieldCount() > 1) {
        column = symbolAllocator.newSymbol("row", type);
        ImmutableList.Builder<Expression> fields = ImmutableList.builder();
        for (int i = 0; i < descriptor.getAllFieldCount(); i++) {
            Field field = descriptor.getFieldByIndex(i);
            if (!field.isHidden()) {
                fields.add(fieldMappings.get(i).toSymbolReference());
            }
        }
        Expression expression = new Cast(new Row(fields.build()), TypeSignatureTranslator.toSqlType(type));
        root = new ProjectNode(idAllocator.getNextId(), root, Assignments.of(column, expression));
    } else {
        column = getOnlyElement(fieldMappings);
    }
    return appendCorrelatedJoin(subPlan, root, scalarSubquery.getQuery(), CorrelatedJoinNode.Type.INNER, TRUE_LITERAL, mapAll(cluster, subPlan.getScope(), column));
}
Also used : Cast(io.trino.sql.tree.Cast) ImmutableList.toImmutableList(com.google.common.collect.ImmutableList.toImmutableList) ImmutableList(com.google.common.collect.ImmutableList) PlanBuilder.newPlanBuilder(io.trino.sql.planner.PlanBuilder.newPlanBuilder) SubqueryExpression(io.trino.sql.tree.SubqueryExpression) Field(io.trino.sql.analyzer.Field) TypeSignatureTranslator.toSqlType(io.trino.sql.analyzer.TypeSignatureTranslator.toSqlType) RelationType(io.trino.sql.analyzer.RelationType) Type(io.trino.spi.type.Type) PlanNode(io.trino.sql.planner.plan.PlanNode) ComparisonExpression(io.trino.sql.tree.ComparisonExpression) QuantifiedComparisonExpression(io.trino.sql.tree.QuantifiedComparisonExpression) Expression(io.trino.sql.tree.Expression) SubqueryExpression(io.trino.sql.tree.SubqueryExpression) NotExpression(io.trino.sql.tree.NotExpression) EnforceSingleRowNode(io.trino.sql.planner.plan.EnforceSingleRowNode) RelationType(io.trino.sql.analyzer.RelationType) ProjectNode(io.trino.sql.planner.plan.ProjectNode) Row(io.trino.sql.tree.Row)

Example 2 with EnforceSingleRowNode

use of io.trino.sql.planner.plan.EnforceSingleRowNode in project trino by trinodb.

the class DecorrelateUnnest method apply.

@Override
public Result apply(CorrelatedJoinNode correlatedJoinNode, Captures captures, Context context) {
    // determine shape of the subquery
    PlanNode searchRoot = correlatedJoinNode.getSubquery();
    // 1. find EnforceSingleRowNode in the subquery
    Optional<EnforceSingleRowNode> enforceSingleRow = PlanNodeSearcher.searchFrom(searchRoot, context.getLookup()).where(EnforceSingleRowNode.class::isInstance).recurseOnlyWhen(planNode -> false).findFirst();
    if (enforceSingleRow.isPresent()) {
        searchRoot = enforceSingleRow.get().getSource();
    }
    // 2. find correlated UnnestNode in the subquery
    Optional<UnnestNode> subqueryUnnest = PlanNodeSearcher.searchFrom(searchRoot, context.getLookup()).where(node -> isSupportedUnnest(node, correlatedJoinNode.getCorrelation(), context.getLookup())).recurseOnlyWhen(node -> node instanceof ProjectNode || (node instanceof LimitNode && ((LimitNode) node).getCount() > 0) || (node instanceof TopNNode && ((TopNNode) node).getCount() > 0)).findFirst();
    if (subqueryUnnest.isEmpty()) {
        return Result.empty();
    }
    UnnestNode unnestNode = subqueryUnnest.get();
    // assign unique id to input rows
    Symbol uniqueSymbol = context.getSymbolAllocator().newSymbol("unique", BIGINT);
    PlanNode input = new AssignUniqueId(context.getIdAllocator().getNextId(), correlatedJoinNode.getInput(), uniqueSymbol);
    // pre-project unnest symbols if they were pre-projected in subquery
    // The correlated UnnestNode either unnests correlation symbols directly, or unnests symbols produced by a projection that uses only correlation symbols.
    // Here, any underlying projection that was a source of the correlated UnnestNode, is appended as a source of the rewritten UnnestNode.
    // If the projection is not necessary for UnnestNode (i.e. it does not produce any unnest symbols), it should be pruned afterwards.
    PlanNode unnestSource = context.getLookup().resolve(unnestNode.getSource());
    if (unnestSource instanceof ProjectNode) {
        ProjectNode sourceProjection = (ProjectNode) unnestSource;
        input = new ProjectNode(sourceProjection.getId(), input, Assignments.builder().putIdentities(input.getOutputSymbols()).putAll(sourceProjection.getAssignments()).build());
    }
    // determine join type for rewritten UnnestNode
    Type unnestJoinType = LEFT;
    if (enforceSingleRow.isEmpty() && correlatedJoinNode.getType() == CorrelatedJoinNode.Type.INNER && unnestNode.getJoinType() == INNER) {
        unnestJoinType = INNER;
    }
    // make sure that the rewritten node is with ordinality, which might be necessary to restore inner unnest semantics after rewrite.
    Symbol ordinalitySymbol = unnestNode.getOrdinalitySymbol().orElseGet(() -> context.getSymbolAllocator().newSymbol("ordinality", BIGINT));
    // rewrite correlated join to UnnestNode.
    UnnestNode rewrittenUnnest = new UnnestNode(context.getIdAllocator().getNextId(), input, input.getOutputSymbols(), unnestNode.getMappings(), Optional.of(ordinalitySymbol), unnestJoinType, Optional.empty());
    // restore all nodes from the subquery
    PlanNode rewrittenPlan = Rewriter.rewriteNodeSequence(correlatedJoinNode.getSubquery(), input.getOutputSymbols(), ordinalitySymbol, uniqueSymbol, rewrittenUnnest, context.getSession(), metadata, context.getLookup(), context.getIdAllocator(), context.getSymbolAllocator());
    // between unnested rows and synthetic rows added by left unnest.
    if (unnestNode.getJoinType() == INNER && rewrittenUnnest.getJoinType() == LEFT) {
        Assignments.Builder assignments = Assignments.builder().putIdentities(correlatedJoinNode.getInput().getOutputSymbols());
        for (Symbol subquerySymbol : correlatedJoinNode.getSubquery().getOutputSymbols()) {
            assignments.put(subquerySymbol, new IfExpression(new IsNullPredicate(ordinalitySymbol.toSymbolReference()), new Cast(new NullLiteral(), toSqlType(context.getSymbolAllocator().getTypes().get(subquerySymbol))), subquerySymbol.toSymbolReference()));
        }
        rewrittenPlan = new ProjectNode(context.getIdAllocator().getNextId(), rewrittenPlan, assignments.build());
    }
    // restrict outputs
    return Result.ofPlanNode(restrictOutputs(context.getIdAllocator(), rewrittenPlan, ImmutableSet.copyOf(correlatedJoinNode.getOutputSymbols())).orElse(rewrittenPlan));
}
Also used : CorrelatedJoin.correlation(io.trino.sql.planner.plan.Patterns.CorrelatedJoin.correlation) IsNullPredicate(io.trino.sql.tree.IsNullPredicate) SymbolAllocator(io.trino.sql.planner.SymbolAllocator) TypeSignatureProvider.fromTypes(io.trino.sql.analyzer.TypeSignatureProvider.fromTypes) CorrelatedJoinNode(io.trino.sql.planner.plan.CorrelatedJoinNode) FilterNode(io.trino.sql.planner.plan.FilterNode) PlanNode(io.trino.sql.planner.plan.PlanNode) LEFT(io.trino.sql.planner.plan.JoinNode.Type.LEFT) Type(io.trino.sql.planner.plan.JoinNode.Type) PlanNodeSearcher(io.trino.sql.planner.optimizations.PlanNodeSearcher) GREATER_THAN(io.trino.sql.tree.ComparisonExpression.Operator.GREATER_THAN) AssignUniqueId(io.trino.sql.planner.plan.AssignUniqueId) FunctionCall(io.trino.sql.tree.FunctionCall) ImmutableSet(com.google.common.collect.ImmutableSet) ImmutableMap(com.google.common.collect.ImmutableMap) ResolvedFunction(io.trino.metadata.ResolvedFunction) EnforceSingleRowNode(io.trino.sql.planner.plan.EnforceSingleRowNode) ImmutableList.toImmutableList(com.google.common.collect.ImmutableList.toImmutableList) TypeSignatureTranslator.toSqlType(io.trino.sql.analyzer.TypeSignatureTranslator.toSqlType) Assignments(io.trino.sql.planner.plan.Assignments) ComparisonExpression(io.trino.sql.tree.ComparisonExpression) DEFAULT_FRAME(io.trino.sql.planner.plan.WindowNode.Frame.DEFAULT_FRAME) LESS_THAN_OR_EQUAL(io.trino.sql.tree.ComparisonExpression.Operator.LESS_THAN_OR_EQUAL) GenericLiteral(io.trino.sql.tree.GenericLiteral) List(java.util.List) Pattern(io.trino.matching.Pattern) IfExpression(io.trino.sql.tree.IfExpression) BIGINT(io.trino.spi.type.BigintType.BIGINT) Optional(java.util.Optional) Expression(io.trino.sql.tree.Expression) WindowNode(io.trino.sql.planner.plan.WindowNode) Session(io.trino.Session) QueryCardinalityUtil.isScalar(io.trino.sql.planner.optimizations.QueryCardinalityUtil.isScalar) INNER(io.trino.sql.planner.plan.JoinNode.Type.INNER) LimitNode(io.trino.sql.planner.plan.LimitNode) BOOLEAN(io.trino.spi.type.BooleanType.BOOLEAN) Cast(io.trino.sql.tree.Cast) CorrelatedJoin.filter(io.trino.sql.planner.plan.Patterns.CorrelatedJoin.filter) VARCHAR(io.trino.spi.type.VarcharType.VARCHAR) ImmutableList(com.google.common.collect.ImmutableList) RowNumberNode(io.trino.sql.planner.plan.RowNumberNode) Objects.requireNonNull(java.util.Objects.requireNonNull) NullLiteral(io.trino.sql.tree.NullLiteral) Rule(io.trino.sql.planner.iterative.Rule) SymbolsExtractor(io.trino.sql.planner.SymbolsExtractor) Pattern.nonEmpty(io.trino.matching.Pattern.nonEmpty) ProjectNode(io.trino.sql.planner.plan.ProjectNode) Symbol(io.trino.sql.planner.Symbol) StringLiteral(io.trino.sql.tree.StringLiteral) Lookup(io.trino.sql.planner.iterative.Lookup) PlanVisitor(io.trino.sql.planner.plan.PlanVisitor) TopNNode(io.trino.sql.planner.plan.TopNNode) TRUE_LITERAL(io.trino.sql.tree.BooleanLiteral.TRUE_LITERAL) UnnestNode(io.trino.sql.planner.plan.UnnestNode) ImplementLimitWithTies.rewriteLimitWithTiesWithPartitioning(io.trino.sql.planner.iterative.rule.ImplementLimitWithTies.rewriteLimitWithTiesWithPartitioning) QualifiedName(io.trino.sql.tree.QualifiedName) Captures(io.trino.matching.Captures) Specification(io.trino.sql.planner.plan.WindowNode.Specification) Util.restrictOutputs(io.trino.sql.planner.iterative.rule.Util.restrictOutputs) Metadata(io.trino.metadata.Metadata) PlanNodeIdAllocator(io.trino.sql.planner.PlanNodeIdAllocator) Patterns.correlatedJoin(io.trino.sql.planner.plan.Patterns.correlatedJoin) Cast(io.trino.sql.tree.Cast) IfExpression(io.trino.sql.tree.IfExpression) Symbol(io.trino.sql.planner.Symbol) Assignments(io.trino.sql.planner.plan.Assignments) Type(io.trino.sql.planner.plan.JoinNode.Type) TypeSignatureTranslator.toSqlType(io.trino.sql.analyzer.TypeSignatureTranslator.toSqlType) PlanNode(io.trino.sql.planner.plan.PlanNode) AssignUniqueId(io.trino.sql.planner.plan.AssignUniqueId) UnnestNode(io.trino.sql.planner.plan.UnnestNode) LimitNode(io.trino.sql.planner.plan.LimitNode) EnforceSingleRowNode(io.trino.sql.planner.plan.EnforceSingleRowNode) IsNullPredicate(io.trino.sql.tree.IsNullPredicate) ProjectNode(io.trino.sql.planner.plan.ProjectNode) NullLiteral(io.trino.sql.tree.NullLiteral) TopNNode(io.trino.sql.planner.plan.TopNNode)

Example 3 with EnforceSingleRowNode

use of io.trino.sql.planner.plan.EnforceSingleRowNode in project trino by trinodb.

the class TestCostCalculator method testEnforceSingleRow.

@Test
public void testEnforceSingleRow() {
    TableScanNode ts1 = tableScan("ts1", "orderkey");
    EnforceSingleRowNode singleRow = new EnforceSingleRowNode(new PlanNodeId("singleRow"), ts1);
    Map<String, PlanNodeStatsEstimate> stats = ImmutableMap.of("ts1", statsEstimate(ts1, 4000), "singleRow", // 1 * Average Row Size
    statsEstimate(ts1, 8));
    Map<String, PlanCostEstimate> costs = ImmutableMap.of("ts1", cpuCost(1000));
    Map<String, Type> types = ImmutableMap.of("orderkey", BIGINT);
    assertCost(singleRow, costs, stats, types).cpu(// Only count the accumulated cost of source nodes
    1000).memory(0).network(0);
    assertCostEstimatedExchanges(singleRow, costs, stats, types).cpu(1000).memory(0).network(0);
}
Also used : PlanNodeId(io.trino.sql.planner.plan.PlanNodeId) TypeSignatureTranslator.toSqlType(io.trino.sql.analyzer.TypeSignatureTranslator.toSqlType) Type(io.trino.spi.type.Type) TableScanNode(io.trino.sql.planner.plan.TableScanNode) EnforceSingleRowNode(io.trino.sql.planner.plan.EnforceSingleRowNode) Test(org.testng.annotations.Test)

Aggregations

TypeSignatureTranslator.toSqlType (io.trino.sql.analyzer.TypeSignatureTranslator.toSqlType)3 EnforceSingleRowNode (io.trino.sql.planner.plan.EnforceSingleRowNode)3 ImmutableList (com.google.common.collect.ImmutableList)2 ImmutableList.toImmutableList (com.google.common.collect.ImmutableList.toImmutableList)2 Type (io.trino.spi.type.Type)2 PlanNode (io.trino.sql.planner.plan.PlanNode)2 ImmutableMap (com.google.common.collect.ImmutableMap)1 ImmutableSet (com.google.common.collect.ImmutableSet)1 Session (io.trino.Session)1 Captures (io.trino.matching.Captures)1 Pattern (io.trino.matching.Pattern)1 Pattern.nonEmpty (io.trino.matching.Pattern.nonEmpty)1 Metadata (io.trino.metadata.Metadata)1 ResolvedFunction (io.trino.metadata.ResolvedFunction)1 BIGINT (io.trino.spi.type.BigintType.BIGINT)1 BOOLEAN (io.trino.spi.type.BooleanType.BOOLEAN)1 VARCHAR (io.trino.spi.type.VarcharType.VARCHAR)1 Field (io.trino.sql.analyzer.Field)1 RelationType (io.trino.sql.analyzer.RelationType)1 TypeSignatureProvider.fromTypes (io.trino.sql.analyzer.TypeSignatureProvider.fromTypes)1