/* * 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.processor.relational; import java.util.List; import java.util.Map; import org.teiid.common.buffer.IndexedTupleSource; import org.teiid.core.TeiidComponentException; import org.teiid.core.TeiidProcessingException; import org.teiid.query.eval.Evaluator; import org.teiid.query.processor.relational.SourceState.ImplicitBuffer; import org.teiid.query.sql.lang.JoinType; import org.teiid.query.sql.symbol.ElementSymbol; import org.teiid.query.sql.symbol.Expression; import org.teiid.query.sql.util.SymbolMap; /** * Variation of a nested loop join that handles nested tables */ public class NestedTableJoinStrategy extends JoinStrategy { private SymbolMap leftMap; private SymbolMap rightMap; private Evaluator eval; private boolean outerMatched; @Override public NestedTableJoinStrategy clone() { NestedTableJoinStrategy clone = new NestedTableJoinStrategy(); clone.leftMap = leftMap; clone.rightMap = rightMap; return clone; } @Override public void initialize(JoinNode joinNode) { super.initialize(joinNode); this.eval = new Evaluator(null, joinNode.getDataManager(), joinNode.getContext()); } public void setLeftMap(SymbolMap leftMap) { this.leftMap = leftMap; } public void setRightMap(SymbolMap rightMap) { this.rightMap = rightMap; } @Override protected void openLeft() throws TeiidComponentException, TeiidProcessingException { if (leftMap == null) { super.openLeft(); } } @Override protected void openRight() throws TeiidComponentException, TeiidProcessingException { if (rightMap == null) { super.openRight(); this.rightSource.setImplicitBuffer(ImplicitBuffer.FULL); } } @Override protected void process() throws TeiidComponentException, TeiidProcessingException { if (leftMap != null && !leftSource.open) { for (Map.Entry<ElementSymbol, Expression> entry : leftMap.asMap().entrySet()) { joinNode.getContext().getVariableContext().setValue(entry.getKey(), eval.evaluate(entry.getValue(), null)); } leftSource.getSource().reset(); super.openLeft(); } IndexedTupleSource its = leftSource.getIterator(); while (its.hasNext() || leftSource.getCurrentTuple() != null) { List<?> leftTuple = leftSource.getCurrentTuple(); if (leftTuple == null) { leftTuple = leftSource.saveNext(); } updateContext(leftTuple, leftSource.getSource().getElements()); if (rightMap != null && !rightSource.open) { for (Map.Entry<ElementSymbol, Expression> entry : rightMap.asMap().entrySet()) { joinNode.getContext().getVariableContext().setValue(entry.getKey(), eval.evaluate(entry.getValue(), null)); } rightSource.getSource().reset(); super.openRight(); } IndexedTupleSource right = rightSource.getIterator(); while (right.hasNext() || rightSource.getCurrentTuple() != null) { List<?> rightTuple = rightSource.getCurrentTuple(); if (rightTuple == null) { rightTuple = rightSource.saveNext(); } List<?> outputTuple = outputTuple(this.leftSource.getCurrentTuple(), this.rightSource.getCurrentTuple()); boolean matches = this.joinNode.matchesCriteria(outputTuple); rightSource.saveNext(); if (matches) { outerMatched = true; joinNode.addBatchRow(outputTuple); } } if (!outerMatched && this.joinNode.getJoinType() == JoinType.JOIN_LEFT_OUTER) { joinNode.addBatchRow(outputTuple(this.leftSource.getCurrentTuple(), this.rightSource.getOuterVals())); } outerMatched = false; if (rightMap == null) { rightSource.getIterator().setPosition(1); } else { rightSource.close(); for (Map.Entry<ElementSymbol, Expression> entry : rightMap.asMap().entrySet()) { joinNode.getContext().getVariableContext().remove(entry.getKey()); } } leftSource.saveNext(); updateContext(null, leftSource.getSource().getElements()); } if (leftMap != null) { leftSource.close(); for (Map.Entry<ElementSymbol, Expression> entry : leftMap.asMap().entrySet()) { joinNode.getContext().getVariableContext().remove(entry.getKey()); } } } private void updateContext(List<?> tuple, List<? extends Expression> elements) { for (int i = 0; i < elements.size(); i++) { Expression element = elements.get(i); if (element instanceof ElementSymbol) { if (tuple == null) { joinNode.getContext().getVariableContext().remove((ElementSymbol)element); } else { joinNode.getContext().getVariableContext().setValue((ElementSymbol)element, tuple.get(i)); } } } } public String toString() { return "NESTED TABLE JOIN"; //$NON-NLS-1$ } }