/* * 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; import java.util.List; import org.teiid.api.exception.query.ExpressionEvaluationException; import org.teiid.common.buffer.BlockedException; import org.teiid.common.buffer.BufferManager; import org.teiid.common.buffer.BufferManager.TupleSourceType; import org.teiid.common.buffer.TupleBatch; import org.teiid.common.buffer.TupleBuffer; import org.teiid.common.buffer.TupleSource; import org.teiid.core.TeiidComponentException; import org.teiid.core.TeiidProcessingException; import org.teiid.core.util.Assertion; import org.teiid.query.util.CommandContext; public class BatchCollector { public interface BatchProducer { /** * Get a batch of results or possibly an Exception. * @return Batch of results * @throws BlockedException indicating next batch is not available yet * @throws TeiidComponentException for non-business rule exception * @throws TeiidProcessingException for business rule exception, related * to user input or modeling */ TupleBatch nextBatch() throws BlockedException, TeiidComponentException, TeiidProcessingException; /** * Get list of resolved elements describing output columns for this plan. * @return List of SingleElementSymbol */ List getOutputElements(); /** * return the final tuple buffer or null if not available * @param maxRows * @return * @throws TeiidProcessingException * @throws TeiidComponentException * @throws BlockedException */ TupleBuffer getBuffer(int maxRows) throws BlockedException, TeiidComponentException, TeiidProcessingException; boolean hasBuffer(); void close() throws TeiidComponentException; } public static class BatchProducerTupleSource implements TupleSource { private final BatchProducer sourceNode; private TupleBatch sourceBatch; // Current batch loaded from the source, if blocked private long sourceRow = 1; public BatchProducerTupleSource(BatchProducer sourceNode) { this.sourceNode = sourceNode; } public BatchProducerTupleSource(BatchProducer sourceNode, long startRow) { this.sourceNode = sourceNode; this.sourceRow = startRow; } @Override public List<Object> nextTuple() throws TeiidComponentException, TeiidProcessingException { while (true) { if(sourceBatch == null) { // Read next batch sourceBatch = sourceNode.nextBatch(); } if(sourceBatch.getRowCount() > 0 && sourceRow <= sourceBatch.getEndRow()) { // Evaluate expressions needed for grouping List tuple = sourceBatch.getTuple(sourceRow); tuple = updateTuple(tuple); sourceRow++; return tuple; } // Check for termination condition if(sourceBatch.getTerminationFlag()) { sourceBatch = null; return null; } sourceBatch = null; } } @SuppressWarnings("unused") protected List<?> updateTuple(List<?> tuple) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { return tuple; } @Override public void closeSource() { } } private BatchProducer sourceNode; private boolean done = false; private TupleBuffer buffer; private boolean forwardOnly; private int rowLimit = -1; //-1 means no_limit private boolean hasFinalBuffer; private boolean saveLastRow; public BatchCollector(BatchProducer sourceNode, BufferManager bm, CommandContext context, boolean forwardOnly) throws TeiidComponentException { this.sourceNode = sourceNode; this.forwardOnly = forwardOnly; this.hasFinalBuffer = this.sourceNode.hasBuffer(); if (!this.hasFinalBuffer) { this.buffer = bm.createTupleBuffer(sourceNode.getOutputElements(), context.getConnectionId(), TupleSourceType.PROCESSOR); this.buffer.setForwardOnly(forwardOnly); } } public TupleBuffer collectTuples() throws TeiidComponentException, TeiidProcessingException { return collectTuples(false); } public TupleBuffer collectTuples(boolean singleBatch) throws TeiidComponentException, TeiidProcessingException { TupleBatch batch = null; while(!done) { if (this.hasFinalBuffer) { if (this.buffer == null) { TupleBuffer finalBuffer = this.sourceNode.getBuffer(rowLimit); Assertion.isNotNull(finalBuffer); this.buffer = finalBuffer; } if (this.buffer.isFinal()) { this.buffer.setForwardOnly(forwardOnly); done = true; break; } } batch = sourceNode.nextBatch(); if (rowLimit > 0 && rowLimit <= batch.getEndRow()) { if (!done) { this.sourceNode.close(); } List<?> lastTuple = null; if (saveLastRow) { if (batch.getTerminationFlag()) { lastTuple = batch.getTuples().get(batch.getTuples().size() - 1); } else if (rowLimit < batch.getBeginRow()) { continue; //skip until end } } boolean modified = false; if (rowLimit < batch.getEndRow()) { //we know row limit must be smaller than max int, so an int cast is safe here int firstRow = (int)Math.min(rowLimit + 1, batch.getBeginRow()); List<List<?>> tuples = batch.getTuples().subList(0, rowLimit - firstRow + 1); batch = new TupleBatch(firstRow, tuples); modified = true; } if (lastTuple != null) { if (!modified) { batch = new TupleBatch(batch.getBeginRow(), batch.getTuples()); } batch.getTuples().add(lastTuple); } batch.setTerminationFlag(true); } flushBatch(batch); // Check for termination condition if(batch.getTerminationFlag()) { done = true; if (!this.sourceNode.hasBuffer()) { buffer.close(); } break; } if (singleBatch) { return null; } } return buffer; } public TupleBuffer getTupleBuffer() { return buffer; } /** * Flush the batch by giving it to the buffer manager. */ private void flushBatch(TupleBatch batch) throws TeiidComponentException, TeiidProcessingException { if (batch.getRowCount() == 0 && batch.getTermination() == TupleBatch.NOT_TERMINATED) { return; } flushBatchDirect(batch, true); } @SuppressWarnings("unused") protected void flushBatchDirect(TupleBatch batch, boolean add) throws TeiidComponentException, TeiidProcessingException { if (!this.hasFinalBuffer) { buffer.addTupleBatch(batch, add); } } public long getRowCount() { if (buffer == null) { return 0; } return buffer.getRowCount(); } public void setRowLimit(int rowLimit) { this.rowLimit = rowLimit; } public void setSaveLastRow(boolean saveLastRow) { this.saveLastRow = saveLastRow; } public boolean isSaveLastRow() { return saveLastRow; } }