/*
* 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.xml;
import java.util.List;
import java.util.Map;
import org.teiid.common.buffer.BlockedException;
import org.teiid.common.buffer.BufferManager;
import org.teiid.common.buffer.TupleSource;
import org.teiid.core.TeiidComponentException;
import org.teiid.core.TeiidProcessingException;
import org.teiid.logging.LogConstants;
import org.teiid.logging.LogManager;
import org.teiid.query.QueryPlugin;
import org.teiid.query.mapping.xml.ResultSetInfo;
import org.teiid.query.metadata.TempMetadataAdapter;
import org.teiid.query.processor.BatchIterator;
import org.teiid.query.processor.ProcessorDataManager;
import org.teiid.query.processor.ProcessorPlan;
import org.teiid.query.processor.QueryProcessor;
import org.teiid.query.processor.RegisterRequestParameter;
import org.teiid.query.sql.lang.Insert;
import org.teiid.query.sql.symbol.ElementSymbol;
import org.teiid.query.sql.util.VariableContext;
import org.teiid.query.tempdata.AlterTempTable;
import org.teiid.query.util.CommandContext;
/**
* This is a helper class which can execute a supplied relational plan and supply
* resulting query results to the caller.
*/
class RelationalPlanExecutor implements PlanExecutor {
QueryProcessor internalProcessor;
// information about the result set.
ResultSetInfo resultInfo;
// buffer store
BufferManager bufferMgr;
// flag to denote the end of rows
boolean endOfRows = false;
// results after the execution bucket.
TupleSource tupleSource;
// cached current row of results.
List<?> currentRow;
int currentRowNumber = 0;
private ProcessorDataManager dataManager;
private boolean executed;
private boolean doneLoading;
public RelationalPlanExecutor (ResultSetInfo resultInfo, CommandContext context, ProcessorDataManager dataMgr, BufferManager bufferMgr) {
this.resultInfo = resultInfo;
this.bufferMgr = bufferMgr;
this.dataManager = dataMgr;
ProcessorPlan plan = resultInfo.getPlan();
CommandContext subContext = context.clone();
subContext.pushVariableContext(new VariableContext());
this.internalProcessor = new QueryProcessor(plan, subContext, bufferMgr, dataMgr);
}
/**
* @see org.teiid.query.processor.xml.PlanExecutor#getOutputElements()
*/
public List getOutputElements() throws TeiidComponentException {
ProcessorPlan plan = resultInfo.getPlan();
return plan.getOutputElements();
}
/**
* @throws TeiidProcessingException
* @see org.teiid.query.processor.xml.PlanExecutor#execute(java.util.Map, boolean)
*/
public void execute(Map referenceValues, boolean openOnly) throws TeiidComponentException, BlockedException, TeiidProcessingException {
if (this.tupleSource == null) {
setReferenceValues(referenceValues);
this.tupleSource = new BatchIterator(internalProcessor);
if (openOnly) {
internalProcessor.init();
}
}
if (!openOnly && !executed) {
String tempTable = this.resultInfo.getTempTable();
if (tempTable != null && !doneLoading && !this.resultInfo.isAutoStaged()) {
LogManager.logDetail(LogConstants.CTX_XML_PLAN, "Loading result set temp table", tempTable); //$NON-NLS-1$
Insert insert = this.resultInfo.getTempInsert();
insert.setTupleSource(this.tupleSource);
TupleSource result = this.dataManager.registerRequest(this.internalProcessor.getContext(), insert, TempMetadataAdapter.TEMP_MODEL.getName(), new RegisterRequestParameter());
result.nextTuple();
doneLoading = true;
internalProcessor.closeProcessing();
AlterTempTable att = new AlterTempTable(tempTable);
//mark the temp table as non-updatable
this.dataManager.registerRequest(this.internalProcessor.getContext(), att, TempMetadataAdapter.TEMP_MODEL.getName(), new RegisterRequestParameter());
this.tupleSource = this.dataManager.registerRequest(this.internalProcessor.getContext(), this.resultInfo.getTempSelect(), TempMetadataAdapter.TEMP_MODEL.getName(), new RegisterRequestParameter());
}
//force execution
currentRow();
if (this.resultInfo.isAutoStaged() && tempTable != null) {
AlterTempTable att = new AlterTempTable(tempTable);
//TODO: if the parent is small, then this is not necessary
att.setIndexColumns(this.resultInfo.getFkColumns());
this.dataManager.registerRequest(this.internalProcessor.getContext(), att, TempMetadataAdapter.TEMP_MODEL.getName(), new RegisterRequestParameter());
}
this.currentRowNumber = 0;
this.executed = true;
}
}
void setReferenceValues(Map<ElementSymbol, Object> referencesValues) {
if (referencesValues == null || referencesValues.isEmpty()) {
return;
}
for (Map.Entry<ElementSymbol, Object> entry : referencesValues.entrySet()) {
this.internalProcessor.getContext().getVariableContext().setValue(entry.getKey(), entry.getValue());
}
}
/**
* Get the next row from the result set
* @return
* @throws TeiidComponentException
*/
public List nextRow() throws TeiidComponentException, TeiidProcessingException {
// if we not already closed the tuple source; look for more results.
if (!endOfRows) {
// get the next row
if (this.currentRow == null || this.currentRowNumber > 0) {
this.currentRow = this.tupleSource.nextTuple();
}
this.currentRowNumber++;
// check if we walked over the row limit
if (this.currentRow != null && this.resultInfo.getUserRowLimit() > 0 && this.currentRowNumber > this.resultInfo.getUserRowLimit()) {
if (this.resultInfo.exceptionOnRowlimit()) {
throw new TeiidProcessingException(QueryPlugin.Event.TEIID30211, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30211, new Object[] { new Integer(this.resultInfo.getUserRowLimit()), this.resultInfo.getResultSetName()}));
}
// well, we did not throw a exception, that means we need to limit it to current row
this.currentRow = null;
}
}
if (this.currentRow == null) {
this.endOfRows = true;
}
return this.currentRow;
}
/**
* Get the current row.
* @return
*/
public List currentRow() throws TeiidComponentException, TeiidProcessingException {
// automatically forward to the very first row.
if (this.currentRow == null && !endOfRows) {
return nextRow();
}
return this.currentRow;
}
/**
* Close the executor and release all the resources.
*/
public void close() throws TeiidComponentException {
this.internalProcessor.closeProcessing();
if (this.tupleSource != null) {
this.tupleSource.closeSource();
}
String rsTempTable = this.resultInfo.getTempTable();
if (rsTempTable != null) {
LogManager.logDetail(LogConstants.CTX_XML_PLAN, "Unloading result set temp table", rsTempTable); //$NON-NLS-1$
internalProcessor.closeProcessing();
try {
this.tupleSource = this.dataManager.registerRequest(this.internalProcessor.getContext(), this.resultInfo.getTempDrop(), TempMetadataAdapter.TEMP_MODEL.getName(), new RegisterRequestParameter());
} catch (TeiidProcessingException e) {
LogManager.logDetail(org.teiid.logging.LogConstants.CTX_XML_PLAN, e, "Error dropping result set temp table", rsTempTable); //$NON-NLS-1$
}
}
LogManager.logTrace(org.teiid.logging.LogConstants.CTX_XML_PLAN, new Object[]{"closed executor", resultInfo.getResultSetName()}); //$NON-NLS-1$
}
}