/*
* 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 java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.teiid.client.xa.XATransactionException;
import org.teiid.common.buffer.BlockedException;
import org.teiid.common.buffer.BufferManager;
import org.teiid.common.buffer.TupleBatch;
import org.teiid.common.buffer.TupleSource;
import org.teiid.core.TeiidComponentException;
import org.teiid.core.TeiidProcessingException;
import org.teiid.dqp.service.TransactionContext;
import org.teiid.dqp.service.TransactionContext.Scope;
import org.teiid.dqp.service.TransactionService;
import org.teiid.query.QueryPlugin;
import org.teiid.query.eval.Evaluator;
import org.teiid.query.processor.BatchCollector;
import org.teiid.query.processor.ProcessorDataManager;
import org.teiid.query.processor.ProcessorPlan;
import org.teiid.query.processor.QueryProcessor;
import org.teiid.query.sql.lang.Command;
import org.teiid.query.sql.symbol.ElementSymbol;
import org.teiid.query.sql.symbol.Expression;
import org.teiid.query.util.CommandContext;
public class ForEachRowPlan extends ProcessorPlan {
private ProcessorPlan queryPlan;
private ProcedurePlan rowProcedure;
private Map<ElementSymbol, Expression> params;
private Map<Expression, Integer> lookupMap;
private ProcessorDataManager dataMgr;
private BufferManager bufferMgr;
private QueryProcessor queryProcessor;
private TupleSource tupleSource;
private QueryProcessor rowProcessor;
private List<?> currentTuple;
private long updateCount;
private TransactionContext planContext;
@Override
public ProcessorPlan clone() {
ForEachRowPlan clone = new ForEachRowPlan();
clone.setQueryPlan(queryPlan.clone());
clone.setRowProcedure((ProcedurePlan) rowProcedure.clone());
clone.setParams(params);
clone.setLookupMap(lookupMap);
return clone;
}
@Override
public void close() throws TeiidComponentException {
if (this.queryProcessor != null) {
this.queryProcessor.closeProcessing();
if (this.rowProcessor != null) {
this.rowProcessor.closeProcessing();
}
}
if (this.planContext != null) {
TransactionService ts = this.getContext().getTransactionServer();
try {
ts.resume(planContext);
ts.rollback(planContext);
this.planContext = null;
} catch (XATransactionException e) {
throw new TeiidComponentException(QueryPlugin.Event.TEIID30165, e);
}
}
}
@Override
public List<Expression> getOutputElements() {
return Command.getUpdateCommandSymbol();
}
@Override
public void initialize(CommandContext context,
ProcessorDataManager dataMgr, BufferManager bufferMgr) {
setContext(context);
this.dataMgr = dataMgr;
this.bufferMgr = bufferMgr;
}
@Override
public TupleBatch nextBatch() throws BlockedException,
TeiidComponentException, TeiidProcessingException {
if (planContext != null) {
this.getContext().getTransactionServer().resume(planContext);
}
try {
while (true) {
if (currentTuple == null) {
currentTuple = tupleSource.nextTuple();
if (currentTuple == null) {
if (this.planContext != null) {
TransactionService ts = this.getContext().getTransactionServer();
ts.commit(this.planContext);
this.planContext = null;
}
TupleBatch result = new TupleBatch(1, new List[] {Arrays.asList((int)Math.min(Integer.MAX_VALUE, updateCount))});
result.setTerminationFlag(true);
return result;
}
}
if (this.rowProcessor == null) {
rowProcedure.reset();
CommandContext context = getContext().clone();
this.rowProcessor = new QueryProcessor(rowProcedure, context, this.bufferMgr, this.dataMgr);
Evaluator eval = new Evaluator(lookupMap, dataMgr, context);
for (Map.Entry<ElementSymbol, Expression> entry : this.params.entrySet()) {
Integer index = this.lookupMap.get(entry.getValue());
if (index != null) {
rowProcedure.getCurrentVariableContext().setValue(entry.getKey(), this.currentTuple.get(index));
} else {
rowProcedure.getCurrentVariableContext().setValue(entry.getKey(), eval.evaluate(entry.getValue(), this.currentTuple));
}
}
}
//just getting the next batch is enough
this.rowProcessor.nextBatch();
this.rowProcessor.closeProcessing();
this.rowProcessor = null;
this.currentTuple = null;
this.updateCount++;
}
} finally {
if (planContext != null) {
this.getContext().getTransactionServer().suspend(planContext);
}
}
}
@Override
public void open() throws TeiidComponentException, TeiidProcessingException {
TransactionContext tc = this.getContext().getTransactionContext();
if (tc != null && tc.getTransactionType() == Scope.NONE) {
//start a transaction - if not each of the row plans will
//be executed in it's own transaction, which is bad for performance
//TODO: should probably allow non-atomic row plans
//the parser accepts a trigger block without atomic
//but the spec mandates it - and we treat it as atomic
//either way
//TODO: for non-transactional environments this will
//trigger an error
this.getContext().getTransactionServer().begin(tc);
this.planContext = tc;
}
if (queryPlan != null) {
queryProcessor = new QueryProcessor(queryPlan, getContext(), this.bufferMgr, this.dataMgr);
tupleSource = new BatchCollector.BatchProducerTupleSource(queryProcessor);
}
}
public void setQueryPlan(ProcessorPlan queryPlan) {
this.queryPlan = queryPlan;
}
public void setRowProcedure(ProcedurePlan rowProcedure) {
this.rowProcedure = rowProcedure;
}
public void setParams(Map<ElementSymbol, Expression> params) {
this.params = params;
}
public void setLookupMap(Map<Expression, Integer> symbolMap) {
this.lookupMap = symbolMap;
}
@Override
public void reset() {
super.reset();
this.queryPlan.reset();
this.updateCount = 0;
this.currentTuple = null;
this.rowProcessor = null;
this.queryProcessor = null;
this.tupleSource = null;
this.planContext = null;
}
@Override
public Boolean requiresTransaction(boolean transactionalReads) {
return true;
}
@Override
public String toString() {
StringBuilder val = new StringBuilder("ForEach "); //$NON-NLS-1$
val.append(this.queryPlan).append("\n{\n"); //$NON-NLS-1$
val.append(this.rowProcedure);
val.append("}\n"); //$NON-NLS-1$
return val.toString();
}
public void setTupleSource(TupleSource tupleSource) {
this.tupleSource = tupleSource;
}
}