[teiid-commits] teiid SVN: r837 - in trunk/engine/src/test/java: org/teiid/dqp/internal/process and 1 other directory.

teiid-commits at lists.jboss.org teiid-commits at lists.jboss.org
Thu Apr 23 17:26:48 EDT 2009


Author: loleary
Date: 2009-04-23 17:26:48 -0400 (Thu, 23 Apr 2009)
New Revision: 837

Added:
   trunk/engine/src/test/java/org/teiid/dqp/internal/process/TestPreparedStatementBatchedUpdate.java
Modified:
   trunk/engine/src/test/java/com/metamatrix/query/processor/FakeDataManager.java
   trunk/engine/src/test/java/com/metamatrix/query/processor/TestVirtualDepJoin.java
   trunk/engine/src/test/java/org/teiid/dqp/internal/process/TestPreparedStatement.java
Log:
TEIID-532: Added UnitTests to cover BatchedUpdate within PreparedStatement.
Reviewed by: ghelblin

Modified: trunk/engine/src/test/java/com/metamatrix/query/processor/FakeDataManager.java
===================================================================
--- trunk/engine/src/test/java/com/metamatrix/query/processor/FakeDataManager.java	2009-04-23 21:24:27 UTC (rev 836)
+++ trunk/engine/src/test/java/com/metamatrix/query/processor/FakeDataManager.java	2009-04-23 21:26:48 UTC (rev 837)
@@ -23,6 +23,7 @@
 package com.metamatrix.query.processor;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -37,11 +38,14 @@
 import com.metamatrix.common.log.LogManager;
 import com.metamatrix.query.eval.Evaluator;
 import com.metamatrix.query.metadata.TempMetadataID;
+import com.metamatrix.query.sql.lang.BatchedUpdateCommand;
 import com.metamatrix.query.sql.lang.Command;
+import com.metamatrix.query.sql.lang.CommandContainer;
 import com.metamatrix.query.sql.lang.From;
 import com.metamatrix.query.sql.lang.ProcedureContainer;
 import com.metamatrix.query.sql.lang.Query;
 import com.metamatrix.query.sql.lang.SetQuery;
+import com.metamatrix.query.sql.lang.Update;
 import com.metamatrix.query.sql.symbol.AliasSymbol;
 import com.metamatrix.query.sql.symbol.ElementSymbol;
 import com.metamatrix.query.sql.symbol.GroupSymbol;
@@ -71,7 +75,7 @@
     private Map blockedState = new HashMap();
 
     // Track history to verify it later
-    private Set queries = new HashSet();
+    private List<String> queries = new ArrayList<String>();
     
 	public FakeDataManager() {
 	}
@@ -80,7 +84,7 @@
      * Return string form of all queries run against this FDM 
      * @return List<String>
      */
-    public Set getQueries() {
+    public List<String> getQueries() {
         return this.queries;
     }
 	        
@@ -89,6 +93,7 @@
 	}
 	
 	public void closeRequest(Object requestID) {
+		// does nothing?
     } 
 	
 	public TupleSource registerRequest(Object processorID, Command command, String modelName, String connectorBindingId, int nodeID)
