/* * 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 static org.junit.Assert.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.junit.Test; import org.teiid.api.exception.query.ExpressionEvaluationException; import org.teiid.common.buffer.BlockedException; import org.teiid.common.buffer.BufferManager; import org.teiid.common.buffer.BufferManagerFactory; 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.events.EventDistributor; import org.teiid.query.eval.Evaluator; import org.teiid.query.processor.CollectionTupleSource; import org.teiid.query.processor.FakeTupleSource; import org.teiid.query.processor.ProcessorDataManager; import org.teiid.query.processor.RegisterRequestParameter; import org.teiid.query.processor.relational.ProjectIntoNode.Mode; import org.teiid.query.sql.lang.BatchedUpdateCommand; import org.teiid.query.sql.lang.Command; import org.teiid.query.sql.lang.Insert; import org.teiid.query.sql.symbol.Constant; 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.util.CommandContext; /** * @since 4.2 */ public class TestProjectIntoNode { // Rows should be a multiple of batch size for this test to work private static final int NUM_ROWS = 1000; private void helpTestNextBatch(int tupleBatchSize, Mode mode) throws Exception { ProjectIntoNode node = new ProjectIntoNode(2); TupleSource tupleSource = new FakeDataTupleSource(NUM_ROWS); RelationalNode child = new FakeRelationalNode(1,tupleSource, tupleBatchSize); node.addChild(child); node.setIntoGroup(new GroupSymbol("myGroup")); //$NON-NLS-1$ ElementSymbol elementSymbol_1 = new ElementSymbol("myGroup.myElement1"); //$NON-NLS-1$ ElementSymbol elementSymbol_2 = new ElementSymbol("myGroup.myElement2"); //$NON-NLS-1$ elementSymbol_1.setType(Integer.class); elementSymbol_2.setType(String.class); List<ElementSymbol> elements = Arrays.asList(elementSymbol_1, elementSymbol_2); node.setIntoElements(elements); child.setElements(elements); node.setMode(mode); node.setModelName("myModel"); //$NON-NLS-1$ CommandContext context = new CommandContext(); BufferManager bm = BufferManagerFactory.getTestBufferManager(tupleBatchSize, tupleBatchSize); ProcessorDataManager dataManager = new FakePDM(tupleBatchSize); child.initialize(context, bm, dataManager); node.initialize(context, bm, dataManager); node.open(); TupleBatch batch = null; // Do the remaining batches while(true) { try { batch = node.nextBatch(); break; } catch (BlockedException e) { // Normal } } assertNotNull(batch); List[] tuples = batch.getAllTuples(); assertEquals(1, tuples.length); Object[] columns = tuples[0].toArray(); assertNotNull(columns); assertEquals(1, columns.length); // Should have inserted all rows assertEquals(new Integer(NUM_ROWS), columns[0]); } @Test public void testNextBatch() throws Exception { helpTestNextBatch(100, Mode.BATCH); } @Test public void testNextBatch_NoBatching() throws Exception { helpTestNextBatch(100, Mode.SINGLE); } @Test public void testNextBatch_Size20Batches() throws Exception { helpTestNextBatch(20, Mode.BATCH); } @Test public void testNextBatch_Iterator() throws Exception { helpTestNextBatch(100, Mode.ITERATOR); } private static final class FakePDM implements ProcessorDataManager { private int expectedBatchSize; private int callCount = 0; private FakePDM(int expectedBatchSize) { this.expectedBatchSize = expectedBatchSize; } public Object lookupCodeValue(CommandContext context,String codeTableName,String returnElementName,String keyElementName,Object keyValue) throws BlockedException,TeiidComponentException {return null;} public TupleSource registerRequest(CommandContext context,Command command,String modelName,RegisterRequestParameter parameterObject) throws TeiidComponentException, TeiidProcessingException { callCount++; int batchSize = 1; // ensure that we have the right kind of insert, and that the data for this row is valid if (command instanceof Insert) { Insert insert = (Insert)command; if (isBulk(insert)) { List batch = getBulkRows(insert, insert.getVariables()); batchSize = batch.size(); assertEquals("Unexpected batch on call " + callCount, expectedBatchSize, batchSize); //$NON-NLS-1$ for (int i = 0; i < batchSize; i++) { ensureValue2((List)batch.get(i), 2, ((callCount-1) * batchSize) + i + 1); } } else if (insert.getTupleSource() != null) { TupleSource ts = insert.getTupleSource(); List tuple = null; int i = 0; while ((tuple = ts.nextTuple()) != null) { ensureValue2(tuple, 2, ++i); } batchSize = i; } else { ensureValue(insert, 2, callCount); } } else if ( command instanceof BatchedUpdateCommand ){ BatchedUpdateCommand bu = (BatchedUpdateCommand)command; List<Command> batch = bu.getUpdateCommands(); batchSize = batch.size(); assertEquals("Unexpected batch on call " + callCount, expectedBatchSize, batchSize); //$NON-NLS-1$ } else { fail("Unexpected command type"); //$NON-NLS-1$ } if (batchSize > 1) { return CollectionTupleSource.createUpdateCountArrayTupleSource(batchSize); } List counts = Arrays.asList(new Object[] { new Integer(batchSize)}); FakeTupleSource fakeTupleSource = new FakeTupleSource(null, new List[] {counts}); return fakeTupleSource; } private void ensureValue(Insert command, int size, int value) { assertNotNull(command.getValues()); assertEquals(size, command.getValues().size()); assertEquals(new Integer(value), ((Constant)command.getValues().get(0)).getValue()); } private void ensureValue2(List row, int size, int value) { assertNotNull(row); assertEquals(size, row.size()); Object val = row.get(0); assertEquals(new Integer(value), val); } @Override public EventDistributor getEventDistributor() { // TODO Auto-generated method stub return null; } } private static final class FakeDataTupleSource implements TupleSource { private int currentRow = 0; private boolean block = true; private int rows; private FakeDataTupleSource(int rows) { this.rows = rows; } public void closeSource() {} public List getSchema() {return null;} public List nextTuple() throws TeiidComponentException { if (currentRow % 100 == 0 && block) { block = false; throw BlockedException.INSTANCE; } return (++currentRow > rows) ? null : Arrays.asList(new Object[] {new Integer(currentRow), Integer.toString(currentRow)}); } } public static List<List<Object>> getBulkRows(Insert insert, List<ElementSymbol> elements) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { int bulkRowCount = 1; if (isBulk(insert)) { Constant c = (Constant)insert.getValues().get(0); bulkRowCount = ((List<?>)c.getValue()).size(); } List<List<Object>> tuples = new ArrayList<List<Object>>(bulkRowCount); for (int row = 0; row < bulkRowCount; row++) { List<Object> currentRow = new ArrayList<Object>(insert.getValues().size()); for (ElementSymbol symbol : elements) { int index = insert.getVariables().indexOf(symbol); Object value = null; if (index != -1) { if (isBulk(insert)) { Constant multiValue = (Constant)insert.getValues().get(index); value = ((List<?>)multiValue.getValue()).get(row); } else { Expression expr = (Expression)insert.getValues().get(index); value = Evaluator.evaluate(expr); } } currentRow.add(value); } tuples.add(currentRow); } return tuples; } public static boolean isBulk(Insert insert) { if (insert.getValues() == null) { return false; } if (!(insert.getValues().get(0) instanceof Constant)) { return false; } return ((Constant)insert.getValues().get(0)).isMultiValued(); } }