Search in sources :

Example 91 with PlanNode

use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.

the class RuleDecomposeJoin method createSource.

static PlanNode createSource(GroupSymbol group, PlanNode child, List<ElementSymbol> newProject) {
    PlanNode branchSource = NodeFactory.getNewNode(NodeConstants.Types.SOURCE);
    branchSource.addGroup(group);
    PlanNode projectNode = NodeEditor.findNodePreOrder(child, NodeConstants.Types.PROJECT);
    branchSource.setProperty(Info.SYMBOL_MAP, SymbolMap.createSymbolMap(newProject, (List<? extends Expression>) projectNode.getProperty(Info.PROJECT_COLS)));
    child.addAsParent(branchSource);
    return branchSource;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) Expression(org.teiid.query.sql.symbol.Expression) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) List(java.util.List)

Example 92 with PlanNode

use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.

the class RuleDecomposeJoin method decomposeJoin.

public PlanNode decomposeJoin(PlanNode joinNode, PlanNode root, QueryMetadataInterface metadata, CommandContext context) throws TeiidComponentException, QueryPlannerException {
    if (joinNode.getParent() == null) {
        // already processed
        return root;
    }
    JoinType joinType = (JoinType) joinNode.getProperty(Info.JOIN_TYPE);
    if (joinType == JoinType.JOIN_ANTI_SEMI || joinType == JoinType.JOIN_CROSS) {
        return root;
    }
    PlanNode left = joinNode.getFirstChild();
    while (left.getType() != NodeConstants.Types.SOURCE) {
        if (left.getType() == NodeConstants.Types.SELECT && left.hasBooleanProperty(Info.IS_PHANTOM)) {
            left = left.getFirstChild();
        } else {
            return root;
        }
    }
    Map<ElementSymbol, List<Set<Constant>>> partitionInfo = (Map<ElementSymbol, List<Set<Constant>>>) left.getProperty(Info.PARTITION_INFO);
    if (partitionInfo == null) {
        return root;
    }
    PlanNode unionNode = left.getFirstChild();
    if (unionNode.getType() != NodeConstants.Types.SET_OP) {
        return root;
    }
    PlanNode right = joinNode.getLastChild();
    while (right.getType() != NodeConstants.Types.SOURCE) {
        if (right.getType() == NodeConstants.Types.SELECT && right.hasBooleanProperty(Info.IS_PHANTOM)) {
            right = right.getFirstChild();
        } else {
            return root;
        }
    }
    Map<ElementSymbol, List<Set<Constant>>> rightPartionInfo = (Map<ElementSymbol, List<Set<Constant>>>) right.getProperty(Info.PARTITION_INFO);
    if (rightPartionInfo == null) {
        return root;
    }
    List<Criteria> criteria = (List<Criteria>) joinNode.getProperty(Info.JOIN_CRITERIA);
    List<Expression> expr = new ArrayList<Expression>();
    List<Expression> exprOther = new ArrayList<Expression>();
    RuleChooseJoinStrategy.separateCriteria(unionNode.getParent().getGroups(), right.getGroups(), expr, exprOther, criteria, new LinkedList<Criteria>());
    // if implicit, we assume that partitions match
    ElementSymbol es = getImplicitPartitionColumn(metadata, left);
    ElementSymbol esOther = getImplicitPartitionColumn(metadata, right);
    if (es != null && esOther != null && getEffectiveModelId(metadata, es.getGroupSymbol()) == getEffectiveModelId(metadata, esOther.getGroupSymbol())) {
        expr.add(es);
        exprOther.add(esOther);
    }
    if (expr.isEmpty()) {
        // no equi-join
        return root;
    }
    List<int[]> matches = findMatches(partitionInfo, rightPartionInfo, expr, exprOther);
    if (matches == null) {
        // no non-overlapping partitions
        return root;
    }
    int branchSize = partitionInfo.values().iterator().next().size();
    int otherBranchSize = rightPartionInfo.values().iterator().next().size();
    if (matches.isEmpty()) {
        if (joinType == JoinType.JOIN_INNER || joinType == JoinType.JOIN_SEMI) {
            // no matches mean that we can just insert a null node (false criteria) and be done with it
            PlanNode critNode = NodeFactory.getNewNode(NodeConstants.Types.SELECT);
            critNode.setProperty(Info.SELECT_CRITERIA, QueryRewriter.FALSE_CRITERIA);
            unionNode.addAsParent(critNode);
        } else if (joinType == JoinType.JOIN_LEFT_OUTER) {
            joinNode.getParent().replaceChild(joinNode, left);
        } else if (joinType == JoinType.JOIN_FULL_OUTER) {
            joinNode.setProperty(Info.JOIN_CRITERIA, QueryRewriter.FALSE_CRITERIA);
        }
        return root;
    }
    List<PlanNode> branches = new ArrayList<PlanNode>();
    // TODO: find union children from RulePushAggregates
    RulePushSelectCriteria.collectUnionChildren(unionNode, branches);
    if (branches.size() != branchSize) {
        // sanity check
        return root;
    }
    List<PlanNode> otherBranches = new ArrayList<PlanNode>();
    RulePushSelectCriteria.collectUnionChildren(right.getFirstChild(), otherBranches);
    if (otherBranches.size() != otherBranchSize) {
        // sanity check
        return root;
    }
    PlanNode newUnion = buildUnion(unionNode, right, criteria, matches, branches, otherBranches, joinType);
    GroupSymbol leftGroup = left.getGroups().iterator().next();
    PlanNode view = rebuild(leftGroup, joinNode, newUnion, metadata, context, left, right);
    // preserve the model of the virtual group as we'll look for this when checking for implicit behavior
    ((TempMetadataID) (view.getGroups().iterator().next().getMetadataID())).getTableData().setModel(getEffectiveModelId(metadata, leftGroup));
    SymbolMap symbolmap = (SymbolMap) view.getProperty(Info.SYMBOL_MAP);
    HashMap<ElementSymbol, List<Set<Constant>>> newPartitionInfo = new LinkedHashMap<ElementSymbol, List<Set<Constant>>>();
    Map<Expression, ElementSymbol> inverse = symbolmap.inserseMapping();
    for (int[] match : matches) {
        updatePartitionInfo(partitionInfo, matches, inverse, newPartitionInfo, match[0]);
        updatePartitionInfo(rightPartionInfo, matches, inverse, newPartitionInfo, match[1]);
    }
    view.setProperty(Info.PARTITION_INFO, newPartitionInfo);
    // since we've created a new union node, there's a chance we can decompose again
    if (view.getParent().getType() == NodeConstants.Types.JOIN) {
        return decomposeJoin(view.getParent(), root, metadata, context);
    }
    return root;
}
Also used : ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) Set(java.util.Set) Constant(org.teiid.query.sql.symbol.Constant) ArrayList(java.util.ArrayList) JoinType(org.teiid.query.sql.lang.JoinType) SymbolMap(org.teiid.query.sql.util.SymbolMap) Criteria(org.teiid.query.sql.lang.Criteria) LinkedHashMap(java.util.LinkedHashMap) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) Expression(org.teiid.query.sql.symbol.Expression) GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) List(java.util.List) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) SymbolMap(org.teiid.query.sql.util.SymbolMap)

