use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.
the class RuleImplementJoinStrategy method execute.
/**
* @see org.teiid.query.optimizer.relational.OptimizerRule#execute(org.teiid.query.optimizer.relational.plantree.PlanNode, org.teiid.query.metadata.QueryMetadataInterface, org.teiid.query.optimizer.capabilities.CapabilitiesFinder, org.teiid.query.optimizer.relational.RuleStack, org.teiid.query.analysis.AnalysisRecord, org.teiid.query.util.CommandContext)
*/
public PlanNode execute(PlanNode plan, QueryMetadataInterface metadata, CapabilitiesFinder capabilitiesFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
for (PlanNode sourceNode : NodeEditor.findAllNodes(plan, NodeConstants.Types.SOURCE, NodeConstants.Types.ACCESS)) {
SymbolMap references = (SymbolMap) sourceNode.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
if (references != null) {
Set<GroupSymbol> groups = GroupsUsedByElementsVisitor.getGroups(references.getValues());
PlanNode joinNode = NodeEditor.findParent(sourceNode, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE);
while (joinNode != null) {
if (joinNode.getGroups().containsAll(groups)) {
joinNode.setProperty(NodeConstants.Info.JOIN_STRATEGY, JoinStrategyType.NESTED_TABLE);
Info info = Info.RIGHT_NESTED_REFERENCES;
if (!FrameUtil.findJoinSourceNode(joinNode.getFirstChild()).getGroups().containsAll(groups)) {
// $NON-NLS-1$
throw new AssertionError("Should not have reordered the join tree to reverse the lateral join");
}
SymbolMap map = (SymbolMap) joinNode.getProperty(info);
if (map == null) {
map = new SymbolMap();
}
joinNode.setProperty(info, map);
map.asUpdatableMap().putAll(references.asMap());
if (joinNode.getProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE) != null) {
// $NON-NLS-1$
throw new AssertionError("Cannot use a depenedent join when the join involves a correlated nested table.");
}
break;
}
joinNode = NodeEditor.findParent(joinNode, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE);
}
}
}
for (PlanNode joinNode : NodeEditor.findAllNodes(plan, NodeConstants.Types.JOIN, NodeConstants.Types.ACCESS)) {
JoinStrategyType stype = (JoinStrategyType) joinNode.getProperty(NodeConstants.Info.JOIN_STRATEGY);
if (!JoinStrategyType.MERGE.equals(stype)) {
continue;
}
List<Expression> leftExpressions = (List<Expression>) joinNode.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS);
List<Expression> rightExpressions = (List<Expression>) joinNode.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS);
int origExpressionCount = leftExpressions.size();
// check index information on each side
// TODO: don't do null order compensation - in fact we should check what the order actually is, but we don't have that metadata
Object key = null;
boolean right = true;
// we check the right first, since it should be larger
if (joinNode.getLastChild().getType() == NodeConstants.Types.ACCESS && NewCalculateCostUtil.isSingleTable(joinNode.getLastChild())) {
key = NewCalculateCostUtil.getKeyUsed(rightExpressions, null, metadata, null);
}
if (key == null && joinNode.getFirstChild().getType() == NodeConstants.Types.ACCESS && NewCalculateCostUtil.isSingleTable(joinNode.getFirstChild())) {
key = NewCalculateCostUtil.getKeyUsed(leftExpressions, null, metadata, null);
right = false;
}
JoinType joinType = (JoinType) joinNode.getProperty(NodeConstants.Info.JOIN_TYPE);
/**
* Don't push sorts for unbalanced inner joins, we prefer to use a processing time cost based decision
*/
boolean pushLeft = true;
boolean pushRight = true;
if ((joinType == JoinType.JOIN_INNER || joinType == JoinType.JOIN_LEFT_OUTER) && context != null) {
float leftCost = NewCalculateCostUtil.computeCostForTree(joinNode.getFirstChild(), metadata);
float rightCost = NewCalculateCostUtil.computeCostForTree(joinNode.getLastChild(), metadata);
if (leftCost != NewCalculateCostUtil.UNKNOWN_VALUE && rightCost != NewCalculateCostUtil.UNKNOWN_VALUE && (leftCost > context.getProcessorBatchSize() || rightCost > context.getProcessorBatchSize())) {
// we use a larger constant here to ensure that we don't unwisely prevent pushdown
pushLeft = leftCost < context.getProcessorBatchSize() || leftCost / rightCost < 8 || (key != null && !right);
pushRight = rightCost < context.getProcessorBatchSize() || rightCost / leftCost < 8 || joinType == JoinType.JOIN_LEFT_OUTER || (key != null && right);
}
}
if (key != null && joinNode.getProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE) == null) {
// redo the join predicates based upon the key alone
List<Object> keyCols = metadata.getElementIDsInKey(key);
int[] reorder = new int[keyCols.size()];
LinkedHashSet<Integer> toCriteria = new LinkedHashSet<Integer>();
List<Expression> keyExpressions = right ? rightExpressions : leftExpressions;
Map<Object, Integer> indexMap = new LinkedHashMap<Object, Integer>();
for (int i = 0; i < keyExpressions.size(); i++) {
Expression ses = keyExpressions.get(i);
if (!(ses instanceof ElementSymbol)) {
toCriteria.add(i);
continue;
}
Integer existing = indexMap.put(((ElementSymbol) ses).getMetadataID(), i);
if (existing != null) {
toCriteria.add(existing);
}
}
boolean found = true;
for (int i = 0; i < keyCols.size(); i++) {
Object id = keyCols.get(i);
Integer index = indexMap.remove(id);
if (index == null) {
found = false;
break;
}
reorder[i] = index;
}
if (found) {
toCriteria.addAll(indexMap.values());
List<Criteria> joinCriteria = (List<Criteria>) joinNode.getProperty(Info.NON_EQUI_JOIN_CRITERIA);
for (int index : toCriteria) {
Expression lses = leftExpressions.get(index);
Expression rses = rightExpressions.get(index);
CompareCriteria cc = new CompareCriteria(lses, CompareCriteria.EQ, rses);
if (joinCriteria == null || joinCriteria.isEmpty()) {
joinCriteria = new ArrayList<Criteria>();
}
joinCriteria.add(cc);
}
joinNode.setProperty(Info.NON_EQUI_JOIN_CRITERIA, joinCriteria);
leftExpressions = RelationalNode.projectTuple(reorder, leftExpressions);
rightExpressions = RelationalNode.projectTuple(reorder, rightExpressions);
joinNode.setProperty(NodeConstants.Info.LEFT_EXPRESSIONS, leftExpressions);
joinNode.setProperty(NodeConstants.Info.RIGHT_EXPRESSIONS, rightExpressions);
}
}
boolean pushedLeft = insertSort(joinNode.getFirstChild(), leftExpressions, joinNode, metadata, capabilitiesFinder, pushLeft, context);
if (origExpressionCount == 1 && joinType == JoinType.JOIN_INNER && joinNode.getProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE) != null && !joinNode.hasCollectionProperty(Info.NON_EQUI_JOIN_CRITERIA)) {
Collection<Expression> output = (Collection<Expression>) joinNode.getProperty(NodeConstants.Info.OUTPUT_COLS);
Collection<GroupSymbol> groups = GroupsUsedByElementsVisitor.getGroups(output);
if (Collections.disjoint(groups, FrameUtil.findJoinSourceNode(joinNode.getFirstChild()).getGroups())) {
pushRight = false;
joinNode.setProperty(Info.IS_SEMI_DEP, Boolean.TRUE);
}
}
boolean pushedRight = insertSort(joinNode.getLastChild(), rightExpressions, joinNode, metadata, capabilitiesFinder, pushRight, context);
if ((!pushedRight || !pushedLeft) && (joinType == JoinType.JOIN_INNER || (joinType == JoinType.JOIN_LEFT_OUTER && !pushedLeft))) {
joinNode.setProperty(NodeConstants.Info.JOIN_STRATEGY, JoinStrategyType.ENHANCED_SORT);
}
}
return plan;
}
use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.
the class RuleMergeVirtual method doMerge.
static PlanNode doMerge(PlanNode frame, PlanNode root, boolean beforeDecomposeJoin, QueryMetadataInterface metadata, CapabilitiesFinder capFinder) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
if (frame.hasBooleanProperty(Info.NO_UNNEST)) {
return root;
}
GroupSymbol virtualGroup = frame.getGroups().iterator().next();
// check to see if frame represents a proc relational query.
if (virtualGroup.isProcedure()) {
return root;
}
List<PlanNode> sources = NodeEditor.findAllNodes(frame.getFirstChild(), NodeConstants.Types.SOURCE, NodeConstants.Types.SOURCE);
SymbolMap references = (SymbolMap) frame.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
if (references != null) {
if (!sources.isEmpty()) {
// correlated nested table commands should not be merged
return root;
}
// this is ok only if all of the references go above the correlating join
// currently this check is simplistic - just look at the parent join more nested scenarios won't work
PlanNode parentJoin = NodeEditor.findParent(frame, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE | NodeConstants.Types.GROUP);
if (parentJoin != null && !parentJoin.getGroups().containsAll(GroupsUsedByElementsVisitor.getGroups(references.getValues()))) {
return root;
}
}
PlanNode parentProject = NodeEditor.findParent(frame, NodeConstants.Types.PROJECT);
// Check whether the upper frame is a SELECT INTO
if (parentProject.getProperty(NodeConstants.Info.INTO_GROUP) != null) {
return root;
}
if (!FrameUtil.canConvertAccessPatterns(frame)) {
return root;
}
PlanNode projectNode = frame.getFirstChild();
// Check if lower frame has only a stored procedure execution - this cannot be merged to parent frame
if (FrameUtil.isProcedure(projectNode)) {
return root;
}
SymbolMap symbolMap = (SymbolMap) frame.getProperty(NodeConstants.Info.SYMBOL_MAP);
PlanNode sortNode = NodeEditor.findParent(parentProject, NodeConstants.Types.SORT, NodeConstants.Types.SOURCE);
if (sortNode != null && sortNode.hasBooleanProperty(NodeConstants.Info.UNRELATED_SORT)) {
OrderBy sortOrder = (OrderBy) sortNode.getProperty(NodeConstants.Info.SORT_ORDER);
boolean unrelated = false;
for (OrderByItem item : sortOrder.getOrderByItems()) {
if (!item.isUnrelated()) {
continue;
}
Collection<ElementSymbol> elements = ElementCollectorVisitor.getElements(item.getSymbol(), true);
for (ElementSymbol elementSymbol : elements) {
if (virtualGroup.equals(elementSymbol.getGroupSymbol())) {
unrelated = true;
}
}
}
// the lower frame cannot contain DUP_REMOVE, GROUP, UNION if unrelated
if (unrelated && NodeEditor.findNodePreOrder(frame, NodeConstants.Types.DUP_REMOVE, NodeConstants.Types.PROJECT) != null || NodeEditor.findNodePreOrder(frame, NodeConstants.Types.SET_OP, NodeConstants.Types.SOURCE) != null || NodeEditor.findNodePreOrder(frame, NodeConstants.Types.GROUP, NodeConstants.Types.SOURCE) != null) {
return root;
}
}
PlanNode parentJoin = NodeEditor.findParent(frame, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE | NodeConstants.Types.GROUP);
// 3. if the frame has no sources
if (projectNode.getType() != NodeConstants.Types.PROJECT || NodeEditor.findNodePreOrder(frame.getFirstChild(), NodeConstants.Types.GROUP, NodeConstants.Types.SOURCE | NodeConstants.Types.JOIN) != null || sources.isEmpty()) {
PlanNode parentSource = NodeEditor.findParent(parentProject, NodeConstants.Types.SOURCE);
if (beforeDecomposeJoin && parentSource != null && parentSource.hasProperty(Info.PARTITION_INFO) && !NodeEditor.findAllNodes(frame.getFirstChild(), NodeConstants.Types.SET_OP, NodeConstants.Types.SOURCE).isEmpty()) {
// don't bother to merge until after
return root;
}
root = checkForSimpleProjection(frame, root, parentProject, metadata, capFinder);
if (frame.getParent() == null || !sources.isEmpty() || projectNode.getType() != NodeConstants.Types.PROJECT || parentJoin == null) {
// only consider no sources when the frame is simple and there is a parent join
return root;
}
if (sources.isEmpty() && parentJoin != null) {
JoinType jt = (JoinType) parentJoin.getProperty(Info.JOIN_TYPE);
if (jt.isOuter()) {
// cannot remove if the no source side is an outer side, or if it can change the meaning of the plan
return root;
}
PlanNode joinToTest = parentJoin;
while (joinToTest != null) {
if (FrameUtil.findJoinSourceNode(joinToTest.getFirstChild()).getGroups().contains(virtualGroup)) {
// scan all sources under the other side as there could be a join structure
for (PlanNode node : NodeEditor.findAllNodes(joinToTest.getLastChild(), NodeConstants.Types.SOURCE, NodeConstants.Types.SOURCE)) {
SymbolMap map = (SymbolMap) node.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
if (map != null && GroupsUsedByElementsVisitor.getGroups(map.getValues()).contains(virtualGroup)) {
// TODO: we don't have the logic yet to then replace the correlated references
return root;
}
}
}
joinToTest = NodeEditor.findParent(joinToTest, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE | NodeConstants.Types.GROUP);
}
}
}
if (!checkJoinCriteria(frame.getFirstChild(), virtualGroup, parentJoin)) {
return root;
}
// we don't have to check for null dependent with no source without criteria since there must be a row
if (!checkProjectedSymbols(projectNode, virtualGroup, parentJoin, metadata, sources, !sources.isEmpty() || frame.getParent() != parentJoin, parentProject)) {
// TODO: propagate constants if just inhibited by subquery/non-deterministic expressions
return root;
}
// Otherwise merge should work
// Convert parent frame before merge
Set<GroupSymbol> groups = Collections.emptySet();
if (!sources.isEmpty()) {
groups = FrameUtil.findJoinSourceNode(projectNode).getGroups();
} else if (references != null) {
// convert from correlated form to regular references
RuleMergeCriteria.ReferenceReplacementVisitor rrv = new RuleMergeCriteria.ReferenceReplacementVisitor(references);
for (Map.Entry<ElementSymbol, Expression> entry : symbolMap.asUpdatableMap().entrySet()) {
if (entry.getValue() instanceof Reference) {
Expression ex = rrv.replaceExpression(entry.getValue());
entry.setValue(ex);
} else {
PreOrPostOrderNavigator.doVisit(entry.getValue(), rrv, PreOrPostOrderNavigator.PRE_ORDER);
}
}
}
FrameUtil.convertFrame(frame, virtualGroup, groups, symbolMap.asMap(), metadata);
PlanNode parentBottom = frame.getParent();
prepareFrame(frame);
if (sources.isEmpty() && parentJoin != null) {
// special handling for no sources
PlanNode parent = frame;
List<PlanNode> criteriaNodes = new ArrayList<PlanNode>();
while (parent.getParent() != parentJoin) {
parent = parent.getParent();
if (!parent.hasBooleanProperty(Info.IS_PHANTOM)) {
criteriaNodes.add(parent);
}
}
PlanNode parentNode = parentJoin.getParent();
parentJoin.removeChild(parent);
PlanNode other = parentJoin.getFirstChild();
NodeEditor.removeChildNode(parentNode, parentJoin);
JoinType jt = (JoinType) parentJoin.getProperty(Info.JOIN_TYPE);
if (!jt.isOuter()) {
// if we are not an outer join then the join/parent criteria is effectively
// applied to the other side
List<Criteria> joinCriteria = (List<Criteria>) parentJoin.getProperty(Info.JOIN_CRITERIA);
if (joinCriteria != null) {
for (Criteria crit : joinCriteria) {
PlanNode critNode = RelationalPlanner.createSelectNode(crit, false);
criteriaNodes.add(critNode);
}
}
if (!criteriaNodes.isEmpty()) {
for (PlanNode selectNode : criteriaNodes) {
selectNode.removeAllChildren();
selectNode.removeFromParent();
other.addAsParent(selectNode);
}
}
}
} else {
// Remove top 2 nodes (SOURCE, PROJECT) of virtual group - they're no longer needed
NodeEditor.removeChildNode(parentBottom, frame);
NodeEditor.removeChildNode(parentBottom, projectNode);
}
return root;
}
use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.
the class JoinUtil method getJoinTypePreventingCriteriaOptimization.
public static JoinType getJoinTypePreventingCriteriaOptimization(PlanNode joinNode, Set<GroupSymbol> groups) {
JoinType joinType = (JoinType) joinNode.getProperty(NodeConstants.Info.JOIN_TYPE);
if (!joinType.isOuter()) {
return null;
}
if (joinType.equals(JoinType.JOIN_FULL_OUTER)) {
return joinType;
}
Set<GroupSymbol> innerGroups = getInnerSideJoinNodes(joinNode)[0].getGroups();
for (GroupSymbol group : groups) {
if (innerGroups.contains(group)) {
return joinType;
}
}
return null;
}
use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.
the class JoinUtil method optimizeJoinType.
/**
* Will attempt to optimize the join type based upon the criteria provided.
*
* Returns the new join type if one is found, otherwise null
*
* An outer join can be optimized if criteria that is not dependent upon null values
* is applied on the inner side of the join.
*
* @param critNode
* @param joinNode
* @return
*/
static final JoinType optimizeJoinType(PlanNode critNode, PlanNode joinNode, QueryMetadataInterface metadata, boolean modifyJoin) {
if (critNode.getGroups().isEmpty() || !joinNode.getGroups().containsAll(critNode.getGroups()) || joinNode.hasBooleanProperty(Info.PRESERVE)) {
return null;
}
JoinType joinType = (JoinType) joinNode.getProperty(NodeConstants.Info.JOIN_TYPE);
if (!joinType.isOuter()) {
return null;
}
PlanNode left = joinNode.getFirstChild();
left = FrameUtil.findJoinSourceNode(left);
PlanNode right = joinNode.getLastChild();
right = FrameUtil.findJoinSourceNode(right);
Collection<GroupSymbol> outerGroups = left.getGroups();
Collection<GroupSymbol> innerGroups = right.getGroups();
if (joinType == JoinType.JOIN_RIGHT_OUTER) {
outerGroups = innerGroups;
innerGroups = left.getGroups();
}
// sanity check
if ((joinType == JoinType.JOIN_LEFT_OUTER || joinType == JoinType.JOIN_RIGHT_OUTER) && outerGroups.containsAll(critNode.getGroups())) {
return null;
}
Criteria crit = (Criteria) critNode.getProperty(NodeConstants.Info.SELECT_CRITERIA);
boolean isNullDepdendent = isNullDependent(metadata, innerGroups, crit);
JoinType result = JoinType.JOIN_INNER;
if (joinType == JoinType.JOIN_LEFT_OUTER || joinType == JoinType.JOIN_RIGHT_OUTER) {
if (isNullDepdendent) {
return null;
}
} else {
boolean isNullDepdendentOther = isNullDependent(metadata, outerGroups, crit);
if (isNullDepdendent && isNullDepdendentOther) {
return null;
}
if (isNullDepdendent && !isNullDepdendentOther) {
result = JoinType.JOIN_LEFT_OUTER;
} else if (!isNullDepdendent && isNullDepdendentOther) {
if (modifyJoin) {
JoinUtil.swapJoinChildren(joinNode);
result = JoinType.JOIN_LEFT_OUTER;
}
}
}
if (modifyJoin) {
joinNode.setProperty(NodeConstants.Info.JOIN_TYPE, result);
}
return result;
}
use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.
the class JoinUtil method swapJoinChildren.
/**
* @param joinNode
*/
static void swapJoinChildren(PlanNode joinNode) {
PlanNode leftChild = joinNode.getFirstChild();
joinNode.removeChild(leftChild);
joinNode.addLastChild(leftChild);
List leftExpressions = (List) joinNode.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS);
List rightExpressions = (List) joinNode.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS);
joinNode.setProperty(NodeConstants.Info.LEFT_EXPRESSIONS, rightExpressions);
joinNode.setProperty(NodeConstants.Info.RIGHT_EXPRESSIONS, leftExpressions);
JoinType jt = (JoinType) joinNode.getProperty(NodeConstants.Info.JOIN_TYPE);
joinNode.setProperty(NodeConstants.Info.JOIN_TYPE, jt.getReverseType());
}
Aggregations