Search in sources :

Example 1 with QueryPlannerException

use of org.teiid.api.exception.query.QueryPlannerException in project teiid by teiid.

the class TestOptimizer method getPlan.

public static ProcessorPlan getPlan(Command command, QueryMetadataInterface md, CapabilitiesFinder capFinder, AnalysisRecord analysisRecord, boolean shouldSucceed, CommandContext cc) {
    ProcessorPlan plan = null;
    if (analysisRecord == null) {
        analysisRecord = new AnalysisRecord(false, DEBUG);
    }
    Exception exception = null;
    try {
        // do planning
        plan = QueryOptimizer.optimizePlan(command, md, null, capFinder, analysisRecord, cc);
    } catch (QueryPlannerException e) {
        exception = e;
    } catch (TeiidComponentException e) {
        exception = e;
    } catch (Throwable e) {
        throw new TeiidRuntimeException(e);
    } finally {
        if (DEBUG) {
            System.out.println(analysisRecord.getDebugLog());
        }
    }
    if (!shouldSucceed) {
        // $NON-NLS-1$
        assertNotNull("Expected exception but did not get one.", exception);
        return null;
    }
    if (plan == null) {
        throw new TeiidRuntimeException(exception);
    }
    // $NON-NLS-1$
    assertNotNull("Output elements are null", plan.getOutputElements());
    // $NON-NLS-1$
    if (DEBUG)
        System.out.println("\n" + plan);
    return plan;
}
Also used : AnalysisRecord(org.teiid.query.analysis.AnalysisRecord) TeiidComponentException(org.teiid.core.TeiidComponentException) TeiidRuntimeException(org.teiid.core.TeiidRuntimeException) ProcessorPlan(org.teiid.query.processor.ProcessorPlan) QueryPlannerException(org.teiid.api.exception.query.QueryPlannerException) TeiidComponentException(org.teiid.core.TeiidComponentException) TeiidProcessingException(org.teiid.core.TeiidProcessingException) TeiidException(org.teiid.core.TeiidException) TeiidRuntimeException(org.teiid.core.TeiidRuntimeException) QueryPlannerException(org.teiid.api.exception.query.QueryPlannerException)

Example 2 with QueryPlannerException

use of org.teiid.api.exception.query.QueryPlannerException in project teiid by teiid.

the class TestBatchedUpdatePlanner method helpPlanCommand.

private BatchedUpdatePlan helpPlanCommand(Command command, QueryMetadataInterface md, CapabilitiesFinder capFinder, boolean shouldSucceed) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
    // plan
    ProcessorPlan plan = null;
    AnalysisRecord analysisRecord = new AnalysisRecord(false, DEBUG);
    if (shouldSucceed) {
        try {
            // do planning
            plan = QueryOptimizer.optimizePlan(command, md, null, capFinder, analysisRecord, null);
        } finally {
            if (DEBUG) {
                System.out.println(analysisRecord.getDebugLog());
            }
        }
        return (BatchedUpdatePlan) plan;
    }
    Exception exception = null;
    try {
        // do planning
        QueryOptimizer.optimizePlan(command, md, null, capFinder, analysisRecord, null);
    } catch (QueryPlannerException e) {
        exception = e;
    } catch (TeiidComponentException e) {
        exception = e;
    } finally {
        if (DEBUG) {
            System.out.println(analysisRecord.getDebugLog());
        }
    }
    // $NON-NLS-1$
    assertNotNull("Expected exception but did not get one.", exception);
    return null;
}
Also used : AnalysisRecord(org.teiid.query.analysis.AnalysisRecord) TeiidComponentException(org.teiid.core.TeiidComponentException) ProcessorPlan(org.teiid.query.processor.ProcessorPlan) QueryPlannerException(org.teiid.api.exception.query.QueryPlannerException) TeiidComponentException(org.teiid.core.TeiidComponentException) QueryMetadataException(org.teiid.api.exception.query.QueryMetadataException) TeiidProcessingException(org.teiid.core.TeiidProcessingException) QueryPlannerException(org.teiid.api.exception.query.QueryPlannerException) BatchedUpdatePlan(org.teiid.query.processor.BatchedUpdatePlan)

