Search in sources :

Example 41 with SymbolMap

use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.

the class RuleRaiseAccess method raiseAccessNode.

/**
 * @return null if nothing changed, and a new plan root if something changed
 */
static PlanNode raiseAccessNode(PlanNode rootNode, PlanNode accessNode, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, boolean afterJoinPlanning, AnalysisRecord record, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
    PlanNode parentNode = accessNode.getParent();
    if (parentNode == null) {
        // Nothing to raise over
        return null;
    }
    Object modelID = getModelIDFromAccess(accessNode, metadata);
    if (modelID == null) {
        return null;
    }
    switch(parentNode.getType()) {
        case NodeConstants.Types.JOIN:
            {
                modelID = canRaiseOverJoin(modelID, parentNode, metadata, capFinder, afterJoinPlanning, record, context);
                if (modelID != null && checkConformedSubqueries(accessNode, parentNode, true)) {
                    raiseAccessOverJoin(parentNode, accessNode, modelID, capFinder, metadata, true);
                    return rootNode;
                }
                return null;
            }
        case NodeConstants.Types.PROJECT:
            {
                if (CapabilitiesUtil.supports(Capability.NO_PROJECTION, modelID, metadata, capFinder)) {
                    return null;
                }
                // Check that the PROJECT contains only functions that can be pushed
                List<Expression> projectCols = (List) parentNode.getProperty(NodeConstants.Info.PROJECT_COLS);
                for (int i = 0; i < projectCols.size(); i++) {
                    Expression symbol = projectCols.get(i);
                    if (!canPushSymbol(symbol, true, modelID, metadata, capFinder, record)) {
                        return null;
                    }
                }
                /*
                 * TODO: this creates an extraneous project node in many circumstances.
                 * However we don't actually support project in this case, so allowing it to be pushed
                 * causes problems with stored procedures and the assumptions made for proc/relational
                 * planning. 
                 */
                if (FrameUtil.isProcedure(parentNode)) {
                    return null;
                }
                PlanNode orderBy = NodeEditor.findParent(parentNode, NodeConstants.Types.SORT, NodeConstants.Types.SOURCE);
                if (orderBy != null && orderBy.hasBooleanProperty(Info.UNRELATED_SORT) && !canRaiseOverSort(accessNode, metadata, capFinder, orderBy, record, false, context)) {
                    // this project node logically has the responsibility of creating the sort keys
                    return null;
                }
                if (accessNode.hasBooleanProperty(Info.IS_MULTI_SOURCE)) {
                    List<WindowFunction> windowFunctions = new ArrayList<WindowFunction>(2);
                    for (Expression ex : projectCols) {
                        AggregateSymbolCollectorVisitor.getAggregates(ex, null, null, null, windowFunctions, null);
                        if (!windowFunctions.isEmpty()) {
                            return null;
                        }
                    }
                }
                return performRaise(rootNode, accessNode, parentNode);
            }
        case NodeConstants.Types.DUP_REMOVE:
            {
                // If model supports the support constant parameter, then move access node
                if (!CapabilitiesUtil.supportsSelectDistinct(modelID, metadata, capFinder)) {
                    // $NON-NLS-1$ //$NON-NLS-2$
                    parentNode.recordDebugAnnotation("distinct is not supported by source", modelID, "cannot push dupremove", record, metadata);
                    return null;
                }
                if (!supportsDistinct(metadata, parentNode, accessNode.hasBooleanProperty(Info.IS_MULTI_SOURCE))) {
                    // $NON-NLS-1$ //$NON-NLS-2$
                    parentNode.recordDebugAnnotation("not all columns are comparable at the source", modelID, "cannot push dupremove", record, metadata);
                    return null;
                }
                return performRaise(rootNode, accessNode, parentNode);
            }
        case NodeConstants.Types.SORT:
            {
                if (canRaiseOverSort(accessNode, metadata, capFinder, parentNode, record, false, context)) {
                    return performRaise(rootNode, accessNode, parentNode);
                }
                return null;
            }
        case NodeConstants.Types.GROUP:
            {
                Set<AggregateSymbol> aggregates = RulePushAggregates.collectAggregates(parentNode);
                if (canRaiseOverGroupBy(parentNode, accessNode, aggregates, metadata, capFinder, record, true)) {
                    accessNode.getGroups().clear();
                    accessNode.getGroups().addAll(parentNode.getGroups());
                    return performRaise(rootNode, accessNode, parentNode);
                }
                return null;
            }
        case NodeConstants.Types.SET_OP:
            if (!canRaiseOverSetQuery(parentNode, metadata, capFinder)) {
                return null;
            }
            String sourceName = null;
            boolean multiSource = false;
            for (PlanNode node : new ArrayList<PlanNode>(parentNode.getChildren())) {
                multiSource |= accessNode.hasBooleanProperty(Info.IS_MULTI_SOURCE);
                if (sourceName == null) {
                    sourceName = (String) accessNode.getProperty(Info.SOURCE_NAME);
                }
                if (node == accessNode) {
                    continue;
                }
                combineSourceHints(accessNode, node);
                combineConformedSources(accessNode, node);
                NodeEditor.removeChildNode(parentNode, node);
            }
            accessNode.getGroups().clear();
            if (multiSource) {
                accessNode.setProperty(Info.IS_MULTI_SOURCE, true);
            } else if (sourceName != null) {
                accessNode.setProperty(Info.SOURCE_NAME, sourceName);
            }
            return performRaise(rootNode, accessNode, parentNode);
        case NodeConstants.Types.SELECT:
            {
                if (parentNode.hasBooleanProperty(NodeConstants.Info.IS_DEPENDENT_SET)) {
                    return null;
                }
                if (canRaiseOverSelect(accessNode, metadata, capFinder, parentNode, record)) {
                    RulePushSelectCriteria.satisfyConditions(parentNode, accessNode, metadata);
                    return performRaise(rootNode, accessNode, parentNode);
                }
                // determine if we should push the select back up
                if (parentNode.getParent() == null) {
                    return null;
                }
                PlanNode selectRoot = parentNode;
                while (selectRoot.getParent() != null && selectRoot.getParent().getType() == NodeConstants.Types.SELECT) {
                    selectRoot = selectRoot.getParent();
                }
                if (selectRoot.getParent() == null || (selectRoot.getParent().getType() & (NodeConstants.Types.PROJECT | NodeConstants.Types.GROUP)) == selectRoot.getParent().getType()) {
                    return null;
                }
                PlanNode grandParent = selectRoot.getParent();
                boolean isLeft = false;
                isLeft = grandParent.getFirstChild() == selectRoot;
                if (grandParent.getType() == NodeConstants.Types.JOIN) {
                    JoinType jt = (JoinType) grandParent.getProperty(NodeConstants.Info.JOIN_TYPE);
                    if (jt == JoinType.JOIN_FULL_OUTER || (jt == JoinType.JOIN_LEFT_OUTER && !isLeft)) {
                        return null;
                    }
                }
                grandParent.removeChild(selectRoot);
                if (isLeft) {
                    grandParent.addFirstChild(accessNode);
                } else {
                    grandParent.addLastChild(accessNode);
                }
                PlanNode newParent = grandParent.getParent();
                // TODO: use costing or heuristics instead of always raising
                PlanNode newRoot = raiseAccessNode(rootNode, accessNode, metadata, capFinder, afterJoinPlanning, record, context);
                if (newRoot == null) {
                    // return the tree to its original state
                    parentNode.addFirstChild(accessNode);
                    if (isLeft) {
                        grandParent.addFirstChild(selectRoot);
                    } else {
                        grandParent.addLastChild(selectRoot);
                    }
                } else {
                    // attach the select nodes above the access node
                    accessNode = grandParent.getParent();
                    if (newParent != null) {
                        isLeft = newParent.getFirstChild() == accessNode;
                        if (isLeft) {
                            newParent.addFirstChild(selectRoot);
                        } else {
                            newParent.addLastChild(selectRoot);
                        }
                    } else {
                        newRoot = selectRoot;
                    }
                    parentNode.addFirstChild(accessNode);
                    return newRoot;
                }
                return null;
            }
        case NodeConstants.Types.SOURCE:
            {
                // if a source has access patterns that are unsatisfied, then the raise cannot occur
                if (parentNode.hasCollectionProperty(NodeConstants.Info.ACCESS_PATTERNS)) {
                    return null;
                }
                SymbolMap references = (SymbolMap) parentNode.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
                if (references != null) {
                    return null;
                }
                // raise only if there is no intervening project into
                PlanNode parentProject = NodeEditor.findParent(parentNode, NodeConstants.Types.PROJECT);
                GroupSymbol intoGroup = (GroupSymbol) parentProject.getProperty(NodeConstants.Info.INTO_GROUP);
                if (intoGroup != null && parentProject.getParent() == null) {
                    if (!parentProject.hasProperty(Info.CONSTRAINT) && CapabilitiesUtil.supports(Capability.INSERT_WITH_QUERYEXPRESSION, modelID, metadata, capFinder) && CapabilitiesUtil.isSameConnector(modelID, metadata.getModelID(intoGroup.getMetadataID()), metadata, capFinder)) {
                        rootNode = performRaise(rootNode, accessNode, parentNode);
                        return performRaise(rootNode, accessNode, parentProject);
                    }
                    return null;
                } else if (!CapabilitiesUtil.supportsInlineView(modelID, metadata, capFinder)) {
                    return null;
                }
                // is there another query that will be used with this source
                if (FrameUtil.getNonQueryCommand(accessNode) != null || FrameUtil.getNestedPlan(accessNode) != null) {
                    return null;
                }
                // switch to inline view and change the group on the access to that of the source
                parentNode.setProperty(NodeConstants.Info.INLINE_VIEW, Boolean.TRUE);
                accessNode.getGroups().clear();
                accessNode.addGroups(parentNode.getGroups());
                RulePlaceAccess.copyProperties(parentNode, accessNode);
                return performRaise(rootNode, accessNode, parentNode);
            }
        case NodeConstants.Types.TUPLE_LIMIT:
            {
                return RulePushLimit.raiseAccessOverLimit(rootNode, accessNode, metadata, capFinder, parentNode, record);
            }
        default:
            {
                return null;
            }
    }
}
Also used : HashSet(java.util.HashSet) Set(java.util.Set) ArrayList(java.util.ArrayList) SymbolMap(org.teiid.query.sql.util.SymbolMap) WindowFunction(org.teiid.query.sql.symbol.WindowFunction) 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)

