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 rh...@apache.org on 2011/04/11 21:14:39 UTC

svn commit: r1091169 - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/types/SQLClob.java testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobTest.java testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DummyReader.java

Author: rhillegas
Date: Mon Apr 11 19:14:39 2011
New Revision: 1091169

URL: http://svn.apache.org/viewvc?rev=1091169&view=rev
Log:
DERBY-4544: Don't use the SQLClob.getLength() optimization on non-resetable streams--this fixes a data corruption.

Added:
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DummyReader.java   (with props)
Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLClob.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobTest.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLClob.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLClob.java?rev=1091169&r1=1091168&r2=1091169&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLClob.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLClob.java Mon Apr 11 19:14:39 2011
@@ -268,6 +268,12 @@ public class SQLClob
         if (stream == null) {
             return super.getLength();
         }
+        //
+        // The following check was put in to fix DERBY-4544. We seem to get
+        // confused if we have to re-use non-resetable streams.
+        //
+        if ( !(stream instanceof Resetable) ) { return super.getLength(); }
+        
         // The Clob is represented as a stream.
         // Make sure we have a stream descriptor.
         boolean repositionStream = (csd != null);

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobTest.java?rev=1091169&r1=1091168&r2=1091169&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobTest.java Mon Apr 11 19:14:39 2011
@@ -45,6 +45,8 @@ import java.util.Arrays;
 
 import junit.framework.Test;
 
+import org.apache.derby.iapi.types.HarmonySerialClob;
+
 import org.apache.derbyTesting.functionTests.util.streams.CharAlphabet;
 import org.apache.derbyTesting.functionTests.util.streams.LoopingAlphabetReader;
 import org.apache.derbyTesting.functionTests.util.streams.LoopingAlphabetStream;
@@ -686,6 +688,133 @@ public class ClobTest
         setAutoCommit(savedAutoCommitValue);
     }
 
