use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.
the class RuleChooseDependent method isValidJoin.
/**
* 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
* @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)) {
// $NON-NLS-1$ //$NON-NLS-2$
sourceNode.recordDebugAnnotation("parent join is CROSS", null, "Rejecting dependent join", analysisRecord, null);
return false;
}
if (!joinNode.getExportedCorrelatedReferences().isEmpty()) {
// $NON-NLS-1$ //$NON-NLS-2$
sourceNode.recordDebugAnnotation("parent join has a correlated nested table", null, "Rejecting dependent join", analysisRecord, null);
return false;
}
// Check that join criteria exist
List jcrit = (List) joinNode.getProperty(NodeConstants.Info.JOIN_CRITERIA);
if (jcrit == null || jcrit.size() == 0) {
// $NON-NLS-1$ //$NON-NLS-2$
sourceNode.recordDebugAnnotation("parent join has has no join criteria", null, "Rejecting dependent join", analysisRecord, null);
return false;
}
if (joinNode.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS) == null) {
// $NON-NLS-1$ //$NON-NLS-2$
sourceNode.recordDebugAnnotation("parent join has no equa-join predicates", null, "Rejecting dependent join", analysisRecord, null);
return false;
}
return true;
}
use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.
the class RuleChooseDependent method findCandidate.
/**
* 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;
}
if (j.hasNext()) {
JoinType jtype = (JoinType) joinNode.getProperty(NodeConstants.Info.JOIN_TYPE);
if (!jtype.isOuter()) {
candidate.leftCandidate = true;
candidates.add(candidate);
}
} else {
candidate.rightCandidate = true;
if (!candidate.leftCandidate) {
candidates.add(candidate);
}
}
}
}
return candidates;
}
use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.
the class RuleChooseJoinStrategy method chooseJoinStrategy.
/**
* Determines whether this node should be converted to a merge join node
* @param joinNode The join node
* @param metadata The metadata
*/
static void chooseJoinStrategy(PlanNode joinNode, QueryMetadataInterface metadata) {
// Check that join is an inner join
JoinType jtype = (JoinType) joinNode.getProperty(NodeConstants.Info.JOIN_TYPE);
if (jtype.equals(JoinType.JOIN_CROSS)) {
return;
}
PlanNode leftChild = joinNode.getFirstChild();
leftChild = FrameUtil.findJoinSourceNode(leftChild);
if (leftChild == null) {
return;
}
PlanNode rightChild = joinNode.getLastChild();
rightChild = FrameUtil.findJoinSourceNode(rightChild);
if (rightChild == null) {
return;
}
Collection<GroupSymbol> leftGroups = leftChild.getGroups();
Collection<GroupSymbol> rightGroups = rightChild.getGroups();
List<Expression> leftExpressions = new ArrayList<Expression>();
List<Expression> rightExpressions = new ArrayList<Expression>();
// Check that join criteria are all equality criteria and that there are elements from
// no more than one group on each side
List<Criteria> crits = (List<Criteria>) joinNode.getProperty(NodeConstants.Info.JOIN_CRITERIA);
filterOptionalCriteria(crits, true);
if (crits.isEmpty() && jtype == JoinType.JOIN_INNER) {
joinNode.setProperty(NodeConstants.Info.JOIN_TYPE, JoinType.JOIN_CROSS);
return;
}
List<Criteria> nonEquiJoinCriteria = new ArrayList<Criteria>();
separateCriteria(leftGroups, rightGroups, leftExpressions, rightExpressions, crits, nonEquiJoinCriteria);
if (!leftExpressions.isEmpty()) {
joinNode.setProperty(NodeConstants.Info.LEFT_EXPRESSIONS, createExpressionSymbols(leftExpressions));
joinNode.setProperty(NodeConstants.Info.RIGHT_EXPRESSIONS, createExpressionSymbols(rightExpressions));
// make use of the one side criteria
joinNode.setProperty(NodeConstants.Info.JOIN_STRATEGY, JoinStrategyType.MERGE);
joinNode.setProperty(NodeConstants.Info.NON_EQUI_JOIN_CRITERIA, nonEquiJoinCriteria);
} else if (nonEquiJoinCriteria.isEmpty()) {
joinNode.setProperty(NodeConstants.Info.JOIN_CRITERIA, nonEquiJoinCriteria);
if (joinNode.getProperty(NodeConstants.Info.JOIN_TYPE) == JoinType.JOIN_INNER) {
joinNode.setProperty(NodeConstants.Info.JOIN_TYPE, JoinType.JOIN_CROSS);
}
}
}
use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.
the class RulePushLimit method canPushLimit.
private boolean canPushLimit(PlanNode[] rootNode, PlanNode limitNode, List<PlanNode> limitNodes, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, AnalysisRecord record, CommandContext context) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
PlanNode child = limitNode.getFirstChild();
if (child == null || child.getChildCount() == 0) {
return false;
}
Expression parentLimit = (Expression) limitNode.getProperty(NodeConstants.Info.MAX_TUPLE_LIMIT);
Expression parentOffset = (Expression) limitNode.getProperty(NodeConstants.Info.OFFSET_TUPLE_COUNT);
switch(child.getType()) {
case NodeConstants.Types.TUPLE_LIMIT:
{
// combine the limits
Expression childLimit = (Expression) child.getProperty(NodeConstants.Info.MAX_TUPLE_LIMIT);
Expression childOffset = (Expression) child.getProperty(NodeConstants.Info.OFFSET_TUPLE_COUNT);
combineLimits(limitNode, metadata, parentLimit, parentOffset, childLimit, childOffset);
if (child.hasBooleanProperty(Info.IS_NON_STRICT)) {
limitNode.setProperty(Info.IS_NON_STRICT, true);
}
NodeEditor.removeChildNode(limitNode, child);
limitNodes.remove(child);
return canPushLimit(rootNode, limitNode, limitNodes, metadata, capFinder, record, context);
}
case NodeConstants.Types.SET_OP:
{
if (!canPushToBranches(limitNode, child)) {
return false;
}
// distribute the limit
List<PlanNode> grandChildren = new LinkedList<PlanNode>(child.getChildren());
for (PlanNode grandChild : grandChildren) {
addBranchLimit(limitNode, limitNodes, metadata, parentLimit, parentOffset, grandChild);
}
return false;
}
case NodeConstants.Types.JOIN:
if (parentLimit == null) {
return false;
}
JoinType joinType = (JoinType) child.getProperty(Info.JOIN_TYPE);
boolean pushLeft = false;
boolean pushRight = false;
if (joinType == JoinType.JOIN_CROSS) {
pushLeft = true;
pushRight = true;
} else if (joinType == JoinType.JOIN_LEFT_OUTER || joinType == JoinType.JOIN_FULL_OUTER) {
// we're allowed to do this based upon two conditions
// 1 - we're not going to further change the join type/structure
// 2 - outer results will be produced using the left product first
pushLeft = true;
}
if (pushLeft && !FrameUtil.findJoinSourceNode(child.getLastChild()).hasProperty(NodeConstants.Info.CORRELATED_REFERENCES)) {
PlanNode newLimit = newLimit(limitNode);
newLimit.setProperty(NodeConstants.Info.MAX_TUPLE_LIMIT, op(SourceSystemFunctions.ADD_OP, parentLimit, parentOffset, metadata.getFunctionLibrary()));
child.getFirstChild().addAsParent(newLimit);
newLimit.setProperty(NodeConstants.Info.OUTPUT_COLS, newLimit.getFirstChild().getProperty(NodeConstants.Info.OUTPUT_COLS));
limitNodes.add(newLimit);
}
if (pushRight) {
PlanNode newLimit = newLimit(limitNode);
newLimit.setProperty(NodeConstants.Info.MAX_TUPLE_LIMIT, op(SourceSystemFunctions.ADD_OP, parentLimit, parentOffset, metadata.getFunctionLibrary()));
child.getLastChild().addAsParent(newLimit);
newLimit.setProperty(NodeConstants.Info.OUTPUT_COLS, newLimit.getFirstChild().getProperty(NodeConstants.Info.OUTPUT_COLS));
limitNodes.add(newLimit);
}
return false;
case NodeConstants.Types.ACCESS:
{
raiseAccessOverLimit(rootNode[0], child, metadata, capFinder, limitNode, record);
return false;
}
case NodeConstants.Types.PROJECT:
{
return child.getProperty(NodeConstants.Info.INTO_GROUP) == null && !child.hasProperty(Info.HAS_WINDOW_FUNCTIONS);
}
case NodeConstants.Types.SOURCE:
{
return canPushThroughView(child);
}
case NodeConstants.Types.SELECT:
case NodeConstants.Types.DUP_REMOVE:
return limitNode.hasBooleanProperty(Info.IS_NON_STRICT);
case NodeConstants.Types.SORT:
switch(child.getFirstChild().getType()) {
case NodeConstants.Types.SOURCE:
{
if (canPushThroughView(child.getFirstChild())) {
PlanNode sourceNode = child.getFirstChild();
NodeEditor.removeChildNode(limitNode, child);
NodeEditor.removeChildNode(limitNode.getParent(), limitNode);
limitNode.setProperty(NodeConstants.Info.OUTPUT_COLS, sourceNode.getFirstChild().getProperty(NodeConstants.Info.OUTPUT_COLS));
child.setProperty(NodeConstants.Info.OUTPUT_COLS, sourceNode.getFirstChild().getProperty(NodeConstants.Info.OUTPUT_COLS));
// project the order through the source - which needs to preserve the aliasing
HashMap<ElementSymbol, Expression> symbolMap = new HashMap<ElementSymbol, Expression>();
List<ElementSymbol> virtual = ((SymbolMap) sourceNode.getProperty(NodeConstants.Info.SYMBOL_MAP)).getKeys();
List<Expression> projected = (List<Expression>) NodeEditor.findNodePreOrder(sourceNode, NodeConstants.Types.PROJECT).getProperty(Info.PROJECT_COLS);
for (int i = 0; i < virtual.size(); i++) {
symbolMap.put(virtual.get(i), projected.get(i));
}
// map the expression directly to avoid issues with naming logic in the general node conversion
OrderBy orderBy = (OrderBy) child.getProperty(Info.SORT_ORDER);
for (OrderByItem item : orderBy.getOrderByItems()) {
Expression ex = symbolMap.get(item.getSymbol());
if (ex != null) {
item.setSymbol(ex);
item.setExpressionPosition(projected.indexOf(ex));
}
}
FrameUtil.convertNode(child, sourceNode.getGroups().iterator().next(), null, symbolMap, metadata, false);
sourceNode.getFirstChild().addAsParent(child);
child.addAsParent(limitNode);
// push again
limitNodes.add(limitNode);
return false;
}
}
case NodeConstants.Types.SET_OP:
{
PlanNode setOp = child.getFirstChild();
if (!canPushToBranches(limitNode, setOp)) {
return false;
}
OrderBy parentOrderBy = (OrderBy) child.getProperty(NodeConstants.Info.SORT_ORDER);
distributeLimit(limitNode, setOp, parentOrderBy, metadata, limitNodes, parentLimit, parentOffset, capFinder, context);
break;
}
case NodeConstants.Types.JOIN:
{
if (parentLimit == null) {
return false;
}
PlanNode join = child.getFirstChild();
JoinType jt = (JoinType) join.getProperty(NodeConstants.Info.JOIN_TYPE);
if (!jt.isOuter()) {
return false;
}
if ((jt == JoinType.JOIN_FULL_OUTER || jt == JoinType.JOIN_LEFT_OUTER) && join.getFirstChild().getGroups().containsAll(child.getGroups()) && !FrameUtil.findJoinSourceNode(join.getLastChild()).hasProperty(NodeConstants.Info.CORRELATED_REFERENCES)) {
pushOrderByAndLimit(limitNode, limitNodes, metadata, capFinder, context, child, parentLimit, parentOffset, join.getFirstChild());
} else if (jt == JoinType.JOIN_FULL_OUTER && join.getLastChild().getGroups().containsAll(child.getGroups())) {
pushOrderByAndLimit(limitNode, limitNodes, metadata, capFinder, context, child, parentLimit, parentOffset, join.getLastChild());
}
break;
}
case NodeConstants.Types.PROJECT:
{
rootNode[0] = RulePlanSorts.checkForProjectOptimization(child, rootNode[0], metadata, capFinder, record, context);
if (child.getFirstChild().getType() != NodeConstants.Types.PROJECT && NodeEditor.findParent(child, NodeConstants.Types.ACCESS) == null) {
return canPushLimit(rootNode, limitNode, limitNodes, metadata, capFinder, record, context);
}
break;
}
case NodeConstants.Types.ACCESS:
{
if (RuleRaiseAccess.canRaiseOverSort(child.getFirstChild(), metadata, capFinder, child, null, false, context)) {
NodeEditor.removeChildNode(limitNode, child);
limitNode.getFirstChild().getFirstChild().addAsParent(child);
// try to keep pushing
limitNodes.add(limitNode);
return false;
}
}
}
return false;
default:
{
return false;
}
}
}
use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.
the class RulePushSelectCriteria method examinePath.
/**
* Examine the path from crit node to source node to determine how far down a node
* can be pushed.
* @return destinationChild
*/
PlanNode examinePath(PlanNode critNode, PlanNode sourceNode, QueryMetadataInterface metadata, CapabilitiesFinder capFinder) throws QueryPlannerException, TeiidComponentException {
// Walk from source node up to critNode to build list of intervening nodes
Stack<PlanNode> path = new Stack<PlanNode>();
PlanNode currentNode = sourceNode.getParent();
while (currentNode != critNode) {
path.push(currentNode);
currentNode = currentNode.getParent();
}
// Examine path in reverse order (by popping stack)
while (!path.empty()) {
currentNode = path.pop();
// Look for situations where we don't allow SELECT to be pushed
switch(currentNode.getType()) {
case NodeConstants.Types.ACCESS:
try {
if (!RuleRaiseAccess.canRaiseOverSelect(currentNode, metadata, capFinder, critNode, null)) {
return currentNode;
}
if (!RuleRaiseAccess.checkConformedSubqueries(currentNode, critNode, this.createdNodes == null)) {
return currentNode;
}
if (this.createdNodes == null) {
satisfyConditions(critNode, currentNode, metadata);
}
if (isDependentFinalDestination(critNode, currentNode)) {
// once a dependent crit node is pushed, don't bother pushing it further into the command
// dependent access node will use this as an assumption for where dependent sets can appear in the command
markDependent(critNode, currentNode, metadata, capFinder);
return currentNode.getFirstChild();
}
} catch (QueryMetadataException e) {
throw new QueryPlannerException(QueryPlugin.Event.TEIID30267, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30267, currentNode.getGroups()));
}
break;
case NodeConstants.Types.JOIN:
// pushing below a join is not necessary under an access node
if (this.createdNodes == null && NodeEditor.findParent(currentNode, NodeConstants.Types.ACCESS) != null) {
return currentNode;
}
// Check whether this criteria is on the inner side of an outer join.
// If so, can't push past the join
JoinType jt = JoinUtil.getJoinTypePreventingCriteriaOptimization(currentNode, critNode);
if (jt != null) {
// if we successfully optimized then this should no longer inhibit the criteria from being pushed
// since the criteria must then be on the outer side of an outer join or on either side of an inner join
JoinType optimized = JoinUtil.optimizeJoinType(critNode, currentNode, metadata, this.createdNodes == null);
if (optimized == null || optimized.isOuter()) {
return currentNode;
}
}
if (this.createdNodes == null) {
satisfyConditions(critNode, currentNode, metadata);
}
break;
default:
if (FrameUtil.isOrderedOrStrictLimit(currentNode)) {
return currentNode;
}
}
}
return sourceNode;
}
Aggregations