Example 42 with SymbolMap

use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.

the class RuleRaiseAccess method isMultiSourceColumn.

/**
 * Check to see if the element is a multi-source source_name column
 * TODO: inner side of an outer join projection
 * do this check as part of metadata validation
 */
private static boolean isMultiSourceColumn(QueryMetadataInterface metadata, Expression ex, PlanNode node) throws QueryMetadataException, TeiidComponentException {
    if (!(ex instanceof ElementSymbol)) {
        return false;
    }
    ElementSymbol es = (ElementSymbol) ex;
    if (metadata.isMultiSourceElement(es.getMetadataID())) {
        return true;
    }
    if (node == null || node.getFirstChild() == null) {
        return false;
    }
    node = FrameUtil.findOriginatingNode(node.getFirstChild(), Collections.singleton(es.getGroupSymbol()));
    if (node == null || node.getType() != NodeConstants.Types.SOURCE) {
        return false;
    }
    SymbolMap map = (SymbolMap) node.getProperty(Info.SYMBOL_MAP);
    if (node.getChildren().isEmpty() || map == null) {
        return false;
    }
    PlanNode set = NodeEditor.findNodePreOrder(node.getFirstChild(), NodeConstants.Types.SET_OP, NodeConstants.Types.SOURCE);
    if (set == null) {
        ex = map.getMappedExpression(es);
        return isMultiSourceColumn(metadata, ex, node.getFirstChild());
    }
    int index = map.getKeys().indexOf(ex);
    if (index == -1) {
        return false;
    }
    for (PlanNode child : set.getChildren()) {
        PlanNode project = NodeEditor.findNodePreOrder(child, NodeConstants.Types.PROJECT, NodeConstants.Types.SOURCE);
        if (project == null) {
            return false;
        }
        List<Expression> cols = (List<Expression>) project.getProperty(Info.PROJECT_COLS);
        if (!isMultiSourceColumn(metadata, cols.get(index), child)) {
            return false;
        }
    }
    return true;
}
Also used : ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) Expression(org.teiid.query.sql.symbol.Expression) SymbolMap(org.teiid.query.sql.util.SymbolMap) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) List(java.util.List)