Example 3 with QueryPlannerException

use of org.teiid.api.exception.query.QueryPlannerException in project teiid by teiid.

the class RuleMergeCriteria method planMergeJoin.

/**
 * Look for:
 * [NOT] EXISTS ( )
 * IN ( ) / SOME ( )
 *
 * and replace with a semi join
 */
private PlanNode planMergeJoin(PlanNode current, PlanNode root) throws QueryMetadataException, TeiidComponentException {
    float sourceCost = NewCalculateCostUtil.computeCostForTree(current.getFirstChild(), metadata);
    Criteria crit = (Criteria) current.getProperty(NodeConstants.Info.SELECT_CRITERIA);
    PlannedResult plannedResult = findSubquery(crit, true);
    if (plannedResult.query == null) {
        return current;
    }
    if (sourceCost != NewCalculateCostUtil.UNKNOWN_VALUE && sourceCost < RuleChooseDependent.DEFAULT_INDEPENDENT_CARDINALITY && !plannedResult.mergeJoin) {
        // TODO: see if a dependent join applies the other direction
        return current;
    }
    RelationalPlan originalPlan = (RelationalPlan) plannedResult.query.getProcessorPlan();
    Number originalCardinality = originalPlan.getRootNode().getEstimateNodeCardinality();
    if (!plannedResult.mergeJoin && originalCardinality.floatValue() == NewCalculateCostUtil.UNKNOWN_VALUE) {
        // if it's currently unknown, removing criteria won't make it any better
        return current;
    }
    Collection<GroupSymbol> leftGroups = FrameUtil.findJoinSourceNode(current).getGroups();
    if (!planQuery(leftGroups, false, plannedResult)) {
        if (plannedResult.mergeJoin && analysisRecord != null && analysisRecord.recordAnnotations()) {
            // $NON-NLS-1$ //$NON-NLS-2$
            this.analysisRecord.addAnnotation(new Annotation(Annotation.HINTS, "Could not plan as a merge join: " + crit, "ignoring MJ hint", Priority.HIGH));
        }
        return current;
    }
    // check if the child is already ordered.  TODO: see if the ordering is compatible.
    PlanNode childSort = NodeEditor.findNodePreOrder(root, NodeConstants.Types.SORT, NodeConstants.Types.SOURCE | NodeConstants.Types.JOIN);
    if (childSort != null) {
        if (plannedResult.mergeJoin && analysisRecord != null && analysisRecord.recordAnnotations()) {
            // $NON-NLS-1$ //$NON-NLS-2$
            this.analysisRecord.addAnnotation(new Annotation(Annotation.HINTS, "Could not plan as a merge join since the parent join requires a sort: " + crit, "ignoring MJ hint", Priority.HIGH));
        }
        return current;
    }
    // add an order by, which hopefully will get pushed down
    plannedResult.query.setOrderBy(new OrderBy(plannedResult.rightExpressions).clone());
    for (OrderByItem item : plannedResult.query.getOrderBy().getOrderByItems()) {
        int index = plannedResult.query.getProjectedSymbols().indexOf(item.getSymbol());
        if (index >= 0 && !(item.getSymbol() instanceof ElementSymbol)) {
            item.setSymbol((Expression) plannedResult.query.getProjectedSymbols().get(index).clone());
        }
        item.setExpressionPosition(index);
    }
    try {
        // clone the symbols as they may change during planning
        List<Expression> projectedSymbols = LanguageObject.Util.deepClone(plannedResult.query.getProjectedSymbols(), Expression.class);
        // NOTE: we could tap into the relationalplanner at a lower level to get this in a plan node form,
        // the major benefit would be to reuse the dependent join planning logic if possible.
        RelationalPlan subPlan = (RelationalPlan) QueryOptimizer.optimizePlan(plannedResult.query, metadata, idGenerator, capFinder, analysisRecord, context);
        Number planCardinality = subPlan.getRootNode().getEstimateNodeCardinality();
        if (!plannedResult.mergeJoin) {
            // if we don't have a specific hint, then use costing
            if (planCardinality.floatValue() == NewCalculateCostUtil.UNKNOWN_VALUE || planCardinality.floatValue() > 10000000 || (sourceCost == NewCalculateCostUtil.UNKNOWN_VALUE && planCardinality.floatValue() > 1000) || (sourceCost != NewCalculateCostUtil.UNKNOWN_VALUE && sourceCost * originalCardinality.floatValue() < planCardinality.floatValue() / (100 * Math.log(Math.max(4, sourceCost))))) {
                // bail-out if both are unknown or the new plan is too large
                if (analysisRecord != null && analysisRecord.recordDebug()) {
                    // $NON-NLS-1$ //$NON-NLS-2$
                    current.recordDebugAnnotation("cost of merge join plan was not favorable", null, "semi merge join will not be used", analysisRecord, metadata);
                }
                return current;
            }
        }
        // assume dependent
        if ((sourceCost != NewCalculateCostUtil.UNKNOWN_VALUE && planCardinality.floatValue() != NewCalculateCostUtil.UNKNOWN_VALUE && planCardinality.floatValue() < sourceCost / 8) || (sourceCost == NewCalculateCostUtil.UNKNOWN_VALUE && planCardinality.floatValue() <= 1000)) {
            plannedResult.makeInd = true;
        }
        /*if (plannedResult.makeInd 
					&& plannedResult.query.getCorrelatedReferences() == null
					&& !plannedResult.not
					&& plannedResult.leftExpressions.size() == 1) {
            	//TODO: this should just be a dependent criteria node to avoid sorts
            }*/
        // $NON-NLS-1$ //$NON-NLS-2$
        current.recordDebugAnnotation("Conditions met (hint or cost)", null, "Converting to a semi merge join", analysisRecord, metadata);
        PlanNode semiJoin = NodeFactory.getNewNode(NodeConstants.Types.JOIN);
        semiJoin.addGroups(current.getGroups());
        Set<GroupSymbol> groups = GroupsUsedByElementsVisitor.getGroups(plannedResult.rightExpressions);
        semiJoin.addGroups(groups);
        semiJoin.setProperty(NodeConstants.Info.JOIN_STRATEGY, JoinStrategyType.MERGE);
        semiJoin.setProperty(NodeConstants.Info.JOIN_TYPE, plannedResult.not ? JoinType.JOIN_ANTI_SEMI : JoinType.JOIN_SEMI);
        semiJoin.setProperty(NodeConstants.Info.NON_EQUI_JOIN_CRITERIA, plannedResult.nonEquiJoinCriteria);
        List<Criteria> joinCriteria = new ArrayList<Criteria>();
        joinCriteria.addAll(plannedResult.nonEquiJoinCriteria);
        for (int i = 0; i < plannedResult.leftExpressions.size(); i++) {
            joinCriteria.add(new CompareCriteria((Expression) plannedResult.rightExpressions.get(i), CompareCriteria.EQ, (Expression) plannedResult.leftExpressions.get(i)));
        }
        semiJoin.setProperty(NodeConstants.Info.JOIN_CRITERIA, joinCriteria);
        // nested subqueries are possibly being promoted, so they need their references updated
        List<SymbolMap> refMaps = semiJoin.getAllReferences();
        SymbolMap parentRefs = plannedResult.query.getCorrelatedReferences();
        for (SymbolMap refs : refMaps) {
            for (Map.Entry<ElementSymbol, Expression> ref : refs.asUpdatableMap().entrySet()) {
                Expression expr = ref.getValue();
                if (expr instanceof ElementSymbol) {
                    Expression convertedExpr = parentRefs.getMappedExpression((ElementSymbol) expr);
                    if (convertedExpr != null) {
                        ref.setValue(convertedExpr);
                    }
                }
                semiJoin.getGroups().addAll(GroupsUsedByElementsVisitor.getGroups(ref.getValue()));
            }
        }
        semiJoin.setProperty(NodeConstants.Info.LEFT_EXPRESSIONS, plannedResult.leftExpressions);
        semiJoin.getGroups().addAll(GroupsUsedByElementsVisitor.getGroups(plannedResult.leftExpressions));
        semiJoin.setProperty(NodeConstants.Info.RIGHT_EXPRESSIONS, plannedResult.rightExpressions);
        semiJoin.getGroups().addAll(GroupsUsedByElementsVisitor.getGroups(plannedResult.rightExpressions));
        semiJoin.setProperty(NodeConstants.Info.SORT_RIGHT, SortOption.ALREADY_SORTED);
        semiJoin.setProperty(NodeConstants.Info.OUTPUT_COLS, root.getProperty(NodeConstants.Info.OUTPUT_COLS));
        List childOutput = (List) current.getFirstChild().getProperty(NodeConstants.Info.OUTPUT_COLS);
        PlanNode toCorrect = root;
        while (toCorrect != current) {
            toCorrect.setProperty(NodeConstants.Info.OUTPUT_COLS, childOutput);
            toCorrect = toCorrect.getFirstChild();
        }
        PlanNode node = NodeFactory.getNewNode(NodeConstants.Types.ACCESS);
        node.setProperty(NodeConstants.Info.PROCESSOR_PLAN, subPlan);
        node.setProperty(NodeConstants.Info.OUTPUT_COLS, projectedSymbols);
        node.setProperty(NodeConstants.Info.EST_CARDINALITY, planCardinality);
        node.addGroups(groups);
        root.addAsParent(semiJoin);
        semiJoin.addLastChild(node);
        PlanNode result = current.getParent();
        NodeEditor.removeChildNode(result, current);
        RuleImplementJoinStrategy.insertSort(semiJoin.getFirstChild(), (List<Expression>) plannedResult.leftExpressions, semiJoin, metadata, capFinder, true, context);
        if (plannedResult.makeInd && !plannedResult.not) {
            // TODO: would like for an enhanced sort merge with the semi dep option to avoid the sorting
            // this is a little different than a typical dependent join in that the right is the independent side
            String id = RuleChooseDependent.nextId();
            PlanNode dep = RuleChooseDependent.getDependentCriteriaNode(id, plannedResult.rightExpressions, plannedResult.leftExpressions, node, metadata, null, false, null);
            semiJoin.getFirstChild().addAsParent(dep);
            semiJoin.setProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE, id);
            this.dependent = true;
        }
        return result;
    } catch (QueryPlannerException e) {
        // can't be done - probably access patterns - what about dependent
        return current;
    }
}
Also used : ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) ArrayList(java.util.ArrayList) RelationalPlan(org.teiid.query.processor.relational.RelationalPlan) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) List(java.util.List) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) SymbolMap(org.teiid.query.sql.util.SymbolMap) Annotation(org.teiid.client.plan.Annotation) Expression(org.teiid.query.sql.symbol.Expression) GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol) Map(java.util.Map) SymbolMap(org.teiid.query.sql.util.SymbolMap) QueryPlannerException(org.teiid.api.exception.query.QueryPlannerException)

