use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RuleAssignOutputElements method execute.
/**
* Execute the rule. This rule is executed exactly once during every planning
* call. The plan is modified in place - only properties are manipulated, structure
* is unchanged.
* @param plan The plan to execute rule on
* @param metadata The metadata interface
* @param rules The rule stack, not modified
* @return The updated plan
*/
public PlanNode execute(PlanNode plan, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
// Record project node output columns in top node
PlanNode projectNode = NodeEditor.findNodePreOrder(plan, NodeConstants.Types.PROJECT);
if (projectNode == null) {
return plan;
}
List<Expression> projectCols = (List<Expression>) projectNode.getProperty(NodeConstants.Info.PROJECT_COLS);
assignOutputElements(plan, projectCols, metadata, capFinder, rules, analysisRecord, context);
return plan;
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RuleAssignOutputElements method assignOutputElements.
/**
* <p>Assign the output elements at a particular node and recurse the tree. The
* outputElements needed from above the node have been collected in
* outputElements.</p>
*
* <p>SOURCE nodes: If we find a SOURCE node, this must define the top
* of a virtual group. Physical groups can be identified by ACCESS nodes
* at this point in the planning stage. So, we filter the virtual elements
* in the virtual source based on the required output elements.</p>
*
* <p>SET_OP nodes: If we hit a SET_OP node, this must be a union. Unions
* require a lot of special care. Unions have many branches and the projected
* elements in each branch are "equivalent" in terms of nodes above the union.
* This means that any filtering must occur in an identical way in all branches
* of a union.</p>
*
* @param root Node to assign
* @param outputElements Output elements needed for this node
* @param metadata Metadata implementation
*/
private void assignOutputElements(PlanNode root, List<Expression> outputElements, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
int nodeType = root.getType();
// Update this node's output columns based on parent's columns
List<Expression> oldOutput = (List<Expression>) root.setProperty(NodeConstants.Info.OUTPUT_COLS, outputElements);
if (root.getChildCount() == 0) {
// update temp access
if (root.getType() == NodeConstants.Types.SOURCE && root.getGroups().size() == 1) {
GroupSymbol gs = root.getGroups().iterator().next();
if (gs.getMetadataID() instanceof TempMetadataID) {
for (Expression ex : outputElements) {
if (ex instanceof ElementSymbol) {
Object id = ((ElementSymbol) ex).getMetadataID();
if (id instanceof TempMetadataID) {
((TempMetadataID) id).setAccessed(true);
}
}
}
}
}
return;
}
switch(nodeType) {
case NodeConstants.Types.ACCESS:
Command command = FrameUtil.getNonQueryCommand(root);
if (command instanceof StoredProcedure) {
// if the access node represents a stored procedure, then we can't actually change the output symbols
root.setProperty(NodeConstants.Info.OUTPUT_COLS, command.getProjectedSymbols());
} else {
ProcessorPlan plan = FrameUtil.getNestedPlan(root);
if (plan != null && (command == null || !RelationalNodeUtil.isUpdate(command))) {
// nested with clauses are handled as sub plans, which have a fixed set of output symbols
root.setProperty(NodeConstants.Info.OUTPUT_COLS, ResolverUtil.resolveElementsInGroup(root.getGroups().iterator().next(), metadata));
}
if (checkSymbols) {
Object modelId = RuleRaiseAccess.getModelIDFromAccess(root, metadata);
for (Expression symbol : outputElements) {
if (!RuleRaiseAccess.canPushSymbol(symbol, true, modelId, metadata, capFinder, analysisRecord)) {
throw new QueryPlannerException(QueryPlugin.Event.TEIID30258, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30258, symbol, modelId));
}
}
}
if (NodeEditor.findParent(root, NodeConstants.Types.PROJECT, NodeConstants.Types.SOURCE) != null) {
// there's a chance that partial projection was used. we are not a defacto project node
// take credit for creating anything that is not an element symbol
LinkedHashSet<Expression> filteredElements = new LinkedHashSet<Expression>();
for (Expression element : outputElements) {
if (element instanceof ElementSymbol) {
filteredElements.add(element);
} else {
filteredElements.addAll(ElementCollectorVisitor.getElements(element, false));
}
}
outputElements = new ArrayList<Expression>(filteredElements);
}
}
assignOutputElements(root.getLastChild(), outputElements, metadata, capFinder, rules, analysisRecord, context);
break;
case NodeConstants.Types.DUP_REMOVE:
// targeted optimization based upon swapping the dup remove for a limit 1
// TODO: may need to also check for grouping over constants
boolean allConstants = true;
for (Expression ex : outputElements) {
if (!(EvaluatableVisitor.willBecomeConstant(SymbolMap.getExpression(ex)))) {
allConstants = false;
break;
}
}
if (allConstants && addLimit(rules, root, metadata, capFinder)) {
// TODO we could more gracefully handle the !addLimit case
PlanNode parent = root.getParent();
if (parent != null) {
NodeEditor.removeChildNode(root.getParent(), root);
execute(parent, metadata, capFinder, rules, analysisRecord, context);
return;
}
}
case NodeConstants.Types.SORT:
// correct expression positions and update the unrelated flag
OrderBy order = (OrderBy) root.getProperty(NodeConstants.Info.SORT_ORDER);
if (order != null && (oldOutput == null || !oldOutput.equals(outputElements))) {
outputElements = new ArrayList<Expression>(outputElements);
boolean hasUnrelated = false;
for (OrderByItem item : order.getOrderByItems()) {
int index = outputElements.indexOf(item.getSymbol());
if (index != -1) {
item.setExpressionPosition(index);
} else {
hasUnrelated = true;
outputElements.add(item.getSymbol());
}
}
if (!hasUnrelated) {
root.setProperty(NodeConstants.Info.UNRELATED_SORT, false);
} else {
root.setProperty(NodeConstants.Info.UNRELATED_SORT, true);
}
}
assignOutputElements(root.getLastChild(), outputElements, metadata, capFinder, rules, analysisRecord, context);
break;
case NodeConstants.Types.TUPLE_LIMIT:
assignOutputElements(root.getLastChild(), outputElements, metadata, capFinder, rules, analysisRecord, context);
break;
case NodeConstants.Types.SOURCE:
{
outputElements = (List<Expression>) determineSourceOutput(root, outputElements, metadata, capFinder);
if (!finalRun && root.getProperty(Info.PARTITION_INFO) != null && NodeEditor.findParent(root, NodeConstants.Types.JOIN) != null) {
GroupSymbol group = root.getGroups().iterator().next();
Object modelId = RuleDecomposeJoin.getEffectiveModelId(metadata, group);
String name = metadata.getExtensionProperty(modelId, RuleDecomposeJoin.IMPLICIT_PARTITION_COLUMN_NAME, true);
if (name != null) {
// keep projecting the implicit partitioning column through the source so that it can
// be used in the decomposition logic
ElementSymbol es = new ElementSymbol(name, group);
if (!outputElements.contains(es)) {
es.setMetadataID(metadata.getElementID(es.getName()));
outputElements.add(es);
}
}
}
root.setProperty(NodeConstants.Info.OUTPUT_COLS, outputElements);
List<Expression> childElements = filterVirtualElements(root, outputElements, metadata);
assignOutputElements(root.getFirstChild(), childElements, metadata, capFinder, rules, analysisRecord, context);
break;
}
case NodeConstants.Types.SET_OP:
{
for (PlanNode childNode : root.getChildren()) {
PlanNode projectNode = NodeEditor.findNodePreOrder(childNode, NodeConstants.Types.PROJECT);
List<Expression> projectCols = (List<Expression>) projectNode.getProperty(NodeConstants.Info.PROJECT_COLS);
assignOutputElements(childNode, projectCols, metadata, capFinder, rules, analysisRecord, context);
}
break;
}
default:
{
PlanNode sortNode = null;
if (root.getType() == NodeConstants.Types.PROJECT) {
GroupSymbol intoGroup = (GroupSymbol) root.getProperty(NodeConstants.Info.INTO_GROUP);
if (intoGroup != null) {
// if this is a project into, treat the nodes under the source as a new plan root
PlanNode intoRoot = NodeEditor.findNodePreOrder(root, NodeConstants.Types.SOURCE);
execute(intoRoot.getFirstChild(), metadata, capFinder, rules, analysisRecord, context);
return;
}
List<Expression> projectCols = outputElements;
sortNode = NodeEditor.findParent(root, NodeConstants.Types.SORT, NodeConstants.Types.SOURCE);
if (finalRun && sortNode != null && sortNode.hasBooleanProperty(NodeConstants.Info.UNRELATED_SORT)) {
root.getGroups().clear();
root.addGroups(GroupsUsedByElementsVisitor.getGroups(projectCols));
root.addGroups(GroupsUsedByElementsVisitor.getGroups(root.getCorrelatedReferenceElements()));
}
root.setProperty(NodeConstants.Info.PROJECT_COLS, projectCols);
if (root.hasBooleanProperty(Info.HAS_WINDOW_FUNCTIONS)) {
Set<WindowFunction> windowFunctions = getWindowFunctions(projectCols);
if (windowFunctions.isEmpty()) {
root.setProperty(Info.HAS_WINDOW_FUNCTIONS, false);
}
}
}
List<Expression> requiredInput = collectRequiredInputSymbols(root, metadata, capFinder);
// targeted optimization for unnecessary aggregation
if (root.getType() == NodeConstants.Types.GROUP && root.hasBooleanProperty(Info.IS_OPTIONAL) && NodeEditor.findParent(root, NodeConstants.Types.ACCESS) == null) {
PlanNode parent = removeGroupBy(root, metadata);
if (!root.hasCollectionProperty(Info.GROUP_COLS)) {
// just lob off everything under the projection
PlanNode project = NodeEditor.findNodePreOrder(parent, NodeConstants.Types.PROJECT);
project.removeAllChildren();
} else if (!addLimit(rules, parent, metadata, capFinder)) {
// $NON-NLS-1$
throw new AssertionError("expected limit node to be added");
}
execute(parent, metadata, capFinder, rules, analysisRecord, context);
return;
}
// Call children recursively
if (root.getChildCount() == 1) {
assignOutputElements(root.getLastChild(), requiredInput, metadata, capFinder, rules, analysisRecord, context);
if (!finalRun && root.getType() == NodeConstants.Types.PROJECT && sortNode != null && sortNode.hasBooleanProperty(NodeConstants.Info.UNRELATED_SORT)) {
// if this is the initial rule run, remove unrelated order to preserve the original projection
OrderBy elements = (OrderBy) sortNode.getProperty(NodeConstants.Info.SORT_ORDER);
outputElements = new ArrayList<Expression>(outputElements);
for (OrderByItem item : elements.getOrderByItems()) {
if (item.getExpressionPosition() == -1) {
outputElements.remove(item.getSymbol());
}
}
root.setProperty(NodeConstants.Info.PROJECT_COLS, outputElements);
}
} else {
// determine which elements go to each side of the join
for (PlanNode childNode : root.getChildren()) {
Set<GroupSymbol> filterGroups = FrameUtil.findJoinSourceNode(childNode).getGroups();
List<Expression> filteredElements = filterElements(requiredInput, filterGroups);
// Call child recursively
assignOutputElements(childNode, filteredElements, metadata, capFinder, rules, analysisRecord, context);
}
}
}
}
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RulePushLargeIn method execute.
@Override
public PlanNode execute(PlanNode plan, QueryMetadataInterface metadata, CapabilitiesFinder capabilitiesFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
for (PlanNode critNode : NodeEditor.findAllNodes(plan, NodeConstants.Types.SELECT, NodeConstants.Types.ACCESS)) {
if (critNode.hasBooleanProperty(NodeConstants.Info.IS_HAVING) || critNode.hasBooleanProperty(NodeConstants.Info.IS_PHANTOM)) {
continue;
}
Criteria crit = (Criteria) critNode.getProperty(NodeConstants.Info.SELECT_CRITERIA);
if (!(crit instanceof SetCriteria)) {
continue;
}
SetCriteria setCriteria = (SetCriteria) crit;
if (setCriteria.isNegated() || !setCriteria.isAllConstants()) {
continue;
}
// we need to be directly over an access node
PlanNode childAccess = critNode.getFirstChild();
accessLoop: while (true) {
switch(childAccess.getType()) {
case NodeConstants.Types.ACCESS:
break accessLoop;
case NodeConstants.Types.SELECT:
break;
default:
break accessLoop;
}
childAccess = childAccess.getFirstChild();
}
if (childAccess.getType() != NodeConstants.Types.ACCESS) {
continue;
}
// use a dummy value to test if we can raise
critNode.setProperty(NodeConstants.Info.SELECT_CRITERIA, new SetCriteria(setCriteria.getExpression(), Collections.EMPTY_LIST));
boolean canRaise = RuleRaiseAccess.canRaiseOverSelect(childAccess, metadata, capabilitiesFinder, critNode, analysisRecord);
critNode.setProperty(NodeConstants.Info.SELECT_CRITERIA, crit);
if (!canRaise) {
continue;
}
// push the crit node and mark as dependent set
critNode.getParent().replaceChild(critNode, critNode.getFirstChild());
childAccess.addAsParent(critNode);
RuleRaiseAccess.performRaise(plan, childAccess, critNode);
childAccess.setProperty(NodeConstants.Info.IS_DEPENDENT_SET, true);
childAccess.setProperty(NodeConstants.Info.EST_CARDINALITY, null);
}
return plan;
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.
the class RulePushLimit method addBranchLimit.
private void addBranchLimit(PlanNode limitNode, List<PlanNode> limitNodes, QueryMetadataInterface metadata, Expression parentLimit, Expression parentOffset, PlanNode grandChild) {
PlanNode newLimit = newLimit(limitNode);
newLimit.setProperty(NodeConstants.Info.MAX_TUPLE_LIMIT, op(SourceSystemFunctions.ADD_OP, parentLimit, parentOffset, metadata.getFunctionLibrary()));
grandChild.addAsParent(newLimit);
newLimit.setProperty(NodeConstants.Info.OUTPUT_COLS, newLimit.getFirstChild().getProperty(NodeConstants.Info.OUTPUT_COLS));
if (NodeEditor.findParent(newLimit, NodeConstants.Types.ACCESS) == null) {
limitNodes.add(newLimit);
}
if (grandChild.getType() == NodeConstants.Types.SET_OP) {
newLimit.setProperty(Info.IS_COPIED, true);
}
newLimit.setProperty(Info.IS_PUSHED, true);
}
use of org.teiid.query.optimizer.relational.plantree.PlanNode 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;
}
Aggregations