+    /**
+     * Verify that generated columns don't interfere with streaming Clobs.
+     * See DERBY-4544.
+     */
+    public  void    test_4544() throws Exception
+    {
+        int     streamLength = 100;
+        String  tableName;
+
+        prepareStatement
+            ( "create table t_4544_0 ( myclob clob )" ).execute();
+        insertClob( streamLength, "t_4544_0" );
+        prepareStatement
+            (
+             "create function replace_4544( inclob clob, target varchar( 32672 ), replacement varchar( 32672 ) )\n" +
+             "returns clob\n" +
+             "language java parameter style java no sql deterministic\n" +
+             "external name '" + getClass().getName() + ".replace'"
+             ).execute();
+
+        vetTable
+            (
+             streamLength,
+             "generated always as (length(myclob))",
+             Integer.toString( streamLength )
+             );
+        vetTable
+            (
+             streamLength,
+             "varchar( 3 ) generated always as ( substr(myclob, 1, 3) )",
+             " ab"
+             );
+        vetTable
+            (
+             streamLength,
+             "generated always as (locate( 'def', myclob ))",
+             "5"
+             );
+        vetTable
+            (
+             streamLength,
+             "clob generated always as (upper( myclob ))",
+             (new DummyReader( streamLength )).toString().toUpperCase()
+             );
+        vetTable
+            (
+             streamLength,
+             "clob generated always as (trim( myclob ))",
+             (new DummyReader( streamLength )).toString().trim()
+             );
+        vetTable
+            (
+             streamLength,
+             "clob generated always as (replace_4544( myclob, 'b', 'B' ))",
+             (new DummyReader( streamLength )).toString().replace( 'b', 'B' )
+             );
+
+        dropTable( "t_4544_0" );
+        prepareStatement( "drop function replace_4544" ).execute();
+    }
+    private void    vetTable( int streamLength, String gencol, String expectedValue ) throws Exception
+    {
+        String tableName = "t_4544_1";
+        
+        prepareStatement
+            ( "create table " + tableName + "( myclob clob, gencol " + gencol + " )" ).execute();
+
+        insertClob( streamLength, tableName );
+        vetTable( tableName, expectedValue );
+        vetClob( streamLength, tableName );
+        
+        prepareStatement( "delete from " + tableName ).executeUpdate();
+
+        prepareStatement( "insert into " + tableName + "( myclob ) select myclob from t_4544_0" ).executeUpdate();
+        vetTable( tableName, expectedValue );
+        vetClob( streamLength, tableName );
+        
+        dropTable( tableName );
+    }
+    private void    vetTable( String tableName, String expectedValue ) throws Exception
+    {
+        ResultSet   rs = prepareStatement( "select gencol from " + tableName ).executeQuery();
+        rs.next();
+        assertEquals( expectedValue, rs.getString( 1 ) );
+        rs.close();
+    }
+    private void    insertClob( int streamLength, String tableName ) throws Exception
+    {
+        PreparedStatement   insert = prepareStatement( "insert into " + tableName + "( myclob ) values ( ? )" );
+        insert.setCharacterStream( 1, new DummyReader( streamLength ), streamLength );
+        insert.executeUpdate();
+    }
+    private void    vetClob( int streamLength, String tableName ) throws Exception
+    {
+        PreparedStatement   select = prepareStatement( "select myclob from " + tableName );
+        ResultSet               rs = select.executeQuery();
+        rs.next();
+        Reader      actualReader = rs.getCharacterStream( 1 );
+        Reader      expectedReader = new DummyReader( streamLength );
+
+        for ( int i = 0; i < streamLength; i++ )
+        {
+            int actual = actualReader.read();
+            if ( actual < 0 )
+            {
+                fail( "    Read stream was only " + i + " characters long." );
+            }
+            
+            int    expected = expectedReader.read();
+            
+            assertEquals( expected, actual );
+        }
+        assertTrue( actualReader.read() < 0 );
+
+        rs.close();
+    }
+    public  static  Clob    replace( Clob clob, String target, String replacement )
+        throws Exception
+    {
+        char    targetChar = target.charAt( 0 );
+        char    replacementChar = replacement.charAt( 0 );
+        String  originalString = clob.getSubString( 1, (int) clob.length() );
+        String  resultString = originalString.replace( targetChar, replacementChar );
+
+        return new HarmonySerialClob( resultString );
+    }
+
     /* Test ideas for more tests
      *
      * truncate:

Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DummyReader.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DummyReader.java?rev=1091169&view=auto
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DummyReader.java (added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DummyReader.java Mon Apr 11 19:14:39 2011
@@ -0,0 +1,72 @@
+/*
+
+   Derby - Class org.apache.derbyTesting.functionTests.tests.jdbcapi.DummyReader
+
+   Licensed to the Apache Software Foundation (ASF) under one
+   or more contributor license agreements.  See the NOTICE file
+   distributed with this work for additional information
+   regarding copyright ownership.  The ASF licenses this file
+   to you under the Apache License, Version 2.0 (the
+   "License"); you may not use this file except in compliance
+   with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing,
+   software distributed under the License is distributed on an
+   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+   KIND, either express or implied.  See the License for the
+   specific language governing permissions and limitations
+   under the License.
+
+ */
+package org.apache.derbyTesting.functionTests.tests.jdbcapi;
+
+import java.io.Reader;
+
+public  class   DummyReader extends Reader
+{
+    private int _idx = 0;
+    private int _readerLength;
+    private static  final   String  _chars = " abcdefghijklmnopqrstuvwxyz ";
+    
+    public DummyReader( int readerLength )
+    {
+        _readerLength = readerLength;
+    }
+
+    public  void    close() {}
+
+    public  int read( char[] buffer, int offset, int length )
+    {
+        if ( _idx >= _readerLength ) { return -1; }
+        
+        for ( int i = 0; i < length; i++ )
+        {
+            if ( _idx >= _readerLength )
+            {
+                return i;
+            }
+            else
+            {
+                buffer[ offset + i ] = value( _idx++ );
+            }
+        }
+
+        return length;
+    }
+    private char    value( int raw )
+    {
+        return _chars.charAt( raw % _chars.length() );
+    }
+
+    public  String  toString()
+    {
+        char[]  buffer = new char[ _readerLength ];
+
+        for ( int i = 0; i < _readerLength; i++ ) { buffer[ i ] = value( i ); }
+
+        return new String( buffer );
+    }
+}
+

Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DummyReader.java
------------------------------------------------------------------------------
    svn:eol-style = native