@@ -96,7 +101,18 @@
         
         LogManager.logTrace(LOG_CONTEXT, new Object[]{"Register Request:", command, ",processorID:", processorID, ",model name:", modelName,",TupleSourceID nodeID:",new Integer(nodeID)}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
 
-        queries.add(command.toString());
+        
+        if ( command instanceof CommandContainer ) {
+        	if ( ((CommandContainer) command).getContainedCommands() != null && ((CommandContainer) command).getContainedCommands().size() > 0 ) {
+            	for ( Iterator<Command> it = ((CommandContainer) command).getContainedCommands().iterator(); it.hasNext(); ) {
+            		this.queries.add(it.next().toString());
+            	}
+        	} else {
+        		this.queries.add(command.toString());
+        	}
+        } else {
+        	this.queries.add(command.toString());
+        }
 
         if (ReferenceCollectorVisitor.getReferences(command).size() > 0) {
             throw new IllegalArgumentException("Found references in the command registered with the DataManager."); //$NON-NLS-1$
@@ -108,8 +124,14 @@
         }else if(command instanceof SetQuery) {
             SetQuery union = (SetQuery) command;            
             group = getQueryGroup(union.getProjectedQuery());
-		}else if(command instanceof ProcedureContainer){
-			group = ((ProcedureContainer)command).getGroup();
+		} else if (command instanceof ProcedureContainer) {
+			group = ((ProcedureContainer) command).getGroup();
+		} else if ( command instanceof BatchedUpdateCommand ) {
+        	if ( ((CommandContainer) command).getContainedCommands() != null && ((CommandContainer) command).getContainedCommands().size() > 0 ) {
+        		if ( command.getSubCommands().get(0) instanceof Update ) {
+        			group = ((Update)command.getSubCommands().get(0)).getGroup();
+        		}
+        	}
 		}
 		
 		Object groupID = group.getMetadataID();
@@ -121,6 +143,13 @@
 		List projectedSymbols = command.getProjectedSymbols();
 		int[] columnMap = getColumnMap(elements, projectedSymbols);
 		
+		/* 
+		*  updateCommands is used to hold a list of commands that 
+		*  either came from a BatchedUpdateCommand or a signle 
+		*  command from an Update command.
+		*/
+		List<Update> updateCommands = new ArrayList<Update>();
+		
 		// Apply query criteria to tuples
 		if(command instanceof Query){
 			Query query = (Query)command;
@@ -150,6 +179,54 @@
 			    tuples = new List[filteredTuples.size()];
 			    filteredTuples.toArray(tuples);
 			}
+		} else if ( command instanceof Update ) {
+			// add single update command to a list to be executed
+			updateCommands.add((Update)command);
+		} else if ( command instanceof BatchedUpdateCommand ) {
+        	if ( ((CommandContainer) command).getContainedCommands() != null && ((CommandContainer) command).getContainedCommands().size() > 0 )
+				// add all update commands to a list to be executed
+        		for ( int i = 0; i < ((CommandContainer) command).getContainedCommands().size(); i++ ) 
+        			if ( ((CommandContainer) command).getContainedCommands().get(i) instanceof Update ) {
+        				updateCommands.add(((Update) ((CommandContainer) command).getContainedCommands().get(i)));
+        		}
+		}
+		
+		// if we had update commands added to the list, execute them now
+		if ( updateCommands.size() > 0 ) {
+		    List<List<Integer>> filteredTuples = new ArrayList<List<Integer>>();
+			for ( int c = 0; c < updateCommands.size(); c++ ) {
+				Update update = (Update)updateCommands.get(c);
+				if ( update.getCriteria() != null ) {
+				    // Build lookupMap from BOTH all the elements and the projected symbols - both may be needed here
+		            Map<Object, Integer> lookupMap = new HashMap<Object, Integer>();
+		            for(int i=0; i<elements.size(); i++) { 
+		                Object element = elements.get(i);
+	                    mapElementToIndex(lookupMap, element, new Integer(i), group);        
+		            }
+		            for(int i=0; i<projectedSymbols.size(); i++) { 
+		            	Object element = projectedSymbols.get(i);
+	                    mapElementToIndex(lookupMap, element, new Integer(columnMap[i]), group);
+		            }
+				    
+				    int updated = 0;
+				    for(int i=0; i<tuples.length; i++) {
+		                try {
+		    				if(new Evaluator(lookupMap, null, null).evaluate(update.getCriteria(), tuples[i])) {
+		                        updated++;
+		                    }
+		                } catch(CriteriaEvaluationException e) {
+		                    throw new MetaMatrixComponentException(e, e.getMessage());
+		                }
+				    }
+			    	List<Integer> updateTuple = new ArrayList<Integer>(1);
+			    	updateTuple.add( new Integer(updated) );
+                    filteredTuples.add(updateTuple);
+				}
+			}
+		    tuples = new List[filteredTuples.size()];
+		    filteredTuples.toArray(tuples);
+		    elements = new ArrayList<Object>(projectedSymbols);
+		    columnMap[0] = 0;
 		}		
 				
         FakeTupleSource ts= new FakeTupleSource(elements, tuples, projectedSymbols, columnMap);

