/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.teiid.query.optimizer.relational.rules; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.teiid.api.exception.query.QueryMetadataException; import org.teiid.api.exception.query.QueryPlannerException; import org.teiid.core.TeiidComponentException; import org.teiid.query.QueryPlugin; import org.teiid.query.analysis.AnalysisRecord; import org.teiid.query.metadata.QueryMetadataInterface; import org.teiid.query.optimizer.capabilities.CapabilitiesFinder; import org.teiid.query.optimizer.relational.OptimizerRule; import org.teiid.query.optimizer.relational.RuleStack; import org.teiid.query.optimizer.relational.plantree.NodeConstants; import org.teiid.query.optimizer.relational.plantree.NodeEditor; import org.teiid.query.optimizer.relational.plantree.PlanNode; import org.teiid.query.sql.LanguageObject; import org.teiid.query.sql.LanguageVisitor; import org.teiid.query.sql.lang.CompareCriteria; import org.teiid.query.sql.lang.Criteria; import org.teiid.query.sql.lang.DependentSetCriteria; import org.teiid.query.sql.lang.IsNullCriteria; import org.teiid.query.sql.lang.SPParameter; import org.teiid.query.sql.lang.SetCriteria; import org.teiid.query.sql.lang.StoredProcedure; import org.teiid.query.sql.lang.DependentSetCriteria.AttributeComparison; import org.teiid.query.sql.symbol.ElementSymbol; import org.teiid.query.sql.symbol.Expression; import org.teiid.query.sql.symbol.Reference; import org.teiid.query.sql.visitor.ReferenceCollectorVisitor; import org.teiid.query.util.CommandContext; public class RulePlanProcedures implements OptimizerRule { /** * @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, final QueryMetadataInterface metadata, CapabilitiesFinder capabilitiesFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException { for (PlanNode node : NodeEditor.findAllNodes(plan, NodeConstants.Types.SOURCE, NodeConstants.Types.ACCESS)) { if (!FrameUtil.isProcedure(node.getFirstChild())) { continue; } StoredProcedure proc = (StoredProcedure)node.getProperty(NodeConstants.Info.NESTED_COMMAND); if (!proc.isProcedureRelational()) { continue; } HashSet<ElementSymbol> inputSymbols = new HashSet<ElementSymbol>(); List<Reference> inputReferences = new LinkedList<Reference>(); PlanNode critNode = node.getParent(); List<Criteria> conjuncts = new LinkedList<Criteria>(); HashSet<ElementSymbol> coveredParams = new HashSet<ElementSymbol>(); //List preExecNodes = new LinkedList(); for (Iterator<SPParameter> params = proc.getInputParameters().iterator(); params.hasNext();) { SPParameter param = params.next(); ElementSymbol symbol = param.getParameterSymbol(); Expression input = param.getExpression(); inputReferences.add((Reference)input); inputSymbols.add(symbol); } findInputNodes(inputSymbols, critNode, conjuncts, coveredParams); List<Expression> defaults = new LinkedList<Expression>(); for (Reference ref : inputReferences) { ElementSymbol symbol = ref.getExpression(); Expression defaultValue = null; /*try { defaultValue = ResolverUtil.getDefault(symbol, metadata); } catch (QueryResolverException qre) { //Just ignore }*/ defaults.add(defaultValue); if (defaultValue == null && !coveredParams.contains(symbol)) { throw new QueryPlannerException(QueryPlugin.Event.TEIID30270, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30270, symbol)); } } /*if (conjuncts.isEmpty()) { for (int j = 0; j < inputReferences.size(); j++) { Reference ref = (Reference)inputReferences.get(j); ref.setValue(defaults.get(j)); } continue; }*/ PlanNode accessNode = NodeEditor.findNodePreOrder(node, NodeConstants.Types.ACCESS); Criteria crit = Criteria.combineCriteria(conjuncts); if (crit != null) { accessNode.setProperty(NodeConstants.Info.PROCEDURE_CRITERIA, crit); accessNode.setProperty(NodeConstants.Info.PROCEDURE_INPUTS, inputReferences); accessNode.setProperty(NodeConstants.Info.PROCEDURE_DEFAULTS, defaults); accessNode.setProperty(NodeConstants.Info.IS_DEPENDENT_SET, Boolean.TRUE); } } return plan; } private void findInputNodes(final HashSet<ElementSymbol> inputs, PlanNode critNode, final List<Criteria> conjuncts, final Set<ElementSymbol> params) { while (critNode.getType() == NodeConstants.Types.SELECT) { final PlanNode currentNode = critNode; final Criteria crit = (Criteria)currentNode.getProperty(NodeConstants.Info.SELECT_CRITERIA); critNode = currentNode.getParent(); if (!currentNode.getGroups().isEmpty()) { continue; } LanguageVisitor visitor = new LanguageVisitor() { public void visit(CompareCriteria compCrit){ if (compCrit.getOperator() == CompareCriteria.EQ && checkForInput(compCrit.getLeftExpression()) && !checkForAnyInput(compCrit.getRightExpression())) { addInputNode((Reference)compCrit.getLeftExpression()); } } private void addInputNode(Reference param) { params.add(param.getExpression()); conjuncts.add(crit); NodeEditor.removeChildNode(currentNode.getParent(), currentNode); } public void visit(IsNullCriteria isNull){ if (!isNull.isNegated() && checkForInput(isNull.getExpression())) { addInputNode((Reference)isNull.getExpression()); } } public void visit(SetCriteria obj) { if (!obj.isNegated() && checkForInput(obj.getExpression()) && !checkForAnyInput(obj.getValues())) { addInputNode((Reference)obj.getExpression()); } } public void visit(DependentSetCriteria obj) { if (obj.isNegated()) { return; //just a sanity check } if (obj.hasMultipleAttributes()) { for (AttributeComparison comp : obj.getAttributes()) { if (!checkForInput(comp.dep)) { return; } } for (AttributeComparison comp : obj.getAttributes()) { params.add(((Reference)comp.dep).getExpression()); } conjuncts.add(crit); NodeEditor.removeChildNode(currentNode.getParent(), currentNode); } else if (checkForInput(obj.getExpression())) { addInputNode((Reference)obj.getExpression()); } } boolean checkForInput(Expression expr) { if (!(expr instanceof Reference)) { return false; } //if the expr is a function containing a reference should give a warning Reference ref = (Reference)expr; return inputs.contains(ref.getExpression()); } boolean checkForAnyInput(LanguageObject expr) { for (Reference ref : ReferenceCollectorVisitor.getReferences(expr)) { if (checkForInput(ref)) { return true; } } return false; } boolean checkForAnyInput(Collection<Expression> expressions) { for (Expression expr : expressions) { if (checkForAnyInput(expr)) { return true; } } return false; } }; for (Criteria conjunct : Criteria.separateCriteriaByAnd(crit)) { conjunct.acceptVisitor(visitor); } } } /** * @see java.lang.Object#toString() */ public String toString() { return "PlanProcedures"; //$NON-NLS-1$ } }