Example 93 with PlanNode

use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.

the class RelationalPlanner method attachSorting.

/**
 * Attach SORT node at top of tree.  The SORT may be pushed down to a source (or sources)
 * if possible by the optimizer.
 * @param plan Existing plan
 * @param orderBy Sort description from the query
 * @return Updated plan
 */
private static PlanNode attachSorting(PlanNode plan, OrderBy orderBy) {
    PlanNode sortNode = NodeFactory.getNewNode(NodeConstants.Types.SORT);
    sortNode.setProperty(NodeConstants.Info.SORT_ORDER, orderBy);
    if (orderBy.hasUnrelated()) {
        sortNode.setProperty(Info.UNRELATED_SORT, true);
    }
    sortNode.addGroups(GroupsUsedByElementsVisitor.getGroups(orderBy));
    attachLast(sortNode, plan);
    return sortNode;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode)

Example 94 with PlanNode

use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.

the class RelationalPlanner method planWith.

private void planWith(PlanNode plan, Command command) throws QueryPlannerException, QueryMetadataException, TeiidComponentException, QueryResolverException {
    if (this.withPlanningState.withList.isEmpty()) {
        return;
    }
    // TODO: merge this logic inline with the main rule execution.
    RuleStack stack = new RuleStack();
    stack.push(new RuleAssignOutputElements(false));
    if (hints.hasRowBasedSecurity) {
        stack.push(new RuleApplySecurity());
    }
    // use a temporary planner to run just the assign output elements
    RelationalPlanner planner = new RelationalPlanner();
    // we don't want to trigger the with processing for just projection
    planner.processWith = false;
    planner.initialize(command, idGenerator, metadata, capFinder, analysisRecord, context);
    planner.executeRules(stack, plan);
    // discover all of the usage
    List<Command> commands = CommandCollectorVisitor.getCommands(command, true);
    while (!commands.isEmpty()) {
        Command cmd = commands.remove(commands.size() - 1);
        commands.addAll(CommandCollectorVisitor.getCommands(cmd, true));
        try {
            PlanNode temp = planner.generatePlan((Command) cmd.clone());
            stack.push(new RuleAssignOutputElements(false));
            planner.executeRules(stack, temp);
        } catch (TeiidProcessingException e) {
            throw new QueryPlannerException(e);
        }
    }
    // plan and minimize projection
    for (WithQueryCommand with : this.withPlanningState.withList.values()) {
        QueryCommand subCommand = with.getCommand();
        TempMetadataID tid = (TempMetadataID) with.getGroupSymbol().getMetadataID();
        if (tid.getTableData().getModel() != TempMetadataAdapter.TEMP_MODEL) {
            tid.getTableData().setModel(null);
        }
        List<TempMetadataID> elements = tid.getElements();
        List<Integer> toRemove = new ArrayList<Integer>();
        for (int i = elements.size() - 1; i >= 0; i--) {
            TempMetadataID elem = elements.get(i);
            if (!elem.isAccessed()) {
                toRemove.add(i);
            }
        }
        // the definition of the with clause consistent
        if (!toRemove.isEmpty()) {
            if (with.isRecursive()) {
                SetQuery setQuery = (SetQuery) subCommand;
                setQuery.setLeftQuery(removeUnusedProjection(with, setQuery.getLeftQuery(), elements, toRemove));
                setQuery.setRightQuery(removeUnusedProjection(with, setQuery.getRightQuery(), elements, toRemove));
            } else {
                subCommand = removeUnusedProjection(with, subCommand, elements, toRemove);
                with.setCommand(subCommand);
            }
        }
        if (with.isRecursive()) {
            SetQuery setQuery = (SetQuery) subCommand;
            QueryCommand qc = setQuery.getLeftQuery();
            final RelationalPlan subPlan = optimize(qc);
            qc.setProcessorPlan(subPlan);
            AccessNode aNode = CriteriaCapabilityValidatorVisitor.getAccessNode(subPlan);
            Object modelID = null;
            QueryCommand withCommand = null;
            if (aNode != null) {
                modelID = CriteriaCapabilityValidatorVisitor.validateCommandPushdown(null, metadata, capFinder, aNode, false);
                if (modelID != null) {
                    if (with.getGroupSymbol().getModelMetadataId() != null || !CapabilitiesUtil.supports(Capability.RECURSIVE_COMMON_TABLE_EXPRESSIONS, modelID, metadata, capFinder) || with.isMaterialize()) {
                        modelID = null;
                    } else {
                        withCommand = CriteriaCapabilityValidatorVisitor.getQueryCommand(aNode);
                        if (withCommand != null) {
                            // provisionally set the source
                            ((TempMetadataID) with.getGroupSymbol().getMetadataID()).getTableData().setModel(modelID);
                        }
                    }
                }
            }
            // now that we possibly have a model id, plan the recursive part
            QueryCommand qc1 = setQuery.getRightQuery();
            RelationalPlan subPlan1 = optimize((Command) qc1.clone());
            qc1.setProcessorPlan(subPlan1);
            if (!isPushdownValid(with, setQuery, modelID, withCommand, subPlan1) && withCommand != null) {
                // reset the source to null and replan
                ((TempMetadataID) with.getGroupSymbol().getMetadataID()).getTableData().setModel(null);
                subPlan1 = optimize(qc1);
                qc1.setProcessorPlan(subPlan1);
            }
            continue;
        }
        RelationalPlan subPlan = optimize(subCommand);
        subCommand.setProcessorPlan(subPlan);
        RelationalPlan procPlan = subPlan;
        RelationalNode root = procPlan.getRootNode();
        Number planCardinality = root.getEstimateNodeCardinality();
        if (planCardinality != null) {
            ((TempMetadataID) with.getGroupSymbol().getMetadataID()).setCardinality(planCardinality.intValue());
        }
        AccessNode aNode = CriteriaCapabilityValidatorVisitor.getAccessNode(procPlan);
        if (aNode == null) {
            continue;
        }
        Object modelID = CriteriaCapabilityValidatorVisitor.validateCommandPushdown(null, metadata, capFinder, aNode, false);
        QueryCommand withCommand = CriteriaCapabilityValidatorVisitor.getQueryCommand(aNode);
        if (modelID == null || withCommand == null) {
            continue;
        }
        if (with.getGroupSymbol().getModelMetadataId() != null || !CapabilitiesUtil.supports(Capability.COMMON_TABLE_EXPRESSIONS, modelID, metadata, capFinder) || with.isMaterialize()) {
            continue;
        }
        WithQueryCommand wqc = new WithQueryCommand(with.getGroupSymbol(), with.getColumns(), withCommand);
        wqc.setNoInline(with.isNoInline());
        ((TempMetadataID) with.getGroupSymbol().getMetadataID()).getTableData().setModel(modelID);
        this.withPlanningState.pushdownWith.put(with.getGroupSymbol().getName(), wqc);
    }
}
Also used : TempMetadataID(org.teiid.query.metadata.TempMetadataID) RelationalPlan(org.teiid.query.processor.relational.RelationalPlan) TeiidProcessingException(org.teiid.core.TeiidProcessingException) SubqueryAwareRelationalNode(org.teiid.query.processor.relational.SubqueryAwareRelationalNode) RelationalNode(org.teiid.query.processor.relational.RelationalNode) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) CreateProcedureCommand(org.teiid.query.sql.proc.CreateProcedureCommand) LanguageObject(org.teiid.query.sql.LanguageObject) AccessNode(org.teiid.query.processor.relational.AccessNode) QueryPlannerException(org.teiid.api.exception.query.QueryPlannerException)

