/*
* 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.proc;
import static org.teiid.query.analysis.AnalysisRecord.*;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import javax.transaction.SystemException;
import org.teiid.api.exception.query.QueryMetadataException;
import org.teiid.api.exception.query.QueryValidatorException;
import org.teiid.client.ProcedureErrorInstructionException;
import org.teiid.client.plan.PlanNode;
import org.teiid.client.xa.XATransactionException;
import org.teiid.common.buffer.BlockedException;
import org.teiid.common.buffer.BufferManager;
import org.teiid.common.buffer.BufferManager.TupleSourceType;
import org.teiid.common.buffer.IndexedTupleSource;
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.types.ArrayImpl;
import org.teiid.core.types.DataTypeManager;
import org.teiid.core.util.Assertion;
import org.teiid.dqp.internal.process.DataTierTupleSource;
import org.teiid.dqp.service.TransactionContext;
import org.teiid.dqp.service.TransactionContext.Scope;
import org.teiid.dqp.service.TransactionService;
import org.teiid.events.EventDistributor;
import org.teiid.jdbc.TeiidSQLException;
import org.teiid.logging.LogConstants;
import org.teiid.logging.LogManager;
import org.teiid.metadata.Procedure;
import org.teiid.query.QueryPlugin;
import org.teiid.query.analysis.AnalysisRecord;
import org.teiid.query.metadata.QueryMetadataInterface;
import org.teiid.query.metadata.SupportConstants;
import org.teiid.query.processor.BatchIterator;
import org.teiid.query.processor.CollectionTupleSource;
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.processor.proc.CreateCursorResultSetInstruction.Mode;
import org.teiid.query.processor.relational.SubqueryAwareEvaluator;
import org.teiid.query.processor.relational.SubqueryAwareRelationalNode;
import org.teiid.query.resolver.command.UpdateProcedureResolver;
import org.teiid.query.sql.ProcedureReservedWords;
import org.teiid.query.sql.lang.Command;
import org.teiid.query.sql.lang.Criteria;
import org.teiid.query.sql.symbol.ElementSymbol;
import org.teiid.query.sql.symbol.Expression;
import org.teiid.query.sql.symbol.GroupSymbol;
import org.teiid.query.sql.util.VariableContext;
import org.teiid.query.sql.visitor.ValueIteratorProviderCollectorVisitor;
import org.teiid.query.tempdata.TempTableStore;
import org.teiid.query.util.CommandContext;
/**
*/
public class ProcedurePlan extends ProcessorPlan implements ProcessorDataManager {
private static class CursorState {
QueryProcessor processor;
IndexedTupleSource ts;
List<?> currentRow;
TupleBuffer resultsBuffer;
public boolean returning;
public boolean usesLocalTemp;
}
static final ElementSymbol ROWCOUNT =
new ElementSymbol(ProcedureReservedWords.VARIABLES+"."+ProcedureReservedWords.ROWCOUNT); //$NON-NLS-1$
static {
ROWCOUNT.setType(DataTypeManager.DefaultDataClasses.INTEGER);
}
private Program originalProgram;
// State initialized by processor
private ProcessorDataManager parentDataMrg;
private BufferManager bufferMgr;
private int batchSize;
private boolean done = false;
private CursorState currentState;
// Temp state for final results
private TupleSource finalTupleSource;
private int beginBatch = 1;
private List<List<?>> batchRows;
private boolean lastBatch = false;
private LinkedHashMap<ElementSymbol, Expression> params;
private boolean runInContext = true;
private List<ElementSymbol> outParams;
private QueryMetadataInterface metadata;
private VariableContext cursorStates;
private VariableContext currentVarContext;
private VariableContext parentContext;
private List outputElements;
private SubqueryAwareEvaluator evaluator;
// Stack of programs, with current program on top
private Stack<Program> programs = new Stack<Program>();
private boolean evaluatedParams;
private int updateCount = Procedure.AUTO_UPDATECOUNT;
private TransactionContext blockContext;
/**
* Resources cannot be held open across the txn boundary. This list is a hack at ensuring the resources are closed.
*/
private LinkedList<WeakReference<DataTierTupleSource>> txnTupleSources = new LinkedList<WeakReference<DataTierTupleSource>>();
private boolean validateAccess;
/**
* Constructor for ProcedurePlan.
*/
public ProcedurePlan(Program originalProgram) {
this.originalProgram = originalProgram;
createVariableContext();
}
public Program getOriginalProgram() {
return originalProgram;
}
/**
* @see ProcessorPlan#initialize(ProcessorDataManager, Object)
*/
public void initialize(CommandContext context, ProcessorDataManager dataMgr, BufferManager bufferMgr) {
this.bufferMgr = bufferMgr;
this.batchSize = bufferMgr.getProcessorBatchSize(getOutputElements());
this.parentContext = context.getVariableContext();
setContext(context.clone());
this.parentDataMrg = dataMgr;
if (evaluator == null) {
this.evaluator = new SubqueryAwareEvaluator(Collections.emptyMap(), getDataManager(), getContext(), this.bufferMgr);
}
}
public void reset() {
super.reset();
if (evaluator != null) {
evaluator.reset();
}
evaluatedParams = false;
if (parentContext != null) {
super.getContext().setVariableContext(parentContext);
}
createVariableContext();
CommandContext cc = super.getContext();
if (cc != null) {
//create fresh local state
setContext(cc.clone());
}
done = false;
currentState = null;
finalTupleSource = null;
beginBatch = 1;
batchRows = null;
lastBatch = false;
//reset program stack
programs.clear();
LogManager.logTrace(org.teiid.logging.LogConstants.CTX_DQP, "ProcedurePlan reset"); //$NON-NLS-1$
}
public ProcessorDataManager getDataManager() {
return this;
}
public void open() throws TeiidProcessingException, TeiidComponentException {
if (!this.evaluatedParams) {
if (this.outParams != null) {
for (ElementSymbol param : this.outParams) {
setParameterValue(param, getCurrentVariableContext(), null);
}
}
if (this.params != null) {
for (Map.Entry<ElementSymbol, Expression> entry : this.params.entrySet()) {
ElementSymbol param = entry.getKey();
Expression expr = entry.getValue();
VariableContext context = getCurrentVariableContext();
if (context.getVariableMap().containsKey(param)) {
continue;
}
Object value = this.evaluateExpression(expr);
//check constraint
checkNotNull(param, value);
setParameterValue(param, context, value);
}
this.evaluator.close();
} else if (runInContext) {
//if there are no params, this needs to run in the current variable context
this.currentVarContext.setParentContext(parentContext);
}
this.push(originalProgram);
}
this.evaluatedParams = true;
}
private void checkNotNull(ElementSymbol param, Object value)
throws TeiidComponentException, QueryMetadataException,
QueryValidatorException {
if (metadata.elementSupports(param.getMetadataID(), SupportConstants.Element.NULL)) {
return;
}
if (metadata.isVariadic(param.getMetadataID())) {
if (value instanceof ArrayImpl) {
ArrayImpl av = (ArrayImpl)value;
for (Object o : av.getValues()) {
if (o == null) {
throw new QueryValidatorException(QueryPlugin.Event.TEIID30164, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30164, param));
}
}
}
} else if (value == null) {
throw new QueryValidatorException(QueryPlugin.Event.TEIID30164, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30164, param));
}
}
protected void setParameterValue(ElementSymbol param,
VariableContext context, Object value) {
context.setValue(param, value);
}
@Override
public TupleBatch nextBatch() throws BlockedException,
TeiidComponentException, TeiidProcessingException {
if (blockContext != null) {
this.getContext().getTransactionServer().resume(blockContext);
}
try {
return nextBatchDirect();
} finally {
//ensure that the generatedkeys don't escape
getContext().clearGeneratedKeys();
if (blockContext != null) {
this.getContext().getTransactionServer().suspend(blockContext);
}
}
}
/**
* @see ProcessorPlan#nextBatch()
*/
public TupleBatch nextBatchDirect()
throws TeiidComponentException, TeiidProcessingException, BlockedException {
// Already returned results?
if(done) {
// Already returned all results
TupleBatch emptyTerminationBatch = new TupleBatch(beginBatch, new List[0]);
emptyTerminationBatch.setTerminationFlag(true);
return emptyTerminationBatch;
}
// First attempt to process
if(this.finalTupleSource == null) {
// Still need to process - this should either
// throw a BlockedException or return a finalTupleSource
this.finalTupleSource = processProcedure();
}
// Next, attempt to return batches if processing completed
while(! isBatchFull()) {
// May throw BlockedException and exit here
List<?> tuple = this.finalTupleSource.nextTuple();
if(tuple == null) {
if (outParams != null) {
VariableContext vc = getCurrentVariableContext();
List<Object> paramTuple = Arrays.asList(new Object[this.getOutputElements().size()]);
int i = this.getOutputElements().size() - this.outParams.size();
for (ElementSymbol param : outParams) {
Object value = vc.getValue(param);
checkNotNull(param, value);
paramTuple.set(i++, value);
}
addBatchRow(paramTuple, true);
}
terminateBatches();
done = true;
break;
}
addBatchRow(tuple, false);
}
return pullBatch();
}
/**
* <p>Process the procedure, using the stack of Programs supplied by the
* ProcessorEnvironment. With each pass through the loop, the
* current Program is gotten off the top of the stack, and the
* current instruction is gotten from that program; each call
* to an instruction's process method may alter the Program
* Stack and/or the current instruction pointer of a Program,
* so it's important that this method's loop refer to the
* call stack of the ProcessorEnvironment each time, and not
* cache things in local variables. If the current Program's
* current instruction is null, then it's time to pop that
* Program off the stack.</p>
*
* @return List a single tuple containing one Integer: the update
* count resulting from the procedure execution.
*/
private TupleSource processProcedure()
throws TeiidComponentException, TeiidProcessingException, BlockedException {
// execute plan
ProgramInstruction inst = null;
while (!this.programs.empty()){
Program program = peek();
inst = program.getCurrentInstruction();
if (inst == null){
LogManager.logTrace(org.teiid.logging.LogConstants.CTX_DQP, "Finished program", program); //$NON-NLS-1$
//look ahead to see if we need to process in place
VariableContext vc = this.cursorStates.getParentContext();
CursorState last = (CursorState) this.cursorStates.getValue(null);
if(last != null){
if (last.resultsBuffer == null && (last.usesLocalTemp || !txnTupleSources.isEmpty())) {
last.resultsBuffer = bufferMgr.createTupleBuffer(last.processor.getOutputElements(), getContext().getConnectionId(), TupleSourceType.PROCESSOR);
last.returning = true;
}
if (last.returning) {
while (last.ts.hasNext()) {
List<?> tuple = last.ts.nextTuple();
last.resultsBuffer.addTuple(tuple);
}
last.resultsBuffer.close();
last.ts = last.resultsBuffer.createIndexedTupleSource(true);
last.returning = false;
}
}
this.pop(true);
continue;
}
try {
if (inst instanceof RepeatedInstruction) {
LogManager.logTrace(org.teiid.logging.LogConstants.CTX_DQP, "Executing repeated instruction", inst); //$NON-NLS-1$
RepeatedInstruction loop = (RepeatedInstruction)inst;
if (loop.testCondition(this)) {
LogManager.logTrace(org.teiid.logging.LogConstants.CTX_DQP, "Passed condition, executing program " + loop.getNestedProgram()); //$NON-NLS-1$
inst.process(this);
this.push(loop.getNestedProgram());
continue;
}
LogManager.logTrace(org.teiid.logging.LogConstants.CTX_DQP, "Exiting repeated instruction", inst); //$NON-NLS-1$
loop.postInstruction(this);
} else {
LogManager.logTrace(org.teiid.logging.LogConstants.CTX_DQP, "Executing instruction", inst); //$NON-NLS-1$
inst.process(this);
this.evaluator.close();
}
} catch (TeiidComponentException e) {
throw e;
} catch (Exception e) {
//processing or teiidsqlexception
boolean atomic = program.isAtomic();
while (program.getExceptionGroup() == null) {
this.pop(false);
if (this.programs.empty()) {
//reached the top without a handler, so throw
if (e instanceof TeiidProcessingException) {
throw (TeiidProcessingException)e;
}
throw new ProcedureErrorInstructionException(QueryPlugin.Event.TEIID30167, e);
}
program = peek();
atomic |= program.isAtomic();
}
try {
this.pop(false); //allow the current program to go out of scope
if (atomic) {
TransactionContext tc = this.getContext().getTransactionContext();
if (tc != null && tc.getTransactionType() != Scope.NONE) {
//a non-completing atomic block under a higher level transaction
//this will not work correctly until we support
//checkpoints/subtransactions
tc.getTransaction().setRollbackOnly();
}
}
} catch (IllegalStateException | SystemException| TeiidComponentException e1) {
LogManager.logDetail(LogConstants.CTX_DQP, "Caught exception while rolling back transaction", e1); //$NON-NLS-1$
} catch (Throwable e1) {
LogManager.logWarning(LogConstants.CTX_DQP, e1); //$NON-NLS-1$
}
if (program.getExceptionProgram() == null) {
continue;
}
Program exceptionProgram = program.getExceptionProgram();
this.push(exceptionProgram);
if (e instanceof RuntimeException) {
LogManager.logWarning(LogConstants.CTX_DQP, e); //$NON-NLS-1$
} else {
LogManager.logDetail(LogConstants.CTX_DQP, "Caught exception in exception hanlding block", e); //$NON-NLS-1$
}
TeiidSQLException tse = TeiidSQLException.create(e);
GroupSymbol gs = new GroupSymbol(program.getExceptionGroup());
this.currentVarContext.setValue(exceptionSymbol(gs, 0), tse.getSQLState());
this.currentVarContext.setValue(exceptionSymbol(gs, 1), tse.getErrorCode());
this.currentVarContext.setValue(exceptionSymbol(gs, 2), tse.getTeiidCode());
this.currentVarContext.setValue(exceptionSymbol(gs, 3), tse);
this.currentVarContext.setValue(exceptionSymbol(gs, 4), tse.getCause());
continue;
}
program.incrementProgramCounter();
}
CursorState last = (CursorState) this.cursorStates.getValue(null);
if(last == null){
return CollectionTupleSource.createNullTupleSource();
}
return last.ts;
}
private ElementSymbol exceptionSymbol(GroupSymbol gs, int pos) {
ElementSymbol es = UpdateProcedureResolver.exceptionGroup.get(pos).clone();
es.setGroupSymbol(gs);
return es;
}
public void close()
throws TeiidComponentException {
while (!programs.isEmpty()) {
try {
pop(false);
} catch (TeiidComponentException e) {
LogManager.logDetail(LogConstants.CTX_DQP, e, "Error closing program"); //$NON-NLS-1$
}
}
if (this.evaluator != null) {
this.evaluator.close();
}
if (this.cursorStates != null) {
removeAllCursors(this.cursorStates);
this.cursorStates = null;
}
this.txnTupleSources.clear();
this.blockContext = null;
this.currentVarContext = null;
}
public String toString() {
return "ProcedurePlan:\n" + this.originalProgram; //$NON-NLS-1$
}
public ProcessorPlan clone(){
ProcedurePlan plan = new ProcedurePlan(originalProgram.clone());
plan.setOutputElements(this.getOutputElements());
plan.setParams(params);
plan.setOutParams(outParams);
plan.setMetadata(metadata);
plan.updateCount = updateCount;
plan.runInContext = runInContext;
return plan;
}
private void addBatchRow(List<?> row, boolean last) {
if(this.batchRows == null) {
this.batchRows = new ArrayList<List<?>>(this.batchSize/4);
}
if (!last && this.outParams != null) {
List<Object> newRow = Arrays.asList(new Object[row.size() + this.outParams.size()]);
for (int i = 0; i < row.size(); i++) {
newRow.set(i, row.get(i));
}
row = newRow;
}
this.batchRows.add(row);
}
protected void terminateBatches() {
this.lastBatch = true;
}
protected boolean isBatchFull() {
return (this.batchRows != null) && (this.batchRows.size() == this.batchSize);
}
protected TupleBatch pullBatch() {
TupleBatch batch = null;
if(this.batchRows != null) {
batch = new TupleBatch(this.beginBatch, this.batchRows);
beginBatch += this.batchRows.size();
} else {
batch = new TupleBatch(this.beginBatch, Collections.EMPTY_LIST);
}
batch.setTerminationFlag(this.lastBatch);
// Reset batch state
this.batchRows = null;
this.lastBatch = false;
// Return batch
return batch;
}
public PlanNode getDescriptionProperties() {
PlanNode node = this.originalProgram.getDescriptionProperties();
node.addProperty(PROP_OUTPUT_COLS, AnalysisRecord.getOutputColumnProperties(getOutputElements()));
return node;
}
public void setMetadata( QueryMetadataInterface metadata ) {
this.metadata = metadata;
}
public void setOutParams(List<ElementSymbol> outParams) {
this.outParams = outParams;
}
public void setParams( LinkedHashMap<ElementSymbol, Expression> params ) {
this.params = params;
}
private void createVariableContext() {
this.currentVarContext = new VariableContext(runInContext && this.params == null);
this.currentVarContext.setValue(ROWCOUNT, 0);
this.cursorStates = new VariableContext(false);
this.cursorStates.setValue(null, null);
}
/**
* <p> Get the current <code>VariavleContext</code> on this environment.
* The VariableContext is updated with variables and their values by
* {@link ProgramInstruction}s that are part of the ProcedurePlan that use
* this environment.</p>
* @return The current <code>VariariableContext</code>.
*/
public VariableContext getCurrentVariableContext() {
return this.currentVarContext;
}
/**
*
* @param command
* @param rsName
* @param procAssignments
* @param mode
* @param usesLocalTemp - only matters in HOLD mode
* @throws TeiidComponentException
* @throws TeiidProcessingException
*/
public void executePlan(ProcessorPlan command, String rsName, Map<ElementSymbol, ElementSymbol> procAssignments, CreateCursorResultSetInstruction.Mode mode, boolean usesLocalTemp)
throws TeiidComponentException, TeiidProcessingException {
CursorState state = (CursorState) this.cursorStates.getValue(rsName);
if (state == null || rsName == null) {
if (this.currentState != null && this.currentState.processor.getProcessorPlan() != command) {
//sanity check for non-deterministic paths
removeState(this.currentState);
this.currentState = null;
}
if (this.currentState == null) {
//this may not be the first time the plan is being run
command.reset();
CommandContext subContext = getContext().clone();
subContext.setVariableContext(this.currentVarContext);
state = new CursorState();
state.usesLocalTemp = usesLocalTemp;
state.processor = new QueryProcessor(command, subContext, this.bufferMgr, this);
state.ts = new BatchIterator(state.processor);
if (mode == Mode.HOLD && procAssignments != null && state.processor.getOutputElements().size() - procAssignments.size() > 0) {
state.resultsBuffer = bufferMgr.createTupleBuffer(state.processor.getOutputElements().subList(0, state.processor.getOutputElements().size() - procAssignments.size()), getContext().getConnectionId(), TupleSourceType.PROCESSOR);
} else if ((this.blockContext != null || this.programs.peek().isTrappingExceptions()) && (mode == Mode.HOLD || rsName != null)) {
state.resultsBuffer = bufferMgr.createTupleBuffer(state.processor.getOutputElements(), getContext().getConnectionId(), TupleSourceType.PROCESSOR);
}
this.currentState = state;
}
//force execution to the first batch to ensure that the plan is executed in the context of the procedure
this.currentState.ts.hasNext();
if (procAssignments != null) {
//proc assignments force us to scroll through the entire results and save as we go
while (this.currentState.ts.hasNext()) {
if (this.currentState.currentRow != null && this.currentState.resultsBuffer != null) {
this.currentState.resultsBuffer.addTuple(this.currentState.currentRow.subList(0, this.currentState.resultsBuffer.getSchema().size()));
this.currentState.currentRow = null;
}
this.currentState.currentRow = this.currentState.ts.nextTuple();
}
//process assignments
Assertion.assertTrue(this.currentState.currentRow != null);
for (Map.Entry<ElementSymbol, ElementSymbol> entry : procAssignments.entrySet()) {
if (entry.getValue() == null || !metadata.elementSupports(entry.getValue().getMetadataID(), SupportConstants.Element.UPDATE)) {
continue;
}
int index = this.currentState.processor.getOutputElements().indexOf(entry.getKey());
getCurrentVariableContext().setValue(entry.getValue(), DataTypeManager.transformValue(this.currentState.currentRow.get(index), entry.getValue().getType()));
}
} else if (this.currentState.resultsBuffer != null) {
//result should be saved, typically to respect txn semantics
while (this.currentState.ts.hasNext()) {
List<?> tuple = this.currentState.ts.nextTuple();
this.currentState.resultsBuffer.addTuple(tuple);
}
getCurrentVariableContext().setValue(ProcedurePlan.ROWCOUNT, 0);
} else if (mode == Mode.UPDATE) {
List<?> t = this.currentState.ts.nextTuple();
if (this.currentState.ts.hasNext()) {
throw new AssertionError("Invalid update count result - more than 1 row returned"); //$NON-NLS-1$
}
removeState(this.currentState);
this.currentState = null;
int rowCount = 0;
if (t != null) {
rowCount = (Integer)t.get(0);
}
getCurrentVariableContext().setValue(ProcedurePlan.ROWCOUNT, rowCount);
return;
}
if (rsName == null && mode == Mode.NOHOLD) {
//unnamed without hold
//process fully, but don't save
//TODO: could set the rowcount in this case
while (this.currentState.ts.hasNext()) {
this.currentState.ts.nextTuple();
}
this.currentState = null;
getCurrentVariableContext().setValue(ProcedurePlan.ROWCOUNT, 0);
return;
}
if (this.currentState.resultsBuffer != null) {
//close the results buffer and use a buffer backed tuplesource
this.currentState.resultsBuffer.close();
this.currentState.ts = this.currentState.resultsBuffer.createIndexedTupleSource(true);
}
CursorState old = (CursorState) this.cursorStates.setValue(rsName, this.currentState);
if (old != null) {
removeState(old);
}
this.currentState = null;
}
}
/**
* @param success
* @throws TeiidComponentException
* @throws XATransactionException
*/
public void pop(boolean success) throws TeiidComponentException {
this.evaluator.close();
Program program = this.programs.pop();
VariableContext vc = this.currentVarContext;
VariableContext cs = this.cursorStates;
try {
this.currentVarContext = this.currentVarContext.getParentContext();
this.cursorStates = this.cursorStates.getParentContext();
TempTableStore tempTableStore = program.getTempTableStore();
this.getContext().setTempTableStore(tempTableStore.getParentTempTableStore());
tempTableStore.removeTempTables();
if (program.startedTxn()) {
TransactionService ts = this.getContext().getTransactionServer();
TransactionContext tc = this.blockContext;
this.blockContext = null;
try {
ts.resume(tc);
for (WeakReference<DataTierTupleSource> ref : txnTupleSources) {
DataTierTupleSource dtts = ref.get();
if (dtts != null) {
dtts.fullyCloseSource();
}
}
this.txnTupleSources.clear();
if (success) {
ts.commit(tc);
} else {
ts.rollback(tc);
}
} catch (XATransactionException e) {
throw new TeiidComponentException(QueryPlugin.Event.TEIID30165, e);
}
}
} finally {
removeAllCursors(cs);
}
}
private void removeAllCursors(VariableContext cs) {
for (Map.Entry<Object, Object> entry : cs.getVariableMap().entrySet()) {
removeState((CursorState) entry.getValue());
}
}
public void push(Program program) throws XATransactionException {
this.evaluator.close();
program.reset(this.getContext().getConnectionId());
program.setTrappingExceptions(program.getExceptionGroup() != null || (!this.programs.isEmpty() && this.programs.peek().isTrappingExceptions()));
TempTableStore tts = getTempTableStore();
getContext().setTempTableStore(program.getTempTableStore());
program.getTempTableStore().setParentTempTableStore(tts);
this.programs.push(program);
VariableContext context = new VariableContext(true);
context.setParentContext(this.currentVarContext);
this.currentVarContext = context;
VariableContext cc = new VariableContext(true);
cc.setParentContext(this.cursorStates);
this.cursorStates = cc;
if (program.isAtomic() && this.blockContext == null) {
TransactionContext tc = this.getContext().getTransactionContext();
if (tc != null && tc.getTransactionType() == Scope.NONE && program.instructionsRequireTransaction(false) != Boolean.FALSE) {
//start a transaction
this.getContext().getTransactionServer().begin(tc);
this.blockContext = tc;
program.setStartedTxn(true);
}
}
}
public void incrementProgramCounter() throws TeiidComponentException {
if (this.programs.isEmpty()) {
return;
}
Program program = peek();
ProgramInstruction instr = program.getCurrentInstruction();
if (instr instanceof RepeatedInstruction) {
RepeatedInstruction repeated = (RepeatedInstruction)instr;
repeated.postInstruction(this);
}
peek().incrementProgramCounter();
}
public List<?> getCurrentRow(String rsName) throws TeiidComponentException {
return getCursorState(rsName).currentRow;
}
public boolean iterateCursor(String rsName)
throws TeiidComponentException, TeiidProcessingException {
CursorState state = getCursorState(rsName);
state.currentRow = state.ts.nextTuple();
return (state.currentRow != null);
}
private CursorState getCursorState(String rsKey) throws TeiidComponentException {
CursorState state = (CursorState) this.cursorStates.getValue(rsKey);
if (state == null) {
throw new TeiidComponentException(QueryPlugin.Event.TEIID30166, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30166, rsKey));
}
return state;
}
public void removeResults(String rsName) {
CursorState state = (CursorState) this.cursorStates.remove(rsName);
removeState(state);
}
private void removeState(CursorState state) {
if (state != null) {
state.processor.closeProcessing();
if (state.resultsBuffer != null) {
state.resultsBuffer.remove();
}
}
}
/**
* Get the schema from the tuple source that
* represents the columns in a result set
* @param rsName the ResultSet name (not a temp group)
* @return List of elements
* @throws QueryProcessorException if the list of elements is null
*/
public List getSchema(String rsName) throws TeiidComponentException {
CursorState cursorState = getCursorState(rsName);
// get the schema from the tuple source
List schema = cursorState.processor.getOutputElements();
return schema;
}
public boolean resultSetExists(String rsName) {
return this.cursorStates.containsVariable(rsName);
}
public CommandContext getContext() {
CommandContext context = super.getContext();
if (evaluatedParams) {
context.setVariableContext(currentVarContext);
}
return context;
}
public List getOutputElements() {
return outputElements;
}
public void setOutputElements(List outputElements) {
this.outputElements = outputElements;
}
/**
* @return Returns the tempTableStore.
* @since 5.5
*/
public TempTableStore getTempTableStore() {
if (this.programs.isEmpty()) {
if (runInContext && params == null) {
return getContext().getTempTableStore();
}
return null;
}
return this.peek().getTempTableStore();
}
boolean evaluateCriteria(Criteria condition) throws BlockedException, TeiidProcessingException, TeiidComponentException {
evaluator.initialize(getContext(), getDataManager());
return evaluator.evaluate(condition, Collections.emptyList());
}
Object evaluateExpression(Expression expression) throws BlockedException, TeiidProcessingException, TeiidComponentException {
evaluator.initialize(getContext(), getDataManager());
return evaluator.evaluate(expression, Collections.emptyList());
}
public Program peek() {
if (this.programs.isEmpty()) {
return null;
}
return programs.peek();
}
public void setUpdateCount(int updateCount) {
this.updateCount = updateCount;
}
@Override
public Boolean requiresTransaction(boolean transactionalReads) {
Boolean paramRequires = false;
if (params != null) {
paramRequires = SubqueryAwareRelationalNode.requiresTransaction(transactionalReads, ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(params.values()));
if (paramRequires != null && paramRequires) {
return true;
}
}
if ((transactionalReads && updateCount < 2) || updateCount == Procedure.AUTO_UPDATECOUNT) {
Boolean programRequires = this.originalProgram.requiresTransaction(transactionalReads);
if (programRequires == null) {
return paramRequires==null?true:null;
}
if (programRequires) {
return true;
}
return paramRequires;
}
if (updateCount == 0) {
return paramRequires;
}
if (updateCount == 1) {
return paramRequires==null?true:null;
}
return true;
}
/**
* For procedures without explicit parameters, sets whether the
* procedure should run in the parent variable context.
* @param runInContext
*/
public void setRunInContext(boolean runInContext) {
this.runInContext = runInContext;
}
@Override
public TupleSource registerRequest(CommandContext context, Command command,
String modelName, RegisterRequestParameter parameterObject)
throws TeiidComponentException, TeiidProcessingException {
//programs will be empty for parameter evaluation
TupleSource ts = parentDataMrg.registerRequest(context, command, modelName, parameterObject);
if (blockContext != null && ts instanceof DataTierTupleSource) {
txnTupleSources.add(new WeakReference<DataTierTupleSource>((DataTierTupleSource)ts));
}
return ts;
}
@Override
public Object lookupCodeValue(CommandContext context, String codeTableName,
String returnElementName, String keyElementName, Object keyValue)
throws BlockedException, TeiidComponentException,
TeiidProcessingException {
return parentDataMrg.lookupCodeValue(context, codeTableName, returnElementName, keyElementName, keyValue);
}
@Override
public EventDistributor getEventDistributor() {
return parentDataMrg.getEventDistributor();
}
public void setValidateAccess(boolean b) {
this.validateAccess = b;
}
public boolean isValidateAccess() {
return validateAccess;
}
}