Example 43 with SymbolMap

use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.

the class PlanNode method getCorrelatedReferenceElements.

public Set<ElementSymbol> getCorrelatedReferenceElements() {
    List<SymbolMap> maps = getCorrelatedReferences();
    if (maps.isEmpty()) {
        return Collections.emptySet();
    }
    HashSet<ElementSymbol> result = new HashSet<ElementSymbol>();
    for (SymbolMap symbolMap : maps) {
        List<Expression> values = symbolMap.getValues();
        for (Expression expr : values) {
            ElementCollectorVisitor.getElements(expr, result);
        }
    }
    return result;
}
Also used : ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) Expression(org.teiid.query.sql.symbol.Expression) SymbolMap(org.teiid.query.sql.util.SymbolMap)

Example 44 with SymbolMap

use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.

the class PlanNode method getExportedCorrelatedReferences.

public List<SymbolMap> getExportedCorrelatedReferences() {
    if (type != NodeConstants.Types.JOIN) {
        return Collections.emptyList();
    }
    LinkedList<SymbolMap> result = new LinkedList<SymbolMap>();
    for (PlanNode child : NodeEditor.findAllNodes(this, NodeConstants.Types.SOURCE, NodeConstants.Types.ACCESS)) {
        SymbolMap references = (SymbolMap) child.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
        if (references == null) {
            continue;
        }
        Set<GroupSymbol> correlationGroups = GroupsUsedByElementsVisitor.getGroups(references.getValues());
        PlanNode joinNode = NodeEditor.findParent(child, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE);
        while (joinNode != null) {
            if (joinNode.getGroups().containsAll(correlationGroups)) {
                if (joinNode == this) {
                    result.add(references);
                }
                break;
            }
            joinNode = NodeEditor.findParent(joinNode, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE);
        }
    }
    return result;
}
Also used : GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol) SymbolMap(org.teiid.query.sql.util.SymbolMap)

