You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by km...@apache.org on 2010/07/12 18:07:20 UTC

svn commit: r963326 - in /db/derby/code/branches/10.5: ./ java/engine/org/apache/derby/iapi/sql/execute/ java/engine/org/apache/derby/impl/sql/compile/ java/engine/org/apache/derby/impl/sql/execute/ java/testing/org/apache/derbyTesting/functionTests/te...

Author: kmarsden
Date: Mon Jul 12 16:07:19 2010
New Revision: 963326

URL: http://svn.apache.org/viewvc?rev=963326&view=rev
Log:
DERBY-3646 Embedded returns wrong results when selecting a blob column twice and using getBinaryStream()

merged svn 902857 with manual merge of BLOBTest.java
Contributed by Kristian Waagan


Modified:
    db/derby/code/branches/10.5/   (props changed)
    db/derby/code/branches/10.5/java/engine/org/apache/derby/iapi/sql/execute/ResultSetFactory.java
    db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/compile/HashTableNode.java
    db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/compile/ProjectRestrictNode.java
    db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/compile/ResultColumnList.java
    db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/execute/GenericResultSetFactory.java
    db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/execute/ProjectRestrictResultSet.java
    db/derby/code/branches/10.5/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BLOBTest.java

Propchange: db/derby/code/branches/10.5/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Jul 12 16:07:19 2010
@@ -1,2 +1,2 @@
 /db/derby/code/branches/10.6:957000,962738
-/db/derby/code/trunk:757811,769596,769602,769606,769962,772090,772337,772449,772534,774281,777105,779681,782991,785131,785139,785163,785570,785662,788369,788670,788674,788968,789264,790218,791027,792434,793089,793588,794106,794303,794955,795166,795459,796020,796027,796316,796372,797147,798347,798742,800523,803548,803948,805696,808494,808850,809643,810860,812669,816531,816536,819006,822289,823659,824694,829022,829410,831304,831319,832379,833430,835286,881074,881444,882732,884163,885421,885659,887246,888311,892912,897161,898635,901165,901648,901760,903108,905224,908418,908586,909176,911315,915733,916075,916897,918359,921028,927430,928065,936215,942286,942476,942480,942587,946794,948045,948069,951346,951366,952138,952581,954748,955001,955634,956075,956445,956659,958163,959550,962716
+/db/derby/code/trunk:757811,769596,769602,769606,769962,772090,772337,772449,772534,774281,777105,779681,782991,785131,785139,785163,785570,785662,788369,788670,788674,788968,789264,790218,791027,792434,793089,793588,794106,794303,794955,795166,795459,796020,796027,796316,796372,797147,798347,798742,800523,803548,803948,805696,808494,808850,809643,810860,812669,816531,816536,819006,822289,823659,824694,829022,829410,831304,831319,832379,833430,835286,881074,881444,882732,884163,885421,885659,887246,888311,892912,897161,898635,901165,901648,901760,902857,903108,905224,908418,908586,909176,911315,915733,916075,916897,918359,921028,927430,928065,936215,942286,942476,942480,942587,946794,948045,948069,951346,951366,952138,952581,954748,955001,955634,956075,956445,956659,958163,959550,962716