Example 4 with QueryPlannerException

use of org.teiid.api.exception.query.QueryPlannerException in project teiid by teiid.

the class RulePlanProcedures method execute.

/**
 * @see org.teiid.query.optimizer.relational.OptimizerRule#execute(org.teiid.query.optimizer.relational.plantree.PlanNode, org.teiid.query.metadata.QueryMetadataInterface, org.teiid.query.optimizer.capabilities.CapabilitiesFinder, org.teiid.query.optimizer.relational.RuleStack, org.teiid.query.analysis.AnalysisRecord, org.teiid.query.util.CommandContext)
 */
public PlanNode execute(PlanNode plan, final QueryMetadataInterface metadata, CapabilitiesFinder capabilitiesFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
    for (PlanNode node : NodeEditor.findAllNodes(plan, NodeConstants.Types.SOURCE, NodeConstants.Types.ACCESS)) {
        if (!FrameUtil.isProcedure(node.getFirstChild())) {
            continue;
        }
        StoredProcedure proc = (StoredProcedure) node.getProperty(NodeConstants.Info.NESTED_COMMAND);
        if (!proc.isProcedureRelational()) {
            continue;
        }
        HashSet<ElementSymbol> inputSymbols = new HashSet<ElementSymbol>();
        List<Reference> inputReferences = new LinkedList<Reference>();
        PlanNode critNode = node.getParent();
        List<Criteria> conjuncts = new LinkedList<Criteria>();
        HashSet<ElementSymbol> coveredParams = new HashSet<ElementSymbol>();
        for (Iterator<SPParameter> params = proc.getInputParameters().iterator(); params.hasNext(); ) {
            SPParameter param = params.next();
            ElementSymbol symbol = param.getParameterSymbol();
            Expression input = param.getExpression();
            inputReferences.add((Reference) input);
            inputSymbols.add(symbol);
        }
        findInputNodes(inputSymbols, critNode, conjuncts, coveredParams);
        List<Expression> defaults = new LinkedList<Expression>();
        for (Reference ref : inputReferences) {
            ElementSymbol symbol = ref.getExpression();
            Expression defaultValue = null;
            /*try {
                    defaultValue = ResolverUtil.getDefault(symbol, metadata);
                } catch (QueryResolverException qre) {
                    //Just ignore
                }*/
            defaults.add(defaultValue);
            if (defaultValue == null && !coveredParams.contains(symbol)) {
                throw new QueryPlannerException(QueryPlugin.Event.TEIID30270, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30270, symbol));
            }
        }
        /*if (conjuncts.isEmpty()) {
                for (int j = 0; j < inputReferences.size(); j++) {
                    Reference ref = (Reference)inputReferences.get(j);
                    ref.setValue(defaults.get(j));
                }
                continue;
            }*/
        PlanNode accessNode = NodeEditor.findNodePreOrder(node, NodeConstants.Types.ACCESS);
        Criteria crit = Criteria.combineCriteria(conjuncts);
        if (crit != null) {
            accessNode.setProperty(NodeConstants.Info.PROCEDURE_CRITERIA, crit);
            accessNode.setProperty(NodeConstants.Info.PROCEDURE_INPUTS, inputReferences);
            accessNode.setProperty(NodeConstants.Info.PROCEDURE_DEFAULTS, defaults);
            accessNode.setProperty(NodeConstants.Info.IS_DEPENDENT_SET, Boolean.TRUE);
        }
    }
    return plan;
}
Also used : ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) Reference(org.teiid.query.sql.symbol.Reference) SPParameter(org.teiid.query.sql.lang.SPParameter) DependentSetCriteria(org.teiid.query.sql.lang.DependentSetCriteria) IsNullCriteria(org.teiid.query.sql.lang.IsNullCriteria) Criteria(org.teiid.query.sql.lang.Criteria) CompareCriteria(org.teiid.query.sql.lang.CompareCriteria) SetCriteria(org.teiid.query.sql.lang.SetCriteria) LinkedList(java.util.LinkedList) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) StoredProcedure(org.teiid.query.sql.lang.StoredProcedure) Expression(org.teiid.query.sql.symbol.Expression) QueryPlannerException(org.teiid.api.exception.query.QueryPlannerException) HashSet(java.util.HashSet)

