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 kr...@apache.org on 2007/04/16 12:03:56 UTC

svn commit: r529185 - in /db/derby/code/trunk/java/client/org/apache/derby/client/am: CallableLocatorProcedures.java CallableStatement.java Connection.java PreparedStatement.java Statement.java

Author: kristwaa
Date: Mon Apr 16 03:03:47 2007
New Revision: 529185

URL: http://svn.apache.org/viewvc?view=rev&rev=529185
Log:
DERBY-2495: Create framework for calling locator related stored procedures from client. Note that most of this code must be enabled in a later commit.

Patch contributed Øystein Grøvlen.

Added:
    db/derby/code/trunk/java/client/org/apache/derby/client/am/CallableLocatorProcedures.java   (with props)
Modified:
    db/derby/code/trunk/java/client/org/apache/derby/client/am/CallableStatement.java
    db/derby/code/trunk/java/client/org/apache/derby/client/am/Connection.java
    db/derby/code/trunk/java/client/org/apache/derby/client/am/PreparedStatement.java
    db/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java

Added: db/derby/code/trunk/java/client/org/apache/derby/client/am/CallableLocatorProcedures.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/am/CallableLocatorProcedures.java?view=auto&rev=529185
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/am/CallableLocatorProcedures.java (added)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/am/CallableLocatorProcedures.java Mon Apr 16 03:03:47 2007
@@ -0,0 +1,889 @@
+/*
+ 
+   Derby - Class org.apache.derby.client.am.CallableLocatorProcedures
+ 
+   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.derby.client.am;
+
+/**
+ * Contains the necessary methods to call the stored procedure that
+ * operate on LOBs identified by locators.  An instance of this class
+ * will be initialized with a <code>Connection</code> parameter and
+ * all calls will be made on that connection.
+ * <p>
+ * The class makes sure that each procedure call is only prepared once
+ * per instance.  Hence, it will keep references to
+ * <code>CallableStatement</code> objects for procedures that have
+ * been called through this instance.  This makes it possible to
+ * prepare each procedure call only once per <code>Connection</code>.
+ * <p> 
+ * Since LOBs can not be parameters to stored procedures, the
+ * framework should make sure that calls involving a byte[] or String
+ * that does not fit in a VARCHAR (FOR BIT DATA), are split into
+ * several calls each operating on a fragment of the LOB.
+ *
+ * @see Connection.locatorProcedureCall() for an example of how to use
+ * this class.
+ */
+class CallableLocatorProcedures 
+{
+    // One member variable for each stored procedure that can be called.
+    // Used to be able to only prepare each procedure call once per connection.
+    private CallableStatement blobCreateLocatorCall;
+    private CallableStatement blobReleaseLocatorCall;
+    private CallableStatement blobGetPositionFromLocatorCall;
+    private CallableStatement blobGetPositionFromBytesCall;
+    private CallableStatement blobGetLengthCall;
+    private CallableStatement blobGetBytesCall;
+    private CallableStatement blobSetBytesCall;
+    private CallableStatement blobTruncateCall;
+    private CallableStatement clobCreateLocatorCall;
+    private CallableStatement clobReleaseLocatorCall;
+    private CallableStatement clobGetPositionFromStringCall;
+    private CallableStatement clobGetPositionFromLocatorCall;
+    private CallableStatement clobGetLengthCall;
+    private CallableStatement clobGetSubStringCall;
+    private CallableStatement clobSetStringCall;
+    private CallableStatement clobTruncateCall;
+
+    /**
+     * The connection to be used when calling the stored procedures.
+     */
+    private final Connection connection; 
+
+    /**
+     * Max size of byte[] and String parameters to procedures
+     */
+    private static final int VARCHAR_MAXWIDTH = 32672;
+
+
+    /**
+     * Create an instance to be used for calling locator-based stored
+     * procedures.
+     *
+     * @param conn the connection to be used to prepare calls.
+     */
+    CallableLocatorProcedures(Connection conn) 
+    {
+        this.connection = conn;
+    }
+
+    /**
+     * Allocates an empty BLOB on server and returns its locator.  Any
+     * subsequent operations on this BLOB value will be stored in temporary
+     * space on the server.
+     *
+     * @throws org.apache.derby.client.am.SqlException
+     * @return locator that identifies the created BLOB.
+     */
+    int blobCreateLocator() throws SqlException
+    {
+        if (blobCreateLocatorCall == null) {
+            blobCreateLocatorCall = connection.prepareCallX
+                ("? = CALL SYSIBM.BLOBCREATELOCATOR()",
+                 java.sql.ResultSet.TYPE_FORWARD_ONLY, 
+                 java.sql.ResultSet.CONCUR_READ_ONLY, 
+                 connection.holdability());
+            blobCreateLocatorCall
+                .registerOutParameterX(1, java.sql.Types.INTEGER);
+            // Make sure this statement does not commit user transaction
+            blobCreateLocatorCall.isAutoCommittableStatement_ = false;
+        }
+
+        blobCreateLocatorCall.executeX();
+        return blobCreateLocatorCall.getIntX(1);
+    }
+
+    /**
+     * This method frees the BLOB and releases the resources that it
+     * holds. (E.g., temporary space used to store this BLOB on the server.)
+     * @param locator locator that designates the BLOB to be released.
+     * @throws org.apache.derby.client.am.SqlException 
+     */
+    void blobReleaseLocator(int locator) throws SqlException
+    {
+        if (blobReleaseLocatorCall == null) {
+            blobReleaseLocatorCall = connection.prepareCallX
+                ("CALL SYSIBM.BLOBRELEASELOCATOR(?)",
+                 java.sql.ResultSet.TYPE_FORWARD_ONLY, 
+                 java.sql.ResultSet.CONCUR_READ_ONLY, 
+                 java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT);
+            // Make sure this statement does not commit user transaction
+            blobReleaseLocatorCall.isAutoCommittableStatement_ = false;
+        }
+
+        blobReleaseLocatorCall.setIntX(1, locator);
+        blobReleaseLocatorCall.executeX();
+    }
+
+    /**
+     * Retrieves the byte position in the BLOB value designated by this
+     * <code>locator</code> at which pattern given by
+     * <code>searchLocator</code> begins. The search begins at position
+     * <code>fromPosition</code>.
+     * @param locator locator that identifies the BLOB to be searched.
+     * @param searchLocator locator designating the BLOB value for which to
+     *        search
+     * @param fromPosition the position in the BLOB value
+     *        at which to begin searching; the first position is 1
+     * @throws org.apache.derby.client.am.SqlException 
+     * @return the position at which the pattern begins, else -1
+     */
+    long blobGetPositionFromLocator(int locator, 
+                                    int searchLocator, 
+                                    long fromPosition) throws SqlException
+    {
+        if (blobGetPositionFromLocatorCall == null) {
+            blobGetPositionFromLocatorCall = connection.prepareCallX
+                ("? = CALL SYSIBM.BLOBGETPOSITIONFROMLOCATOR(?, ?, ?)",
+                 java.sql.ResultSet.TYPE_FORWARD_ONLY, 
+                 java.sql.ResultSet.CONCUR_READ_ONLY, 
+                 java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT);
+            blobGetPositionFromLocatorCall
+                .registerOutParameterX(1, java.sql.Types.BIGINT);
+            // Make sure this statement does not commit user transaction
+            blobGetPositionFromLocatorCall.isAutoCommittableStatement_ = false;
+        }
+
+        blobGetPositionFromLocatorCall.setIntX(2, locator);
+        blobGetPositionFromLocatorCall.setIntX(3, searchLocator);
+        blobGetPositionFromLocatorCall.setLongX(4, fromPosition);
+        blobGetPositionFromLocatorCall.executeX();
+        return blobGetPositionFromLocatorCall.getLongX(1);
+    }
+
+
+    /**
+     * Retrieves the byte position at which the specified byte array
+     * <code>searchLiteral</code> begins within the <code>BLOB</code> value
+     * identified by <code>locator</code>.  The search for
+     * <code>searchLiteral</code> begins at position <code>fromPosition</code>.
+     * <p>
+     * If <code>searchLiteral</code> is longer than the maximum length of a
+     * VARCHAR FOR BIT DATA, it will be split into smaller fragments, and
+     * repeated procedure calls will be made to perform the entire search
+     *
+     * @param locator locator that identifies the BLOB to be searched.
+     * @param searchLiteral the byte array for which to search
+     * @param fromPosition the position at which to begin searching; the
+     *        first position is 1
+     * @throws org.apache.derby.client.am.SqlException 
+     * @return the position at which the pattern appears, else -1
+     */
+    long blobGetPositionFromBytes(int locator, 
+                                  byte[] searchLiteral, 
+                                  long fromPosition) throws SqlException
+    {
+        long blobLength = -1;  // Will be fetched from server if needed
+        int patternLength = searchLiteral.length;
+
+        // If searchLiteral needs to be partitioned, 
+        // we may have to try several start positions
+        do { 
+            long foundAt = blobGetPositionFromBytes(locator, 
+                                                    fromPosition,
+                                                    searchLiteral, 
+                                                    0,
+                                                    VARCHAR_MAXWIDTH);
+
+            // If searchLiteral is longer than VARCHAR_MAXWIDTH, 
+            // we need to check the rest
+            boolean tryAgain = false;
+            if ((patternLength > VARCHAR_MAXWIDTH) && (foundAt > 0)) {
+                // First part of searchLiteral matched, check rest
+                int comparedSoFar = VARCHAR_MAXWIDTH;
+                while (comparedSoFar < patternLength) {
+                    int numBytesThisRound 
+                        = Math.min(patternLength - comparedSoFar, 
+                                   VARCHAR_MAXWIDTH);
+                    long pos = blobGetPositionFromBytes(locator,
+                                                        foundAt + comparedSoFar,
+                                                        searchLiteral,
+                                                        comparedSoFar,
+                                                        numBytesThisRound);
+
+                    if (pos != (foundAt + comparedSoFar)) { 
+                        // This part did not match
+                        // Try to find a later match for the same prefix
+                        tryAgain = true;
+                        fromPosition = foundAt + 1;
+                        break;
+                    }
+
+                    comparedSoFar += numBytesThisRound;
+                }
+            }
+            
+            if (!tryAgain) return foundAt;
+
+            // Need Blob length in order to determine when to stop
+            if (blobLength < 0) {  
+                blobLength = blobGetLength(locator);
+            }
+        } while (fromPosition + patternLength <= blobLength);
+
+        return -1;  // No match
+    }
+        
+    
+    /**
+     * Retrieves the byte position at which the specified part of the byte
+     * array <code>searchLiteral</code> begins within the <code>BLOB</code>
+     * value identified by <code>locator</code>.  The search for
+     * <code>searchLiteral</code> begins at position <code>fromPosition</code>.
+     * <p>
+     * This is a helper function used by blobGetPositionFromBytes(int, byte[],
+     * long) for each call to the BLOBGETPOSITIONFROMBYTES procedure.
+     *
+     * @param locator locator that identifies the BLOB to be searched.
+     * @param searchLiteral the byte array for which to search
+     * @param fromPosition the position at which to begin searching; the
+     *        first position is 1
+     * @param offset the offset into the array <code>searchLiteral</code> at
+     *        which the pattern to search for starts
+     * @param length the number of bytes from the array of bytes
+     *        <code>searchLiteral</code> to use for the pattern to search
+     *        for. It is assumed that this length is smaller than the maximum
+     *        size of a VARCHAR FOR BIT DATA column.  Otherwise, an exception
+     *        will be thrown.
+     * @throws org.apache.derby.client.am.SqlException 
+     * @return the position at which the pattern appears, else -1
+     */
+    private long blobGetPositionFromBytes(int locator,
+                                          long fromPosition, 
+                                          byte[] searchLiteral,
+                                          int offset,
+                                          int length) throws SqlException
+    {
+        if (blobGetPositionFromBytesCall == null) {
+            blobGetPositionFromBytesCall = connection.prepareCallX
+                ("? = CALL SYSIBM.BLOBGETPOSITIONFROMBYTES(?, ?, ?)",
+                 java.sql.ResultSet.TYPE_FORWARD_ONLY, 
+                 java.sql.ResultSet.CONCUR_READ_ONLY, 
+                 java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT);
+            blobGetPositionFromBytesCall
+                .registerOutParameterX(1, java.sql.Types.BIGINT);
+            // Make sure this statement does not commit user transaction
+            blobGetPositionFromBytesCall.isAutoCommittableStatement_ = false;
+        }
+        
+        byte[] bytesToBeCompared = searchLiteral;
+        int numBytes = Math.min(searchLiteral.length - offset, length);
+        if (numBytes != bytesToBeCompared.length) {
+            // Need an array that contains just what is to be sent
+            bytesToBeCompared = new byte[numBytes];
+            System.arraycopy(searchLiteral, offset,
+                             bytesToBeCompared, 0, numBytes);
+        }
+
+        blobGetPositionFromBytesCall.setIntX(2, locator);
+        blobGetPositionFromBytesCall.setBytesX(3, bytesToBeCompared);
+        blobGetPositionFromBytesCall.setLongX(4, fromPosition);
+        blobGetPositionFromBytesCall.executeX();
+        return blobGetPositionFromBytesCall.getLongX(1);
+    }
+
+    
+    /**
+     * Returns the number of bytes in the <code>BLOB</code> value
+     * designated by this <code>sourceLocator</code>.
+     * 
+     * @param sourceLocator locator that identifies the BLOB
+     * @throws org.apache.derby.client.am.SqlException 
+     * @return length of the <code>BLOB</code> in bytes 
+     */
+    long blobGetLength(int sourceLocator) throws SqlException
+    {
+        if (blobGetLengthCall == null) {
+            blobGetLengthCall = connection.prepareCallX
+                ("? = CALL SYSIBM.BLOBGETLENGTH(?)",
+                 java.sql.ResultSet.TYPE_FORWARD_ONLY, 
+                 java.sql.ResultSet.CONCUR_READ_ONLY, 
+                 java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT);
+            blobGetLengthCall.registerOutParameterX(1, java.sql.Types.BIGINT);
+            // Make sure this statement does not commit user transaction
+            blobGetLengthCall.isAutoCommittableStatement_ = false;
+        }
+
+        blobGetLengthCall.setIntX(2, sourceLocator);
+        blobGetLengthCall.executeX();
+        return blobGetLengthCall.getLongX(1);
+    }
+
+    /**
+     * Retrieves all or part of the <code>BLOB</code> value that is identified
+     * by <code>sourceLocator</code>, as an array of bytes.  This
+     * <code>byte</code> array contains up to <code>forLength</code>
+     * consecutive bytes starting at position <code>fromPosition</code>.
+     * <p>
+     * If <code>forLength</code> is larger than the maximum length of a VARCHAR
+     * FOR BIT DATA, the reading of the BLOB will be split into repeated
+     * procedure calls.
+     *
+     * @param sourceLocator locator that identifies the Blob to operate on
+     * @param fromPosition the ordinal position of the first byte in the
+     *        <code>BLOB</code> value to be extracted; the first byte is at
+     *        position 1
+     * @param forLength the number of consecutive bytes to be copied; the value
+     *        for length must be 0 or greater.  Specifying a length that goes
+     *        beyond the end of the BLOB (i.e., <code>fromPosition + forLength
+     *        > blob.length()</code>), will result in an error.
+     * @throws org.apache.derby.client.am.SqlException 
+     * @return a byte array containing up to <code>forLength</code> consecutive
+     *         bytes from the <code>BLOB</code> value designated by
+     *         <code>sourceLocator</code>, starting with the byte at position
+     *         <code>fromPosition</code>
+     */
+    byte[] blobGetBytes(int sourceLocator, long fromPosition, int forLength) 
+        throws SqlException
+    {
+        if (forLength == 0) return new byte[0];
+        
+        if (blobGetBytesCall == null) {
+            blobGetBytesCall = connection.prepareCallX
+                ("? = CALL SYSIBM.BLOBGETBYTES(?, ?, ?)",
+                 java.sql.ResultSet.TYPE_FORWARD_ONLY, 
+                 java.sql.ResultSet.CONCUR_READ_ONLY, 
+                 java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT);
+            blobGetBytesCall.registerOutParameterX(1, java.sql.Types.VARBINARY);
+            // Make sure this statement does not commit user transaction
+            blobGetBytesCall.isAutoCommittableStatement_ = false;
+        }
+
+        byte retVal[] = null;
+        int gotSoFar = 0;
+        while (gotSoFar < forLength) {
+            blobGetBytesCall.setIntX(2, sourceLocator);
+            blobGetBytesCall.setLongX(3, fromPosition + gotSoFar);
+            blobGetBytesCall.setIntX(4, forLength - gotSoFar);
+            blobGetBytesCall.executeX();
+            byte[] result = blobGetBytesCall.getBytesX(1);
+            
+            if (gotSoFar == 0) {  // First round of reading
+                if (result.length == forLength) {  // Got everything
+                    return result;
+                } else {
+                    // Blob is probably greater than MAX VARCHAR length, need to
+                    // read in parts, create array for putting pieces together
+                    retVal = new byte[forLength];
+                }
+            }
+           
+            // If not able to read more, stop
+            if (result.length == 0) break;
+            
+            System.arraycopy(result, 0,
+                             retVal, gotSoFar, result.length);
+            gotSoFar += result.length;
+        }
+        return retVal;
+    }
+
+    /**
+     * Writes all or part of the given <code>byte</code> array to the
+     * <code>BLOB</code> value designated by <code>sourceLocator</code>.
+     * Writing starts at position <code>fromPosition</code> in the
+     * <code>BLOB</code> value; <code>forLength</code> bytes from the given
+     * byte array are written. If the end of the <code>Blob</code> value is
+     * reached while writing the array of bytes, then the length of the
+     * <code>Blob</code> value will be increased to accomodate the extra bytes.
+     * <p>
+     * If <code>forLength</code> is larger than the maximum length of a VARCHAR
+     * FOR BIT DATA, the writing to the BLOB value will be split into repeated
+     * procedure calls.
+     *
+     * @param sourceLocator locator that identifies the Blob to operated on
+     * @param fromPosition the position in the <code>BLOB</code> value at which
+     *        to start writing; the first position is 1
+     * @param forLength the number of bytes to be written to the
+     *        <code>BLOB</code> value from the array of bytes
+     *        <code>bytes</code>.  Specifying a length that goes beyond the end
+     *        of the BLOB (i.e., <code>fromPosition + forLength >
+     *        blob.length()</code>, will result in an error.
+     * @param bytes the array of bytes to be written
+     * @throws org.apache.derby.client.am.SqlException 
+     */
+    void blobSetBytes(int sourceLocator, 
+                      long fromPosition, 
+                      int forLength, 
+                      byte[] bytes) throws SqlException
+    {
+        if (blobSetBytesCall == null) {
+            blobSetBytesCall = connection.prepareCallX
+                ("CALL SYSIBM.BLOBSETBYTES(?, ?, ?, ?)",
+                 java.sql.ResultSet.TYPE_FORWARD_ONLY, 
+                 java.sql.ResultSet.CONCUR_READ_ONLY, 
+                 java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT);
+            // Make sure this statement does not commit user transaction
+            blobSetBytesCall.isAutoCommittableStatement_ = false;
+        }
+
+        int sentSoFar = 0;
+        byte[] bytesToBeSent = bytes;
+        while (sentSoFar < forLength) {
+            // Only send what can fit in a VARCHAR FOR BIT DATA parameter
+            int numBytesThisRound 
+                = Math.min(forLength - sentSoFar, VARCHAR_MAXWIDTH);
+            if (numBytesThisRound != bytesToBeSent.length) {
+                // Need an array that contains just what is to be sent
+                bytesToBeSent = new byte[numBytesThisRound];                
+            }
+            if (bytesToBeSent != bytes) {
+                // Need to copy from original array
+                System.arraycopy(bytes, sentSoFar,
+                                 bytesToBeSent, 0, numBytesThisRound);
+            }
+            
+            blobSetBytesCall.setIntX(1, sourceLocator);
+            blobSetBytesCall.setLongX(2, fromPosition + sentSoFar);
+            blobSetBytesCall.setIntX(3, numBytesThisRound);
+            blobSetBytesCall.setBytesX(4, bytesToBeSent);
+            blobSetBytesCall.executeX();
+            
+            sentSoFar += numBytesThisRound;
+        }
+    }
+
+    /**
+     * Truncates the <code>BLOB</code> value identified by
+     * <code>sourceLocator</code> to be <code>length</code> bytes.  <p>
+     * <b>Note:</b> If the value specified for <code>length</code> is greater
+     * than the length+1 of the <code>BLOB</code> value then an
+     * <code>SQLException</code> will be thrown.
+     * 
+     * @param sourceLocator locator identifying the Blob to be truncated
+     * @param length the length, in bytes, to which the <code>BLOB</code> value
+     *        should be truncated
+     * @throws org.apache.derby.client.am.SqlException 
+     */
+    void blobTruncate(int sourceLocator, long length) throws SqlException
+    {
+        if (blobTruncateCall == null) {
+            blobTruncateCall = connection.prepareCallX
+                ("CALL SYSIBM.BLOBTRUNCATE(?, ?)",
+                 java.sql.ResultSet.TYPE_FORWARD_ONLY, 
+                 java.sql.ResultSet.CONCUR_READ_ONLY, 
+                 java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT);
+            // Make sure this statement does not commit user transaction
+            blobTruncateCall.isAutoCommittableStatement_ = false;
+        }
+
+        blobTruncateCall.setIntX(1, sourceLocator);
+        blobTruncateCall.setLongX(2, length);
+        blobTruncateCall.executeX();
+    }
+
+    /**
+     * Allocates an empty CLOB on server and returns its locator. Any
+     * subsequent operations on this CLOB value will be stored in temporary
+     * space on the server.
+     * 
+     * @throws org.apache.derby.client.am.SqlException 
+     * @return locator that identifies the created CLOB.
+     */
+    int clobCreateLocator() throws SqlException
+    {
+        if (clobCreateLocatorCall == null) {
+            clobCreateLocatorCall = connection.prepareCallX
+                ("? = CALL SYSIBM.CLOBCREATELOCATOR()",
+                 java.sql.ResultSet.TYPE_FORWARD_ONLY, 
+                 java.sql.ResultSet.CONCUR_READ_ONLY, 
+                 java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT);
+            clobCreateLocatorCall
+                .registerOutParameterX(1, java.sql.Types.INTEGER);
+            // Make sure this statement does not commit user transaction
+            clobCreateLocatorCall.isAutoCommittableStatement_ = false;
+        }
+
+        clobCreateLocatorCall.executeX();
+        return clobCreateLocatorCall.getIntX(1);
+    }
+
+    /**
+     * This method frees the CLOB and releases the resources that it
+     * holds. (E.g., temporary space used to store this CLOB on the server.)
+     * @param locator locator that designates the CLOB to be released.
+     * @throws org.apache.derby.client.am.SqlException 
+     */
+    void clobReleaseLocator(int locator) throws SqlException
+    {
+        if (clobReleaseLocatorCall == null) {
+            clobReleaseLocatorCall = connection.prepareCallX
+                ("CALL SYSIBM.CLOBRELEASELOCATOR(?)",
+                 java.sql.ResultSet.TYPE_FORWARD_ONLY, 
+                 java.sql.ResultSet.CONCUR_READ_ONLY, 
+                 java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT);
+            // Make sure this statement does not commit user transaction
+            clobReleaseLocatorCall.isAutoCommittableStatement_ = false;
+        }
+
+        clobReleaseLocatorCall.setIntX(1, locator);
+        clobReleaseLocatorCall.executeX();
+    }
+
+
+    /**
+     * Retrieves the character position at which the specified substring 
+     * <code>searchLiteral</code> begins within the <code>CLOB</code> value
+     * identified by <code>locator</code>.  The search for
+     * <code>searchLiteral</code> begins at position <code>fromPosition</code>.
+     * <p>
+     * If <code>searchLiteral</code> is longer than the maximum length of a
+     * VARCHAR, it will be split into smaller fragments, and
+     * repeated procedure calls will be made to perform the entire search
+     *
+     * @param locator locator that identifies the CLOB to be searched.
+     * @param searchLiteral the substring for which to search
+     * @param fromPosition the position at which to begin searching; the
+     *        first position is 1
+     * @throws org.apache.derby.client.am.SqlException 
+     * @return the position at which the pattern appears, else -1
+     */
+    long clobGetPositionFromString(int locator, 
+                                   String searchLiteral, 
+                                   long fromPosition) throws SqlException
+    {
+        long clobLength = -1;  // Will be fetched from server if needed
+        int patternLength = searchLiteral.length();
+        do {
+            long foundAt = clobGetPositionFromString(locator, 
+                                                     fromPosition,
+                                                     searchLiteral, 
+                                                     0,
+                                                     VARCHAR_MAXWIDTH);
+
+            // If searchLiteral is longer than VARCHAR_MAXWIDTH, 
+            // we need to check the rest
+            boolean tryAgain = false;
+            if ((patternLength > VARCHAR_MAXWIDTH) && (foundAt > 0)) {
+                // First part of searchLiteral matched, check rest
+                int comparedSoFar = VARCHAR_MAXWIDTH;
+                while (comparedSoFar < patternLength) {
+                    int numCharsThisRound 
+                        = Math.min(patternLength - comparedSoFar, 
+                                   VARCHAR_MAXWIDTH);
+                    long pos = clobGetPositionFromString(locator,
+                                                         foundAt+comparedSoFar,
+                                                         searchLiteral,
+                                                         comparedSoFar,
+                                                         numCharsThisRound);
+
+                    if (pos != (foundAt + comparedSoFar)) { 
+                        // This part did not match
+                        // Try to find a later match for the same prefix
+                        tryAgain = true;
+                        fromPosition = foundAt + 1;
+                        break;
+                    }
+
+                    comparedSoFar += numCharsThisRound;
+                }
+            }
+            
+            if (!tryAgain) return foundAt;
+
+            // Need Clob length in order to determine when to stop
+            if (clobLength < 0) {  
+                clobLength = clobGetLength(locator);
+            }
+        } while (fromPosition + patternLength <= clobLength);
+
+        return -1;  // No match
+    }
+
+    /**
+     * 
+     * Retrieves the character position at which the specified part of the
+     * substring <code>searchLiteral</code> begins within the <code>CLOB</code>
+     * value identified by <code>locator</code>.  The search for
+     * <code>searchLiteral</code> begins at position <code>fromPosition</code>.
+     * <p> This is a helper function used by clobGetPositionFromString(int,
+     * String, long) for each call to the CLOBGETPOSITIONFROMSTRING procedure.
+     *
+     * @param locator locator that identifies the CLOB to be searched.
+     * @param searchLiteral the substring for which to search
+     * @param fromPosition the position at which to begin searching; the
+     *        first position is 1
+     * @param offset the offset into the string <code>searchLiteral</code> at
+     *        which the pattern to search for starts
+     * @param length the number of characters from the string
+     *        <code>searchLiteral</code> to use for the pattern to search
+     *        for. It is assumed that this length is smaller than the maximum
+     *        size of a VARCHAR column.  Otherwise, an exception will be
+     *        thrown.
+     * @throws org.apache.derby.client.am.SqlException 
+     * @return the position at which the pattern appears, else -1
+     */
+    private long clobGetPositionFromString(int locator, 
+                                           long fromPosition,
+                                           String searchLiteral, 
+                                           int offset,
+                                           int length) throws SqlException
+    {
+        if (clobGetPositionFromStringCall == null) {
+            clobGetPositionFromStringCall = connection.prepareCallX
+                ("? = CALL SYSIBM.CLOBGETPOSITIONFROMSTRING(?, ?, ?)",
+                 java.sql.ResultSet.TYPE_FORWARD_ONLY, 
+                 java.sql.ResultSet.CONCUR_READ_ONLY, 
+                 java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT);
+            clobGetPositionFromStringCall
+                .registerOutParameterX(1, java.sql.Types.BIGINT);
+            // Make sure this statement does not commit user transaction
+            clobGetPositionFromStringCall.isAutoCommittableStatement_ = false;
+        }
+
+        String stringToBeCompared = searchLiteral;
+        int numChars = Math.min(searchLiteral.length() - offset, length);
+        if (numChars != stringToBeCompared.length()) {
+            // Need a String that contains just what is to be sent
+            stringToBeCompared 
+                = searchLiteral.substring(offset, offset + numChars);
+        }
+
+        clobGetPositionFromStringCall.setIntX(2, locator);
+        clobGetPositionFromStringCall.setStringX(3, searchLiteral);
+        clobGetPositionFromStringCall.setLongX(4, fromPosition);
+        clobGetPositionFromStringCall.executeX();
+        return clobGetPositionFromStringCall.getLongX(1);
+    }
+
+    /**
+     * Retrieves the character position in the CLOB value designated by this
+     * <code>locator</code> at which substring given by
+     * <code>searchLocator</code> begins. The search begins at position
+     * <code>fromPosition</code>.
+     * @param locator locator that identifies the CLOB to be searched.
+     * @param searchLocator locator designating the CLOB value for which to
+     *        search
+     * @param fromPosition the position in the CLOB value
+     *        at which to begin searching; the first position is 1
+     * @throws org.apache.derby.client.am.SqlException 
+     * @return the position at which the pattern begins, else -1
+     */
+    long clobGetPositionFromLocator(int locator, 
+                                    int searchLocator, 
+                                    long fromPosition) throws SqlException
+    {
+        if (clobGetPositionFromLocatorCall == null) {
+            clobGetPositionFromLocatorCall = connection.prepareCallX
+                ("? = CALL SYSIBM.CLOBGETPOSITIONFROMLOCATOR(?, ?, ?)",
+                 java.sql.ResultSet.TYPE_FORWARD_ONLY, 
+                 java.sql.ResultSet.CONCUR_READ_ONLY, 
+                 java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT);
+            clobGetPositionFromLocatorCall
+                .registerOutParameterX(1, java.sql.Types.BIGINT);
+            // Make sure this statement does not commit user transaction
+            clobGetPositionFromLocatorCall.isAutoCommittableStatement_ = false;
+        }
+
+        clobGetPositionFromLocatorCall.setIntX(2, locator);
+        clobGetPositionFromLocatorCall.setIntX(3, searchLocator);
+        clobGetPositionFromLocatorCall.setLongX(4, fromPosition);
+        clobGetPositionFromLocatorCall.executeX();
+        return clobGetPositionFromLocatorCall.getLongX(1);
+    }
+
+    /**
+     * Returns the number of character in the <code>CLOB</code> value
+     * designated by this <code>sourceLocator</code>.
+     * 
+     * @param sourceLocator locator that identifies the CLOB
+     * @throws org.apache.derby.client.am.SqlException 
+     * @return length of the <code>CLOB</code> in characters 
+     */
+    long clobGetLength(int sourceLocator) throws SqlException
+    {
+        if (clobGetLengthCall == null) {
+            clobGetLengthCall = connection.prepareCallX
+                ("? = CALL SYSIBM.CLOBGETLENGTH(?)",
+                 java.sql.ResultSet.TYPE_FORWARD_ONLY, 
+                 java.sql.ResultSet.CONCUR_READ_ONLY, 
+                 java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT);
+            clobGetLengthCall.registerOutParameterX(1, java.sql.Types.BIGINT);
+            // Make sure this statement does not commit user transaction
+            clobGetLengthCall.isAutoCommittableStatement_ = false;
+        }
+
+        clobGetLengthCall.setIntX(2, sourceLocator);
+        clobGetLengthCall.executeX();
+        return clobGetLengthCall.getLongX(1);
+    }
+
+    /**
+     * Retrieves all or part of the <code>CLOB</code> value that is identified
+     * by <code>sourceLocator</code>, as a <code>String</code>.  This
+     * <code>String</code> contains up to <code>forLength</code> consecutive
+     * characters starting at position <code>fromPosition</code>.  
+     * <p> 
+     * If <code>forLength</code> is larger than the maximum length of a
+     * VARCHAR, the reading of the CLOB will be split into repeated procedure
+     * calls.
+     *
+     * @param sourceLocator locator that identifies the CLOB to operate on
+     * @param fromPosition the ordinal position of the first character in the
+     *        <code>CLOB</code> value to be extracted; the first character is
+     *        at position 1
+     * @param forLength the number of consecutive characters to be copied; the
+     *        value for length must be 0 or greater.  Specifying a length that
+     *        goes beyond the end of the CLOB (i.e., <code>fromPosition +
+     *        forLength > clob.length()</code>, will result in an error.
+     * @throws org.apache.derby.client.am.SqlException 
+     * @return a string containing up to <code>forLength</code> consecutive
+     *         characters from the <code>CLOB</code> value designated by
+     *         <code>sourceLocator</code>, starting with the character at
+     *         position <code>fromPosition</code>
+     */
+    String clobGetSubString(int sourceLocator, long fromPosition, int forLength)
+        throws SqlException
+    {
+        if (forLength == 0) return new String("");
+
+        if (clobGetSubStringCall == null) {
+            clobGetSubStringCall = connection.prepareCallX
+                ("? = CALL SYSIBM.CLOBGETSUBSTRING(?, ?, ?)",
+                 java.sql.ResultSet.TYPE_FORWARD_ONLY, 
+                 java.sql.ResultSet.CONCUR_READ_ONLY, 
+                 java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT);
+            clobGetSubStringCall
+                .registerOutParameterX(1, java.sql.Types.VARCHAR);
+            // Make sure this statement does not commit user transaction
+            clobGetSubStringCall.isAutoCommittableStatement_ = false;
+        }
+
+        StringBuffer retVal = null;
+        int gotSoFar = 0;
+        while (gotSoFar < forLength) {
+            clobGetSubStringCall.setIntX(2, sourceLocator);
+            clobGetSubStringCall.setLongX(3, fromPosition + gotSoFar);
+            clobGetSubStringCall.setIntX(4, forLength - gotSoFar);
+            clobGetSubStringCall.executeX();
+            String result =  clobGetSubStringCall.getStringX(1);
+
+            if (gotSoFar == 0) {  // First round of reading
+                if (result.length() == forLength) {  // Got everything
+                    return result;
+                } else {
+                    // Clob is probably greater than MAX VARCHAR length, 
+                    // need to read it in parts, 
+                    // create StringBuffer for putting pieces together
+                    retVal = new StringBuffer(forLength);
+                }
+            }
+           
+            // If not able to read more, stop
+            if (result.length() == 0) break;
+            
+            retVal.append(result);
+            gotSoFar += result.length();
+        }
+        return retVal.toString();
+    }
+
+    /**
+     * Writes all or part of the given <code>String</code> to the
+     * <code>CLOB</code> value designated by <code>sourceLocator</code>.
+     * Writing starts at position <code>fromPosition</code> in the
+     * <code>CLOB</code> value; <code>forLength</code> characters from the
+     * given string are written. If the end of the <code>Clob</code> value is
+     * reached while writing the string, then the length of the
+     * <code>Clob</code> value will be increased to accomodate the extra
+     * characters.
+     * <p> 
+     * If <code>forLength</code> is larger than the maximum length of a
+     * VARCHAR, the writing to the CLOB value will be split into repeated
+     * procedure calls.
+     *
+     * @param sourceLocator locator that identifies the Clob to operated on
+     * @param fromPosition the position in the <code>CLOB</code> value at which
+     *        to start writing; the first position is 1
+     * @param forLength the number of characters to be written to the
+     *        <code>CLOB</code> value from the string <code>string</code>.
+     *        Specifying a length that goes beyond the end of the CLOB (i.e.,
+     *        <code>fromPosition + forLength > clob.length()</code>, will
+     *        result in an error.
+     * @param string the string to be written
+     * @throws org.apache.derby.client.am.SqlException 
+     */
+    void clobSetString(int sourceLocator, 
+                       long fromPosition, 
+                       int forLength, 
+                       String string) throws SqlException
+    {
+        if (clobSetStringCall == null) {
+            clobSetStringCall = connection.prepareCallX
+                ("CALL SYSIBM.CLOBSETSTRING(?, ?, ?, ?)",
+                 java.sql.ResultSet.TYPE_FORWARD_ONLY, 
+                 java.sql.ResultSet.CONCUR_READ_ONLY, 
+                 java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT);
+            // Make sure this statement does not commit user transaction
+            clobSetStringCall.isAutoCommittableStatement_ = false;
+        }
+
+        int sentSoFar = 0;
+        String stringToBeSent = string;
+        while (sentSoFar < forLength) {
+            // Only send what can fit in a VARCHAR parameter
+            int numCharsThisRound 
+                = Math.min(forLength - sentSoFar, VARCHAR_MAXWIDTH);
+            if (numCharsThisRound < string.length()) {
+                // Need a String that contains just what is to be sent
+                stringToBeSent 
+                    = string.substring(sentSoFar, sentSoFar+numCharsThisRound);
+            }
+            
+            clobSetStringCall.setIntX(1, sourceLocator);
+            clobSetStringCall.setLongX(2, fromPosition + sentSoFar);
+            clobSetStringCall.setIntX(3, numCharsThisRound);
+            clobSetStringCall.setStringX(4, stringToBeSent);
+            clobSetStringCall.executeX();
+
+            sentSoFar += numCharsThisRound;
+        }
+    }
+
+    /**
+     * Truncates the <code>CLOB</code> value identified by
+     * <code>sourceLocator</code> to be <code>length</code> characters.  
+     * <p>
+     * <b>Note:</b> If the value specified for <code>length</code> is greater
+     * than the length+1 of the <code>CLOB</code> value then an
+     * <code>SQLException</code> will be thrown.
+     * 
+     * @param sourceLocator locator identifying the Clob to be truncated
+     * @param length the length, in characters, to which the <code>CLOB</code>
+     *        value should be truncated
+     * @throws org.apache.derby.client.am.SqlException 
+     */
+    void clobTruncate(int sourceLocator, long length) throws SqlException
+    {
+        if (clobTruncateCall == null) {
+            clobTruncateCall = connection.prepareCallX
+                ("CALL SYSIBM.CLOBTRUNCATE(?, ?)",
+                 java.sql.ResultSet.TYPE_FORWARD_ONLY, 
+                 java.sql.ResultSet.CONCUR_READ_ONLY, 
+                 java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT);
+            // Make sure this statement does not commit user transaction
+            clobTruncateCall.isAutoCommittableStatement_ = false;
+        }
+
+        clobTruncateCall.setIntX(1, sourceLocator);
+        clobTruncateCall.setLongX(2, length);
+        clobTruncateCall.executeX();
+    }
+}