Modified: db/derby/code/branches/10.5/java/engine/org/apache/derby/iapi/sql/execute/ResultSetFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.5/java/engine/org/apache/derby/iapi/sql/execute/ResultSetFactory.java?rev=963326&r1=963325&r2=963326&view=diff
==============================================================================
--- db/derby/code/branches/10.5/java/engine/org/apache/derby/iapi/sql/execute/ResultSetFactory.java (original)
+++ db/derby/code/branches/10.5/java/engine/org/apache/derby/iapi/sql/execute/ResultSetFactory.java Mon Jul 12 16:07:19 2010
@@ -293,7 +293,8 @@ public interface ResultSetFactory {
 				Boolean restriction() throws StandardException;
 			</verbatim>
 		@param mapArrayItem	Item # for mapping of source to target columns
-		@param reuseResult	Whether or not to reuse the result row.
+        @param cloneMapItem Item # for columns that need cloning
+        @param reuseResult  Whether or not to reuse the result row.
 		@param doesProjection	Whether or not this PRN does a projection
 		@param optimizerEstimatedRowCount	Estimated total # of rows by
 											optimizer
@@ -307,6 +308,7 @@ public interface ResultSetFactory {
 		GeneratedMethod projection, int resultSetNumber,
 		GeneratedMethod constantRestriction,
 		int mapArrayItem,
+        int cloneMapItem,
 		boolean reuseResult,
 		boolean doesProjection,
 		double optimizerEstimatedRowCount,

Modified: db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/compile/HashTableNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/compile/HashTableNode.java?rev=963326&r1=963325&r2=963326&view=diff
==============================================================================
--- db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/compile/HashTableNode.java (original)
+++ db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/compile/HashTableNode.java Mon Jul 12 16:07:19 2010
@@ -256,7 +256,11 @@ public class HashTableNode extends Singl
 
 
 		// Map the result columns to the source columns
-		int[] mapArray = resultColumns.mapSourceColumns();
+        ResultColumnList.ColumnMapping  mappingArrays =
+            resultColumns.mapSourceColumns();
+
+        int[] mapArray = mappingArrays.mapArray;
+
 		int mapArrayItem = acb.addItem(new ReferencedColumnsDescriptorImpl(mapArray));
 
 		// Save the hash key columns 

Modified: db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/compile/ProjectRestrictNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/compile/ProjectRestrictNode.java?rev=963326&r1=963325&r2=963326&view=diff
==============================================================================
--- db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/compile/ProjectRestrictNode.java (original)
+++ db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/compile/ProjectRestrictNode.java Mon Jul 12 16:07:19 2010
@@ -1423,8 +1423,15 @@ public class ProjectRestrictNode extends
 
 	
 		// Map the result columns to the source columns
-		int[] mapArray = resultColumns.mapSourceColumns();
+
+        ResultColumnList.ColumnMapping mappingArrays =
+            resultColumns.mapSourceColumns();
+
+        int[] mapArray = mappingArrays.mapArray;
+        boolean[] cloneMap = mappingArrays.cloneMap;
+
 		int mapArrayItem = acb.addItem(new ReferencedColumnsDescriptorImpl(mapArray));
+        int cloneMapItem = acb.addItem(cloneMap);
 
 		/* Will this node do a projection? */
 		boolean doesProjection = true;
@@ -1462,13 +1469,14 @@ public class ProjectRestrictNode extends
 		 *  arg6: constantExpress - Expression for constant restriction
 		 *			(for example, where 1 = 2)
 		 *  arg7: mapArrayItem - item # for mapping of source columns
-		 *  arg8: reuseResult - whether or not the result row can be reused
-		 *						(ie, will it always be the same)
-		 *  arg9: doesProjection - does this node do a projection
-		 *  arg10: estimated row count
-		 *  arg11: estimated cost
-		 *  arg12: close method
-		 */
+         *  arg8: cloneMapItem - item # for mapping of columns that need cloning
+         *  arg9: reuseResult - whether or not the result row can be reused
+         *                      (ie, will it always be the same)
+         *  arg10: doesProjection - does this node do a projection
+         *  arg11: estimated row count
+         *  arg12: estimated cost
+         *  arg13: close method
+         */
 		
 		acb.pushGetResultSetFactoryExpression(mb);
 		if (genChildResultSet)
@@ -1597,13 +1605,14 @@ public class ProjectRestrictNode extends
 		}
 		
 		mb.push(mapArrayItem);
+        mb.push(cloneMapItem);
 		mb.push(resultColumns.reusableResult());
 		mb.push(doesProjection);
 		mb.push(costEstimate.rowCount());
 		mb.push(costEstimate.getEstimatedCost());
 
 		mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getProjectRestrictResultSet",
-					ClassName.NoPutResultSet, 10);
+                    ClassName.NoPutResultSet, 11);
 	}
 
 	/**

Modified: db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/compile/ResultColumnList.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/compile/ResultColumnList.java?rev=963326&r1=963325&r2=963326&view=diff
==============================================================================
--- db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/compile/ResultColumnList.java (original)
+++ db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/compile/ResultColumnList.java Mon Jul 12 16:07:19 2010
@@ -26,6 +26,8 @@ import java.sql.ResultSetMetaData;
 import java.sql.Types;
 import java.util.Hashtable;
 import java.util.Vector;
+import java.util.Map;
+import java.util.HashMap;
 
 import org.apache.derby.catalog.types.DefaultInfoImpl;
 import org.apache.derby.iapi.error.StandardException;
@@ -3504,14 +3506,23 @@ public class ResultColumnList extends Qu
 	 * -1.
 	 * This is useful for determining if we need to do reflection
 	 * at execution time.
+     * <p/>
+     * Also build an array of boolean for columns that point to the same virtual
+     * column and have types that are streamable to be able to determine if
+     * cloning is needed at execution time.
 	 *
 	 * @return	Array representiong mapping of RCs to source RCs.
 	 */