Example 5 with QueryPlannerException

use of org.teiid.api.exception.query.QueryPlannerException in project teiid by teiid.

the class RuleAssignOutputElements method assignOutputElements.

/**
 * <p>Assign the output elements at a particular node and recurse the tree.  The
 * outputElements needed from above the node have been collected in
 * outputElements.</p>
 *
 * <p>SOURCE nodes:  If we find a SOURCE node, this must define the top
 * of a virtual group.  Physical groups can be identified by ACCESS nodes
 * at this point in the planning stage.  So, we filter the virtual elements
 * in the virtual source based on the required output elements.</p>
 *
 * <p>SET_OP nodes:  If we hit a SET_OP node, this must be a union.  Unions
 * require a lot of special care.  Unions have many branches and the projected
 * elements in each branch are "equivalent" in terms of nodes above the union.
 * This means that any filtering must occur in an identical way in all branches
 * of a union.</p>
 *
 * @param root Node to assign
 * @param outputElements Output elements needed for this node
 * @param metadata Metadata implementation
 */
private void assignOutputElements(PlanNode root, List<Expression> outputElements, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
    int nodeType = root.getType();
    // Update this node's output columns based on parent's columns
    List<Expression> oldOutput = (List<Expression>) root.setProperty(NodeConstants.Info.OUTPUT_COLS, outputElements);
    if (root.getChildCount() == 0) {
        // update temp access
        if (root.getType() == NodeConstants.Types.SOURCE && root.getGroups().size() == 1) {
            GroupSymbol gs = root.getGroups().iterator().next();
            if (gs.getMetadataID() instanceof TempMetadataID) {
                for (Expression ex : outputElements) {
                    if (ex instanceof ElementSymbol) {
                        Object id = ((ElementSymbol) ex).getMetadataID();
                        if (id instanceof TempMetadataID) {
                            ((TempMetadataID) id).setAccessed(true);
                        }
                    }
                }
            }
        }
        return;
    }
    switch(nodeType) {
        case NodeConstants.Types.ACCESS:
            Command command = FrameUtil.getNonQueryCommand(root);
            if (command instanceof StoredProcedure) {
                // if the access node represents a stored procedure, then we can't actually change the output symbols
                root.setProperty(NodeConstants.Info.OUTPUT_COLS, command.getProjectedSymbols());
            } else {
                ProcessorPlan plan = FrameUtil.getNestedPlan(root);
                if (plan != null && (command == null || !RelationalNodeUtil.isUpdate(command))) {
                    // nested with clauses are handled as sub plans, which have a fixed set of output symbols
                    root.setProperty(NodeConstants.Info.OUTPUT_COLS, ResolverUtil.resolveElementsInGroup(root.getGroups().iterator().next(), metadata));
                }
                if (checkSymbols) {
                    Object modelId = RuleRaiseAccess.getModelIDFromAccess(root, metadata);
                    for (Expression symbol : outputElements) {
                        if (!RuleRaiseAccess.canPushSymbol(symbol, true, modelId, metadata, capFinder, analysisRecord)) {
                            throw new QueryPlannerException(QueryPlugin.Event.TEIID30258, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30258, symbol, modelId));
                        }
                    }
                }
                if (NodeEditor.findParent(root, NodeConstants.Types.PROJECT, NodeConstants.Types.SOURCE) != null) {
                    // there's a chance that partial projection was used.  we are not a defacto project node
                    // take credit for creating anything that is not an element symbol
                    LinkedHashSet<Expression> filteredElements = new LinkedHashSet<Expression>();
                    for (Expression element : outputElements) {
                        if (element instanceof ElementSymbol) {
                            filteredElements.add(element);
                        } else {
                            filteredElements.addAll(ElementCollectorVisitor.getElements(element, false));
                        }
                    }
                    outputElements = new ArrayList<Expression>(filteredElements);
                }
            }
            assignOutputElements(root.getLastChild(), outputElements, metadata, capFinder, rules, analysisRecord, context);
            break;
        case NodeConstants.Types.DUP_REMOVE:
            // targeted optimization based upon swapping the dup remove for a limit 1
            // TODO: may need to also check for grouping over constants
            boolean allConstants = true;
            for (Expression ex : outputElements) {
                if (!(EvaluatableVisitor.willBecomeConstant(SymbolMap.getExpression(ex)))) {
                    allConstants = false;
                    break;
                }
            }
            if (allConstants && addLimit(rules, root, metadata, capFinder)) {
                // TODO we could more gracefully handle the !addLimit case
                PlanNode parent = root.getParent();
                if (parent != null) {
                    NodeEditor.removeChildNode(root.getParent(), root);
                    execute(parent, metadata, capFinder, rules, analysisRecord, context);
                    return;
                }
            }
        case NodeConstants.Types.SORT:
            // correct expression positions and update the unrelated flag
            OrderBy order = (OrderBy) root.getProperty(NodeConstants.Info.SORT_ORDER);
            if (order != null && (oldOutput == null || !oldOutput.equals(outputElements))) {
                outputElements = new ArrayList<Expression>(outputElements);
                boolean hasUnrelated = false;
                for (OrderByItem item : order.getOrderByItems()) {
                    int index = outputElements.indexOf(item.getSymbol());
                    if (index != -1) {
                        item.setExpressionPosition(index);
                    } else {
                        hasUnrelated = true;
                        outputElements.add(item.getSymbol());
                    }
                }
                if (!hasUnrelated) {
                    root.setProperty(NodeConstants.Info.UNRELATED_SORT, false);
                } else {
                    root.setProperty(NodeConstants.Info.UNRELATED_SORT, true);
                }
            }
            assignOutputElements(root.getLastChild(), outputElements, metadata, capFinder, rules, analysisRecord, context);
            break;
        case NodeConstants.Types.TUPLE_LIMIT:
            assignOutputElements(root.getLastChild(), outputElements, metadata, capFinder, rules, analysisRecord, context);
            break;
        case NodeConstants.Types.SOURCE:
            {
                outputElements = (List<Expression>) determineSourceOutput(root, outputElements, metadata, capFinder);
                if (!finalRun && root.getProperty(Info.PARTITION_INFO) != null && NodeEditor.findParent(root, NodeConstants.Types.JOIN) != null) {
                    GroupSymbol group = root.getGroups().iterator().next();
                    Object modelId = RuleDecomposeJoin.getEffectiveModelId(metadata, group);
                    String name = metadata.getExtensionProperty(modelId, RuleDecomposeJoin.IMPLICIT_PARTITION_COLUMN_NAME, true);
                    if (name != null) {
                        // keep projecting the implicit partitioning column through the source so that it can
                        // be used in the decomposition logic
                        ElementSymbol es = new ElementSymbol(name, group);
                        if (!outputElements.contains(es)) {
                            es.setMetadataID(metadata.getElementID(es.getName()));
                            outputElements.add(es);
                        }
                    }
                }
                root.setProperty(NodeConstants.Info.OUTPUT_COLS, outputElements);
                List<Expression> childElements = filterVirtualElements(root, outputElements, metadata);
                assignOutputElements(root.getFirstChild(), childElements, metadata, capFinder, rules, analysisRecord, context);
                break;
            }
        case NodeConstants.Types.SET_OP:
            {
                for (PlanNode childNode : root.getChildren()) {
                    PlanNode projectNode = NodeEditor.findNodePreOrder(childNode, NodeConstants.Types.PROJECT);
                    List<Expression> projectCols = (List<Expression>) projectNode.getProperty(NodeConstants.Info.PROJECT_COLS);
                    assignOutputElements(childNode, projectCols, metadata, capFinder, rules, analysisRecord, context);
                }
                break;
            }
        default:
            {
                PlanNode sortNode = null;
                if (root.getType() == NodeConstants.Types.PROJECT) {
                    GroupSymbol intoGroup = (GroupSymbol) root.getProperty(NodeConstants.Info.INTO_GROUP);
                    if (intoGroup != null) {
                        // if this is a project into, treat the nodes under the source as a new plan root
                        PlanNode intoRoot = NodeEditor.findNodePreOrder(root, NodeConstants.Types.SOURCE);
                        execute(intoRoot.getFirstChild(), metadata, capFinder, rules, analysisRecord, context);
                        return;
                    }
                    List<Expression> projectCols = outputElements;
                    sortNode = NodeEditor.findParent(root, NodeConstants.Types.SORT, NodeConstants.Types.SOURCE);
                    if (finalRun && sortNode != null && sortNode.hasBooleanProperty(NodeConstants.Info.UNRELATED_SORT)) {
                        root.getGroups().clear();
                        root.addGroups(GroupsUsedByElementsVisitor.getGroups(projectCols));
                        root.addGroups(GroupsUsedByElementsVisitor.getGroups(root.getCorrelatedReferenceElements()));
                    }
                    root.setProperty(NodeConstants.Info.PROJECT_COLS, projectCols);
                    if (root.hasBooleanProperty(Info.HAS_WINDOW_FUNCTIONS)) {
                        Set<WindowFunction> windowFunctions = getWindowFunctions(projectCols);
                        if (windowFunctions.isEmpty()) {
                            root.setProperty(Info.HAS_WINDOW_FUNCTIONS, false);
                        }
                    }
                }
                List<Expression> requiredInput = collectRequiredInputSymbols(root, metadata, capFinder);
                // targeted optimization for unnecessary aggregation
                if (root.getType() == NodeConstants.Types.GROUP && root.hasBooleanProperty(Info.IS_OPTIONAL) && NodeEditor.findParent(root, NodeConstants.Types.ACCESS) == null) {
                    PlanNode parent = removeGroupBy(root, metadata);
                    if (!root.hasCollectionProperty(Info.GROUP_COLS)) {
                        // just lob off everything under the projection
                        PlanNode project = NodeEditor.findNodePreOrder(parent, NodeConstants.Types.PROJECT);
                        project.removeAllChildren();
                    } else if (!addLimit(rules, parent, metadata, capFinder)) {
                        // $NON-NLS-1$
                        throw new AssertionError("expected limit node to be added");
                    }
                    execute(parent, metadata, capFinder, rules, analysisRecord, context);
                    return;
                }
                // Call children recursively
                if (root.getChildCount() == 1) {
                    assignOutputElements(root.getLastChild(), requiredInput, metadata, capFinder, rules, analysisRecord, context);
                    if (!finalRun && root.getType() == NodeConstants.Types.PROJECT && sortNode != null && sortNode.hasBooleanProperty(NodeConstants.Info.UNRELATED_SORT)) {
                        // if this is the initial rule run, remove unrelated order to preserve the original projection
                        OrderBy elements = (OrderBy) sortNode.getProperty(NodeConstants.Info.SORT_ORDER);
                        outputElements = new ArrayList<Expression>(outputElements);
                        for (OrderByItem item : elements.getOrderByItems()) {
                            if (item.getExpressionPosition() == -1) {
                                outputElements.remove(item.getSymbol());
                            }
                        }
                        root.setProperty(NodeConstants.Info.PROJECT_COLS, outputElements);
                    }
                } else {
                    // determine which elements go to each side of the join
                    for (PlanNode childNode : root.getChildren()) {
                        Set<GroupSymbol> filterGroups = FrameUtil.findJoinSourceNode(childNode).getGroups();
                        List<Expression> filteredElements = filterElements(requiredInput, filterGroups);
                        // Call child recursively
                        assignOutputElements(childNode, filteredElements, metadata, capFinder, rules, analysisRecord, context);
                    }
                }
            }
    }
}
Also used : ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) LinkedHashSet(java.util.LinkedHashSet) OrderBy(org.teiid.query.sql.lang.OrderBy) Set(java.util.Set) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) TempMetadataID(org.teiid.query.metadata.TempMetadataID) ArrayList(java.util.ArrayList) StoredProcedure(org.teiid.query.sql.lang.StoredProcedure) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) OrderByItem(org.teiid.query.sql.lang.OrderByItem) Expression(org.teiid.query.sql.symbol.Expression) Command(org.teiid.query.sql.lang.Command) GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol) List(java.util.List) ArrayList(java.util.ArrayList) ProcessorPlan(org.teiid.query.processor.ProcessorPlan) QueryPlannerException(org.teiid.api.exception.query.QueryPlannerException)

