/** * Walk the tree pre-order, finding all access nodes that are candidates and adding them to the * matches list. * * @param metadata Metadata implementation * @param node Root node to search * @param matches Collection to accumulate matches in * @throws TeiidComponentException * @throws QueryMetadataException */ List<CandidateJoin> findCandidate( PlanNode root, QueryMetadataInterface metadata, AnalysisRecord analysisRecord) throws QueryMetadataException, TeiidComponentException { List<CandidateJoin> candidates = new ArrayList<CandidateJoin>(); for (PlanNode joinNode : NodeEditor.findAllNodes(root, NodeConstants.Types.JOIN, NodeConstants.Types.ACCESS)) { CandidateJoin candidate = null; for (Iterator<PlanNode> j = joinNode.getChildren().iterator(); j.hasNext(); ) { PlanNode child = j.next(); child = FrameUtil.findJoinSourceNode(child); if (child.hasBooleanProperty(NodeConstants.Info.MAKE_NOT_DEP) || !isValidJoin(joinNode, child, analysisRecord)) { continue; } if (candidate == null) { candidate = new CandidateJoin(); candidate.joinNode = joinNode; candidates.add(candidate); } if (j.hasNext()) { candidate.leftCandidate = true; } else { candidate.rightCandidate = true; } } } return candidates; }
private void setRoutingName(AccessNode accessNode, PlanNode node, Command command) throws QueryPlannerException, TeiidComponentException { // Look up connector binding name try { Object modelID = node.getProperty(NodeConstants.Info.MODEL_ID); if (modelID == null || modelID instanceof TempMetadataID) { if (command instanceof StoredProcedure) { modelID = ((StoredProcedure) command).getModelID(); } else if (!(command instanceof Create || command instanceof Drop)) { Collection<GroupSymbol> groups = GroupCollectorVisitor.getGroups(command, true); GroupSymbol group = groups.iterator().next(); modelID = metadata.getModelID(group.getMetadataID()); } } String cbName = metadata.getFullName(modelID); accessNode.setModelName(cbName); accessNode.setModelId(modelID); accessNode.setConformedTo((Set<Object>) node.getProperty(Info.CONFORMED_SOURCES)); } catch (QueryMetadataException e) { throw new QueryPlannerException( QueryPlugin.Event.TEIID30251, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30251)); } }
static PlanNode createDependentSetNode( String id, List<DependentSetCriteria.AttributeComparison> expressions) { DependentSetCriteria crit = createDependentSetCriteria(id, expressions); PlanNode selectNode = RelationalPlanner.createSelectNode(crit, false); selectNode.setProperty(NodeConstants.Info.IS_DEPENDENT_SET, Boolean.TRUE); return selectNode; }
/** * Check whether a join is valid. Invalid joins are CROSS JOIN, FULL OUTER JOIN, any join without * criteria, any join with no equality criteria, and any outer join that has the outer side not * the same as the dependent. * * @param joinNode The join node to check * @param sourceNode The access node being considered * @param analysisRecord TODO * @return True if valid for making dependent * @throws TeiidComponentException * @throws QueryMetadataException */ boolean isValidJoin(PlanNode joinNode, PlanNode sourceNode, AnalysisRecord analysisRecord) throws QueryMetadataException, TeiidComponentException { JoinType jtype = (JoinType) joinNode.getProperty(NodeConstants.Info.JOIN_TYPE); // Check that join is not a CROSS join or FULL OUTER join if (jtype.equals(JoinType.JOIN_CROSS) || jtype.equals(JoinType.JOIN_FULL_OUTER)) { sourceNode.recordDebugAnnotation( "parent join is CROSS or FULL OUTER", null, "Rejecting dependent join", analysisRecord, null); //$NON-NLS-1$ //$NON-NLS-2$ return false; } if (!joinNode.getExportedCorrelatedReferences().isEmpty()) { sourceNode.recordDebugAnnotation( "parent join has a correlated nested table", null, "Rejecting dependent join", analysisRecord, null); //$NON-NLS-1$ //$NON-NLS-2$ return false; } // Check that join criteria exist List jcrit = (List) joinNode.getProperty(NodeConstants.Info.JOIN_CRITERIA); if (jcrit == null || jcrit.size() == 0) { sourceNode.recordDebugAnnotation( "parent join has has no join criteria", null, "Rejecting dependent join", analysisRecord, null); //$NON-NLS-1$ //$NON-NLS-2$ return false; } if (joinNode.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS) == null) { sourceNode.recordDebugAnnotation( "parent join has no equa-join predicates", null, "Rejecting dependent join", analysisRecord, null); //$NON-NLS-1$ //$NON-NLS-2$ return false; } // Check that for a left or right outer join the dependent side must be the inner if (jtype.isOuter() && JoinUtil.getInnerSideJoinNodes(joinNode)[0] != sourceNode) { sourceNode.recordDebugAnnotation( "node is on outer side of the join", null, "Rejecting dependent join", analysisRecord, null); //$NON-NLS-1$ //$NON-NLS-2$ return false; } return true; }
/** * @param independentExpressions * @param dependentExpressions * @param makeDep * @return * @throws TeiidComponentException * @throws QueryMetadataException * @since 4.3 */ private PlanNode getDependentCriteriaNode( String id, List<Expression> independentExpressions, List<Expression> dependentExpressions, PlanNode indNode, QueryMetadataInterface metadata, DependentCostAnalysis dca, Boolean bound, MakeDep makeDep) throws QueryMetadataException, TeiidComponentException { Float cardinality = null; List<DependentSetCriteria.AttributeComparison> expressions = new ArrayList<DependentSetCriteria.AttributeComparison>(dependentExpressions.size()); for (int i = 0; i < dependentExpressions.size(); i++) { Expression depExpr = dependentExpressions.get(i); Expression indExpr = independentExpressions.get(i); DependentSetCriteria.AttributeComparison comp = new DependentSetCriteria.AttributeComparison(); if (dca != null && dca.expectedNdv[i] != null) { if (dca.expectedNdv[i] > 4 * dca.maxNdv[i]) { continue; // not necessary to use } comp.ndv = dca.expectedNdv[i]; comp.maxNdv = dca.maxNdv[i]; } else { Collection<ElementSymbol> elems = ElementCollectorVisitor.getElements(indExpr, true); if (cardinality == null) { cardinality = NewCalculateCostUtil.computeCostForTree(indNode, metadata); } comp.ndv = NewCalculateCostUtil.getNDVEstimate(indNode, metadata, cardinality, elems, true); if (bound) { if (dca != null) { comp.maxNdv = Math.max(comp.ndv * 4, dca.expectedCardinality * 2); } else { comp.maxNdv = Math.max(UNKNOWN_INDEPENDENT_CARDINALITY, comp.ndv * 4); } } } comp.ind = indExpr; comp.dep = SymbolMap.getExpression(depExpr); expressions.add(comp); } PlanNode result = createDependentSetNode(id, expressions); if (makeDep != null) { DependentSetCriteria dsc = (DependentSetCriteria) result.getProperty(Info.SELECT_CRITERIA); dsc.setMakeDepOptions(makeDep); } return result; }
private void updateGroupName(PlanNode node, TableFunctionReference tt) { String groupName = node.getGroups().iterator().next().getName(); tt.getGroupSymbol().setName(groupName); for (ElementSymbol symbol : tt.getProjectedSymbols()) { symbol.setGroupSymbol(new GroupSymbol(groupName)); } }
private RelationalNode correctProjectionInternalTables(PlanNode node, AccessNode aNode) throws QueryMetadataException, TeiidComponentException { if (node.getGroups().size() != 1) { return aNode; } GroupSymbol group = node.getGroups().iterator().next(); if (!CoreConstants.SYSTEM_MODEL.equals( metadata.getFullName(metadata.getModelID(group.getMetadataID()))) && !CoreConstants.SYSTEM_ADMIN_MODEL.equals( metadata.getFullName(metadata.getModelID(group.getMetadataID())))) { return aNode; } List projectSymbols = (List) node.getProperty(NodeConstants.Info.OUTPUT_COLS); List<ElementSymbol> acutalColumns = ResolverUtil.resolveElementsInGroup(group, metadata); if (projectSymbols.equals(acutalColumns)) { return aNode; } node.setProperty(NodeConstants.Info.OUTPUT_COLS, acutalColumns); if (node.getParent() != null && node.getParent().getType() == NodeConstants.Types.PROJECT) { // if the parent is already a project, just correcting the output cols is enough return aNode; } ProjectNode pnode = new ProjectNode(getID()); pnode.setSelectSymbols(projectSymbols); aNode = (AccessNode) prepareToAdd(node, aNode); node.setProperty(NodeConstants.Info.OUTPUT_COLS, projectSymbols); pnode.addChild(aNode); return pnode; }
private RelationalNode convertPlan(PlanNode planNode) throws TeiidComponentException, TeiidProcessingException { // Convert current node in planTree RelationalNode convertedNode = convertNode(planNode); if (convertedNode == null) { Assertion.assertTrue(planNode.getChildCount() == 1); return convertPlan(planNode.getFirstChild()); } RelationalNode nextParent = convertedNode; // convertedNode may be the head of 1 or more nodes - go to end of chain while (nextParent.getChildren()[0] != null) { nextParent = nextParent.getChildren()[0]; } // Call convertPlan recursively on children for (PlanNode childNode : planNode.getChildren()) { RelationalNode child = convertPlan(childNode); if (planNode.getType() == NodeConstants.Types.SET_OP && nextParent instanceof UnionAllNode && childNode.getProperty(Info.SET_OPERATION) == childNode.getProperty(Info.SET_OPERATION) && childNode.getType() == NodeConstants.Types.SET_OP && childNode.hasBooleanProperty(Info.USE_ALL)) { for (RelationalNode grandChild : child.getChildren()) { if (grandChild != null) { nextParent.addChild(grandChild); } } } else { nextParent.addChild(child); } } // Return root of tree for top node return convertedNode; }
private RelationalNode prepareToAdd(PlanNode node, RelationalNode processNode) { // Set the output elements from the plan node List cols = (List) node.getProperty(NodeConstants.Info.OUTPUT_COLS); processNode.setElements(cols); // Set the Cost Estimates Number estimateNodeCardinality = (Number) node.getProperty(NodeConstants.Info.EST_CARDINALITY); processNode.setEstimateNodeCardinality(estimateNodeCardinality); Number estimateNodeSetSize = (Number) node.getProperty(NodeConstants.Info.EST_SET_SIZE); processNode.setEstimateNodeSetSize(estimateNodeSetSize); Number estimateDepAccessCardinality = (Number) node.getProperty(NodeConstants.Info.EST_DEP_CARDINALITY); processNode.setEstimateDepAccessCardinality(estimateDepAccessCardinality); Number estimateDepJoinCost = (Number) node.getProperty(NodeConstants.Info.EST_DEP_JOIN_COST); processNode.setEstimateDepJoinCost(estimateDepJoinCost); Number estimateJoinCost = (Number) node.getProperty(NodeConstants.Info.EST_JOIN_COST); processNode.setEstimateJoinCost(estimateJoinCost); return processNode; }
private PlanNode optimizeSorts( boolean parentBlocking, PlanNode node, PlanNode root, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, AnalysisRecord record, CommandContext context) throws QueryMetadataException, TeiidComponentException, QueryPlannerException { node = NodeEditor.findNodePreOrder( node, NodeConstants.Types.SORT | NodeConstants.Types.DUP_REMOVE | NodeConstants.Types.GROUP | NodeConstants.Types.JOIN | NodeConstants.Types.SET_OP, NodeConstants.Types.ACCESS); if (node == null) { return root; } switch (node.getType()) { case NodeConstants.Types.SORT: parentBlocking = true; if (node.hasBooleanProperty(NodeConstants.Info.IS_DUP_REMOVAL)) { break; } if (mergeSortWithDupRemoval(node)) { node.setProperty(NodeConstants.Info.IS_DUP_REMOVAL, true); } else { root = checkForProjectOptimization(node, root, metadata, capFinder, record, context); if (NodeEditor.findParent(node, NodeConstants.Types.ACCESS) != null) { return root; } } OrderBy orderBy = (OrderBy) node.getProperty(NodeConstants.Info.SORT_ORDER); List<Expression> orderColumns = orderBy.getSortKeys(); List<Expression> sortExpressions = new ArrayList<Expression>(orderColumns.size()); PlanNode possibleSort = NodeEditor.findNodePreOrder( node, NodeConstants.Types.GROUP, NodeConstants.Types.SOURCE | NodeConstants.Types.ACCESS); if (possibleSort != null && !possibleSort.hasBooleanProperty(Info.ROLLUP)) { boolean otherExpression = false; SymbolMap groupMap = (SymbolMap) possibleSort.getProperty(Info.SYMBOL_MAP); for (Expression singleElementSymbol : orderColumns) { Expression ex = SymbolMap.getExpression(singleElementSymbol); if (ex instanceof ElementSymbol) { sortExpressions.add(groupMap.getMappedExpression((ElementSymbol) ex)); } else { otherExpression = true; break; } } List<Expression> exprs = (List<Expression>) possibleSort.getProperty(Info.GROUP_COLS); if (!otherExpression && exprs != null && exprs.containsAll(sortExpressions)) { exprs.removeAll(sortExpressions); exprs.addAll(0, sortExpressions); if (node.getParent() == null) { root = node.getFirstChild(); root.removeFromParent(); node = root; } else { PlanNode nextNode = node.getFirstChild(); NodeEditor.removeChildNode(node.getParent(), node); node = nextNode; } possibleSort.setProperty(Info.SORT_ORDER, orderBy); } } break; case NodeConstants.Types.DUP_REMOVE: if (parentBlocking) { node.setType(NodeConstants.Types.SORT); node.setProperty(NodeConstants.Info.IS_DUP_REMOVAL, true); } break; case NodeConstants.Types.GROUP: if (!node.hasCollectionProperty(NodeConstants.Info.GROUP_COLS)) { break; } SymbolMap map = (SymbolMap) node.getProperty(Info.SYMBOL_MAP); boolean cardinalityDependent = false; boolean canOptimize = true; for (Expression ex : map.asMap().values()) { if (ex instanceof AggregateSymbol) { AggregateSymbol agg = (AggregateSymbol) ex; if (agg.isCardinalityDependent()) { cardinalityDependent = true; break; } } else if (!(ex instanceof ElementSymbol)) { // there is an expression in the grouping columns canOptimize = false; break; } } if (canOptimize && mergeSortWithDupRemovalAcrossSource(node)) { node.setProperty(NodeConstants.Info.IS_DUP_REMOVAL, true); if (cardinalityDependent) { PlanNode source = NodeEditor.findNodePreOrder(node, NodeConstants.Types.SOURCE); List<Expression> sourceOutput = (List<Expression>) source.getProperty(Info.OUTPUT_COLS); PlanNode child = node.getFirstChild(); while (child != source) { child.setProperty(Info.OUTPUT_COLS, sourceOutput); child = child.getFirstChild(); } } } // TODO: check the join interesting order parentBlocking = true; break; case NodeConstants.Types.JOIN: if (node.getProperty(NodeConstants.Info.JOIN_STRATEGY) == JoinStrategyType.NESTED_LOOP || node.getProperty(NodeConstants.Info.JOIN_STRATEGY) == JoinStrategyType.NESTED_TABLE) { break; } /* * Look under the left and the right sources for a dup removal operation * join * [project] * source * dup remove | union not all */ parentBlocking = true; PlanNode toTest = node.getFirstChild(); if (mergeSortWithDupRemovalAcrossSource(toTest)) { node.setProperty(NodeConstants.Info.SORT_LEFT, SortOption.SORT_DISTINCT); if (node.getProperty(NodeConstants.Info.SORT_RIGHT) != SortOption.SORT) { node.setProperty(NodeConstants.Info.JOIN_STRATEGY, JoinStrategyType.MERGE); } } toTest = node.getLastChild(); if (mergeSortWithDupRemovalAcrossSource(toTest)) { node.setProperty(NodeConstants.Info.SORT_RIGHT, SortOption.SORT_DISTINCT); if (node.getProperty(NodeConstants.Info.SORT_LEFT) != SortOption.SORT) { node.setProperty(NodeConstants.Info.JOIN_STRATEGY, JoinStrategyType.MERGE); } } break; case NodeConstants.Types.SET_OP: // assumes the use of the merge algorithm if (node.getProperty(NodeConstants.Info.SET_OPERATION) != SetQuery.Operation.UNION) { parentBlocking = true; } else if (!node.hasBooleanProperty(NodeConstants.Info.USE_ALL) && !parentBlocking) { // do the incremental dup removal for lower latency node.setProperty(NodeConstants.Info.IS_DUP_REMOVAL, true); } break; } for (PlanNode child : node.getChildren()) { root = optimizeSorts(parentBlocking, child, root, metadata, capFinder, record, context); } return root; }
private boolean mergeSortWithDupRemoval(PlanNode node) { if (node.getFirstChild() == null) { return false; } switch (node.getFirstChild().getType()) { case NodeConstants.Types.SET_OP: if (node.getFirstChild().getProperty(NodeConstants.Info.SET_OPERATION) == SetQuery.Operation.UNION && !node.getFirstChild().hasBooleanProperty(NodeConstants.Info.USE_ALL)) { node.getFirstChild().setProperty(NodeConstants.Info.USE_ALL, true); return true; } break; case NodeConstants.Types.DUP_REMOVE: NodeEditor.removeChildNode(node, node.getFirstChild()); return true; } if (node.hasBooleanProperty(Info.UNRELATED_SORT)) { PlanNode source = NodeEditor.findNodePreOrder(node, NodeConstants.Types.SOURCE); if (source != null) { PlanNode parentProject = NodeEditor.findParent(source, NodeConstants.Types.PROJECT); if (parentProject != null && parentProject .getProperty(Info.PROJECT_COLS) .equals(source.getProperty(Info.OUTPUT_COLS))) { // can't sort on a derived expression return mergeSortWithDupRemoval(source); } } } return false; }
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; }
protected RelationalNode convertNode(PlanNode node) throws TeiidComponentException, TeiidProcessingException { RelationalNode processNode = null; switch (node.getType()) { case NodeConstants.Types.PROJECT: GroupSymbol intoGroup = (GroupSymbol) node.getProperty(NodeConstants.Info.INTO_GROUP); if (intoGroup != null) { try { Insert insert = (Insert) node.getFirstChild().getProperty(Info.VIRTUAL_COMMAND); List<ElementSymbol> allIntoElements = insert.getVariables(); Object groupID = intoGroup.getMetadataID(); Object modelID = metadata.getModelID(groupID); String modelName = metadata.getFullName(modelID); if (metadata.isVirtualGroup(groupID)) { InsertPlanExecutionNode ipen = new InsertPlanExecutionNode(getID(), metadata); ipen.setProcessorPlan( (ProcessorPlan) node.getFirstChild().getProperty(Info.PROCESSOR_PLAN)); ipen.setReferences(insert.getValues()); processNode = ipen; } else { ProjectIntoNode pinode = new ProjectIntoNode(getID()); pinode.setIntoGroup(intoGroup); pinode.setIntoElements(allIntoElements); pinode.setModelName(modelName); pinode.setConstraint((Criteria) node.getProperty(Info.CONSTRAINT)); processNode = pinode; SourceCapabilities caps = capFinder.findCapabilities(modelName); if (caps.supportsCapability(Capability.INSERT_WITH_ITERATOR)) { pinode.setMode(org.teiid.query.processor.relational.ProjectIntoNode.Mode.ITERATOR); } else if (caps.supportsCapability(Capability.BATCHED_UPDATES)) { pinode.setMode(org.teiid.query.processor.relational.ProjectIntoNode.Mode.BATCH); } else { pinode.setMode(org.teiid.query.processor.relational.ProjectIntoNode.Mode.SINGLE); } } } catch (QueryMetadataException e) { throw new TeiidComponentException(QueryPlugin.Event.TEIID30247, e); } } else { List<Expression> symbols = (List) node.getProperty(NodeConstants.Info.PROJECT_COLS); ProjectNode pnode = new ProjectNode(getID()); pnode.setSelectSymbols(symbols); processNode = pnode; if (node.hasBooleanProperty(Info.HAS_WINDOW_FUNCTIONS)) { WindowFunctionProjectNode wfpn = new WindowFunctionProjectNode(getID()); // with partial projection the window function may already be pushed, we'll check for // that here ArrayList<Expression> filtered = new ArrayList<Expression>(); List<Expression> childSymbols = (List) node.getFirstChild().getProperty(NodeConstants.Info.OUTPUT_COLS); for (Expression ex : symbols) { ex = SymbolMap.getExpression(ex); if (childSymbols.contains(ex)) { continue; } filtered.add(ex); } Set<WindowFunction> windowFunctions = RuleAssignOutputElements.getWindowFunctions(filtered); if (!windowFunctions.isEmpty()) { // TODO: check for selecting all window functions List<Expression> outputElements = new ArrayList<Expression>(windowFunctions); // collect the other projected expressions for (Expression singleElementSymbol : (List<Expression>) node.getFirstChild().getProperty(Info.OUTPUT_COLS)) { outputElements.add(singleElementSymbol); } wfpn.setElements(outputElements); wfpn.init(); pnode.addChild(wfpn); } } } break; case NodeConstants.Types.JOIN: JoinType jtype = (JoinType) node.getProperty(NodeConstants.Info.JOIN_TYPE); JoinStrategyType stype = (JoinStrategyType) node.getProperty(NodeConstants.Info.JOIN_STRATEGY); JoinNode jnode = new JoinNode(getID()); jnode.setJoinType(jtype); jnode.setLeftDistinct(node.hasBooleanProperty(NodeConstants.Info.IS_LEFT_DISTINCT)); jnode.setRightDistinct(node.hasBooleanProperty(NodeConstants.Info.IS_RIGHT_DISTINCT)); List joinCrits = (List) node.getProperty(NodeConstants.Info.JOIN_CRITERIA); String depValueSource = (String) node.getProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE); SortOption leftSort = (SortOption) node.getProperty(NodeConstants.Info.SORT_LEFT); if (stype == JoinStrategyType.MERGE || stype == JoinStrategyType.ENHANCED_SORT) { MergeJoinStrategy mjStrategy = null; if (stype.equals(JoinStrategyType.ENHANCED_SORT)) { EnhancedSortMergeJoinStrategy esmjStrategy = new EnhancedSortMergeJoinStrategy( leftSort, (SortOption) node.getProperty(NodeConstants.Info.SORT_RIGHT)); esmjStrategy.setSemiDep(node.hasBooleanProperty(Info.IS_SEMI_DEP)); mjStrategy = esmjStrategy; } else { mjStrategy = new MergeJoinStrategy( leftSort, (SortOption) node.getProperty(NodeConstants.Info.SORT_RIGHT), false); } jnode.setJoinStrategy(mjStrategy); List leftExpressions = (List) node.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS); List rightExpressions = (List) node.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS); jnode.setJoinExpressions(leftExpressions, rightExpressions); joinCrits = (List) node.getProperty(NodeConstants.Info.NON_EQUI_JOIN_CRITERIA); } else if (stype == JoinStrategyType.NESTED_TABLE) { NestedTableJoinStrategy ntjStrategy = new NestedTableJoinStrategy(); jnode.setJoinStrategy(ntjStrategy); SymbolMap references = (SymbolMap) node.getProperty(Info.LEFT_NESTED_REFERENCES); ntjStrategy.setLeftMap(references); references = (SymbolMap) node.getProperty(Info.RIGHT_NESTED_REFERENCES); ntjStrategy.setRightMap(references); } else { NestedLoopJoinStrategy nljStrategy = new NestedLoopJoinStrategy(); jnode.setJoinStrategy(nljStrategy); } Criteria joinCrit = Criteria.combineCriteria(joinCrits); jnode.setJoinCriteria(joinCrit); processNode = jnode; jnode.setDependentValueSource(depValueSource); break; case NodeConstants.Types.ACCESS: ProcessorPlan plan = (ProcessorPlan) node.getProperty(NodeConstants.Info.PROCESSOR_PLAN); if (plan != null) { PlanExecutionNode peNode = null; Criteria crit = (Criteria) node.getProperty(NodeConstants.Info.PROCEDURE_CRITERIA); if (crit != null) { List references = (List) node.getProperty(NodeConstants.Info.PROCEDURE_INPUTS); List defaults = (List) node.getProperty(NodeConstants.Info.PROCEDURE_DEFAULTS); peNode = new DependentProcedureExecutionNode(getID(), crit, references, defaults); } else { peNode = new PlanExecutionNode(getID()); } peNode.setProcessorPlan(plan); processNode = peNode; } else { AccessNode aNode = null; Command command = (Command) node.getProperty(NodeConstants.Info.ATOMIC_REQUEST); Object modelID = node.getProperty(NodeConstants.Info.MODEL_ID); if (modelID != null) { String fullName = metadata.getFullName(modelID); if (!capFinder.isValid(fullName)) { // TODO: we ideally want to handle the partial resutls case here differently // by adding a null node / and a source warning // for now it's just as easy to say that the user needs to take steps to // return static capabilities SourceCapabilities caps = capFinder.findCapabilities(fullName); Exception cause = null; if (caps != null) { cause = (Exception) caps.getSourceProperty(Capability.INVALID_EXCEPTION); } throw new QueryPlannerException( QueryPlugin.Event.TEIID30498, cause, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30498, fullName)); } } EvaluatableVisitor ev = null; if (node.hasBooleanProperty(NodeConstants.Info.IS_DEPENDENT_SET)) { if (command instanceof StoredProcedure) { List references = (List) node.getProperty(NodeConstants.Info.PROCEDURE_INPUTS); List defaults = (List) node.getProperty(NodeConstants.Info.PROCEDURE_DEFAULTS); Criteria crit = (Criteria) node.getProperty(NodeConstants.Info.PROCEDURE_CRITERIA); DependentProcedureAccessNode depAccessNode = new DependentProcedureAccessNode(getID(), crit, references, defaults); processNode = depAccessNode; aNode = depAccessNode; } else { // create dependent access node DependentAccessNode depAccessNode = new DependentAccessNode(getID()); if (modelID != null) { depAccessNode.setPushdown( CapabilitiesUtil.supports( Capability.DEPENDENT_JOIN, modelID, metadata, capFinder)); depAccessNode.setMaxSetSize( CapabilitiesUtil.getMaxInCriteriaSize(modelID, metadata, capFinder)); depAccessNode.setMaxPredicates( CapabilitiesUtil.getMaxDependentPredicates(modelID, metadata, capFinder)); depAccessNode.setUseBindings( CapabilitiesUtil.supports( Capability.DEPENDENT_JOIN_BINDINGS, modelID, metadata, capFinder)); // TODO: allow the translator to drive this property // simplistic check of whether this query is complex to re-execute Query query = (Query) command; if (query.getGroupBy() != null || query.getFrom().getClauses().size() > 1 || !(query.getFrom().getClauses().get(0) instanceof UnaryFromClause) || query.getWith() != null) { depAccessNode.setComplexQuery(true); } else { // check to see if there in an index on at least one of the dependent sets Set<GroupSymbol> groups = new HashSet<GroupSymbol>(query.getFrom().getGroups()); boolean found = false; for (Criteria crit : Criteria.separateCriteriaByAnd(query.getCriteria())) { if (crit instanceof DependentSetCriteria) { DependentSetCriteria dsc = (DependentSetCriteria) crit; if (NewCalculateCostUtil.getKeyUsed( ElementCollectorVisitor.getElements(dsc.getExpression(), true), groups, metadata, null) != null) { found = true; break; } } } if (!found) { depAccessNode.setComplexQuery(true); } } } processNode = depAccessNode; aNode = depAccessNode; } aNode.setShouldEvaluateExpressions(true); } else { // create access node aNode = new AccessNode(getID()); processNode = aNode; } // -- special handling for system tables. currently they cannot perform projection try { if (command instanceof Query) { processNode = correctProjectionInternalTables(node, aNode); } } catch (QueryMetadataException err) { throw new TeiidComponentException(QueryPlugin.Event.TEIID30248, err); } setRoutingName(aNode, node, command); boolean shouldEval = false; if (command instanceof Insert) { Insert insert = (Insert) command; if (insert.getQueryExpression() != null) { insert.setQueryExpression( (QueryCommand) aliasCommand(aNode, insert.getQueryExpression(), modelID)); } else { for (int i = 0; i < insert.getValues().size(); i++) { Expression ex = (Expression) insert.getValues().get(i); if (!CriteriaCapabilityValidatorVisitor.canPushLanguageObject( ex, modelID, metadata, capFinder, analysisRecord)) { // replace with an expression symbol to let the rewriter know that it should be // replaced insert.getValues().set(i, new ExpressionSymbol("x", ex)); shouldEval = true; } } } } else if (command instanceof QueryCommand) { command = aliasCommand(aNode, command, modelID); } ev = EvaluatableVisitor.needsEvaluation(command, modelID, metadata, capFinder); aNode.setShouldEvaluateExpressions( ev.requiresEvaluation(EvaluationLevel.PROCESSING) || shouldEval); aNode.setCommand(command); Map<GroupSymbol, PlanNode> subPlans = (Map<GroupSymbol, PlanNode>) node.getProperty(Info.SUB_PLANS); // it makes more sense to allow the multisource affect to be elevated above just access // nodes if (aNode.getModelId() != null && metadata.isMultiSource(aNode.getModelId())) { VDBMetaData vdb = context.getVdb(); aNode.setShouldEvaluateExpressions(true); // forces a rewrite aNode.setElements((List) node.getProperty(NodeConstants.Info.OUTPUT_COLS)); if (node.hasBooleanProperty(Info.IS_MULTI_SOURCE)) { Expression ex = rewriteMultiSourceCommand(aNode.getCommand()); aNode.setConnectorBindingExpression(ex); aNode.setMultiSource(true); } else { String sourceName = (String) node.getProperty(Info.SOURCE_NAME); aNode.setConnectorBindingExpression(new Constant(sourceName)); } } else if (subPlans == null) { if (!aNode.isShouldEvaluate()) { aNode.minimizeProject(command); } // check if valid to share this with other nodes if (ev != null && ev.getDeterminismLevel().compareTo(Determinism.COMMAND_DETERMINISTIC) >= 0 && command.areResultsCachable()) { checkForSharedSourceCommand(aNode); } } if (subPlans != null) { QueryCommand qc = (QueryCommand) command; if (qc.getWith() == null) { qc.setWith(new ArrayList<WithQueryCommand>(subPlans.size())); } Map<GroupSymbol, RelationalPlan> plans = new LinkedHashMap<GroupSymbol, RelationalPlan>(); for (Map.Entry<GroupSymbol, PlanNode> entry : subPlans.entrySet()) { RelationalPlan subPlan = convert(entry.getValue()); List<ElementSymbol> elems = ResolverUtil.resolveElementsInGroup(entry.getKey(), metadata); subPlan.setOutputElements(elems); plans.put(entry.getKey(), subPlan); WithQueryCommand withQueryCommand = new WithQueryCommand(entry.getKey(), elems, null); qc.getWith().add(withQueryCommand); } aNode.setSubPlans(plans); } } break; case NodeConstants.Types.SELECT: Criteria crit = (Criteria) node.getProperty(NodeConstants.Info.SELECT_CRITERIA); if (!node.hasCollectionProperty(Info.OUTPUT_COLS)) { // the late optimization to create a dependent join from a subquery introduces // criteria that have no output elements set // TODO that should be cleaner, but the logic currently expects to be run after join // implementation // and rerunning assign output elements seems excessive node.setProperty(Info.OUTPUT_COLS, node.getFirstChild().getProperty(Info.OUTPUT_COLS)); } SelectNode selnode = new SelectNode(getID()); selnode.setCriteria(crit); // in case the parent was a source selnode.setProjectedExpressions( (List<Expression>) node.getProperty(NodeConstants.Info.PROJECT_COLS)); processNode = selnode; break; case NodeConstants.Types.SORT: case NodeConstants.Types.DUP_REMOVE: if (node.getType() == NodeConstants.Types.DUP_REMOVE) { processNode = new DupRemoveNode(getID()); } else { SortNode sortNode = new SortNode(getID()); OrderBy orderBy = (OrderBy) node.getProperty(NodeConstants.Info.SORT_ORDER); if (orderBy != null) { sortNode.setSortElements(orderBy.getOrderByItems()); } if (node.hasBooleanProperty(NodeConstants.Info.IS_DUP_REMOVAL)) { sortNode.setMode(Mode.DUP_REMOVE_SORT); } processNode = sortNode; } break; case NodeConstants.Types.GROUP: GroupingNode gnode = new GroupingNode(getID()); gnode.setRollup(node.hasBooleanProperty(Info.ROLLUP)); SymbolMap groupingMap = (SymbolMap) node.getProperty(NodeConstants.Info.SYMBOL_MAP); gnode.setOutputMapping(groupingMap); gnode.setRemoveDuplicates(node.hasBooleanProperty(NodeConstants.Info.IS_DUP_REMOVAL)); List<Expression> gCols = (List) node.getProperty(NodeConstants.Info.GROUP_COLS); OrderBy orderBy = (OrderBy) node.getProperty(Info.SORT_ORDER); if (orderBy == null) { if (gCols != null) { LinkedHashSet<Expression> exprs = new LinkedHashSet<Expression>(); for (Expression ex : gCols) { exprs.add(SymbolMap.getExpression(ex)); } orderBy = new OrderBy( RuleChooseJoinStrategy.createExpressionSymbols( new ArrayList<Expression>(exprs))); } } else { HashSet<Expression> seen = new HashSet<Expression>(); for (int i = 0; i < gCols.size(); i++) { if (i < orderBy.getOrderByItems().size()) { OrderByItem orderByItem = orderBy.getOrderByItems().get(i); Expression ex = SymbolMap.getExpression(orderByItem.getSymbol()); if (!seen.add(ex)) { continue; } if (ex instanceof ElementSymbol) { ex = groupingMap.getMappedExpression((ElementSymbol) ex); orderByItem.setSymbol(new ExpressionSymbol("expr", ex)); // $NON-NLS-1$ } } else { orderBy.addVariable( new ExpressionSymbol("expr", gCols.get(i)), OrderBy.ASC); // $NON-NLS-1$ } } } if (orderBy != null) { gnode.setOrderBy(orderBy.getOrderByItems()); } processNode = gnode; break; case NodeConstants.Types.SOURCE: Object source = node.getProperty(NodeConstants.Info.TABLE_FUNCTION); if (source instanceof XMLTable) { XMLTable xt = (XMLTable) source; XMLTableNode xtn = new XMLTableNode(getID()); // we handle the projection filtering once here rather than repeating the // path analysis on a per plan basis updateGroupName(node, xt); Map<Expression, Integer> elementMap = RelationalNode.createLookupMap(xt.getProjectedSymbols()); List cols = (List) node.getProperty(NodeConstants.Info.OUTPUT_COLS); int[] projectionIndexes = RelationalNode.getProjectionIndexes(elementMap, cols); ArrayList<XMLColumn> filteredColumns = new ArrayList<XMLColumn>(projectionIndexes.length); for (int col : projectionIndexes) { filteredColumns.add(xt.getColumns().get(col)); } xt.getXQueryExpression().useDocumentProjection(filteredColumns, analysisRecord); xtn.setProjectedColumns(filteredColumns); xtn.setTable(xt); processNode = xtn; break; } if (source instanceof ObjectTable) { ObjectTable ot = (ObjectTable) source; ObjectTableNode otn = new ObjectTableNode(getID()); // we handle the projection filtering once here rather than repeating the // path analysis on a per plan basis updateGroupName(node, ot); Map<Expression, Integer> elementMap = RelationalNode.createLookupMap(ot.getProjectedSymbols()); List<Expression> cols = (List<Expression>) node.getProperty(NodeConstants.Info.OUTPUT_COLS); int[] projectionIndexes = RelationalNode.getProjectionIndexes(elementMap, cols); ArrayList<ObjectColumn> filteredColumns = new ArrayList<ObjectColumn>(projectionIndexes.length); for (int col : projectionIndexes) { filteredColumns.add(ot.getColumns().get(col)); } otn.setProjectedColumns(filteredColumns); otn.setTable(ot); processNode = otn; break; } if (source instanceof TextTable) { TextTableNode ttn = new TextTableNode(getID()); TextTable tt = (TextTable) source; updateGroupName(node, tt); ttn.setTable(tt); processNode = ttn; break; } if (source instanceof ArrayTable) { ArrayTableNode atn = new ArrayTableNode(getID()); ArrayTable at = (ArrayTable) source; updateGroupName(node, at); atn.setTable(at); processNode = atn; break; } SymbolMap symbolMap = (SymbolMap) node.getProperty(NodeConstants.Info.SYMBOL_MAP); if (symbolMap != null) { PlanNode child = node.getLastChild(); if (child.getType() == NodeConstants.Types.PROJECT || child.getType() == NodeConstants.Types.SELECT) { // update the project cols based upon the original output child.setProperty( NodeConstants.Info.PROJECT_COLS, child.getProperty(NodeConstants.Info.OUTPUT_COLS)); } child.setProperty( NodeConstants.Info.OUTPUT_COLS, node.getProperty(NodeConstants.Info.OUTPUT_COLS)); } return null; case NodeConstants.Types.SET_OP: Operation setOp = (Operation) node.getProperty(NodeConstants.Info.SET_OPERATION); boolean useAll = ((Boolean) node.getProperty(NodeConstants.Info.USE_ALL)).booleanValue(); if (setOp == Operation.UNION) { RelationalNode unionAllNode = new UnionAllNode(getID()); if (useAll) { processNode = unionAllNode; } else { boolean onlyDupRemoval = node.hasBooleanProperty(NodeConstants.Info.IS_DUP_REMOVAL); if (onlyDupRemoval) { processNode = new DupRemoveNode(getID()); } else { SortNode sNode = new SortNode(getID()); sNode.setMode(Mode.DUP_REMOVE_SORT); processNode = sNode; } unionAllNode.setElements((List) node.getProperty(NodeConstants.Info.OUTPUT_COLS)); processNode.addChild(unionAllNode); } } else { JoinNode joinAsSet = new JoinNode(getID()); joinAsSet.setJoinStrategy( new MergeJoinStrategy(SortOption.SORT_DISTINCT, SortOption.SORT_DISTINCT, true)); // If we push these sorts, we will have to enforce null order, since nulls are equal here List leftExpressions = (List) node.getFirstChild().getProperty(NodeConstants.Info.OUTPUT_COLS); List rightExpressions = (List) node.getLastChild().getProperty(NodeConstants.Info.OUTPUT_COLS); joinAsSet.setJoinType( setOp == Operation.EXCEPT ? JoinType.JOIN_ANTI_SEMI : JoinType.JOIN_SEMI); joinAsSet.setJoinExpressions(leftExpressions, rightExpressions); processNode = joinAsSet; } break; case NodeConstants.Types.TUPLE_LIMIT: Expression rowLimit = (Expression) node.getProperty(NodeConstants.Info.MAX_TUPLE_LIMIT); Expression offset = (Expression) node.getProperty(NodeConstants.Info.OFFSET_TUPLE_COUNT); LimitNode ln = new LimitNode(getID(), rowLimit, offset); ln.setImplicit(node.hasBooleanProperty(Info.IS_IMPLICIT_LIMIT)); processNode = ln; break; case NodeConstants.Types.NULL: processNode = new NullNode(getID()); break; default: throw new QueryPlannerException( QueryPlugin.Event.TEIID30250, QueryPlugin.Util.gs( QueryPlugin.Event.TEIID30250, NodeConstants.getNodeTypeString(node.getType()))); } if (processNode != null) { processNode = prepareToAdd(node, processNode); } return processNode; }
public PlanNode execute( PlanNode plan, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException { // Find first criteria node in plan with conjuncts List<CandidateJoin> matches = findCandidate(plan, metadata, analysisRecord); boolean pushCriteria = false; // Handle all cases where both siblings are possible matches for (CandidateJoin entry : matches) { PlanNode joinNode = entry.joinNode; PlanNode sourceNode = entry.leftCandidate ? joinNode.getFirstChild() : joinNode.getLastChild(); PlanNode siblingNode = entry.leftCandidate ? joinNode.getLastChild() : joinNode.getFirstChild(); boolean bothCandidates = entry.leftCandidate && entry.rightCandidate; PlanNode chosenNode = chooseDepWithoutCosting(sourceNode, bothCandidates ? siblingNode : null, analysisRecord); if (chosenNode != null) { pushCriteria |= markDependent(chosenNode, joinNode, metadata, null, false); continue; } DependentCostAnalysis dca = NewCalculateCostUtil.computeCostForDepJoin( joinNode, !entry.leftCandidate, metadata, capFinder, context); PlanNode dependentNode = sourceNode; if (bothCandidates && dca.expectedCardinality == null) { dca = NewCalculateCostUtil.computeCostForDepJoin( joinNode, true, metadata, capFinder, context); if (dca.expectedCardinality != null) { dependentNode = siblingNode; } } if (dca.expectedCardinality != null) { pushCriteria |= markDependent(dependentNode, joinNode, metadata, dca, null); } else { float sourceCost = NewCalculateCostUtil.computeCostForTree(sourceNode, metadata); float siblingCost = NewCalculateCostUtil.computeCostForTree(siblingNode, metadata); List leftExpressions = (List) joinNode.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS); List rightExpressions = (List) joinNode.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS); float sourceNdv = NewCalculateCostUtil.getNDVEstimate( joinNode.getFirstChild(), metadata, sourceCost, leftExpressions, true); float siblingNdv = NewCalculateCostUtil.getNDVEstimate( joinNode.getLastChild(), metadata, siblingCost, rightExpressions, true); if (sourceCost != NewCalculateCostUtil.UNKNOWN_VALUE && sourceNdv == NewCalculateCostUtil.UNKNOWN_VALUE) { sourceNdv = sourceCost; } if (siblingCost != NewCalculateCostUtil.UNKNOWN_VALUE && siblingNdv == NewCalculateCostUtil.UNKNOWN_VALUE) { siblingNdv = siblingCost; } if (bothCandidates && sourceNdv != NewCalculateCostUtil.UNKNOWN_VALUE && ((sourceCost <= RuleChooseDependent.DEFAULT_INDEPENDENT_CARDINALITY && sourceCost <= siblingCost) || (siblingCost == NewCalculateCostUtil.UNKNOWN_VALUE && sourceNdv <= UNKNOWN_INDEPENDENT_CARDINALITY))) { pushCriteria |= markDependent( siblingNode, joinNode, metadata, null, sourceCost > RuleChooseDependent.DEFAULT_INDEPENDENT_CARDINALITY ? true : null); } else if (siblingNdv != NewCalculateCostUtil.UNKNOWN_VALUE && (siblingCost <= RuleChooseDependent.DEFAULT_INDEPENDENT_CARDINALITY || (sourceCost == NewCalculateCostUtil.UNKNOWN_VALUE && siblingNdv <= UNKNOWN_INDEPENDENT_CARDINALITY))) { pushCriteria |= markDependent( sourceNode, joinNode, metadata, null, siblingCost > RuleChooseDependent.DEFAULT_INDEPENDENT_CARDINALITY ? true : null); } } } if (pushCriteria) { // Insert new rules to push down the SELECT criteria rules.push( RuleConstants .CLEAN_CRITERIA); // it's important to run clean criteria here since it will remove // unnecessary dependent sets rules.push(RuleConstants.PUSH_SELECT_CRITERIA); } return plan; }
/** * 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; }
PlanNode chooseDepWithoutCosting( PlanNode rootNode1, PlanNode rootNode2, AnalysisRecord analysisRecord) throws QueryMetadataException, TeiidComponentException { PlanNode sourceNode1 = FrameUtil.findJoinSourceNode(rootNode1); PlanNode sourceNode2 = null; if (rootNode2 != null) { sourceNode2 = FrameUtil.findJoinSourceNode(rootNode2); } if (sourceNode1.hasCollectionProperty(NodeConstants.Info.ACCESS_PATTERNS)) { if (sourceNode2 != null && sourceNode2.hasCollectionProperty(NodeConstants.Info.ACCESS_PATTERNS)) { // Return null - query planning should fail because both access nodes // have unsatisfied access patterns rootNode1 .getParent() .recordDebugAnnotation( "both children have unsatisfied access patterns", null, "Neither node can be made dependent", analysisRecord, null); //$NON-NLS-1$ //$NON-NLS-2$ return null; } rootNode1.recordDebugAnnotation( "unsatisfied access pattern detected", null, "marking as dependent side of join", analysisRecord, null); //$NON-NLS-1$ //$NON-NLS-2$ return rootNode1; } else if (sourceNode2 != null && sourceNode2.hasCollectionProperty(NodeConstants.Info.ACCESS_PATTERNS)) { // Access node 2 has unsatisfied access pattern, // so try to make node 2 dependent sourceNode2.recordDebugAnnotation( "unsatisfied access pattern detected", null, "marking as dependent side of join", analysisRecord, null); //$NON-NLS-1$ //$NON-NLS-2$ return rootNode2; } // Check for hints, which over-rule heuristics if (sourceNode1.hasProperty(NodeConstants.Info.MAKE_DEP)) { sourceNode1.recordDebugAnnotation( "MAKE_DEP hint detected", null, "marking as dependent side of join", analysisRecord, null); //$NON-NLS-1$ //$NON-NLS-2$ rootNode1.setProperty(Info.MAKE_DEP, sourceNode1.getProperty(Info.MAKE_DEP)); return rootNode1; } else if (sourceNode2 != null && sourceNode2.hasProperty(NodeConstants.Info.MAKE_DEP)) { sourceNode2.recordDebugAnnotation( "MAKE_DEP hint detected", null, "marking as dependent side of join", analysisRecord, null); //$NON-NLS-1$ //$NON-NLS-2$ rootNode2.setProperty(Info.MAKE_DEP, sourceNode2.getProperty(Info.MAKE_DEP)); return rootNode2; } else if (sourceNode1.hasBooleanProperty(NodeConstants.Info.MAKE_IND) && sourceNode2 != null) { sourceNode2.recordDebugAnnotation( "MAKE_IND hint detected", null, "marking as dependent side of join", analysisRecord, null); //$NON-NLS-1$ //$NON-NLS-2$ return rootNode2; } else if (sourceNode2 != null && sourceNode2.hasBooleanProperty(NodeConstants.Info.MAKE_IND)) { sourceNode1.recordDebugAnnotation( "MAKE_IND hint detected", null, "marking as dependent side of join", analysisRecord, null); //$NON-NLS-1$ //$NON-NLS-2$ return rootNode1; } return null; }