-	int[] mapSourceColumns()
+    ColumnMapping mapSourceColumns()
 	{
-		int[]			mapArray = new int[size()];
+        int[] mapArray = new int[size()];
+        boolean[] cloneMap = new boolean[size()];
+
 		ResultColumn	resultColumn;
 
+        // key: virtual column number, value: index
+        Map seenMap = new HashMap();
+
 		int size = size();
 
 		for (int index = 0; index < size; index++)
@@ -3529,7 +3540,9 @@ public class ResultColumnList extends Qu
 				else
 				{
 					// Virtual column #s are 1-based
-					mapArray[index] = vcn.getSourceColumn().getVirtualColumnId();
+                    ResultColumn rc = vcn.getSourceColumn();
+                    updateArrays(mapArray, cloneMap, seenMap, rc, index);
+
 				}
 			}
 			else if (resultColumn.getExpression() instanceof ColumnReference)
@@ -3544,7 +3557,9 @@ public class ResultColumnList extends Qu
 				else
 				{
 					// Virtual column #s are 1-based
-					mapArray[index] = cr.getSource().getVirtualColumnId();
+                    ResultColumn rc = cr.getSource();
+
+                    updateArrays(mapArray, cloneMap, seenMap, rc, index);
 				}
 			}
 			else
@@ -3553,7 +3568,8 @@ public class ResultColumnList extends Qu
 			}
 		}
 
-		return mapArray;
+        ColumnMapping result = new ColumnMapping(mapArray, cloneMap);
+        return result;
 	}
 
 	/** Set the nullability of every ResultColumn in this list 
@@ -4231,6 +4247,7 @@ public class ResultColumnList extends Qu
 		return false;
 	}
 
+
 	/*
 	 * Remove all window functions columns from this list
 	 */
@@ -4250,4 +4267,52 @@ public class ResultColumnList extends Qu
 		resetVirtualColumnIds();
 	}
 
+
+    private static boolean streamableType(ResultColumn rc) {
+        DataTypeDescriptor dtd = rc.getType();
+        TypeId s = TypeId.getBuiltInTypeId(dtd.getTypeName());
+
+        if (s != null) {
+            return s.streamStorable();
+        } else {
+            return false;
+        }
+    }
+
+
+    private static void updateArrays(int[] mapArray,
+                             boolean[] cloneMap,
+                             Map seenMap,
+                             ResultColumn rc,
+                             int index) {
+
+        int vcId = rc.getVirtualColumnId();
+
+        mapArray[index] = vcId;
+
+        if (streamableType(rc)) {
+            Integer seenIndex =
+                (Integer)seenMap.get(new Integer(vcId));
+
+            if (seenIndex != null) {
+                // We have already mapped this column at index
+                // seenIndex, so mark occurence 2..n  for cloning.
+                cloneMap[index] = true;
+            } else {
+                seenMap.put(new Integer(vcId), new Integer(index));
+            }
+        }
+    }
+
+
+    public class ColumnMapping {
+
+        public final int[] mapArray;
+        public final boolean[] cloneMap;
+
+        public ColumnMapping(int[] mapArray, boolean[] cloneMap) {
+            this.mapArray = mapArray;
+            this.cloneMap = cloneMap;
+        }
+    }
 }