Example 45 with SymbolMap

use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.

the class CriteriaCapabilityValidatorVisitor method validateSubqueryPushdown.

/**
 * Return null if the subquery cannot be pushed down, otherwise the model
 * id of the pushdown target.
 * @param subqueryContainer
 * @param critNodeModelID
 * @param metadata
 * @param capFinder
 * @return
 * @throws TeiidComponentException
 */
public static Object validateSubqueryPushdown(SubqueryContainer<?> subqueryContainer, Object critNodeModelID, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, AnalysisRecord analysisRecord) throws TeiidComponentException {
    ProcessorPlan plan = subqueryContainer.getCommand().getProcessorPlan();
    if (plan != null) {
        AccessNode aNode = getAccessNode(plan);
        if (aNode == null) {
            return null;
        }
        critNodeModelID = validateCommandPushdown(critNodeModelID, metadata, capFinder, aNode, true);
    }
    if (critNodeModelID == null) {
        return null;
    }
    // Check whether source supports correlated subqueries and if not, whether criteria has them
    SymbolMap refs = subqueryContainer.getCommand().getCorrelatedReferences();
    try {
        if (refs != null && !refs.asMap().isEmpty()) {
            if (!CapabilitiesUtil.supports(Capability.QUERY_SUBQUERIES_CORRELATED, critNodeModelID, metadata, capFinder)) {
                return null;
            }
            if (!CapabilitiesUtil.supports(Capability.SUBQUERY_CORRELATED_LIMIT, critNodeModelID, metadata, capFinder)) {
                QueryCommand command = (QueryCommand) subqueryContainer.getCommand();
                if (command.getLimit() != null && !command.getLimit().isImplicit()) {
                    return null;
                }
            }
            // but this is only an issue with deeply nested subqueries
            if (!CriteriaCapabilityValidatorVisitor.canPushLanguageObject(subqueryContainer.getCommand(), critNodeModelID, metadata, capFinder, analysisRecord)) {
                return null;
            }
        } else if (CapabilitiesUtil.supports(Capability.QUERY_SUBQUERIES_ONLY_CORRELATED, critNodeModelID, metadata, capFinder)) {
            return null;
        }
    } catch (QueryMetadataException e) {
        throw new TeiidComponentException(QueryPlugin.Event.TEIID30271, e);
    }
    if (!CapabilitiesUtil.supports(Capability.SUBQUERY_COMMON_TABLE_EXPRESSIONS, critNodeModelID, metadata, capFinder) && subqueryContainer.getCommand() instanceof QueryCommand) {
        QueryCommand command = (QueryCommand) subqueryContainer.getCommand();
        if (command.getWith() != null) {
            return null;
        }
    }
    // Found no reason why this node is not eligible
    return critNodeModelID;
}
Also used : SymbolMap(org.teiid.query.sql.util.SymbolMap) TeiidComponentException(org.teiid.core.TeiidComponentException) AccessNode(org.teiid.query.processor.relational.AccessNode) ProcessorPlan(org.teiid.query.processor.ProcessorPlan) QueryMetadataException(org.teiid.api.exception.query.QueryMetadataException)

Aggregations

SymbolMap (org.teiid.query.sql.util.SymbolMap)56 PlanNode (org.teiid.query.optimizer.relational.plantree.PlanNode)37 ElementSymbol (org.teiid.query.sql.symbol.ElementSymbol)28 Expression (org.teiid.query.sql.symbol.Expression)28 GroupSymbol (org.teiid.query.sql.symbol.GroupSymbol)21 ArrayList (java.util.ArrayList)16 List (java.util.List)15 Criteria (org.teiid.query.sql.lang.Criteria)10 LinkedList (java.util.LinkedList)8 Map (java.util.Map)8 HashMap (java.util.HashMap)6 Constant (org.teiid.query.sql.symbol.Constant)6 LinkedHashSet (java.util.LinkedHashSet)5 QueryPlannerException (org.teiid.api.exception.query.QueryPlannerException)5 CompareCriteria (org.teiid.query.sql.lang.CompareCriteria)5 OrderBy (org.teiid.query.sql.lang.OrderBy)5 HashSet (java.util.HashSet)4 ExpressionMappingVisitor (org.teiid.query.sql.visitor.ExpressionMappingVisitor)4 LinkedHashMap (java.util.LinkedHashMap)3 Set (java.util.Set)3