Aggregations

QueryPlannerException (org.teiid.api.exception.query.QueryPlannerException)27 TeiidProcessingException (org.teiid.core.TeiidProcessingException)11 PlanNode (org.teiid.query.optimizer.relational.plantree.PlanNode)11 ElementSymbol (org.teiid.query.sql.symbol.ElementSymbol)11 ProcessorPlan (org.teiid.query.processor.ProcessorPlan)8 Expression (org.teiid.query.sql.symbol.Expression)8 GroupSymbol (org.teiid.query.sql.symbol.GroupSymbol)8 SymbolMap (org.teiid.query.sql.util.SymbolMap)8 TeiidRuntimeException (org.teiid.core.TeiidRuntimeException)6 QueryMetadataException (org.teiid.api.exception.query.QueryMetadataException)5 TempMetadataID (org.teiid.query.metadata.TempMetadataID)5 CreateProcedureCommand (org.teiid.query.sql.proc.CreateProcedureCommand)5 ArrayList (java.util.ArrayList)4 TeiidComponentException (org.teiid.core.TeiidComponentException)4 Criteria (org.teiid.query.sql.lang.Criteria)4 HashSet (java.util.HashSet)3 List (java.util.List)3 AnalysisRecord (org.teiid.query.analysis.AnalysisRecord)3 RelationalPlan (org.teiid.query.processor.relational.RelationalPlan)3 HashMap (java.util.HashMap)2