Modified: db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/execute/GenericResultSetFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/execute/GenericResultSetFactory.java?rev=963326&r1=963325&r2=963326&view=diff
==============================================================================
--- db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/execute/GenericResultSetFactory.java (original)
+++ db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/execute/GenericResultSetFactory.java Mon Jul 12 16:07:19 2010
@@ -212,6 +212,7 @@ public class GenericResultSetFactory imp
 		GeneratedMethod projection, int resultSetNumber,
 		GeneratedMethod constantRestriction,
 		int mapRefItem,
+        int cloneMapItem,
 		boolean reuseResult,
 		boolean doesProjection,
 		double optimizerEstimatedRowCount,
@@ -220,7 +221,7 @@ public class GenericResultSetFactory imp
 	{
 		return new ProjectRestrictResultSet(source, source.getActivation(), 
 			restriction, projection, resultSetNumber, 
-			constantRestriction, mapRefItem, 
+            constantRestriction, mapRefItem, cloneMapItem,
 			reuseResult,
 			doesProjection,
 		    optimizerEstimatedRowCount,

Modified: db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/execute/ProjectRestrictResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/execute/ProjectRestrictResultSet.java?rev=963326&r1=963325&r2=963326&view=diff
==============================================================================
--- db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/execute/ProjectRestrictResultSet.java (original)
+++ db/derby/code/branches/10.5/java/engine/org/apache/derby/impl/sql/execute/ProjectRestrictResultSet.java Mon Jul 12 16:07:19 2010
@@ -24,6 +24,7 @@ package org.apache.derby.impl.sql.execut
 import org.apache.derby.iapi.services.monitor.Monitor;
 
 import org.apache.derby.iapi.services.sanity.SanityManager;
+import org.apache.derby.iapi.services.io.StreamStorable;
 
 import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
 import org.apache.derby.iapi.services.stream.InfoStreams;
@@ -69,6 +70,13 @@ class ProjectRestrictResultSet extends N
 	public boolean doesProjection;
     private GeneratedMethod projection;
 	private int[]			projectMapping;
+
+    /**
+     * Holds columns present more than once in the result set and which may be
+     * represented by a stream, since such columns need to be cloned.
+     */
+    private boolean[] cloneMap;
+
 	private boolean runTimeStatsOn;
 	private ExecRow			mappedResultRow;
 	public boolean reuseResult;
@@ -87,6 +95,7 @@ class ProjectRestrictResultSet extends N
 					int resultSetNumber,
 					GeneratedMethod cr,
 					int mapRefItem,
+                    int cloneMapItem,
 					boolean reuseResult,
 					boolean doesProjection,
 				    double optimizerEstimatedRowCount,
@@ -115,6 +124,9 @@ class ProjectRestrictResultSet extends N
 			mappedResultRow = activation.getExecutionFactory().getValueRow(projectMapping.length);
 		}
 
+        cloneMap =
+            ((boolean[])a.getPreparedStatement().getSavedObject(cloneMapItem));
+
 		/* Remember whether or not RunTimeStatistics is on */
 		runTimeStatsOn = getLanguageConnectionContext().getRunTimeStatisticsMode();
 		recordConstructorTime();
@@ -507,7 +519,28 @@ class ProjectRestrictResultSet extends N
 		{
 			if (projectMapping[index] != -1)
 			{
-				result.setColumn(index + 1, sourceRow.getColumn(projectMapping[index]));
+                DataValueDescriptor dvd =
+                        sourceRow.getColumn(projectMapping[index]);
+
+                // See if the column has been marked for cloning.
+                // If the value isn't a stream, don't bother cloning it.
+                if (cloneMap[index] && dvd.getStream() != null) {
+
+                    // Enable this code after DERBY-3650 is in: FIXME
+                    //
+                    // long length = dvd.getLengthIfKnown();
+                    //
+                    // If the stream isn't clonable, we have to load the stream.
+                    // if ((length > 32*1024 || length == -1) &&
+                    //     dvd.getStream() instanceof CloneableStream) {
+                    //     // Copy the stream, the value may be large.
+                    //     dvd = dvd.copyForRead();
+                    // } else {
+                    //     // Load the stream, then we don't have to clone.
+                    ((StreamStorable)dvd).loadStream();
+                    // }
+                }
+                result.setColumn(index + 1, dvd);
 			}
 		}
 

Modified: db/derby/code/branches/10.5/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BLOBTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.5/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BLOBTest.java?rev=963326&r1=963325&r2=963326&view=diff
==============================================================================
--- db/derby/code/branches/10.5/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BLOBTest.java (original)
+++ db/derby/code/branches/10.5/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BLOBTest.java Mon Jul 12 16:07:19 2010
@@ -40,8 +40,6 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.Random;
 
-import org.apache.derbyTesting.functionTests.util.streams.LoopingAlphabetStream;
-
 /**
  * Tests reading and updating binary large objects (BLOBs).
  */
@@ -388,11 +386,14 @@ final public class BLOBTest extends Base
         rs.close();
     }
 