Propchange: db/derby/code/trunk/java/client/org/apache/derby/client/am/CallableLocatorProcedures.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/client/org/apache/derby/client/am/CallableStatement.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/am/CallableStatement.java?view=diff&rev=529185&r1=529184&r2=529185
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/am/CallableStatement.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/am/CallableStatement.java Mon Apr 16 03:03:47 2007
@@ -338,11 +338,7 @@
                 if (agent_.loggingEnabled()) {
                     agent_.logWriter_.traceEntry(this, "getLong", parameterIndex);
                 }
-                super.checkForClosedStatement();
-                long result;
-                checkGetterPreconditions(parameterIndex);
-                setWasNull(parameterIndex);
-                result = wasNullX() ? 0 : singletonRowData_.getLong(parameterIndex);
+                long result = getLongX(parameterIndex);
                 if (agent_.loggingEnabled()) {
                     agent_.logWriter_.traceExit(this, "getLong", result);
                 }
@@ -355,6 +351,13 @@
         }
     }
 
+    long getLongX(int parameterIndex) throws SqlException {
+        super.checkForClosedStatement();
+        checkGetterPreconditions(parameterIndex);
+        setWasNull(parameterIndex);
+        return wasNullX() ? 0 : singletonRowData_.getLong(parameterIndex);
+    }
+
     public float getFloat(int parameterIndex) throws SQLException {
         try
         {
@@ -649,10 +652,7 @@
                 if (agent_.loggingEnabled()) {
                     agent_.logWriter_.traceEntry(this, "getBytes", parameterIndex);
                 }
-                super.checkForClosedStatement();
-                checkGetterPreconditions(parameterIndex);
-                setWasNull(parameterIndex);
-                byte[] result = wasNullX() ? null : singletonRowData_.getBytes(parameterIndex);
+                byte[] result = getBytesX(parameterIndex);
                 if (agent_.loggingEnabled()) {
                     agent_.logWriter_.traceExit(this, "getBytes", result);
                 }
@@ -664,6 +664,14 @@
             throw se.getSQLException();
         }
     }