Modified: trunk/engine/src/test/java/com/metamatrix/query/processor/TestVirtualDepJoin.java
===================================================================
--- trunk/engine/src/test/java/com/metamatrix/query/processor/TestVirtualDepJoin.java	2009-04-23 21:24:27 UTC (rev 836)
+++ trunk/engine/src/test/java/com/metamatrix/query/processor/TestVirtualDepJoin.java	2009-04-23 21:26:48 UTC (rev 837)
@@ -24,6 +24,7 @@
 
 import java.math.BigDecimal;
 import java.sql.SQLException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -219,7 +220,7 @@
 
         // Second query *will not be run* as no values were passed and dependent side has always false criteria
         // So, the list should contain only the first query
-        assertEquals(1, dataManager.getQueries().size());       
+        assertEquals(3, dataManager.getQueries().size());       
     }  
     
     public void helpTestVirtualDepJoinSourceSelection(boolean setPushdown) throws Exception {  
@@ -389,8 +390,13 @@
         // Run query 
         TestProcessor.helpProcess(plan, context, dataManager, expected); 
         
-        assertEquals(new HashSet<String>(Arrays.asList("SELECT g_0.id AS c_0, g_0.amount AS c_1 FROM Europe.CustAccts AS g_0 WHERE g_0.id = 100 ORDER BY c_0", "SELECT g_0.id AS c_0, g_0.first AS c_1, g_0.last AS c_2 FROM CustomerMaster.Customers AS g_0 WHERE g_0.first = 'Miles' ORDER BY c_0")), //$NON-NLS-1$ //$NON-NLS-2$  
-                     dataManager.getQueries());
+        List<String> expectedQueries = new ArrayList<String>(6);
+        for (int i = 0; i < 3; i++) {
+        	expectedQueries.add("SELECT g_0.id AS c_0, g_0.first AS c_1, g_0.last AS c_2 FROM CustomerMaster.Customers AS g_0 WHERE g_0.first = 'Miles' ORDER BY c_0"); //$NON-NLS-1$
+        	expectedQueries.add("SELECT g_0.id AS c_0, g_0.amount AS c_1 FROM Europe.CustAccts AS g_0 WHERE g_0.id = 100 ORDER BY c_0"); //$NON-NLS-1$
+        }
+        
+        assertEquals(expectedQueries, dataManager.getQueries());
     }    
     
     public void testVirtualDepJoinSelects() throws Exception {

Modified: trunk/engine/src/test/java/org/teiid/dqp/internal/process/TestPreparedStatement.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/dqp/internal/process/TestPreparedStatement.java	2009-04-23 21:24:27 UTC (rev 836)
+++ trunk/engine/src/test/java/org/teiid/dqp/internal/process/TestPreparedStatement.java	2009-04-23 21:26:48 UTC (rev 837)
@@ -80,38 +80,74 @@
 	}	        	    
 	
 	static void helpTestProcessing(String preparedSql, List values, List[] expected, QueryMetadataInterface metadata, boolean callableStatement) throws Exception {
-		// Construct data manager with data
-        FakeDataManager dataManager = new FakeDataManager();
-        TestProcessor.sampleData1(dataManager);    
-		
-        helpTestProcessing(preparedSql, values, expected, dataManager, metadata, callableStatement);
+        helpTestProcessing(preparedSql, values, expected, (ProcessorDataManager)null, metadata, callableStatement);
 	}
 	
     static void helpTestProcessing(String preparedSql, List values, List[] expected, ProcessorDataManager dataManager, QueryMetadataInterface metadata, boolean callableStatement) throws Exception { 
     	helpTestProcessing(preparedSql, values, expected, dataManager, metadata, callableStatement, false);
     }
 
