use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RuleRemoveOptionalJoins method execute.
public PlanNode execute(PlanNode plan, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
List<PlanNode> projectNodes = NodeEditor.findAllNodes(plan, NodeConstants.Types.PROJECT);
HashSet<PlanNode> skipNodes = new HashSet<PlanNode>();
for (PlanNode projectNode : projectNodes) {
if (projectNode.getChildCount() == 0 || projectNode.getProperty(NodeConstants.Info.INTO_GROUP) != null) {
continue;
}
PlanNode groupNode = NodeEditor.findNodePreOrder(projectNode, NodeConstants.Types.GROUP, NodeConstants.Types.SOURCE | NodeConstants.Types.JOIN);
if (groupNode != null) {
projectNode = groupNode;
}
Set<GroupSymbol> requiredForOptional = getRequiredGroupSymbols(projectNode.getFirstChild());
boolean done = false;
while (!done) {
done = true;
List<PlanNode> joinNodes = NodeEditor.findAllNodes(projectNode, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE);
for (PlanNode planNode : joinNodes) {
if (skipNodes.contains(planNode)) {
continue;
}
if (!planNode.getExportedCorrelatedReferences().isEmpty()) {
skipNodes.add(planNode);
continue;
}
Set<GroupSymbol> required = getRequiredGroupSymbols(planNode);
List<PlanNode> removed = removeJoin(required, requiredForOptional, planNode, planNode.getFirstChild(), analysisRecord, metadata);
if (removed != null) {
skipNodes.addAll(removed);
done = false;
continue;
}
removed = removeJoin(required, requiredForOptional, planNode, planNode.getLastChild(), analysisRecord, metadata);
if (removed != null) {
skipNodes.addAll(removed);
done = false;
}
}
}
}
return plan;
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RuleRemoveOptionalJoins method removeJoin.
/**
* remove the optional node if possible
* @throws QueryPlannerException
* @throws TeiidComponentException
* @throws QueryMetadataException
*/
private List<PlanNode> removeJoin(Set<GroupSymbol> required, Set<GroupSymbol> requiredForOptional, PlanNode joinNode, PlanNode optionalNode, AnalysisRecord record, QueryMetadataInterface metadata) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
boolean correctFrame = false;
boolean isOptional = optionalNode.hasBooleanProperty(NodeConstants.Info.IS_OPTIONAL);
if (isOptional) {
required = requiredForOptional;
correctFrame = true;
}
if (!Collections.disjoint(optionalNode.getGroups(), required)) {
return null;
}
if (isOptional) {
// prevent bridge table removal
HashSet<GroupSymbol> joinGroups = new HashSet<GroupSymbol>();
PlanNode parentNode = joinNode;
while (parentNode.getType() != NodeConstants.Types.PROJECT) {
PlanNode current = parentNode;
parentNode = parentNode.getParent();
if (current.getType() != NodeConstants.Types.SELECT && current.getType() != NodeConstants.Types.JOIN) {
continue;
}
Set<GroupSymbol> currentGroups = current.getGroups();
if (current.getType() == NodeConstants.Types.JOIN) {
currentGroups = GroupsUsedByElementsVisitor.getGroups((List<Criteria>) current.getProperty(NodeConstants.Info.JOIN_CRITERIA));
}
if (!Collections.disjoint(currentGroups, optionalNode.getGroups()) && !optionalNode.getGroups().containsAll(currentGroups)) {
// we're performing a join
boolean wasEmpty = joinGroups.isEmpty();
boolean modified = joinGroups.addAll(current.getGroups());
if (!wasEmpty && modified) {
return null;
}
}
}
}
JoinType jt = (JoinType) joinNode.getProperty(NodeConstants.Info.JOIN_TYPE);
boolean usesKey = false;
boolean isRight = optionalNode == joinNode.getLastChild();
if (!isOptional && (jt == JoinType.JOIN_INNER || (jt == JoinType.JOIN_LEFT_OUTER && isRight))) {
usesKey = isOptionalUsingKey(joinNode, optionalNode, metadata, isRight);
}
if (!isOptional && !usesKey && (jt != JoinType.JOIN_LEFT_OUTER || !isRight || useNonDistinctRows(joinNode.getParent()))) {
return null;
}
// remove the parent node and move the sibling node upward
PlanNode parentNode = joinNode.getParent();
joinNode.removeChild(optionalNode);
joinNode.getFirstChild().setProperty(NodeConstants.Info.OUTPUT_COLS, joinNode.getProperty(NodeConstants.Info.OUTPUT_COLS));
NodeEditor.removeChildNode(parentNode, joinNode);
// $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
joinNode.recordDebugAnnotation((isOptional ? "node was marked as optional " : "node will not affect the results"), null, "Removing join node", record, null);
while (parentNode.getType() != NodeConstants.Types.PROJECT) {
PlanNode current = parentNode;
parentNode = parentNode.getParent();
if (correctFrame) {
if (current.getType() == NodeConstants.Types.SELECT) {
if (!Collections.disjoint(current.getGroups(), optionalNode.getGroups())) {
current.getFirstChild().setProperty(NodeConstants.Info.OUTPUT_COLS, current.getProperty(NodeConstants.Info.OUTPUT_COLS));
NodeEditor.removeChildNode(parentNode, current);
}
} else if (current.getType() == NodeConstants.Types.JOIN) {
if (!Collections.disjoint(current.getGroups(), optionalNode.getGroups())) {
List<Criteria> crits = (List<Criteria>) current.getProperty(NodeConstants.Info.JOIN_CRITERIA);
if (crits != null && !crits.isEmpty()) {
for (Iterator<Criteria> iterator = crits.iterator(); iterator.hasNext(); ) {
Criteria criteria = iterator.next();
if (!Collections.disjoint(GroupsUsedByElementsVisitor.getGroups(criteria), optionalNode.getGroups())) {
iterator.remove();
}
}
if (crits.isEmpty()) {
JoinType joinType = (JoinType) current.getProperty(NodeConstants.Info.JOIN_TYPE);
if (joinType == JoinType.JOIN_INNER) {
current.setProperty(NodeConstants.Info.JOIN_TYPE, JoinType.JOIN_CROSS);
}
}
}
}
}
} else if (current.getType() != NodeConstants.Types.JOIN) {
break;
}
if (current.getType() == NodeConstants.Types.JOIN) {
current.getGroups().removeAll(optionalNode.getGroups());
}
}
return NodeEditor.findAllNodes(optionalNode, NodeConstants.Types.JOIN);
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RuleChooseDependent method fullyPush.
/**
* Check for fully pushable dependent joins
* currently we only look for the simplistic scenario where there are no intervening
* nodes above the dependent side
* @param independentExpressions
*/
private boolean fullyPush(PlanNode sourceNode, PlanNode joinNode, QueryMetadataInterface metadata, CapabilitiesFinder capabilitiesFinder, CommandContext context, PlanNode indNode, RuleStack rules, MakeDep makeDep, AnalysisRecord analysisRecord, List independentExpressions) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
if (sourceNode.getType() != NodeConstants.Types.ACCESS) {
// don't remove as we may raise an access node to make this possible
return false;
}
Object modelID = RuleRaiseAccess.getModelIDFromAccess(sourceNode, metadata);
boolean hasHint = false;
if (makeDep != null && makeDep.getJoin() != null) {
if (!makeDep.getJoin()) {
// $NON-NLS-1$ //$NON-NLS-2$
sourceNode.recordDebugAnnotation("cannot pushdown dependent join", modelID, "honoring hint", analysisRecord, null);
return false;
}
hasHint = true;
}
if (!CapabilitiesUtil.supports(Capability.FULL_DEPENDENT_JOIN, modelID, metadata, capabilitiesFinder)) {
if (hasHint) {
// $NON-NLS-1$ //$NON-NLS-2$
sourceNode.recordDebugAnnotation("cannot pushdown dependent join", modelID, "dependent join pushdown needs enabled at the source", analysisRecord, null);
}
return false;
}
List<? extends Expression> projected = (List<? extends Expression>) indNode.getProperty(Info.OUTPUT_COLS);
if (projected == null) {
PlanNode plan = sourceNode;
while (plan.getParent() != null) {
plan = plan.getParent();
}
new RuleAssignOutputElements(false).execute(plan, metadata, capabilitiesFinder, null, AnalysisRecord.createNonRecordingRecord(), context);
projected = (List<? extends Expression>) indNode.getProperty(Info.OUTPUT_COLS);
}
if (!hasHint) {
// require no lobs
for (Expression ex : projected) {
if (DataTypeManager.isLOB(ex.getClass())) {
return false;
}
}
// old optimizer tests had no buffermanager
if (context.getBufferManager() == null) {
return false;
}
if (makeDep != null && makeDep.getMax() != null) {
// if the user specifies a max, it's best to just use a regular dependent join
return false;
}
}
/*
* check to see how far the access node can be raised
*/
PlanNode tempAccess = NodeFactory.getNewNode(NodeConstants.Types.ACCESS);
// $NON-NLS-1$
GroupSymbol gs = RulePlaceAccess.recontextSymbol(new GroupSymbol("TEIID_TEMP"), context.getGroups());
gs.setDefinition(null);
tempAccess.addGroup(gs);
tempAccess.setProperty(Info.MODEL_ID, modelID);
indNode.addAsParent(tempAccess);
PlanNode originalSource = sourceNode;
sourceNode = originalSource.clone();
// more deeply clone
if (sourceNode.hasCollectionProperty(Info.ACCESS_PATTERNS)) {
sourceNode.setProperty(Info.ACCESS_PATTERNS, new ArrayList<AccessPattern>((List) sourceNode.getProperty(Info.ACCESS_PATTERNS)));
}
if (sourceNode.hasCollectionProperty(Info.CONFORMED_SOURCES)) {
sourceNode.setProperty(Info.CONFORMED_SOURCES, new LinkedHashSet<Object>((Set) sourceNode.getProperty(Info.CONFORMED_SOURCES)));
}
originalSource.addAsParent(sourceNode);
boolean raised = false;
boolean moreProcessing = false;
boolean first = true;
while (sourceNode.getParent() != null && RuleRaiseAccess.raiseAccessNode(sourceNode, sourceNode, metadata, capabilitiesFinder, true, null, context) != null) {
raised = true;
if (first) {
// raising over join required
first = false;
continue;
}
switch(sourceNode.getFirstChild().getType()) {
case NodeConstants.Types.PROJECT:
// TODO: check for correlated subqueries
if (sourceNode.getFirstChild().hasBooleanProperty(Info.HAS_WINDOW_FUNCTIONS)) {
moreProcessing = true;
}
break;
case NodeConstants.Types.SORT:
case NodeConstants.Types.DUP_REMOVE:
case NodeConstants.Types.GROUP:
case NodeConstants.Types.SELECT:
case NodeConstants.Types.TUPLE_LIMIT:
case NodeConstants.Types.JOIN:
moreProcessing = true;
break;
}
}
if (!raised) {
tempAccess.getParent().replaceChild(tempAccess, tempAccess.getFirstChild());
sourceNode.getParent().replaceChild(sourceNode, sourceNode.getFirstChild());
return false;
}
if (!moreProcessing && !hasHint) {
// restore the plan
if (sourceNode.getParent() != null) {
sourceNode.getParent().replaceChild(sourceNode, sourceNode.getFirstChild());
} else {
sourceNode.removeAllChildren();
}
return false;
}
originalSource.getParent().replaceChild(originalSource, originalSource.getFirstChild());
// all the references to any groups from this join have to changed over to the new group
// and we need to insert a source/project node to turn this into a proper plan
PlanNode project = NodeFactory.getNewNode(NodeConstants.Types.PROJECT);
PlanNode source = NodeFactory.getNewNode(NodeConstants.Types.SOURCE);
source.addGroup(gs);
project.setProperty(Info.OUTPUT_COLS, projected);
project.setProperty(Info.PROJECT_COLS, projected);
Set<GroupSymbol> newGroups = Collections.singleton(gs);
ArrayList<ElementSymbol> virtualSymbols = new ArrayList<ElementSymbol>(projected.size());
for (int i = 0; i < projected.size(); i++) {
// $NON-NLS-1$
ElementSymbol es = new ElementSymbol("col" + (i + 1));
Expression ex = projected.get(i);
es.setType(ex.getType());
virtualSymbols.add(es);
// TODO: set a metadata id from either side
if (ex instanceof ElementSymbol) {
es.setMetadataID(((ElementSymbol) ex).getMetadataID());
}
}
List<ElementSymbol> newCols = RulePushAggregates.defineNewGroup(gs, virtualSymbols, metadata);
SymbolMap symbolMap = SymbolMap.createSymbolMap(newCols, projected);
Map<Expression, ElementSymbol> inverse = symbolMap.inserseMapping();
// TODO: the util logic should handle multiple groups
for (GroupSymbol group : indNode.getGroups()) {
FrameUtil.convertFrame(joinNode, group, newGroups, inverse, metadata);
}
// add the source a new group for the join
indNode.addAsParent(source);
// convert the lower plan into a subplan
// it needs to be rooted by a project - a view isn't really needed
indNode.removeFromParent();
project.addFirstChild(indNode);
// run the remaining rules against the subplan
RuleStack ruleCopy = rules.clone();
RuleChooseDependent ruleChooseDependent = new RuleChooseDependent();
ruleChooseDependent.traditionalOnly = true;
ruleCopy.push(ruleChooseDependent);
if (indNode.getType() == NodeConstants.Types.ACCESS) {
PlanNode root = RuleRaiseAccess.raiseAccessNode(project, indNode, metadata, capabilitiesFinder, true, null, context);
if (root != project) {
project = root;
}
}
// fully plan the sub-plan with the remaining rules
project = rules.getPlanner().executeRules(ruleCopy, project);
source.setProperty(Info.SYMBOL_MAP, symbolMap);
source.setProperty(Info.SUB_PLAN, project);
return true;
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RuleChooseDependent method createDependentSetNode.
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;
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RuleChooseDependent method handleDuplicate.
private void handleDuplicate(PlanNode joinNode, boolean isLeft, List independentExpressions, List dependentExpressions) {
Map<Expression, Integer> seen = new HashMap<Expression, Integer>();
for (int i = 0; i < dependentExpressions.size(); i++) {
Expression ex = (Expression) dependentExpressions.get(i);
Integer index = seen.get(ex);
if (index == null) {
seen.put(ex, i);
} else {
Expression e1 = (Expression) independentExpressions.get(i);
Expression e2 = (Expression) independentExpressions.get(index);
CompareCriteria cc = new CompareCriteria(e1, CompareCriteria.EQ, e2);
PlanNode impliedCriteria = RelationalPlanner.createSelectNode(cc, false);
if (isLeft) {
joinNode.getLastChild().addAsParent(impliedCriteria);
} else {
joinNode.getFirstChild().addAsParent(impliedCriteria);
}
independentExpressions.remove(i);
dependentExpressions.remove(i);
i--;
}
}
}
Aggregations