*/
static PlanNode raiseAccessNode(PlanNode rootNode, PlanNode accessNode, QueryMetadataInterface metadata,
CapabilitiesFinder capFinder, boolean afterJoinPlanning, AnalysisRecord record)
throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
PlanNode parentNode = accessNode.getParent();
if(parentNode == null) {
// Nothing to raise over
return null;
}
Object modelID = getModelIDFromAccess(accessNode, metadata);
if(modelID == null) {
return null;
}
switch(parentNode.getType()) {
case NodeConstants.Types.JOIN:
{
modelID = canRaiseOverJoin(modelID, parentNode, metadata, capFinder, afterJoinPlanning, record);
if(modelID != null) {
raiseAccessOverJoin(parentNode, modelID, true);
return rootNode;
}
return null;
}
case NodeConstants.Types.PROJECT:
{
// Check that the PROJECT contains only functions that can be pushed
List projectCols = (List) parentNode.getProperty(NodeConstants.Info.PROJECT_COLS);
for (int i = 0; i < projectCols.size(); i++) {
SingleElementSymbol symbol = (SingleElementSymbol)projectCols.get(i);
if(! canPushSymbol(symbol, true, modelID, metadata, capFinder, record)) {
return null;
}
}
/*
* TODO: this creates an extraneous project node in many circumstances.
* However we don't actually support project in this case, so allowing it to be pushed
* causes problems with stored procedures and the assumptions made for proc/relational
* planning.
*/
if (FrameUtil.isProcedure(parentNode)) {
return null;
}
return performRaise(rootNode, accessNode, parentNode);
}
case NodeConstants.Types.DUP_REMOVE:
{
// If model supports the support constant parameter, then move access node
if(!CapabilitiesUtil.supportsSelectDistinct(modelID, metadata, capFinder)) {
recordDebug("cannot push dupremove, since distinct is not supported by source", parentNode, record); //$NON-NLS-1$
return null;
}
//TODO: this check is too specific the columns could be used in expressions that are comparable
if (!CapabilitiesUtil.checkElementsAreSearchable((List)NodeEditor.findNodePreOrder(parentNode, NodeConstants.Types.PROJECT).getProperty(NodeConstants.Info.PROJECT_COLS), metadata, SupportConstants.Element.SEARCHABLE_COMPARE)) {
recordDebug("cannot push dupremove, since not all columns are comparable at the source", parentNode, record); //$NON-NLS-1$
return null;
}
return performRaise(rootNode, accessNode, parentNode);
}
case NodeConstants.Types.SORT:
{
if (canRaiseOverSort(accessNode, metadata, capFinder, parentNode, record, false)) {
return performRaise(rootNode, accessNode, parentNode);
}
return null;
}
case NodeConstants.Types.GROUP:
{
Set<AggregateSymbol> aggregates = RulePushAggregates.collectAggregates(parentNode);
if (canRaiseOverGroupBy(parentNode, accessNode, aggregates, metadata, capFinder, record)) {
return performRaise(rootNode, accessNode, parentNode);
}
return null;
}
case NodeConstants.Types.SET_OP:
if (!canRaiseOverSetQuery(parentNode, metadata, capFinder)) {
return null;
}
for (PlanNode node : new ArrayList<PlanNode>(parentNode.getChildren())) {
if (node == accessNode) {
continue;
}
NodeEditor.removeChildNode(parentNode, node);
}
accessNode.getGroups().clear();
return performRaise(rootNode, accessNode, parentNode);
case NodeConstants.Types.SELECT:
{
if (parentNode.hasBooleanProperty(NodeConstants.Info.IS_DEPENDENT_SET)) {
return null;
}
if (canRaiseOverSelect(accessNode, metadata, capFinder, parentNode, record)) {
RulePushSelectCriteria.satisfyAccessPatterns(parentNode, accessNode);
return performRaise(rootNode, accessNode, parentNode);
}
//determine if we should push the select back up
if (parentNode.getParent() == null) {
return null;
}
PlanNode selectRoot = parentNode;
while (selectRoot.getParent() != null && selectRoot.getParent().getType() == NodeConstants.Types.SELECT) {
selectRoot = selectRoot.getParent();
}
if (selectRoot.getParent() == null || (selectRoot.getParent().getType() & (NodeConstants.Types.PROJECT|NodeConstants.Types.GROUP)) == selectRoot.getParent().getType()) {
return null;
}
PlanNode grandParent = selectRoot.getParent();
boolean isLeft = false;
isLeft = grandParent.getFirstChild() == selectRoot;
if (grandParent.getType() == NodeConstants.Types.JOIN) {
JoinType jt = (JoinType)grandParent.getProperty(NodeConstants.Info.JOIN_TYPE);
if (jt == JoinType.JOIN_FULL_OUTER || (jt == JoinType.JOIN_LEFT_OUTER && !isLeft)) {
return null;
}
}
grandParent.removeChild(selectRoot);
if (isLeft) {
grandParent.addFirstChild(accessNode);
} else {
grandParent.addLastChild(accessNode);
}
PlanNode newParent = grandParent.getParent();
//TODO: use costing or heuristics instead of always raising
PlanNode newRoot = raiseAccessNode(rootNode, accessNode, metadata, capFinder, afterJoinPlanning, record);
if (newRoot == null) {
//return the tree to its original state
parentNode.addFirstChild(accessNode);
if (isLeft) {
grandParent.addFirstChild(selectRoot);
} else {
grandParent.addLastChild(selectRoot);
}
} else {
//attach the select nodes above the access node
accessNode = grandParent.getParent();
if (newParent != null) {
isLeft = newParent.getFirstChild() == accessNode;
if (isLeft) {
newParent.addFirstChild(selectRoot);
} else {
newParent.addLastChild(selectRoot);
}
} else {
newRoot = selectRoot;
}
parentNode.addFirstChild(accessNode);
return newRoot;
}
return null;
}
case NodeConstants.Types.SOURCE:
{
//if a source has access patterns that are unsatisfied, then the raise cannot occur
if (parentNode.hasCollectionProperty(NodeConstants.Info.ACCESS_PATTERNS)) {
return null;
}
SymbolMap references = (SymbolMap)parentNode.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
if (references != null) {
return null;
}
//raise only if there is no intervening project into
PlanNode parentProject = NodeEditor.findParent(parentNode, NodeConstants.Types.PROJECT);
GroupSymbol intoGroup = (GroupSymbol)parentProject.getProperty(NodeConstants.Info.INTO_GROUP);
if (intoGroup != null && parentProject.getParent() == null) {
if (CapabilitiesUtil.supports(Capability.INSERT_WITH_QUERYEXPRESSION, modelID, metadata, capFinder) && CapabilitiesUtil.isSameConnector(modelID, metadata.getModelID(intoGroup.getMetadataID()), metadata, capFinder)) {
rootNode = performRaise(rootNode, accessNode, parentNode);
return performRaise(rootNode, accessNode, parentProject);
}
return null;