Example 95 with PlanNode

use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.

the class RelationalPlanner method optimize.

public RelationalPlan optimize(Command command) throws QueryPlannerException, QueryMetadataException, TeiidComponentException, QueryResolverException {
    boolean debug = analysisRecord.recordDebug();
    if (debug) {
        // $NON-NLS-1$
        analysisRecord.println("\n----------------------------------------------------------------------------");
        // $NON-NLS-1$
        analysisRecord.println("GENERATE CANONICAL: \n" + command);
    }
    SourceHint previous = this.sourceHint;
    this.sourceHint = SourceHint.combine(previous, command.getSourceHint());
    PlanToProcessConverter planToProcessConverter = new PlanToProcessConverter(metadata, idGenerator, analysisRecord, capFinder, context);
    WithPlanningState saved = this.withPlanningState;
    this.withPlanningState = new WithPlanningState();
    Command original = (Command) command.clone();
    PlanNode plan;
    try {
        plan = generatePlan(command);
    } catch (TeiidProcessingException e) {
        throw new QueryPlannerException(e);
    }
    planWith(plan, command);
    if (plan.getType() == NodeConstants.Types.SOURCE) {
        // this was effectively a rewrite
        return (RelationalPlan) plan.getProperty(Info.PROCESSOR_PLAN);
    }
    if (debug) {
        // $NON-NLS-1$
        analysisRecord.println("\nCANONICAL PLAN: \n" + plan);
    }
    // Connect ProcessorPlan to SubqueryContainer (if any) of SELECT or PROJECT nodes
    // TODO: merge with node creation
    connectSubqueryContainers(plan);
    // Set top column information on top node
    List<Expression> topCols = Util.deepClone(command.getProjectedSymbols(), Expression.class);
    // Build rule set based on hints
    RuleStack rules = buildRules();
    // Run rule-based optimizer
    plan = executeRules(rules, plan);
    RelationalPlan result = planToProcessConverter.convert(plan);
    boolean fullPushdown = false;
    if (!this.withPlanningState.pushdownWith.isEmpty()) {
        AccessNode aNode = CriteriaCapabilityValidatorVisitor.getAccessNode(result);
        if (aNode != null) {
            QueryCommand queryCommand = CriteriaCapabilityValidatorVisitor.getQueryCommand(aNode);
            if (queryCommand != null) {
                fullPushdown = true;
                for (SubqueryContainer<?> container : ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(queryCommand)) {
                    if (container instanceof Evaluatable<?> && ((Evaluatable<?>) container).shouldEvaluate()) {
                        // we could more deeply check, but we'll just assume that the references are needed
                        fullPushdown = false;
                        break;
                    }
                }
            }
        }
        // distribute the appropriate clauses to the pushdowns
        assignWithClause(result.getRootNode(), this.withPlanningState.pushdownWith, false);
        List<String> toReplan = new ArrayList<String>();
        for (Map.Entry<String, Object> entry : this.withPlanningState.pushdownState.entrySet()) {
            if (Boolean.TRUE.equals(entry.getValue())) {
                GroupSymbol gs = this.withPlanningState.pushdownWith.get(entry.getKey()).getGroupSymbol();
                TempMetadataID tmi = (TempMetadataID) gs.getMetadataID();
                tmi.getTableData().setModel(TempMetadataAdapter.TEMP_MODEL);
                toReplan.add(entry.getKey());
            }
        }
        if (!toReplan.isEmpty()) {
            for (WithQueryCommand wqc : this.withPlanningState.withList.values()) {
                this.context.getGroups().remove(wqc.getGroupSymbol().getName());
            }
            this.sourceHint = previous;
            this.withPlanningState = saved;
            if (debug) {
                // $NON-NLS-1$ //$NON-NLS-2$
                analysisRecord.println("\nReplanning due to multiple common table references: " + toReplan + "\n");
            }
            return optimize(original);
        }
    }
    if (!fullPushdown && !this.withPlanningState.withList.isEmpty()) {
        // generally any with item associated with a pushdown will not be needed as we're converting to a source query
        result.setWith(new ArrayList<WithQueryCommand>(this.withPlanningState.withList.values()));
        // assign any with clauses in this subplan
        for (WithQueryCommand wqc : this.withPlanningState.withList.values()) {
            if (wqc.isRecursive()) {
                SetQuery sq = (SetQuery) wqc.getCommand();
                assignWithClause(((RelationalPlan) sq.getLeftQuery().getProcessorPlan()).getRootNode(), this.withPlanningState.pushdownWith, false);
                assignWithClause(((RelationalPlan) sq.getRightQuery().getProcessorPlan()).getRootNode(), this.withPlanningState.pushdownWith, false);
            } else {
                assignWithClause(((RelationalPlan) wqc.getCommand().getProcessorPlan()).getRootNode(), this.withPlanningState.pushdownWith, false);
            }
        }
    }
    result.setOutputElements(topCols);
    this.sourceHint = previous;
    this.withPlanningState = saved;
    return result;
}
Also used : RelationalPlan(org.teiid.query.processor.relational.RelationalPlan) TeiidProcessingException(org.teiid.core.TeiidProcessingException) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) AccessNode(org.teiid.query.processor.relational.AccessNode) TempMetadataID(org.teiid.query.metadata.TempMetadataID) CreateProcedureCommand(org.teiid.query.sql.proc.CreateProcedureCommand) LanguageObject(org.teiid.query.sql.LanguageObject) QueryPlannerException(org.teiid.api.exception.query.QueryPlannerException) SymbolMap(org.teiid.query.sql.util.SymbolMap)