+
+    byte[] getBytesX(final int parameterIndex) throws SqlException 
+    {
+        super.checkForClosedStatement();
+        checkGetterPreconditions(parameterIndex);
+        setWasNull(parameterIndex);
+        return  wasNullX() ? null : singletonRowData_.getBytes(parameterIndex);
+     }
 
     public java.sql.Blob getBlob(int parameterIndex) throws SQLException {
         try

Modified: db/derby/code/trunk/java/client/org/apache/derby/client/am/Connection.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/am/Connection.java?view=diff&rev=529185&r1=529184&r2=529185
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/am/Connection.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/am/Connection.java Mon Apr 16 03:03:47 2007
@@ -1656,7 +1656,7 @@
         }
     }
 
-    private CallableStatement prepareCallX(String sql,
+    CallableStatement prepareCallX(String sql,
                                            int resultSetType,
                                            int resultSetConcurrency,
                                            int resultSetHoldability) throws SqlException {
@@ -2010,6 +2010,33 @@
             agent_.logWriter_.traceConnectResetExit(this);
         }
     }
+    
+    /**
+     * Reference to object with prepared statements for calling the locator
+     * procedures. Makes it possible to reuse prepared statements within 
+     * the connection.
+     */
+    private CallableLocatorProcedures lobProcs;
+    
+    /**
+     * Get handle to the object that contains prepared statements for calling
+     * locator procedures for this connection.  The object will be created on 
+     * the first invocation.
+     *
+     * An example of how to call a stored procedure via this method:
+     * <pre> <code>
+     *    connection.locatorProcedureCall().blobReleaseLocator(locator);
+     * </code> </pre>
+     *
+     * @return object with prepared statements for calling locator procedures
+     */
+    CallableLocatorProcedures locatorProcedureCall() 
+    {
+        if (lobProcs == null) {
+            lobProcs = new CallableLocatorProcedures(this);
+        }
+        return lobProcs;
+    }    
 
 
     //-------------------------------helper methods-------------------------------