-    static void helpTestProcessing(String preparedSql, List values, List[] expected, ProcessorDataManager dataManager, QueryMetadataInterface metadata, boolean callableStatement, boolean differentPlan) throws Exception { 
-        TestablePreparedPlanCache prepPlan = new TestablePreparedPlanCache();
-        //Create plan
-        PreparedStatementRequest plan = TestPreparedStatement.helpGetProcessorPlan(preparedSql, values, new DefaultCapabilitiesFinder(), metadata, prepPlan, SESSION_ID, callableStatement, false);
+    static void helpTestProcessing(String preparedSql, List values, List[] expected, ProcessorDataManager dataManager, QueryMetadataInterface metadata, boolean callableStatement, boolean isSessionSpecific) throws Exception { 
+        helpTestProcessing(preparedSql, values, expected, dataManager, (CapabilitiesFinder)null, metadata, (TestablePreparedPlanCache)null, callableStatement, isSessionSpecific, /* isAlreadyCached */false); 
+    }
+    
+    static public void helpTestProcessing(String preparedSql, List values, List[] expected, ProcessorDataManager dataManager, CapabilitiesFinder capFinder, QueryMetadataInterface metadata, TestablePreparedPlanCache prepPlanCache, boolean callableStatement, boolean isSessionSpecific, boolean isAlreadyCached) throws Exception { 
+        TestablePreparedPlanCache pPlanCache = null;
+        CapabilitiesFinder cFinder = null;
+        ProcessorDataManager dManager = null;
+        
+        if ( dataManager == null ) {
+            // Construct data manager with data
+            dManager = new FakeDataManager();
+            TestProcessor.sampleData1((FakeDataManager)dManager);    
+        } else dManager = dataManager;
+        
+        if ( capFinder == null ) cFinder = new DefaultCapabilitiesFinder();
+        else cFinder = capFinder;
+        
+        if ( prepPlanCache == null ) pPlanCache = new TestablePreparedPlanCache();
+        else pPlanCache = prepPlanCache;
+        
+		// expected cache hit count
+        int exHitCount = -1;
+        
+		/*
+		 * If the plan is already cached we want our expected hit
+		 * count of the cache to be at least 2 because we will 
+		 * get the plan twice.  Otherwise, we want it to be 1.
+		 */
+        if ( isAlreadyCached ) {
+        	exHitCount = pPlanCache.hitCount + 2;
+        } else {
+        	exHitCount = pPlanCache.hitCount + 1;
+        }
+        
+        //Create plan or used cache plan if isPlanCached
+        PreparedStatementRequest plan = TestPreparedStatement.helpGetProcessorPlan(preparedSql, values, cFinder, metadata, pPlanCache, SESSION_ID, callableStatement, false);
 
         // Run query
-        TestProcessor.helpProcess(plan.processPlan, plan.context, dataManager, expected);
+        TestProcessor.helpProcess(plan.processPlan, plan.context, dManager, expected);
         
         //test cached plan
-    	plan = TestPreparedStatement.helpGetProcessorPlan(preparedSql, values, new DefaultCapabilitiesFinder(), metadata, prepPlan, SESSION_ID, callableStatement, false);
+    	plan = TestPreparedStatement.helpGetProcessorPlan(preparedSql, values, cFinder, metadata, pPlanCache, SESSION_ID, callableStatement, false);
     	
         //make sure the plan is only created once
-        assertEquals("should reuse the plan", 1, prepPlan.hitCount); //$NON-NLS-1$
+        assertEquals("should reuse the plan", exHitCount, pPlanCache.hitCount); //$NON-NLS-1$
                 
         // Run query again
-        TestProcessor.helpProcess(plan.processPlan, plan.context, dataManager, expected);
+        TestProcessor.helpProcess(plan.processPlan, plan.context, dManager, expected);
         
         //get the plan again with a new connection
-        assertNotNull(TestPreparedStatement.helpGetProcessorPlan(preparedSql, values, new DefaultCapabilitiesFinder(), metadata, prepPlan, 7, callableStatement, false));
+        assertNotNull(TestPreparedStatement.helpGetProcessorPlan(preparedSql, values, cFinder, metadata, pPlanCache, 7, callableStatement, false));
 
-        assertEquals(differentPlan?1:2, prepPlan.hitCount); 
+        /*
+         * If the command is not specific to a session we expect
+         * another hit against the cache because we will use the 
+         * cached plan, otherwise, a new plan would have been 
+         * created and the hit count will be unchanged.
+         */
+        if ( !isSessionSpecific ) exHitCount++;
+        assertEquals(exHitCount, pPlanCache.hitCount); 
 	}
     	    
     public void testWhere() throws Exception { 
@@ -181,14 +217,14 @@
         TestOptimizer.checkNodeTypes(plan.processPlan, TestOptimizer.FULL_PUSHDOWN);  
     }
     
-	private PreparedStatementRequest helpGetProcessorPlan(String preparedSql, List values, PreparedPlanCache prepPlanCache)
+	static public PreparedStatementRequest helpGetProcessorPlan(String preparedSql, List values, PreparedPlanCache prepPlanCache)
 			throws MetaMatrixComponentException, QueryParserException,
 			QueryResolverException, QueryValidatorException,
 			QueryPlannerException {    	
 		return helpGetProcessorPlan(preparedSql, values, new DefaultCapabilitiesFinder(), FakeMetadataFactory.example1Cached(), prepPlanCache, SESSION_ID, false, false);
     }
 	
-	private PreparedStatementRequest helpGetProcessorPlan(String preparedSql, List values,
+	static public PreparedStatementRequest helpGetProcessorPlan(String preparedSql, List values,
 			PreparedPlanCache prepPlanCache, int conn)
 			throws MetaMatrixComponentException, QueryParserException,
 			QueryResolverException, QueryValidatorException,
@@ -209,6 +245,9 @@
         request.setPreparedStatement(true);
         request.setCallableStatement(callableStatement);
         request.setParameterValues(values);
+		if (values != null && values.size() > 0 && values.get(0) instanceof List) {
+			request.setPreparedBatchUpdate(true);
+		}
         if (limitResults) {
         	request.setRowLimit(1);
         }

Added: trunk/engine/src/test/java/org/teiid/dqp/internal/process/TestPreparedStatementBatchedUpdate.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/dqp/internal/process/TestPreparedStatementBatchedUpdate.java	                        (rev 0)
+++ trunk/engine/src/test/java/org/teiid/dqp/internal/process/TestPreparedStatementBatchedUpdate.java	2009-04-23 21:26:48 UTC (rev 837)
@@ -0,0 +1,252 @@
+/*
+ * 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.dqp.internal.process;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.teiid.dqp.internal.process.TestPreparedStatement.TestablePreparedPlanCache;
+
+import com.metamatrix.query.optimizer.capabilities.BasicSourceCapabilities;
+import com.metamatrix.query.optimizer.capabilities.FakeCapabilitiesFinder;
+import com.metamatrix.query.optimizer.capabilities.SourceCapabilities.Capability;
+import com.metamatrix.query.processor.FakeDataManager;
+import com.metamatrix.query.processor.TestProcessor;
+import com.metamatrix.query.unittest.FakeMetadataFactory;
+
+/**
+ * JUnit TestCase to test planning and caching of <code>PreparedStatement</code>
+ * plans that contain batched updates.
+ *
+ */
+public class TestPreparedStatementBatchedUpdate extends TestCase {
+
+	public TestPreparedStatementBatchedUpdate(String name) {
+		super(name);
+	}
+
+    /**
+     * Test prepared statements that use batched updates using the same prepared
+     * command with varying number of commands in the batch.
+     * <p>
+     * The test verifies that no errors occur when planning the same batched 
+     * command SQL with varying number of batched command parameter value sets.
+     * For example, if the first executeBatch() call were to occur with three 
+     * batched commands a repeated call with only two batched commands should 
+     * not result in an error.
+     * <p>
+     * The test also verifies that a cached version of the PreparedStatement plan 
+     * is used on each subsequent execution of the same SQL command even though 
+     * the number of batched commands may vary. 
+     * <p>
+     * The batched command "UPDATE pm1.g1 SET pm1.g1.e1=?, pm1.g1.e3=? WHERE pm1.g1.e2=?"
+     * will appear as:
+     * <p>
+     * UPDATE pm1.g1 SET pm1.g1.e1='a', pm1.g1.e3=false WHERE pm1.g1.e2=0
+     * UPDATE pm1.g1 SET pm1.g1.e1=null, pm1.g1.e3=false WHERE pm1.g1.e2=1
+     * <p>
+     * UPDATE pm1.g1 SET pm1.g1.e1='a', pm1.g1.e3=false WHERE pm1.g1.e2=0
+     * <p>
+     * UPDATE pm1.g1 SET pm1.g1.e1='a', pm1.g1.e3=false WHERE pm1.g1.e2=0
+     * UPDATE pm1.g1 SET pm1.g1.e1=null, pm1.g1.e3=false WHERE pm1.g1.e2=1
+     * UPDATE pm1.g1 SET pm1.g1.e1='c', pm1.g1.e3=true WHERE pm1.g1.e2=4
+     * UPDATE pm1.g1 SET pm1.g1.e1='b', pm1.g1.e3=true WHERE pm1.g1.e2=5
+     * <p>
+     * The result should be that only one command is in the plan cache and 
+     * no plan creation or validation errors will occur.
+     * 
+     * @throws Exception
+     */
+    public void testPlanCache_VarNumCmds() throws Exception {
+        // Create query 
+		String preparedSql = "UPDATE pm1.g1 SET pm1.g1.e1=?, pm1.g1.e3=? WHERE pm1.g1.e2=?"; //$NON-NLS-1$
+        
+		// Create PrepareedPlanCache
+		PreparedPlanCache prepPlanCache = new PreparedPlanCache();
+		
+        // batch with two commands
+		ArrayList<ArrayList<Object>> values = new ArrayList<ArrayList<Object>>(2);
+		values.add( new ArrayList<Object>( Arrays.asList( new Object[] { "a",  Boolean.FALSE, new Integer(0) } ) ) );  //$NON-NLS-1$
+    	values.add( new ArrayList<Object>( Arrays.asList( new Object[] { null, Boolean.FALSE, new Integer(1) } ) ) );
+    	
+        //Create plan
+    	TestPreparedStatement.helpGetProcessorPlan(preparedSql, values, prepPlanCache);
+
+        // batch with one command
+		values = new ArrayList<ArrayList<Object>>(1);
+		values.add( new ArrayList<Object>( Arrays.asList( new Object[] { "a",  Boolean.FALSE, new Integer(0) } ) ) );  //$NON-NLS-1$
+    	
+        //Create plan
+		TestPreparedStatement.helpGetProcessorPlan(preparedSql, values, prepPlanCache);
+
+        // batch with four commands
+		values = new ArrayList<ArrayList<Object>>(4);
+		values.add( new ArrayList<Object>( Arrays.asList( new Object[] { "a",  Boolean.FALSE, new Integer(0) } ) ) );  //$NON-NLS-1$
+    	values.add( new ArrayList<Object>( Arrays.asList( new Object[] { null, Boolean.FALSE, new Integer(1) } ) ) );
+		values.add( new ArrayList<Object>( Arrays.asList( new Object[] { "c",  Boolean.TRUE, new Integer(4) } ) ) );  //$NON-NLS-1$
+		values.add( new ArrayList<Object>( Arrays.asList( new Object[] { "b",  Boolean.TRUE, new Integer(5) } ) ) );  //$NON-NLS-1$
+    	
+        //Create plan
+		TestPreparedStatement.helpGetProcessorPlan(preparedSql, values, prepPlanCache);
+		assertEquals("PreparedPlanCache size is invalid - ", 1, prepPlanCache.getSpaceUsed() ); //$NON-NLS-1$
+    }
+
+    /**
+     * Test prepared statements that use batched updates using the same prepared
+     * command with varying number of commands in the batch.
+     * <p>
+     * The test verifies that no errors occur when planning and executing the 
+     * same batched command SQL with varying number of batched command parameter 
+     * value sets.  For example, if the first executeBatch() call were to occur 
+     * with two batched commands a repeated call with only one batched command 
+     * should not result in an error during planning or execution.
+     * <p>
+     * The test also verifies that a cached version of the PreparedStatement plan 
+     * is used on each subsequent execution of the same SQL command even though 
+     * the number of batched commands may vary. 
+     * <p>
+     * The test also verifies that the correct SQL is pushed to the data manager 
+     * to verify that the parameter substitution occurred and is correct and the
+     * correct number of statements made it to the data manager for the respective 
+     * batch.
+     * <p>
+     * The batched command "UPDATE pm1.g1 SET pm1.g1.e1=?, pm1.g1.e3=? WHERE pm1.g1.e2=?"
+     * will appear as:
+     * <p>
+     * UPDATE pm1.g1 SET pm1.g1.e1='a', pm1.g1.e3=false WHERE pm1.g1.e2=0
+     * UPDATE pm1.g1 SET pm1.g1.e1=null, pm1.g1.e3=false WHERE pm1.g1.e2=1
+     * <p>
+     * UPDATE pm1.g1 SET pm1.g1.e1='a', pm1.g1.e3=false WHERE pm1.g1.e2=0
+     * <p>
+     * UPDATE pm1.g1 SET pm1.g1.e1='a', pm1.g1.e3=false WHERE pm1.g1.e2=0
+     * UPDATE pm1.g1 SET pm1.g1.e1=null, pm1.g1.e3=false WHERE pm1.g1.e2=1
+     * UPDATE pm1.g1 SET pm1.g1.e1='c', pm1.g1.e3=true WHERE pm1.g1.e2=4
+     * UPDATE pm1.g1 SET pm1.g1.e1='b', pm1.g1.e3=true WHERE pm1.g1.e2=5
+     * <p>
+     * The result should be that only one command is in the plan cache and 
+     * no plan creation, validation, or execution errors will occur and 
+     * a predetermined set of queries were executed in the data manager.
+     * 
+     * @throws Exception
+     */
+    public void testProcessor_VarNumCmds() throws Exception {
+        // Create query 
+		String preparedSql = "UPDATE pm1.g1 SET pm1.g1.e1=?, pm1.g1.e3=? WHERE pm1.g1.e2=?"; //$NON-NLS-1$
+        
+		// Create a testable prepared plan cache
+		TestablePreparedPlanCache prepPlanCache = new TestablePreparedPlanCache();
+		
+		// Construct data manager with data
+        FakeDataManager dataManager = new FakeDataManager();
+        TestProcessor.sampleData1(dataManager);    
+		
+		// Source capabilities must support batched updates
+        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+        BasicSourceCapabilities caps = new BasicSourceCapabilities();
+        caps.setCapabilitySupport(Capability.BATCHED_UPDATES, true);
+        caps.setCapabilitySupport(Capability.PREPARED_BATCH_UPDATE, false);
+        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
+        
+        // Something to hold our final query list
+        List<String> finalQueryList = new ArrayList<String>(42);
+        
+		// Create expected results
+        // first command should result in 2 rows affected
+        // second command should result in 2 rows affected  
+        List<?>[] expected = new List[] { 
+            Arrays.asList(new Object[] { new Integer(2) }),
+            Arrays.asList(new Object[] { new Integer(2) })
+        };    
+
+        // batch with two commands
+		ArrayList<ArrayList<Object>> values = new ArrayList<ArrayList<Object>>(2);
+		values.add(new ArrayList<Object>(Arrays.asList(new Object[] { "a",  Boolean.FALSE, new Integer(0) })));  //$NON-NLS-1$
+    	values.add(new ArrayList<Object>(Arrays.asList(new Object[] { null, Boolean.FALSE, new Integer(1) })));
+    	
+    	// Add our expected queries to the final query list
+    	for ( int i = 0; i < 6; i++ ) {
+    		finalQueryList.add(new String("UPDATE pm1.g1 SET e1 = 'a', e3 = FALSE WHERE pm1.g1.e2 = 0")); //$NON-NLS-1$
+    		finalQueryList.add(new String("UPDATE pm1.g1 SET e1 = null, e3 = FALSE WHERE pm1.g1.e2 = 1")); //$NON-NLS-1$
+    	}
+    	
+    	// Create the plan and process the query
+    	TestPreparedStatement.helpTestProcessing(preparedSql, values, expected, dataManager, capFinder, FakeMetadataFactory.example1Cached(), prepPlanCache, false, false, false);
+
+    	// Repeat with different number of commands in batch
+    	// Create expected results
+        // first command should result in 2 rows affected
+        expected = new List[] { 
+            Arrays.asList(new Object[] { new Integer(2) })
+        };    
+
+        // batch with one command
+		values = new ArrayList<ArrayList<Object>>(1);
+		values.add(new ArrayList<Object>(Arrays.asList(new Object[] { "a",  Boolean.FALSE, new Integer(0) })));  //$NON-NLS-1$
+    	
+    	// Add our expected queries to the final query list
+    	for (int i = 0; i < 6; i++) {
+    		finalQueryList.add(new String("UPDATE pm1.g1 SET e1 = 'a', e3 = FALSE WHERE pm1.g1.e2 = 0")); //$NON-NLS-1$
+    	}
+    	
+    	// Use the cached plan and process the query
+    	TestPreparedStatement.helpTestProcessing(preparedSql, values, expected, dataManager, capFinder, FakeMetadataFactory.example1Cached(), prepPlanCache, false, false, true);
+
+    	// Repeat with different number of commands in batch
+		// Create expected results
+        // first command should result in 2 rows affected
+        // second command should result in 2 rows affected  
+        // third command should result in 0 rows affected  
+        // fourth command should result in 0 rows affected  
+        expected = new List[] { 
+                Arrays.asList(new Object[] { new Integer(2) }),
+                Arrays.asList(new Object[] { new Integer(2) }),
+                Arrays.asList(new Object[] { new Integer(0) }),
+                Arrays.asList(new Object[] { new Integer(0) })
+        };    
+
+        // batch with four commands
+		values = new ArrayList<ArrayList<Object>>(4);
+		values.add(new ArrayList<Object>(Arrays.asList(new Object[] { "a",  Boolean.FALSE, new Integer(0)} )));  //$NON-NLS-1$
+    	values.add(new ArrayList<Object>(Arrays.asList(new Object[] { null, Boolean.FALSE, new Integer(1)} )));
+		values.add(new ArrayList<Object>(Arrays.asList(new Object[] { "c",  Boolean.TRUE, new Integer(4)} )));  //$NON-NLS-1$
+		values.add(new ArrayList<Object>(Arrays.asList(new Object[] { "b",  Boolean.TRUE, new Integer(5)} )));  //$NON-NLS-1$
+
+    	// Add our expected queries to the final query list
+    	for (int i = 0; i < 6; i++) {
+    		finalQueryList.add(new String("UPDATE pm1.g1 SET e1 = 'a', e3 = FALSE WHERE pm1.g1.e2 = 0")); //$NON-NLS-1$
+    		finalQueryList.add(new String("UPDATE pm1.g1 SET e1 = null, e3 = FALSE WHERE pm1.g1.e2 = 1")); //$NON-NLS-1$
+    		finalQueryList.add(new String("UPDATE pm1.g1 SET e1 = 'c', e3 = TRUE WHERE pm1.g1.e2 = 4")); //$NON-NLS-1$
+    		finalQueryList.add(new String("UPDATE pm1.g1 SET e1 = 'b', e3 = TRUE WHERE pm1.g1.e2 = 5")); //$NON-NLS-1$
+    	}
+    	
+    	TestPreparedStatement.helpTestProcessing(preparedSql, values, expected, dataManager, capFinder, FakeMetadataFactory.example1Cached(), prepPlanCache, false, false, true);
+
+    	// Verify all the queries that were run
+    	assertNotNull(dataManager);
+    	assertEquals("Unexpected queries executed -", finalQueryList, dataManager.getQueries()); //$NON-NLS-1$
+    }
+
+}


Property changes on: trunk/engine/src/test/java/org/teiid/dqp/internal/process/TestPreparedStatementBatchedUpdate.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain




More information about the teiid-commits mailing list