db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From krist...@apache.org
Subject svn commit: r710070 - in /db/derby/code/branches/10.4/java: engine/org/apache/derby/impl/jdbc/ testing/org/apache/derby/impl/jdbc/
Date Mon, 03 Nov 2008 14:43:06 GMT
Author: kristwaa
Date: Mon Nov  3 06:43:05 2008
New Revision: 710070

URL: http://svn.apache.org/viewvc?rev=710070&view=rev
Log:
DERBY-3825: StoreStreamClob.getReader(charPos) performs poorly.
Backported patches 1a, 2b and 3a (revisions 689803, 704547 and 710033) from trunk to the 10.4
branch.

Added:
    db/derby/code/branches/10.4/java/testing/org/apache/derby/impl/jdbc/UTF8ReaderTest.java
      - copied unchanged from r704547, db/derby/code/trunk/java/testing/org/apache/derby/impl/jdbc/UTF8ReaderTest.java
Modified:
    db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/EmbedClob.java
    db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/InternalClob.java
    db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/StoreStreamClob.java
    db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/TemporaryClob.java
    db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/UTF8Reader.java
    db/derby/code/branches/10.4/java/testing/org/apache/derby/impl/jdbc/_Suite.java

Modified: db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/EmbedClob.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/EmbedClob.java?rev=710070&r1=710069&r2=710070&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/EmbedClob.java (original)
+++ db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/EmbedClob.java Mon
Nov  3 06:43:05 2008
@@ -217,8 +217,14 @@
 
         String result;
         // An exception will be thrown if the position is larger than the Clob.
+        Reader reader;
         try {
-            Reader reader = this.clob.getReader(pos);
+            try {
+                reader = this.clob.getInternalReader(pos);
+            } catch (EOFException eofe) {
+                throw Util.generateCsSQLException(
+                        SQLState.BLOB_POSITION_TOO_LARGE, new Long(pos), eofe);
+            }
             char[] chars = new char[length];
             int charsRead = 0;
             // Read all the characters requested, or until EOF is reached.
@@ -236,9 +242,6 @@
             } else {
                 result = String.copyValueOf(chars, 0, charsRead);
             }
-        } catch (EOFException eofe) {
-            throw Util.generateCsSQLException(
-                                        SQLState.BLOB_POSITION_TOO_LARGE, eofe);
         } catch (IOException ioe) {
             throw Util.setStreamFailure(ioe);
         }
@@ -342,7 +345,7 @@
                 int matchCount = 0;
                 long pos = start;
                 long newStart = -1;
-                Reader reader = this.clob.getReader(start);
+                Reader reader = this.clob.getInternalReader(start);
                 char [] tmpClob = new char [4096];
                 boolean reset;
                 for (;;) {
@@ -381,7 +384,8 @@
                                 if (newStart < pos) {
                                     pos = newStart;
                                     reader.close();
-                                    reader = this.clob.getReader(newStart);
+                                    reader = this.clob.getInternalReader(
+                                                                    newStart);
                                     newStart = -1;
                                     reset = true;
                                     break;
@@ -398,6 +402,9 @@
                 }
 
             }
