/* * 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.xml; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; import org.teiid.core.TeiidException; import org.teiid.core.TeiidRuntimeException; import org.teiid.query.mapping.relational.QueryNode; import org.teiid.query.mapping.xml.MappingAttribute; import org.teiid.query.mapping.xml.MappingCriteriaNode; import org.teiid.query.mapping.xml.MappingElement; import org.teiid.query.mapping.xml.MappingNode; import org.teiid.query.mapping.xml.MappingRecursiveElement; import org.teiid.query.mapping.xml.MappingSourceNode; import org.teiid.query.mapping.xml.MappingVisitor; import org.teiid.query.mapping.xml.Navigator; import org.teiid.query.mapping.xml.ResultSetInfo; import org.teiid.query.resolver.util.ResolverUtil; import org.teiid.query.sql.LanguageObject; import org.teiid.query.sql.lang.Criteria; import org.teiid.query.sql.lang.Query; import org.teiid.query.sql.lang.Select; import org.teiid.query.sql.symbol.Constant; import org.teiid.query.sql.symbol.ElementSymbol; import org.teiid.query.sql.symbol.Expression; import org.teiid.query.sql.symbol.ExpressionSymbol; import org.teiid.query.sql.symbol.GroupSymbol; import org.teiid.query.sql.visitor.ElementCollectorVisitor; import org.teiid.query.sql.visitor.ExpressionMappingVisitor; public class XMLProjectionMinimizer { /** * Implements projection minimization through two passes over the document */ static void minimizeProjection(final XMLPlannerEnvironment planEnv) { final HashMap<MappingSourceNode, LinkedHashSet<ElementSymbol>> neededElements = new HashMap<MappingSourceNode, LinkedHashSet<ElementSymbol>>(); //collect included elements MappingVisitor visitor = new Navigator(true, new MappingVisitor() { @Override public void visit(MappingAttribute attribute) { collectElementSymbol(attribute); } private void collectElementSymbol(MappingNode node) { if (node.isExcluded() || node.getElementSymbol() == null) { return; } MappingSourceNode msn = node.getSourceNode(); ElementSymbol es = node.getElementSymbol(); collectElementSymbol(msn, es); } private void collectElementSymbol( MappingSourceNode msn, ElementSymbol es) { msn = getActualSourceNode(msn); LinkedHashSet<ElementSymbol> elems = neededElements.get(msn); if (elems == null) { elems = new LinkedHashSet<ElementSymbol>(); neededElements.put(msn, elems); } elems.add(es); } @Override public void visit(MappingElement element) { collectElementSymbol(element); } @Override public void visit(MappingSourceNode element) { try { QueryNode node = QueryUtil.getQueryNode(element.getResultName(), planEnv.getGlobalMetadata()); Collection<ElementSymbol> bindings = QueryUtil.getBindingElements(node); MappingSourceNode parent = element.getParentSourceNode(); collectElementSymbols(element, bindings, parent); } catch (TeiidException e) { throw new TeiidRuntimeException(e); } } private void collectElementSymbols( MappingSourceNode element, Collection<ElementSymbol> bindings, MappingSourceNode parent) { for (ElementSymbol elementSymbol : bindings) { if (element != null) { elementSymbol = element.getMappedSymbol(elementSymbol); } MappingSourceNode nextParent = parent; while (nextParent != null) { if (nextParent.getActualResultSetName().equalsIgnoreCase(elementSymbol.getGroupSymbol().getNonCorrelationName())) { collectElementSymbol(nextParent, elementSymbol); break; } nextParent = nextParent.getParentSourceNode(); } } } @Override public void visit(MappingCriteriaNode element) { Criteria crit = element.getCriteriaNode(); if (crit == null) { return; } collectElementSymbols(null, ElementCollectorVisitor.getElements(crit, true), element.getSourceNode()); } @Override public void visit(MappingRecursiveElement element) { Criteria crit = element.getCriteriaNode(); if (crit == null) { return; } collectElementSymbols(null, ElementCollectorVisitor.getElements(crit, true), element.getSourceNode()); } }); planEnv.mappingDoc.acceptVisitor(visitor); visitor = new Navigator(true, new MappingVisitor() { @Override public void visit(MappingSourceNode element) { try { ResultSetInfo rsInfo = element.getResultSetInfo(); Query rsQuery = (Query)rsInfo.getCommand(); if (rsQuery.getSelect().isDistinct()) { return; } LinkedHashSet<ElementSymbol> elements = neededElements.get(element); if (elements != null) { rsQuery.setSelect(new Select(LanguageObject.Util.deepClone(elements, ElementSymbol.class))); } else { String alias = element.getAliasResultName(); if (alias == null) { rsQuery.setSelect(new Select(Arrays.asList(new ExpressionSymbol("foo", new Constant(1))))); //$NON-NLS-1$ } else { MappingSourceNode actual = getActualSourceNode(element); elements = neededElements.get(actual); if (elements != null) { Map reverseMap = QueryUtil.createSymbolMap(new GroupSymbol(element.getAliasResultName()), rsInfo.getResultSetName(), ResolverUtil.resolveElementsInGroup(QueryUtil.createResolvedGroup(element.getAliasResultName(), planEnv.getGlobalMetadata()), planEnv.getGlobalMetadata())); Select select = new Select(new ArrayList<Expression>(elements)); ExpressionMappingVisitor.mapExpressions(select, reverseMap); rsQuery.setSelect(select); } } } } catch (TeiidException e) { throw new TeiidRuntimeException(e); } } }); planEnv.mappingDoc.acceptVisitor(visitor); } private static MappingSourceNode getActualSourceNode(MappingSourceNode element) { if (element.getAliasResultName() == null) { return element; } String actual = element.getActualResultSetName(); MappingSourceNode parent = element.getParentSourceNode(); while (parent != null) { if (parent.getActualResultSetName().equalsIgnoreCase(actual) ) { return parent; } parent = parent.getParentSourceNode(); } return null; } }