use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.
the class JoinUtil method getInnerSideJoinNodes.
/**
* Can be called after join planning on a join node to get the inner sides of the join
* @param joinNode
* @return
*/
static PlanNode[] getInnerSideJoinNodes(PlanNode joinNode) {
Assertion.assertTrue(joinNode.getType() == NodeConstants.Types.JOIN);
JoinType jt = (JoinType) joinNode.getProperty(NodeConstants.Info.JOIN_TYPE);
if (jt == JoinType.JOIN_INNER || jt == JoinType.JOIN_CROSS) {
return new PlanNode[] { joinNode.getFirstChild(), joinNode.getLastChild() };
}
if (jt == JoinType.JOIN_RIGHT_OUTER) {
return new PlanNode[] { joinNode.getFirstChild() };
}
if (jt == JoinType.JOIN_LEFT_OUTER) {
return new PlanNode[] { joinNode.getLastChild() };
}
// must be full outer, so there is no inner side
return new PlanNode[] {};
}
use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.
the class RulePushNonJoinCriteria method execute.
/**
* Execute the rule as described in the class comments.
* @param plan Incoming query plan, may be modified during method and may be returned from method
* @param metadata Metadata source
* @param rules Rules from optimizer rule stack, may be manipulated during method
* @return Updated query plan if rule fired, else original query plan
*/
public PlanNode execute(PlanNode plan, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
boolean treeChanged = false;
boolean removeCopiedFlag = false;
boolean pushRuleRaiseNull = false;
for (PlanNode node : NodeEditor.findAllNodes(plan, NodeConstants.Types.JOIN)) {
List criteria = (List) node.getProperty(NodeConstants.Info.JOIN_CRITERIA);
JoinType joinType = (JoinType) node.getProperty(NodeConstants.Info.JOIN_TYPE);
// criteria cannot be pushed out of a full outer join clause
if (joinType == JoinType.JOIN_FULL_OUTER || joinType == JoinType.JOIN_CROSS) {
continue;
}
Iterator crits = criteria.iterator();
while (crits.hasNext()) {
Criteria crit = (Criteria) crits.next();
// special case handling for true/false criteria
if (crit.equals(QueryRewriter.FALSE_CRITERIA) || crit.equals(QueryRewriter.UNKNOWN_CRITERIA)) {
if (joinType == JoinType.JOIN_INNER) {
FrameUtil.replaceWithNullNode(node);
} else {
// must be a left or right outer join, replace the inner side with null
FrameUtil.replaceWithNullNode(JoinUtil.getInnerSideJoinNodes(node)[0]);
removeCopiedFlag = true;
}
// since a null node has been created, raise it to its highest point
pushRuleRaiseNull = true;
treeChanged = true;
break;
} else if (crit.equals(QueryRewriter.TRUE_CRITERIA)) {
crits.remove();
break;
}
if (pushCriteria(node, crit, crits, metadata)) {
treeChanged = true;
}
}
// degrade the join if there is no criteria left
if (criteria.isEmpty() && joinType == JoinType.JOIN_INNER) {
node.setProperty(NodeConstants.Info.JOIN_TYPE, JoinType.JOIN_CROSS);
treeChanged = true;
}
if (removeCopiedFlag) {
// allow the criteria above the join to be eligible for pushing and copying
PlanNode parent = node.getParent();
while (parent != null && parent.getType() == NodeConstants.Types.SELECT) {
parent.setProperty(NodeConstants.Info.IS_COPIED, Boolean.FALSE);
parent = parent.getParent();
}
}
}
if (treeChanged) {
rules.push(RuleConstants.PUSH_SELECT_CRITERIA);
}
if (pushRuleRaiseNull) {
rules.push(RuleConstants.RAISE_NULL);
}
return plan;
}
use of org.teiid.query.sql.lang.JoinType 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.sql.lang.JoinType in project teiid by teiid.
the class RuleCopyCriteria method tryToCopy.
/**
* Recursively tries to copy criteria across join nodes. toCopy will contain only the single group criteria
* that has not yet been copied. allCriteria will contain all criteria present at the join that can effect
* copying.
*
* @param node
* @return true if criteria has been created
*/
private boolean tryToCopy(PlanNode node, Set<Criteria>[] criteriaInfo, QueryMetadataInterface metadata, boolean underAccess) {
boolean changedTree = false;
if (node == null) {
return false;
}
// visit join nodes in order
if (node.getType() == NodeConstants.Types.JOIN) {
JoinType jt = (JoinType) node.getProperty(NodeConstants.Info.JOIN_TYPE);
if (jt == JoinType.JOIN_FULL_OUTER) {
return visitChildern(node, criteriaInfo, changedTree, metadata, underAccess);
}
Set<Criteria>[] leftChildCriteria = new Set[2];
Set<Criteria>[] rightChildCriteria = new Set[2];
changedTree |= tryToCopy(node.getFirstChild(), leftChildCriteria, metadata, underAccess);
changedTree |= tryToCopy(node.getLastChild(), rightChildCriteria, metadata, underAccess);
List<Criteria> joinCrits = (List<Criteria>) node.getProperty(NodeConstants.Info.JOIN_CRITERIA);
Set<Criteria> combinedCriteria = null;
if (joinCrits != null) {
combinedCriteria = new LinkedHashSet<Criteria>(joinCrits);
combinedCriteria.addAll(leftChildCriteria[1]);
combinedCriteria.addAll(rightChildCriteria[1]);
}
// combine the criteria
leftChildCriteria[0].addAll(rightChildCriteria[0]);
leftChildCriteria[1].addAll(rightChildCriteria[1]);
// set the applicable criteria
criteriaInfo[0] = leftChildCriteria[0];
// set the all criteria
criteriaInfo[1] = leftChildCriteria[1];
// there's no join criteria here, so just let the criteria go up
if (jt == JoinType.JOIN_CROSS) {
return changedTree;
}
Set<Criteria> toCopy = criteriaInfo[0];
Set<Criteria> allCriteria = criteriaInfo[1];
if (joinCrits != null && !joinCrits.isEmpty()) {
List<Criteria> newJoinCrits = new LinkedList<Criteria>();
// we don't want to continue discovery since that could be recursive
Map<Expression, Expression> srcToTgt = buildElementMap(joinCrits, node.hasBooleanProperty(NodeConstants.Info.IS_COPIED) ? null : newJoinCrits, combinedCriteria, metadata, underAccess);
changedTree |= !newJoinCrits.isEmpty();
if (!toCopy.isEmpty()) {
changedTree |= createCriteria(false, toCopy, combinedCriteria, srcToTgt, newJoinCrits, metadata, underAccess);
srcToTgt = buildElementMap(allCriteria, null, null, metadata, underAccess);
changedTree |= createCriteria(true, joinCrits, combinedCriteria, srcToTgt, newJoinCrits, metadata, underAccess);
}
joinCrits.addAll(newJoinCrits);
}
// before returning, filter out criteria that cannot go above the join node
if (jt == JoinType.JOIN_RIGHT_OUTER || jt == JoinType.JOIN_ANTI_SEMI || jt == JoinType.JOIN_SEMI || jt == JoinType.JOIN_UNION) {
// $NON-NLS-1$
throw new AssertionError("Unexpected join type");
} else if (jt == JoinType.JOIN_LEFT_OUTER) {
criteriaInfo[0].removeAll(rightChildCriteria[0]);
criteriaInfo[1].removeAll(rightChildCriteria[1]);
} else if (node.getSubqueryContainers().isEmpty()) {
if (!node.hasBooleanProperty(NodeConstants.Info.IS_COPIED)) {
toCopy.addAll(combinedCriteria);
}
allCriteria.addAll(joinCrits);
}
return changedTree;
}
changedTree = visitChildern(node, criteriaInfo, changedTree, metadata, underAccess);
// visit select nodes on the way back up
switch(node.getType()) {
case NodeConstants.Types.SELECT:
{
if (criteriaInfo[0] != null) {
visitSelectNode(node, criteriaInfo[0], criteriaInfo[1]);
}
break;
}
// clear the criteria when hitting the following
case NodeConstants.Types.NULL:
case NodeConstants.Types.SOURCE:
case NodeConstants.Types.GROUP:
case NodeConstants.Types.SET_OP:
case NodeConstants.Types.PROJECT:
{
if (criteriaInfo[0] == null) {
criteriaInfo[0] = new LinkedHashSet<Criteria>();
criteriaInfo[1] = new LinkedHashSet<Criteria>();
} else {
criteriaInfo[0].clear();
criteriaInfo[1].clear();
}
break;
}
}
return changedTree;
}
use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.
the class RuleDecomposeJoin method decomposeJoin.
public PlanNode decomposeJoin(PlanNode joinNode, PlanNode root, QueryMetadataInterface metadata, CommandContext context) throws TeiidComponentException, QueryPlannerException {
if (joinNode.getParent() == null) {
// already processed
return root;
}
JoinType joinType = (JoinType) joinNode.getProperty(Info.JOIN_TYPE);
if (joinType == JoinType.JOIN_ANTI_SEMI || joinType == JoinType.JOIN_CROSS) {
return root;
}
PlanNode left = joinNode.getFirstChild();
while (left.getType() != NodeConstants.Types.SOURCE) {
if (left.getType() == NodeConstants.Types.SELECT && left.hasBooleanProperty(Info.IS_PHANTOM)) {
left = left.getFirstChild();
} else {
return root;
}
}
Map<ElementSymbol, List<Set<Constant>>> partitionInfo = (Map<ElementSymbol, List<Set<Constant>>>) left.getProperty(Info.PARTITION_INFO);
if (partitionInfo == null) {
return root;
}
PlanNode unionNode = left.getFirstChild();
if (unionNode.getType() != NodeConstants.Types.SET_OP) {
return root;
}
PlanNode right = joinNode.getLastChild();
while (right.getType() != NodeConstants.Types.SOURCE) {
if (right.getType() == NodeConstants.Types.SELECT && right.hasBooleanProperty(Info.IS_PHANTOM)) {
right = right.getFirstChild();
} else {
return root;
}
}
Map<ElementSymbol, List<Set<Constant>>> rightPartionInfo = (Map<ElementSymbol, List<Set<Constant>>>) right.getProperty(Info.PARTITION_INFO);
if (rightPartionInfo == null) {
return root;
}
List<Criteria> criteria = (List<Criteria>) joinNode.getProperty(Info.JOIN_CRITERIA);
List<Expression> expr = new ArrayList<Expression>();
List<Expression> exprOther = new ArrayList<Expression>();
RuleChooseJoinStrategy.separateCriteria(unionNode.getParent().getGroups(), right.getGroups(), expr, exprOther, criteria, new LinkedList<Criteria>());
// if implicit, we assume that partitions match
ElementSymbol es = getImplicitPartitionColumn(metadata, left);
ElementSymbol esOther = getImplicitPartitionColumn(metadata, right);
if (es != null && esOther != null && getEffectiveModelId(metadata, es.getGroupSymbol()) == getEffectiveModelId(metadata, esOther.getGroupSymbol())) {
expr.add(es);
exprOther.add(esOther);
}
if (expr.isEmpty()) {
// no equi-join
return root;
}
List<int[]> matches = findMatches(partitionInfo, rightPartionInfo, expr, exprOther);
if (matches == null) {
// no non-overlapping partitions
return root;
}
int branchSize = partitionInfo.values().iterator().next().size();
int otherBranchSize = rightPartionInfo.values().iterator().next().size();
if (matches.isEmpty()) {
if (joinType == JoinType.JOIN_INNER || joinType == JoinType.JOIN_SEMI) {
// no matches mean that we can just insert a null node (false criteria) and be done with it
PlanNode critNode = NodeFactory.getNewNode(NodeConstants.Types.SELECT);
critNode.setProperty(Info.SELECT_CRITERIA, QueryRewriter.FALSE_CRITERIA);
unionNode.addAsParent(critNode);
} else if (joinType == JoinType.JOIN_LEFT_OUTER) {
joinNode.getParent().replaceChild(joinNode, left);
} else if (joinType == JoinType.JOIN_FULL_OUTER) {
joinNode.setProperty(Info.JOIN_CRITERIA, QueryRewriter.FALSE_CRITERIA);
}
return root;
}
List<PlanNode> branches = new ArrayList<PlanNode>();
// TODO: find union children from RulePushAggregates
RulePushSelectCriteria.collectUnionChildren(unionNode, branches);
if (branches.size() != branchSize) {
// sanity check
return root;
}
List<PlanNode> otherBranches = new ArrayList<PlanNode>();
RulePushSelectCriteria.collectUnionChildren(right.getFirstChild(), otherBranches);
if (otherBranches.size() != otherBranchSize) {
// sanity check
return root;
}
PlanNode newUnion = buildUnion(unionNode, right, criteria, matches, branches, otherBranches, joinType);
GroupSymbol leftGroup = left.getGroups().iterator().next();
PlanNode view = rebuild(leftGroup, joinNode, newUnion, metadata, context, left, right);
// preserve the model of the virtual group as we'll look for this when checking for implicit behavior
((TempMetadataID) (view.getGroups().iterator().next().getMetadataID())).getTableData().setModel(getEffectiveModelId(metadata, leftGroup));
SymbolMap symbolmap = (SymbolMap) view.getProperty(Info.SYMBOL_MAP);
HashMap<ElementSymbol, List<Set<Constant>>> newPartitionInfo = new LinkedHashMap<ElementSymbol, List<Set<Constant>>>();
Map<Expression, ElementSymbol> inverse = symbolmap.inserseMapping();
for (int[] match : matches) {
updatePartitionInfo(partitionInfo, matches, inverse, newPartitionInfo, match[0]);
updatePartitionInfo(rightPartionInfo, matches, inverse, newPartitionInfo, match[1]);
}
view.setProperty(Info.PARTITION_INFO, newPartitionInfo);
// since we've created a new union node, there's a chance we can decompose again
if (view.getParent().getType() == NodeConstants.Types.JOIN) {
return decomposeJoin(view.getParent(), root, metadata, context);
}
return root;
}
Aggregations