private PlanNode checkForProjectOptimization( PlanNode node, PlanNode root, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, AnalysisRecord record, CommandContext context) throws QueryMetadataException, TeiidComponentException, QueryPlannerException { PlanNode projectNode = node.getFirstChild(); PlanNode parent = node.getParent(); boolean raiseAccess = false; // special check for unrelated order by compensation if (projectNode.getType() == NodeConstants.Types.ACCESS && RuleRaiseAccess.canRaiseOverSort( projectNode, metadata, capFinder, node, record, true, context)) { projectNode = NodeEditor.findNodePreOrder( projectNode, NodeConstants.Types.PROJECT, NodeConstants.Types.SOURCE | NodeConstants.Types.SET_OP); if (projectNode == null) { return root; // no interviening project } raiseAccess = true; } else if (projectNode.getType() == NodeConstants.Types.PROJECT && projectNode.getFirstChild() != null) { raiseAccess = projectNode.getFirstChild().getType() == NodeConstants.Types.ACCESS && RuleRaiseAccess.canRaiseOverSort( projectNode.getFirstChild(), metadata, capFinder, node, record, false, context); // if we can't raise the access node and this doesn't have a limit, there's no point in // optimizing if (!raiseAccess && (parent == null || parent.getType() != NodeConstants.Types.TUPLE_LIMIT)) { return root; } } else { return root; } List<Expression> childOutputCols = (List<Expression>) projectNode.getFirstChild().getProperty(Info.OUTPUT_COLS); OrderBy orderBy = (OrderBy) node.getProperty(Info.SORT_ORDER); List<Expression> orderByKeys = orderBy.getSortKeys(); LinkedHashSet<Expression> toProject = new LinkedHashSet(); for (Expression ss : orderByKeys) { Expression original = ss; if (ss instanceof AliasSymbol) { ss = ((AliasSymbol) ss).getSymbol(); } if (ss instanceof ExpressionSymbol) { if (!raiseAccess) { return root; // TODO: insert a new project node to handle this case } } if (!childOutputCols.contains(ss)) { if (!raiseAccess) { return root; } toProject.add(original); } } PlanNode toRepair = projectNode.getParent(); if (!toProject.isEmpty()) { PlanNode intermediateProject = NodeFactory.getNewNode(NodeConstants.Types.PROJECT); toProject.addAll(childOutputCols); List<Expression> projectCols = new ArrayList<Expression>(toProject); childOutputCols = projectCols; intermediateProject.setProperty(NodeConstants.Info.PROJECT_COLS, projectCols); intermediateProject.setProperty( NodeConstants.Info.OUTPUT_COLS, new ArrayList<Expression>(projectCols)); toRepair.getFirstChild().addAsParent(intermediateProject); } NodeEditor.removeChildNode(projectNode.getParent(), projectNode); if (parent != null && parent.getType() == NodeConstants.Types.TUPLE_LIMIT && parent.getParent() != null) { parent.addAsParent(projectNode); } else { if (parent == null) { root = projectNode; } if (parent != null && parent.getType() == NodeConstants.Types.TUPLE_LIMIT) { if (root == parent) { root = projectNode; } projectNode.addFirstChild(parent); } else { projectNode.addFirstChild(node); } } List<Expression> orderByOutputSymbols = (List<Expression>) node.getProperty(Info.OUTPUT_COLS); boolean unrelated = false; if (node.hasBooleanProperty(Info.UNRELATED_SORT)) { node.setProperty(Info.UNRELATED_SORT, false); unrelated = true; } for (OrderByItem item : orderBy.getOrderByItems()) { if (unrelated || !toProject.isEmpty()) { // update sort order int index = childOutputCols.indexOf(item.getSymbol()); item.setExpressionPosition(index); } if (toProject.isEmpty()) { // strip alias as project was raised if (item.getSymbol() instanceof AliasSymbol) { item.setSymbol(((AliasSymbol) item.getSymbol()).getSymbol()); } } } while (toRepair != node) { toRepair.setProperty(Info.OUTPUT_COLS, childOutputCols); toRepair = toRepair.getParent(); } projectNode.setProperty(Info.OUTPUT_COLS, orderByOutputSymbols); projectNode.setProperty(Info.PROJECT_COLS, orderByOutputSymbols); node.setProperty(Info.OUTPUT_COLS, childOutputCols); if (parent != null) { parent.setProperty(Info.OUTPUT_COLS, childOutputCols); } if (raiseAccess) { PlanNode accessNode = NodeEditor.findNodePreOrder(node, NodeConstants.Types.ACCESS); // instead of just calling ruleraiseaccess, we're more selective // we do not want to raise the access node over a project that is handling an unrelated sort PlanNode newRoot = RuleRaiseAccess.raiseAccessNode( root, accessNode, metadata, capFinder, true, record, context); if (newRoot != null) { accessNode.setProperty(NodeConstants.Info.OUTPUT_COLS, childOutputCols); root = newRoot; if (!toProject.isEmpty()) { newRoot = RuleRaiseAccess.raiseAccessNode( root, accessNode, metadata, capFinder, true, record, context); } if (newRoot != null) { root = newRoot; if (accessNode.getParent().getType() == NodeConstants.Types.TUPLE_LIMIT) { newRoot = RulePushLimit.raiseAccessOverLimit( root, accessNode, metadata, capFinder, accessNode.getParent(), record); } if (newRoot != null) { root = newRoot; } } } } return root; }
/** * Mark the specified access node to be made dependent * * @param sourceNode Node to make dependent * @param dca * @throws TeiidComponentException * @throws QueryMetadataException */ boolean markDependent( PlanNode sourceNode, PlanNode joinNode, QueryMetadataInterface metadata, DependentCostAnalysis dca, Boolean bound) throws QueryMetadataException, TeiidComponentException { boolean isLeft = joinNode.getFirstChild() == sourceNode; // Get new access join node properties based on join criteria List independentExpressions = (List) (isLeft ? joinNode.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS) : joinNode.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS)); List dependentExpressions = (List) (isLeft ? joinNode.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS) : joinNode.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS)); if (independentExpressions == null || independentExpressions.isEmpty()) { return false; } String id = "$dsc/id" + ID.getAndIncrement(); // $NON-NLS-1$ // Create DependentValueSource and set on the independent side as this will feed the values joinNode.setProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE, id); PlanNode indNode = isLeft ? joinNode.getLastChild() : joinNode.getFirstChild(); if (bound == null) { List<PlanNode> sources = NodeEditor.findAllNodes(indNode, NodeConstants.Types.SOURCE); for (PlanNode planNode : sources) { for (GroupSymbol gs : planNode.getGroups()) { if (gs.isTempTable() && metadata.getCardinality(gs.getMetadataID()) == QueryMetadataInterface.UNKNOWN_CARDINALITY) { bound = true; break; } } } if (bound == null) { bound = false; } } PlanNode crit = getDependentCriteriaNode( id, independentExpressions, dependentExpressions, indNode, metadata, dca, bound, (MakeDep) sourceNode.getProperty(Info.MAKE_DEP)); sourceNode.addAsParent(crit); if (isLeft) { JoinUtil.swapJoinChildren(joinNode); } return true; }