/* * 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.Arrays; import java.util.List; import org.teiid.common.buffer.BlockedException; import org.teiid.common.buffer.BufferManager; import org.teiid.common.buffer.BufferManager.BufferReserveMode; import org.teiid.common.buffer.TupleBatch; import org.teiid.common.buffer.TupleBuffer; import org.teiid.core.TeiidComponentException; import org.teiid.core.TeiidException; import org.teiid.core.TeiidProcessingException; import org.teiid.core.util.Assertion; import org.teiid.dqp.internal.process.PreparedPlan; import org.teiid.dqp.internal.process.RequestWorkItem; import org.teiid.dqp.internal.process.TupleSourceCache; import org.teiid.logging.LogConstants; import org.teiid.logging.LogManager; import org.teiid.logging.MessageLevel; import org.teiid.query.QueryPlugin; import org.teiid.query.metadata.QueryMetadataInterface; import org.teiid.query.optimizer.capabilities.CapabilitiesFinder; import org.teiid.query.processor.BatchCollector.BatchProducer; import org.teiid.query.util.CommandContext; /** * Driver for plan processing. */ public class QueryProcessor implements BatchProducer { public static class ExpiredTimeSliceException extends BlockedException { private static final long serialVersionUID = 4585044674826578060L; } private static ExpiredTimeSliceException EXPIRED_TIME_SLICE = new ExpiredTimeSliceException(); public interface ProcessorFactory { QueryProcessor createQueryProcessor(String query, String recursionGroup, CommandContext commandContext, Object... params) throws TeiidProcessingException, TeiidComponentException; PreparedPlan getPreparedPlan(String query, String recursionGroup, CommandContext commandContext, QueryMetadataInterface metadata) throws TeiidProcessingException, TeiidComponentException; CapabilitiesFinder getCapabiltiesFinder(); } private CommandContext context; private ProcessorDataManager dataMgr; private BufferManager bufferMgr; private ProcessorPlan processPlan; private boolean initialized; private boolean open; private int reserved; /** Flag that marks whether the request has been canceled. */ private volatile boolean requestCanceled; private static final int DEFAULT_WAIT = 50; private boolean processorClosed; private boolean continuous; private String query; //used only in continuous mode private PreparedPlan plan; private long rowOffset = 1; /** * Construct a processor with all necessary information to process. * @param plan The plan to process * @param context The context that this plan is being processed in * @param bufferMgr The buffer manager that provides access to tuple sources * @param dataMgr The data manager that provides access to get data * @throws TeiidComponentException */ public QueryProcessor(ProcessorPlan plan, CommandContext context, BufferManager bufferMgr, final ProcessorDataManager dataMgr) { this.context = context; this.context.setTupleSourceCache(new TupleSourceCache()); this.dataMgr = dataMgr; this.processPlan = plan; this.bufferMgr = bufferMgr; } public CommandContext getContext() { return context; } public ProcessorPlan getProcessorPlan() { return this.processPlan; } public TupleBatch nextBatch() throws BlockedException, TeiidProcessingException, TeiidComponentException { while (true) { long wait = DEFAULT_WAIT; try { return nextBatchDirect(); } catch (BlockedException e) { if (!this.context.isNonBlocking()) { throw e; } if (e == BlockedException.BLOCKED_ON_MEMORY_EXCEPTION) { continue; //TODO: pass the commandcontext into sortutility } } try { Thread.sleep(wait); } catch (InterruptedException err) { throw new TeiidComponentException(QueryPlugin.Event.TEIID30159, err); } } } private TupleBatch nextBatchDirect() throws BlockedException, TeiidProcessingException, TeiidComponentException { boolean done = false; TupleBatch result = null; try { init(); long currentTime = System.currentTimeMillis(); Assertion.assertTrue(!processorClosed); //TODO: see if there is pending work before preempting while(currentTime < context.getTimeSliceEnd() || context.isNonBlocking()) { if (requestCanceled) { throw new TeiidProcessingException(QueryPlugin.Event.TEIID30160, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30160, this.context.getRequestId())); } if (currentTime > context.getTimeoutEnd()) { throw new TeiidProcessingException(QueryPlugin.Event.TEIID30161, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30161)); } result = processPlan.nextBatch(); if (continuous) { result.setRowOffset(rowOffset); if (result.getTerminationFlag()) { result.setTermination(TupleBatch.ITERATION_TERMINATED); List<Object> terminationTuple = Arrays.asList(new Object[this.getOutputElements().size()]); result.getTuples().add(terminationTuple); this.context.getTupleSourceCache().close(); this.processPlan.close(); this.processPlan.reset(); this.context.incrementReuseCount(); this.open = false; } rowOffset = result.getEndRow() + 1; } if(result.getTermination() != TupleBatch.NOT_TERMINATED) { if (result.getTerminationFlag()) { done = true; } break; } if (result.getRowCount() > 0) { break; } } } catch (BlockedException e) { throw e; } catch (TeiidException e) { closeProcessing(); if (e instanceof TeiidProcessingException) { throw (TeiidProcessingException)e; } if (e instanceof TeiidComponentException) { throw (TeiidComponentException)e; } throw new TeiidComponentException(QueryPlugin.Event.TEIID30162, e); } if(done) { closeProcessing(); } if (result == null) { RequestWorkItem workItem = this.getContext().getWorkItem(); if (workItem != null) { //if we have a workitem (non-test scenario) then before //throwing exprired time slice we need to indicate there's more work workItem.moreWork(); } throw EXPIRED_TIME_SLICE; } return result; } public void init() throws TeiidComponentException, TeiidProcessingException { if (!open) { if (continuous && context.getReuseCount() > 0) { //validate the plan prior to the next run if (this.plan != null && !this.plan.validate()) { this.plan = null; } if (this.plan == null) { this.plan = context.getQueryProcessorFactory().getPreparedPlan(query, null, context, context.getMetadata()); this.processPlan = this.plan.getPlan().clone(); this.processPlan.initialize(context, dataMgr, bufferMgr); } } // initialize if necessary if(!initialized) { reserved = this.bufferMgr.reserveBuffers(this.bufferMgr.getSchemaSize(this.getOutputElements()), BufferReserveMode.FORCE); this.processPlan.initialize(context, dataMgr, bufferMgr); initialized = true; } // Open the top node for reading processPlan.open(); open = true; } } /** * Close processing and clean everything up. Should only be called by the same thread that called process. */ public void closeProcessing() { if (processorClosed) { return; } if (LogManager.isMessageToBeRecorded(LogConstants.CTX_DQP, MessageLevel.DETAIL)) { LogManager.logDetail(LogConstants.CTX_DQP, "QueryProcessor: closing processor"); //$NON-NLS-1$ } this.context.getTupleSourceCache().close(); this.bufferMgr.releaseBuffers(reserved); reserved = 0; processorClosed = true; if (initialized) { try { processPlan.close(); } catch (TeiidComponentException e1){ LogManager.logDetail(LogConstants.CTX_DQP, e1, "Error closing processor"); //$NON-NLS-1$ } } } @Override public List getOutputElements() { return this.processPlan.getOutputElements(); } public List<Exception> getAndClearWarnings() { return this.context.getAndClearWarnings(); } /** * Asynch shutdown of the QueryProcessor, which may trigger exceptions in the processing thread */ public void requestCanceled() { this.requestCanceled = true; this.context.requestCancelled(); } public BatchCollector createBatchCollector() throws TeiidComponentException { return new BatchCollector(this, this.bufferMgr, this.context, false); } public void setNonBlocking(boolean nonBlocking) { this.context.setNonBlocking(nonBlocking); } @Override public TupleBuffer getBuffer(int maxRows) throws BlockedException, TeiidComponentException, TeiidProcessingException { while (true) { long wait = DEFAULT_WAIT; try { init(); return this.processPlan.getBuffer(maxRows); } catch (BlockedException e) { if (!this.context.isNonBlocking()) { throw e; } if (e == BlockedException.BLOCKED_ON_MEMORY_EXCEPTION) { continue; //TODO: pass the commandcontext into sortutility } } catch (TeiidComponentException e) { closeProcessing(); throw e; } catch (TeiidProcessingException e) { closeProcessing(); throw e; } try { Thread.sleep(wait); } catch (InterruptedException err) { throw new TeiidComponentException(QueryPlugin.Event.TEIID30163, err); } } } @Override public boolean hasBuffer() { return !continuous && this.processPlan.hasBuffer(); } public BufferManager getBufferManager() { return bufferMgr; } public void setContinuous(PreparedPlan prepPlan, String query) { this.continuous = true; this.plan = prepPlan; this.query = query; this.context.setContinuous(); } @Override public void close() throws TeiidComponentException { closeProcessing(); } public ProcessorDataManager getProcessorDataManager() { return dataMgr; } }