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 2008/11/03 15:43:06 UTC
svn commit: r710070 - in /db/derby/code/branches/10.4/java:
engine/org/apache/derby/impl/jdbc/ testing/org/apache/derby/impl/jdbc/
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;
}