Modified: db/derby/code/trunk/java/client/org/apache/derby/client/am/PreparedStatement.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/am/PreparedStatement.java?view=diff&rev=529185&r1=529184&r2=529185
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/am/PreparedStatement.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/am/PreparedStatement.java Mon Apr 16 03:03:47 2007
@@ -601,15 +601,20 @@
                                                       java.sql.Types.INTEGER,
                                                       paramType);
                 }
-                
-                parameterMetaData_.clientParamtertype_[parameterIndex - 1] = java.sql.Types.BIGINT;
-                setInput(parameterIndex, new Long(x));
+                setLongX(parameterIndex, x);
             }
         }
         catch ( SqlException se )
         {
             throw se.getSQLException();
         }
+    }
+
+    void setLongX(final int parameterIndex, final long x) 
+    {
+        parameterMetaData_.clientParamtertype_[parameterIndex - 1] 
+                = java.sql.Types.BIGINT;
+        setInput(parameterIndex, new Long(x));
     }
 
     public void setFloat(int parameterIndex, float x) throws SQLException {

Modified: db/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java?view=diff&rev=529185&r1=529184&r2=529185
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java Mon Apr 16 03:03:47 2007
@@ -145,7 +145,7 @@
     // When this is false we skip autocommit for this PreparedStatement.
     // This is needed when the PreparedStatement object is used internally by
     // the driver and a commit is not desired, e.g., Blob/Clob API calls
-    public boolean isAutoCommittableStatement_ = true;
+    boolean isAutoCommittableStatement_ = true;
 
     // The user has no control over the statement that owns a catalog query, and has no ability to close that statement.
     // We need a special member variable on our internal catalog query statements so that