use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.
the class RuleRaiseAccess method raiseAccessNode.
/**
* @return null if nothing changed, and a new plan root if something changed
*/
static PlanNode raiseAccessNode(PlanNode rootNode, PlanNode accessNode, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, boolean afterJoinPlanning, AnalysisRecord record, CommandContext context) 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, context);
if (modelID != null && checkConformedSubqueries(accessNode, parentNode, true)) {
raiseAccessOverJoin(parentNode, accessNode, modelID, capFinder, metadata, true);
return rootNode;
}
return null;
}
case NodeConstants.Types.PROJECT:
{
if (CapabilitiesUtil.supports(Capability.NO_PROJECTION, modelID, metadata, capFinder)) {
return null;
}
// Check that the PROJECT contains only functions that can be pushed
List<Expression> projectCols = (List) parentNode.getProperty(NodeConstants.Info.PROJECT_COLS);
for (int i = 0; i < projectCols.size(); i++) {
Expression symbol = 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;
}
PlanNode orderBy = NodeEditor.findParent(parentNode, NodeConstants.Types.SORT, NodeConstants.Types.SOURCE);
if (orderBy != null && orderBy.hasBooleanProperty(Info.UNRELATED_SORT) && !canRaiseOverSort(accessNode, metadata, capFinder, orderBy, record, false, context)) {
// this project node logically has the responsibility of creating the sort keys
return null;
}
if (accessNode.hasBooleanProperty(Info.IS_MULTI_SOURCE)) {
List<WindowFunction> windowFunctions = new ArrayList<WindowFunction>(2);
for (Expression ex : projectCols) {
AggregateSymbolCollectorVisitor.getAggregates(ex, null, null, null, windowFunctions, null);
if (!windowFunctions.isEmpty()) {
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)) {
// $NON-NLS-1$ //$NON-NLS-2$
parentNode.recordDebugAnnotation("distinct is not supported by source", modelID, "cannot push dupremove", record, metadata);
return null;
}
if (!supportsDistinct(metadata, parentNode, accessNode.hasBooleanProperty(Info.IS_MULTI_SOURCE))) {
// $NON-NLS-1$ //$NON-NLS-2$
parentNode.recordDebugAnnotation("not all columns are comparable at the source", modelID, "cannot push dupremove", record, metadata);
return null;
}
return performRaise(rootNode, accessNode, parentNode);
}
case NodeConstants.Types.SORT:
{
if (canRaiseOverSort(accessNode, metadata, capFinder, parentNode, record, false, context)) {
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, true)) {
accessNode.getGroups().clear();
accessNode.getGroups().addAll(parentNode.getGroups());
return performRaise(rootNode, accessNode, parentNode);
}
return null;
}
case NodeConstants.Types.SET_OP:
if (!canRaiseOverSetQuery(parentNode, metadata, capFinder)) {
return null;
}
String sourceName = null;
boolean multiSource = false;
for (PlanNode node : new ArrayList<PlanNode>(parentNode.getChildren())) {
multiSource |= accessNode.hasBooleanProperty(Info.IS_MULTI_SOURCE);
if (sourceName == null) {
sourceName = (String) accessNode.getProperty(Info.SOURCE_NAME);
}
if (node == accessNode) {
continue;
}
combineSourceHints(accessNode, node);
combineConformedSources(accessNode, node);
NodeEditor.removeChildNode(parentNode, node);
}
accessNode.getGroups().clear();
if (multiSource) {
accessNode.setProperty(Info.IS_MULTI_SOURCE, true);
} else if (sourceName != null) {
accessNode.setProperty(Info.SOURCE_NAME, sourceName);
}
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.satisfyConditions(parentNode, accessNode, metadata);
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, context);
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 (!parentProject.hasProperty(Info.CONSTRAINT) && 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;
} else if (!CapabilitiesUtil.supportsInlineView(modelID, metadata, capFinder)) {
return null;
}
// is there another query that will be used with this source
if (FrameUtil.getNonQueryCommand(accessNode) != null || FrameUtil.getNestedPlan(accessNode) != null) {
return null;
}
// switch to inline view and change the group on the access to that of the source
parentNode.setProperty(NodeConstants.Info.INLINE_VIEW, Boolean.TRUE);
accessNode.getGroups().clear();
accessNode.addGroups(parentNode.getGroups());
RulePlaceAccess.copyProperties(parentNode, accessNode);
return performRaise(rootNode, accessNode, parentNode);
}
case NodeConstants.Types.TUPLE_LIMIT:
{
return RulePushLimit.raiseAccessOverLimit(rootNode, accessNode, metadata, capFinder, parentNode, record);
}
default:
{
return null;
}
}
}
use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.
the class RuleRaiseAccess method isMultiSourceColumn.
/**
* Check to see if the element is a multi-source source_name column
* TODO: inner side of an outer join projection
* do this check as part of metadata validation
*/
private static boolean isMultiSourceColumn(QueryMetadataInterface metadata, Expression ex, PlanNode node) throws QueryMetadataException, TeiidComponentException {
if (!(ex instanceof ElementSymbol)) {
return false;
}
ElementSymbol es = (ElementSymbol) ex;
if (metadata.isMultiSourceElement(es.getMetadataID())) {
return true;
}
if (node == null || node.getFirstChild() == null) {
return false;
}
node = FrameUtil.findOriginatingNode(node.getFirstChild(), Collections.singleton(es.getGroupSymbol()));
if (node == null || node.getType() != NodeConstants.Types.SOURCE) {
return false;
}
SymbolMap map = (SymbolMap) node.getProperty(Info.SYMBOL_MAP);
if (node.getChildren().isEmpty() || map == null) {
return false;
}
PlanNode set = NodeEditor.findNodePreOrder(node.getFirstChild(), NodeConstants.Types.SET_OP, NodeConstants.Types.SOURCE);
if (set == null) {
ex = map.getMappedExpression(es);
return isMultiSourceColumn(metadata, ex, node.getFirstChild());
}
int index = map.getKeys().indexOf(ex);
if (index == -1) {
return false;
}
for (PlanNode child : set.getChildren()) {
PlanNode project = NodeEditor.findNodePreOrder(child, NodeConstants.Types.PROJECT, NodeConstants.Types.SOURCE);
if (project == null) {
return false;
}
List<Expression> cols = (List<Expression>) project.getProperty(Info.PROJECT_COLS);
if (!isMultiSourceColumn(metadata, cols.get(index), child)) {
return false;
}
}
return true;
}
use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.
the class PlanNode method getCorrelatedReferenceElements.
public Set<ElementSymbol> getCorrelatedReferenceElements() {
List<SymbolMap> maps = getCorrelatedReferences();
if (maps.isEmpty()) {
return Collections.emptySet();
}
HashSet<ElementSymbol> result = new HashSet<ElementSymbol>();
for (SymbolMap symbolMap : maps) {
List<Expression> values = symbolMap.getValues();
for (Expression expr : values) {
ElementCollectorVisitor.getElements(expr, result);
}
}
return result;
}
use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.
the class PlanNode method getExportedCorrelatedReferences.
public List<SymbolMap> getExportedCorrelatedReferences() {
if (type != NodeConstants.Types.JOIN) {
return Collections.emptyList();
}
LinkedList<SymbolMap> result = new LinkedList<SymbolMap>();
for (PlanNode child : NodeEditor.findAllNodes(this, NodeConstants.Types.SOURCE, NodeConstants.Types.ACCESS)) {
SymbolMap references = (SymbolMap) child.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
if (references == null) {
continue;
}
Set<GroupSymbol> correlationGroups = GroupsUsedByElementsVisitor.getGroups(references.getValues());
PlanNode joinNode = NodeEditor.findParent(child, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE);
while (joinNode != null) {
if (joinNode.getGroups().containsAll(correlationGroups)) {
if (joinNode == this) {
result.add(references);
}
break;
}
joinNode = NodeEditor.findParent(joinNode, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE);
}
}
return result;
}
use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.
the class CriteriaCapabilityValidatorVisitor method validateSubqueryPushdown.
/**
* Return null if the subquery cannot be pushed down, otherwise the model
* id of the pushdown target.
* @param subqueryContainer
* @param critNodeModelID
* @param metadata
* @param capFinder
* @return
* @throws TeiidComponentException
*/
public static Object validateSubqueryPushdown(SubqueryContainer<?> subqueryContainer, Object critNodeModelID, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, AnalysisRecord analysisRecord) throws TeiidComponentException {
ProcessorPlan plan = subqueryContainer.getCommand().getProcessorPlan();
if (plan != null) {
AccessNode aNode = getAccessNode(plan);
if (aNode == null) {
return null;
}
critNodeModelID = validateCommandPushdown(critNodeModelID, metadata, capFinder, aNode, true);
}
if (critNodeModelID == null) {
return null;
}
// Check whether source supports correlated subqueries and if not, whether criteria has them
SymbolMap refs = subqueryContainer.getCommand().getCorrelatedReferences();
try {
if (refs != null && !refs.asMap().isEmpty()) {
if (!CapabilitiesUtil.supports(Capability.QUERY_SUBQUERIES_CORRELATED, critNodeModelID, metadata, capFinder)) {
return null;
}
if (!CapabilitiesUtil.supports(Capability.SUBQUERY_CORRELATED_LIMIT, critNodeModelID, metadata, capFinder)) {
QueryCommand command = (QueryCommand) subqueryContainer.getCommand();
if (command.getLimit() != null && !command.getLimit().isImplicit()) {
return null;
}
}
// but this is only an issue with deeply nested subqueries
if (!CriteriaCapabilityValidatorVisitor.canPushLanguageObject(subqueryContainer.getCommand(), critNodeModelID, metadata, capFinder, analysisRecord)) {
return null;
}
} else if (CapabilitiesUtil.supports(Capability.QUERY_SUBQUERIES_ONLY_CORRELATED, critNodeModelID, metadata, capFinder)) {
return null;
}
} catch (QueryMetadataException e) {
throw new TeiidComponentException(QueryPlugin.Event.TEIID30271, e);
}
if (!CapabilitiesUtil.supports(Capability.SUBQUERY_COMMON_TABLE_EXPRESSIONS, critNodeModelID, metadata, capFinder) && subqueryContainer.getCommand() instanceof QueryCommand) {
QueryCommand command = (QueryCommand) subqueryContainer.getCommand();
if (command.getWith() != null) {
return null;
}
}
// Found no reason why this node is not eligible
return critNodeModelID;
}
Aggregations