+        } catch (EOFException eofe) {
+            throw Util.generateCsSQLException(
+                                        SQLState.BLOB_POSITION_TOO_LARGE, eofe);
         } catch (IOException ioe) {
             throw Util.setStreamFailure(ioe);
         } finally {

Modified: db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/InternalClob.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/InternalClob.java?rev=710070&r1=710069&r2=710070&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/InternalClob.java (original)
+++ db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/InternalClob.java Mon
Nov  3 06:43:05 2008
@@ -91,6 +91,25 @@
     Reader getReader(long characterPosition) throws IOException, SQLException;
 
     /**
+     * Returns an internal reader for the Clob content, initialized at the
+     * specified character position.
+     * <p>
+     * This method can return a shared reader object, avoiding instantiation and
+     * repositioning costs for internal operations where the stream itself is
+     * not published to the end-user. One such example is
+     * {@code Clob.getSubString}.
+     *
+     * @param characterPosition character position. The first character is at
+     *      position {@code 1}.
+     * @return A {@code Reader} serving the content of the Clob.
+     * @throws EOFException if the position is larger then the Clob
+     * @throws IOException if accessing underlying I/O resources fail
+     * @throws SQLException if accessing underlying resources fail
+     */
+    Reader getInternalReader(long characterPosition)
+            throws IOException, SQLException;
+
+    /**
      * Returns a writer to write data into the Clob.
      * <p>
      * The semantics of the writer is the same as for {@link #insertString}.

Modified: db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/StoreStreamClob.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/StoreStreamClob.java?rev=710070&r1=710069&r2=710070&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/StoreStreamClob.java
(original)
+++ db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/StoreStreamClob.java
Mon Nov  3 06:43:05 2008
@@ -24,6 +24,7 @@
 
 import java.io.BufferedInputStream;
 import java.io.EOFException;
+import java.io.FilterReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
@@ -68,7 +69,18 @@
     private final ConnectionChild conChild;
     /** Object used for synchronizing access to the store stream. */
     private final Object synchronizationObject;
-
+    /**
+     * Shared internal reader, closed when the Clob is released.
+     * This is a performance optimization, and the stream is shared between
+     * "one time" operations, for instance {@code getSubString} calls. Often a
+     * subset, or the whole, of the Clob is read subsequently and then this
+     * optimization avoids repositioning costs (the store does not support
+     * random access for LOBs).
+     * <b>NOTE</b>: Do not publish this reader to the end-user.
+     */
+    private UTF8Reader internalReader;
+    /** The internal reader wrapped so that it cannot be closed. */
+    private FilterReader unclosableInternalReader;
 
     /**
      * Creates a new Clob based on a stream from store.
@@ -104,6 +116,9 @@
      */
     public void release() {
         if (!released) {
+            if (this.internalReader != null) {
+                this.internalReader.close();
+            }
             this.positionedStoreStream.closeStream();
             this.released = true;
         }
@@ -192,6 +207,37 @@
     }
 
     /**
+     * Returns an internal reader for the Clob, initialized at the specified
+     * character position.
+     *
+     * @param characterPosition 1-based character position.
+     * @return A reader initialized at the specified position.
+     * @throws EOFException if the positions is larger than the Clob
+     * @throws IOException if accessing the I/O resources fail
+     * @throws SQLException if accessing the store resources fail
+     */
+    public Reader getInternalReader(long characterPosition)
+            throws IOException, SQLException {
+        if (this.internalReader == null) {
+            this.internalReader = new UTF8Reader(positionedStoreStream,
+                    TypeId.CLOB_MAXWIDTH, conChild, synchronizationObject);
+            this.unclosableInternalReader =
+                    new FilterReader(this.internalReader) {
+                        public void close() {
+                            // Do nothing.
+                            // Stream will be closed when the Clob is released.
+                        }
+                    };
+        }
+        try {
+            this.internalReader.reposition(characterPosition);
+        } catch (StandardException se) {
+            throw Util.generateCsSQLException(se);
+        }
+        return this.unclosableInternalReader;
+    }
+
+    /**
      * Not supported.
      *
      * @see InternalClob#getWriter

Modified: db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/TemporaryClob.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/TemporaryClob.java?rev=710070&r1=710069&r2=710070&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/TemporaryClob.java
(original)
+++ db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/TemporaryClob.java
Mon Nov  3 06:43:05 2008
@@ -249,6 +249,15 @@
     }
 
     /**
+     * @see #getReader
+     */
+    public Reader getInternalReader(long characterPosition)
+            throws IOException, SQLException {
+        // TODO: See if we can optimize for a shared internal reader.
+        return getReader(characterPosition);
+    }
+
+    /**
      * Returns number of characters in the Clob.
      *
      * @return The length of the Clob in number of characters.

Modified: db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/UTF8Reader.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/UTF8Reader.java?rev=710070&r1=710069&r2=710070&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/UTF8Reader.java (original)
+++ db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/UTF8Reader.java Mon
Nov  3 06:43:05 2008
@@ -234,7 +234,6 @@
                 if (fillBuffer()) {
                     return -1;
                 }
-                readPositionInBuffer = 0;
             }
 
             return buffer[readPositionInBuffer++];
@@ -258,7 +257,6 @@
                 if (fillBuffer()) {
                     return -1;
                 }
-                readPositionInBuffer = 0;
             }
 
             int remainingInBuffer = charactersInBuffer - readPositionInBuffer;
@@ -297,7 +295,6 @@
                 if (fillBuffer()) {
                     return 0L;
                 }
-                readPositionInBuffer = 0;
             }
 
             int remainingInBuffer = charactersInBuffer - readPositionInBuffer;
@@ -348,7 +345,6 @@
                 if (fillBuffer()) {
                     return -1;
                 }
-                readPositionInBuffer = 0;
             }
 
             int remainingInBuffer = charactersInBuffer - readPositionInBuffer;
@@ -387,7 +383,6 @@
                 if (fillBuffer()) {
                     return -1;
                 }
-                readPositionInBuffer = 0;
             }
 
             int remainingInBuffer = charactersInBuffer - readPositionInBuffer;
@@ -456,6 +451,7 @@
             return true;
 
         charactersInBuffer = 0;
+        readPositionInBuffer = 0;
 
         try {
         try {
@@ -585,6 +581,66 @@
     }
 
     /**
+     * Resets the reader.
+     * <p>
+     * This method is used internally to achieve better performance.
+     * @see #reposition(long)
+     *
+     * @throws IOException if resetting or reading from the stream fails
+     * @throws StandardException if resetting the stream fails
+     */
+    private void resetUTF8Reader()
+            throws IOException, StandardException {
+        // 2L to skip the length encoding bytes.
+        this.positionedIn.reposition(2L);
+        this.rawStreamPos = this.positionedIn.getPosition();
+        this.in = this.positionedIn;
+        this.readerCharCount = this.utfCount = 0L;
+        this.charactersInBuffer = this.readPositionInBuffer = 0;
+    }
+
+    /**
+     * Repositions the stream so that the next character read will be the
+     * character at the requested position.
+     * <p>
+     * There are three types of repositioning, ordered after increasing cost:
+     * <ol> <li>Reposition within current character buffer (small hops forwards
+     *          and potentially backwards - in range 1 char to
+     *          {@code MAXIMUM_BUFFER_SIZE} chars)</li>
+     *      <li>Forward stream from current position (hops forwards)</li>
+     *      <li>Reset stream and skip data (hops backwards)</li>
+     * </ol>
+     *
+     * @param requestedCharPos 1-based requested character position
+     * @throws IOException if resetting or reading from the stream fails
+     * @throws StandardException if resetting the stream fails
+     */
+    void reposition(long requestedCharPos)
+            throws IOException, StandardException {
+        if (SanityManager.DEBUG) {
+            SanityManager.ASSERT(this.positionedIn != null);
+            SanityManager.ASSERT(requestedCharPos > 0);
+        }
+        if (requestedCharPos <= readerCharCount - charactersInBuffer) {
+            // The stream must be reset, because the requested position is
+            // before the current lower buffer boundary.
+            resetUTF8Reader();
+        }
+
+        long currentCharPos =
+            readerCharCount - charactersInBuffer + readPositionInBuffer;
+        long difference = (requestedCharPos - 1) - currentCharPos;
+
+        if (difference <= 0) {
+            // Move back in the buffer.
+            readPositionInBuffer += difference;
+        } else {
+            // Skip forward.
+            persistentSkip(difference);
+        }
+    }
+
+    /**
      * Decode the length encoded in the stream.
      * 
      * This method came from {@link java.io.DataInputStream}
@@ -623,4 +679,29 @@
         }
         return bufferSize;
     }
+
+    /**
+     * Skips the requested number of characters.
+     *
+     * @param toSkip number of characters to skip
+     * @throws EOFException if there are too few characters in the stream
+     * @throws IOException if reading from the stream fails
+     */
+    private final void persistentSkip(long toSkip)
+            throws IOException {
+        long remaining = toSkip;
+        while (remaining > 0) {
+            long skipped = skip(remaining);
+            if (skipped == 0) {
+                if (SanityManager.DEBUG) {
+                    // Add details to the exception in sane builds.
+                    throw new EOFException("Reached end-of-stream after " +
+                        readerCharCount + " characters, " + remaining +
+                        " remaining to skip");
+                }
+                throw new EOFException();
+            }
+            remaining -= skipped;
+        }
+    }
 }

Modified: db/derby/code/branches/10.4/java/testing/org/apache/derby/impl/jdbc/_Suite.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/testing/org/apache/derby/impl/jdbc/_Suite.java?rev=710070&r1=710069&r2=710070&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/testing/org/apache/derby/impl/jdbc/_Suite.java (original)
+++ db/derby/code/branches/10.4/java/testing/org/apache/derby/impl/jdbc/_Suite.java Mon Nov
 3 06:43:05 2008
@@ -46,6 +46,7 @@
         suite.addTest(BiggerTemporaryClobTest.suite());
         suite.addTest(SmallStoreStreamClobTest.suite());
         suite.addTest(BiggerStoreStreamClobTest.suite());
+        suite.addTest(UTF8ReaderTest.suite());
 
         return suite;
     }



Mime
View raw message