Aggregations

PlanNode (org.teiid.query.optimizer.relational.plantree.PlanNode)204 Expression (org.teiid.query.sql.symbol.Expression)50 GroupSymbol (org.teiid.query.sql.symbol.GroupSymbol)50 ArrayList (java.util.ArrayList)47 List (java.util.List)43 SymbolMap (org.teiid.query.sql.util.SymbolMap)42 ElementSymbol (org.teiid.query.sql.symbol.ElementSymbol)36 Criteria (org.teiid.query.sql.lang.Criteria)35 LinkedList (java.util.LinkedList)24 CompareCriteria (org.teiid.query.sql.lang.CompareCriteria)24 Test (org.junit.Test)22 HashSet (java.util.HashSet)17 JoinType (org.teiid.query.sql.lang.JoinType)17 LinkedHashSet (java.util.LinkedHashSet)16 CompoundCriteria (org.teiid.query.sql.lang.CompoundCriteria)12 DependentSetCriteria (org.teiid.query.sql.lang.DependentSetCriteria)12 QueryPlannerException (org.teiid.api.exception.query.QueryPlannerException)11 LanguageObject (org.teiid.query.sql.LanguageObject)11 OrderBy (org.teiid.query.sql.lang.OrderBy)10 IsNullCriteria (org.teiid.query.sql.lang.IsNullCriteria)9