/* * 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.relational; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import org.teiid.client.plan.PlanNode; import org.teiid.common.buffer.BlockedException; 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.query.QueryPlugin; import org.teiid.query.analysis.AnalysisRecord; import org.teiid.query.eval.Evaluator; import org.teiid.query.optimizer.relational.RowBasedSecurityHelper; import org.teiid.query.processor.RegisterRequestParameter; import org.teiid.query.sql.LanguageObject; import org.teiid.query.sql.lang.BatchedUpdateCommand; import org.teiid.query.sql.lang.Command; import org.teiid.query.sql.util.VariableContext; import org.teiid.query.util.CommandContext; /** * Node that batches commands sent to the DataManager. * @since 4.2 */ public class BatchedUpdateNode extends SubqueryAwareRelationalNode { private static final List<Integer> ZERO_COUNT_TUPLE = Arrays.asList(Integer.valueOf(0)); /** The commands in this batch. */ private List<Command> updateCommands; private List<VariableContext> contexts; private List<Boolean> shouldEvaluate; /** The model name within the scope of which these commands are being executed. */ private String modelName; /** The tuple source containing the update counts after the batch has been executed. */ private TupleSource tupleSource; /** Set containing the indexes of commands that weren't executed. */ private boolean[] unexecutedCommands; private int commandCount; /** * * @param nodeID * @param commands The Commands in this batch * @param modelName The name of the model. All the commands in this batch must update groups only within this model. * @since 4.2 */ public BatchedUpdateNode(int nodeID, List<Command> commands, List<VariableContext> contexts, List<Boolean> shouldEvaluate, String modelName) { super(nodeID); this.shouldEvaluate = shouldEvaluate; this.contexts = contexts; this.updateCommands = commands; this.modelName = modelName; } /** * @see org.teiid.query.processor.relational.RelationalNode#open() * @since 4.2 */ public void open() throws TeiidComponentException, TeiidProcessingException { super.open(); unexecutedCommands = new boolean[updateCommands.size()]; List<Command> commandsToExecute = new ArrayList<Command>(updateCommands.size()); // Find the commands to be executed for (int i = 0; i < updateCommands.size(); i++) { Command updateCommand = (Command)updateCommands.get(i).clone(); CommandContext context = this.getContext(); if (this.contexts != null && !this.contexts.isEmpty()) { context = context.clone(); context.setVariableContext(this.contexts.get(i)); } boolean needProcessing = false; if(shouldEvaluate != null && shouldEvaluate.get(i)) { updateCommand = (Command) updateCommand.clone(); Evaluator eval = getEvaluator(Collections.emptyMap()); eval.initialize(context, getDataManager()); AccessNode.rewriteAndEvaluate(updateCommand, eval, context, context.getMetadata()); } needProcessing = RelationalNodeUtil.shouldExecute(updateCommand, true); if (needProcessing) { commandsToExecute.add(updateCommand); } else { unexecutedCommands[i] = true; } } if (!commandsToExecute.isEmpty()) { BatchedUpdateCommand command = new BatchedUpdateCommand(commandsToExecute); RowBasedSecurityHelper.checkConstraints(command, getEvaluator(Collections.emptyMap())); tupleSource = getDataManager().registerRequest(getContext(), command, modelName, new RegisterRequestParameter(null, getID(), -1)); } } /** * @throws TeiidProcessingException * @see org.teiid.query.processor.relational.RelationalNode#nextBatchDirect() * @since 4.2 */ public TupleBatch nextBatchDirect() throws BlockedException, TeiidComponentException, TeiidProcessingException { int numExpectedCounts = updateCommands.size(); for (;commandCount < numExpectedCounts; commandCount++) { // If the command at this index was not executed if (tupleSource == null || unexecutedCommands[commandCount]) { addBatchRow(ZERO_COUNT_TUPLE); } else { // Otherwise, get the next count in the batch List<?> tuple = tupleSource.nextTuple(); if (tuple != null) { // Assumption: the number of returned tuples exactly equals the number of commands submitted addBatchRow(Arrays.asList(new Object[] {tuple.get(0)})); } else { // Should never happen since the number of expected results is known throw new TeiidComponentException(QueryPlugin.Event.TEIID30192, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30192, commandCount, numExpectedCounts)); } } } // This is the only tuple batch we need. terminateBatches(); return pullBatch(); } /** * @see org.teiid.query.processor.relational.RelationalNode#close() * @since 4.2 */ public void closeDirect() { super.closeDirect(); if (tupleSource != null) { tupleSource.closeSource(); tupleSource = null; } } /** * @see org.teiid.query.processor.relational.RelationalNode#reset() * @since 4.2 */ public void reset() { super.reset(); tupleSource = null; unexecutedCommands = null; commandCount = 0; } /** * @see java.lang.Object#clone() * @since 4.2 */ public Object clone() { BatchedUpdateNode clonedNode = new BatchedUpdateNode(getID(), updateCommands, contexts, shouldEvaluate, modelName); super.copyTo(clonedNode); return clonedNode; } @Override public Collection<? extends LanguageObject> getObjects() { return null; } @Override public Boolean requiresTransaction(boolean transactionalReads) { return null; } @Override public PlanNode getDescriptionProperties() { PlanNode node = super.getDescriptionProperties(); AnalysisRecord.addLanaguageObjects(node, AnalysisRecord.PROP_SQL, this.updateCommands); return node; } }