+
     /**
      * Tests that a lob can be safely occur multiple times in a SQL select.
      * <p/>
      * See DERBY-4477.
      * <p/>
+     * @see org.apache.derbyTesting.functionTests.tests.memory.BlobMemTest#testDerby4477_3645_3646_Repro_lowmem
+     * @see org.apache.derbyTesting.functionTests.tests.memory.ClobMemTest#testDerby4477_3645_3646_Repro_lowmem_clob
      */
     public void testDerby4477_3645_3646_Repro() throws SQLException, IOException {
         setAutoCommit(false);
@@ -693,34 +694,35 @@ final public class BLOBTest extends Base
         throws IOException, SQLException
     {
         println("Verify new value in table: " + newVal);
-        
-        final Statement stmt = createStatement(ResultSet.TYPE_FORWARD_ONLY, 
+
+        final Statement stmt = createStatement(ResultSet.TYPE_FORWARD_ONLY,
                                                    ResultSet.CONCUR_READ_ONLY);
-        
-        final ResultSet rs = 
-            stmt.executeQuery("SELECT * FROM " +  
+
+        final ResultSet rs =
+            stmt.executeQuery("SELECT * FROM " +
                               BLOBDataModelSetup.getBlobTableName() +
                               " WHERE val = " + newVal);
-        
+
         println("Query executed, calling next");
-        
+
         boolean foundVal = false;
-        
+
         while (rs.next()) {
             println("Next called, verifying row");
-            
-            assertEquals("Unexpected value in val column", 
+
+            assertEquals("Unexpected value in val column",
                          newVal, rs.getInt(1));
-            
+
             verifyBlob(newVal, newSize, rs.getBlob(3));
             foundVal = true;
         }
         assertTrue("No column with value= " + newVal + " found ", foundVal);
-        
+
         rs.close();
         stmt.close();
     }
-                          
+
+
     /**
      * Verifies that the blob is consistent
      * @param expectedVal the InputStream for the Blob should return this value
@@ -730,19 +732,19 @@ final public class BLOBTest extends Base
      * @exception SQLException causes test to fail with error
      * @exception IOException causes test to fail with error
      */
-    private void verifyBlob(final int expectedVal, 
-                            final int expectedSize, 
-                            final Blob blob) 
+    private void verifyBlob(final int expectedVal,
+                            final int expectedSize,
+                            final Blob blob)
         throws IOException, SQLException
     {
         final InputStream stream = blob.getBinaryStream();
         int blobSize = 0;
         for (int val = stream.read(); val!=-1; val = stream.read()) {
             blobSize++;
-            
+
             // avoid doing a string-concat for every byte in blob
             if (expectedVal!=val) {
-                assertEquals("Unexpected value in stream at position " + 
+                assertEquals("Unexpected value in stream at position " +
                              blobSize,
                              expectedVal, val);
             }
@@ -768,7 +770,6 @@ final public class BLOBTest extends Base
     public final void setUp() 
         throws Exception
     {
-        println("Setup of: " + getName());
         getConnection().setAutoCommit(false);
     }
 }