blob: 8005ddf20c7f7507611bc85ba67e51305c24903c [file] [log] [blame]
/*
*
* Derby - Class LobStreamsTest
*
* 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.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import junit.framework.Test;
import org.apache.derbyTesting.functionTests.util.streams.LoopingAlphabetReader;
import org.apache.derbyTesting.functionTests.util.streams.LoopingAlphabetStream;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.Decorator;
import org.apache.derbyTesting.junit.JDBC;
import org.apache.derbyTesting.junit.TestConfiguration;
public class LobStreamsTest extends BaseJDBCTestCase {
int[] streamSize = new int[2];
private final String unicodeTestString =
"This is a test string containing a few non-ascii characters:\n " +
"\u00E6\u00F8\u00E5 and \u00C6\u00D8\u00C5 are used in " +
"norwegian: 'Bl\u00E5b\u00E6rsyltet\u00F8y' means 'blueberry jam'" +
", and tastes great on pancakes. =)";
{
streamSize[0] = 300000;
streamSize[1] = 10000;
}
/** Creates a new instance of LobStreamsTest */
public LobStreamsTest(String name) {
super(name);
}
/**
* Set up the connection to the database.
*/
public void setUp() throws Exception {
getConnection().setAutoCommit(false);
Statement stmt1 = createStatement();
stmt1.execute("create table testBlobX1 (" +
"a integer, " +
"b blob(300K), " +
"c clob(300K))");
stmt1.close();
byte[] b2 = new byte[1];
b2[0] = (byte)64;
String c2 = "c";
PreparedStatement stmt2 = prepareStatement(
"INSERT INTO testBlobX1(a, b, c) " +
"VALUES (?, ?, ?)");
stmt2.setInt(1, 1);
stmt2.setBytes(2, b2);
stmt2.setString(3, c2);
stmt2.execute();
stmt2.close();
}
/**
* Originally tested that the usage pattern {@code rs.getBlob().method()}
* didn't cause the underlying source stream to be closed too early. This
* behavior was forbidden, the test now checks that an exception is thrown.
* <p>
* Test description: Select from a BLOB column, access the BLOB using the
* pattern rs.getBlob(1).blobMethod() (note that we do not keep a reference
* to the Blob-object), provoke/invoke GC and finalization, and finally try
* to access the same BLOB again (through a different/another call to
* rs.getBlob(1)).
* <p>
* Note that the BLOB must be of a certain size (i.e. multiple pages), such
* that it is stored/accessed as a stream in store.
* <p>
* See DERBY-3844 and DERBY-4440.
*
* @throws Exception if something goes wrong
*/
public void testGettingBlobTwice()
throws Exception {
setAutoCommit(false);
// We need a Blob represented as a stream in store.
int length = 71*1024+7;
PreparedStatement ps =
prepareStatement("insert into testBlobX1(a,b) values (?,?)");
ps.setInt(1, 2);
ps.setBinaryStream(2, new LoopingAlphabetStream(length), length);
ps.executeUpdate();
ps.close();
// Get a result set with the Blob.
ps = prepareStatement("select b from testBlobX1 where a = ?");
ps.setInt(1, 2);
ResultSet rs = ps.executeQuery();
assertTrue(rs.next());
Blob b = rs.getBlob(1);
try {
// Get the length, but don't keep a reference to the Blob.
assertEquals(length, rs.getBlob(1).length());
fail("Getting the Blob the second time should have failed");
} catch (SQLException sqle) {
assertSQLState("XCL18", sqle);
}
// Increase the likelyhood of getting the finalizer run.
// Create some junk to fill up the heap, hopefully not JIT'ed away...
int size = 10*1024; // 10 K
byte[] bytes = null;
for (int i=0; i < 50; i++) {
bytes = new byte[size *(i +1)];
}
// For good measure...
System.gc();
System.runFinalization();
try {
Thread.sleep(100L);
} catch (InterruptedException ie) {
// No need to reset the interrupted flag here in the test.
}
// This will fail if the finalizer caused the source stream to be
// closed and the source page to be unlatched.
InputStream is = b.getBinaryStream();
while (is.read() != -1) {
// Keep on reading...
}
assertNotNull(bytes);
}
/**
* Tests that accessing the same Clob multiple times on a row results in
* an exception being thrown.
*
* @throws Exception if something goes wrong
*/
public void testGettingClobTwice()
throws SQLException {
// We need a few Clobs.
int length = 71*1024+7;
PreparedStatement ps =
prepareStatement("insert into testBlobX1(a,c) values (?,?)");
ps.setInt(1, 3);
ps.setCharacterStream(2, new LoopingAlphabetReader(length), length);
ps.executeUpdate();
ps.setInt(1, 4);
ps.setString(2, "short clob");
ps.executeUpdate();
ps.close();
// Get a result set with the Clobs.
final int clobCount = 2;
int count = 0;
ps = prepareStatement(
"select c from testBlobX1 where a >= ? and a <= ?");
ps.setInt(1, 3);
ps.setInt(2, 4);
ResultSet rs = ps.executeQuery();
assertTrue(rs.next());
do {
count++;
// First get a Clob.
Clob c = rs.getClob(1);
// Get a second Clob, which should fail.
try {
rs.getClob(1);
fail("Getting the Clob the second time should have failed");
} catch (SQLException sqle) {
assertSQLState("XCL18", sqle);
}
// Finally try to access the column as a stream.
try {
rs.getCharacterStream(1);
fail("Getting the Clob the third time should have failed");
} catch (SQLException sqle) {
assertSQLState("XCL18", sqle);
}
} while (rs.next());
rs.close();
assertEquals(clobCount, count);
}
/**
* Tests the BlobOutputStream.write(byte b[], int off, int len) method
**/
public void testBlobWrite3Param() throws Exception {
InputStream streamIn = new LoopingAlphabetStream(streamSize[0]);
assertTrue("FAIL -- file not found", streamIn != null);
PreparedStatement stmt3 = prepareStatement(
"SELECT b FROM testBlobX1 WHERE a = 1");
ResultSet rs3 = stmt3.executeQuery();
rs3.next();
Blob blob = rs3.getBlob(1);
assertTrue ("FAIL -- blob is NULL", (blob != null));
int count = 0;
byte[] buffer = new byte[1024];
OutputStream outstream = blob.setBinaryStream(1L);
while ((count = streamIn.read(buffer)) != -1) {
outstream.write(buffer, 0, count);
}
outstream.close();
streamIn.close();
PreparedStatement stmt4 = prepareStatement(
"UPDATE testBlobX1 SET b = ? WHERE a = 1");
stmt4.setBlob(1, blob);
stmt4.executeUpdate();
stmt4.close();
rs3.close();
rs3 = stmt3.executeQuery();
assertTrue("FAIL -- blob not found", rs3.next());
blob = rs3.getBlob(1);
long new_length = blob.length();
assertEquals("FAIL -- wrong blob length;",
streamSize[0], new_length);
// Check contents ...
InputStream fStream = new LoopingAlphabetStream(streamSize[0]);
InputStream lStream = blob.getBinaryStream();
assertTrue("FAIL - Blob and file contents do not match",
compareLob2File(fStream, lStream));
fStream.close();
lStream.close();
rs3.close();
stmt3.close();
}
/**
* Tests the BlobOutputStream.write(int b) method
**/
public void testBlobWrite1Param() throws Exception {
InputStream streamIn = new LoopingAlphabetStream(streamSize[1]);
PreparedStatement stmt3 = prepareStatement(
"SELECT b FROM testBlobX1 WHERE a = 1");
ResultSet rs3 = stmt3.executeQuery();
rs3.next();
Blob blob = rs3.getBlob(1);
assertTrue("FAIL -- blob is NULL", blob != null);
int buffer;
OutputStream outstream = blob.setBinaryStream(1L);
while ((buffer = streamIn.read()) != -1) {
outstream.write(buffer);
}
outstream.close();
streamIn.close();
PreparedStatement stmt4 = prepareStatement(
"UPDATE testBlobX1 SET b = ? WHERE a = 1");
stmt4.setBlob(1, blob);
stmt4.executeUpdate();
stmt4.close();
rs3.close();
rs3 = stmt3.executeQuery();
assertTrue("FAIL -- blob not found", rs3.next());
blob = rs3.getBlob(1);
long new_length = blob.length();
assertEquals("FAIL -- wrong blob length", streamSize[1], new_length);
// Check contents ...
InputStream fStream = new LoopingAlphabetStream(streamSize[1]);
InputStream lStream = blob.getBinaryStream();
assertTrue("FAIL - Blob and file contents do not match",
compareLob2File(fStream, lStream));
fStream.close();
lStream.close();
rs3.close();
stmt3.close();
}
/**
* Tests the ClobOutputStream.write(int b) method
**/
public void testClobAsciiWrite1Param() throws Exception
{
InputStream streamIn = new LoopingAlphabetStream(streamSize[1]);
PreparedStatement stmt3 = prepareStatement(
"SELECT c FROM testBlobX1 WHERE a = 1");
ResultSet rs3 = stmt3.executeQuery();
rs3.next();
Clob clob = rs3.getClob(1);
assertTrue("FAIL -- clob is NULL", clob != null);
int buffer;
OutputStream outstream = clob.setAsciiStream(1L);
while ((buffer = streamIn.read()) != -1) {
outstream.write(buffer);
}
outstream.close();
streamIn.close();
PreparedStatement stmt4 = prepareStatement(
"UPDATE testBlobX1 SET c = ? WHERE a = 1");
stmt4.setClob(1, clob);
stmt4.executeUpdate();
stmt4.close();
rs3.close();
rs3 = stmt3.executeQuery();
assertTrue("FAIL -- clob not found", rs3.next());
clob = rs3.getClob(1);
long new_length = clob.length();
assertEquals("FAIL -- wrong clob length", streamSize[1], new_length);
// Check contents ...
InputStream fStream = new LoopingAlphabetStream(streamSize[1]);
InputStream lStream = clob.getAsciiStream();
assertTrue("FAIL - Clob and file contents do not match", compareLob2File(fStream, lStream));
fStream.close();
lStream.close();
rs3.close();
stmt3.close();
}
/**
* Tests the ClobOutputStream.write(byte b[], int off, int len) method
**/
public void testClobAsciiWrite3Param() throws Exception {
InputStream streamIn = new LoopingAlphabetStream(streamSize[0]);
assertTrue("FAIL -- file not found", streamIn != null);
PreparedStatement stmt3 = prepareStatement(
"SELECT c FROM testBlobX1 WHERE a = 1");
ResultSet rs3 = stmt3.executeQuery();
rs3.next();
Clob clob = rs3.getClob(1);
assertTrue("FAIL -- clob is NULL", clob != null);
int count = 0;
byte[] buffer = new byte[1024];
OutputStream outstream = clob.setAsciiStream(1L);
while ((count = streamIn.read(buffer)) != -1) {
outstream.write(buffer, 0, count);
}
outstream.close();
streamIn.close();
PreparedStatement stmt4 = prepareStatement(
"UPDATE testBlobX1 SET c = ? WHERE a = 1");
stmt4.setClob(1, clob);
stmt4.executeUpdate();
stmt4.close();
rs3.close();
rs3 = stmt3.executeQuery();
assertTrue("FAIL -- clob not found", rs3.next());
clob = rs3.getClob(1);
long new_length = clob.length();
assertEquals("FAIL -- wrong clob length",
streamSize[0], new_length);
// Check contents ...
InputStream fStream = new LoopingAlphabetStream(streamSize[0]);
InputStream lStream = clob.getAsciiStream();
assertTrue("FAIL - Clob and file contents do not match",
compareLob2File(fStream, lStream));
fStream.close();
lStream.close();
rs3.close();
stmt3.close();
}
/**
* Tests the ClobWriter.write(char cbuf[], int off, int len) method
**/
public void testClobCharacterWrite3ParamChar() throws Exception
{
char[] testdata = unicodeTestString.toCharArray();
PreparedStatement stmt3 = prepareStatement(
"SELECT c FROM testBlobX1 WHERE a = 1");
ResultSet rs3 = stmt3.executeQuery();
rs3.next();
Clob clob = rs3.getClob(1);
assertTrue("FAIL -- clob is NULL", clob != null);
Writer clobWriter = clob.setCharacterStream(1L);
clobWriter.write(testdata, 0, testdata.length);
clobWriter.close();
PreparedStatement stmt4 = prepareStatement(
"UPDATE testBlobX1 SET c = ? WHERE a = 1");
stmt4.setClob(1, clob);
stmt4.executeUpdate();
stmt4.close();
rs3.close();
rs3 = stmt3.executeQuery();
assertTrue("FAIL -- clob not found", rs3.next());
clob = rs3.getClob(1);
long new_length = clob.length();
assertEquals("FAIL -- wrong clob length",
testdata.length, new_length);
// Check contents ...
Reader lStream = clob.getCharacterStream();
assertTrue("FAIL - Clob and buffer contents do not match",
compareClobReader2CharArray(testdata, lStream));
lStream.close();
rs3.close();
stmt3.close();
}
/**
* Tests the ClobWriter.write(String str, int off, int len) method
**/
public void testClobCharacterWrite3ParamString() throws Exception
{
PreparedStatement stmt3 = prepareStatement(
"SELECT c FROM testBlobX1 WHERE a = 1");
ResultSet rs3 = stmt3.executeQuery();
rs3.next();
Clob clob = rs3.getClob(1);
assertTrue("FAIL -- clob is NULL", clob != null);
Writer clobWriter = clob.setCharacterStream(1L);
clobWriter.write(unicodeTestString, 0, unicodeTestString.length());
clobWriter.close();
PreparedStatement stmt4 = prepareStatement(
"UPDATE testBlobX1 SET c = ? WHERE a = 1");
stmt4.setClob(1, clob);
stmt4.executeUpdate();
stmt4.close();
rs3.close();
rs3 = stmt3.executeQuery();
assertTrue("FAIL -- clob not found", rs3.next());
clob = rs3.getClob(1);
long new_length = clob.length();
assertEquals("FAIL -- wrong clob length", unicodeTestString.length(), new_length);
// Check contents ...
Reader lStream = clob.getCharacterStream();
assertTrue("FAIL - Clob and buffer contents do not match",
compareClobReader2CharArray(
unicodeTestString.toCharArray(),
lStream));
lStream.close();
rs3.close();
stmt3.close();
}
/**
* Tests the ClobWriter.write(String str) method
**/
public void testClobCharacterWrite1ParamString() throws Exception
{
PreparedStatement stmt3 = prepareStatement(
"SELECT c FROM testBlobX1 WHERE a = 1");
ResultSet rs3 = stmt3.executeQuery();
rs3.next();
Clob clob = rs3.getClob(1);
assertTrue("FAIL -- clob is NULL", clob != null);
Writer clobWriter = clob.setCharacterStream(1L);
clobWriter.write(unicodeTestString);
clobWriter.close();
PreparedStatement stmt4 = prepareStatement(
"UPDATE testBlobX1 SET c = ? WHERE a = 1");
stmt4.setClob(1, clob);
stmt4.executeUpdate();
stmt4.close();
rs3.close();
rs3 = stmt3.executeQuery();
assertTrue("FAIL -- clob not found", rs3.next());
clob = rs3.getClob(1);
long new_length = clob.length();
assertEquals("FAIL -- wrong clob length", unicodeTestString.length(), new_length);
// Check contents ...
Reader lStream = clob.getCharacterStream();
assertTrue("FAIL - Clob and buffer contents do not match",
compareClobReader2CharArray(
unicodeTestString.toCharArray(),
lStream));
lStream.close();
rs3.close();
stmt3.close();
}
/**
* Tests the ClobWriter.write(int c) method
**/
public void testClobCharacterWrite1Char() throws Exception
{
char testchar = 'a';
PreparedStatement stmt3 = prepareStatement(
"SELECT c FROM testBlobX1 WHERE a = 1");
ResultSet rs3 = stmt3.executeQuery();
rs3.next();
Clob clob = rs3.getClob(1);
assertTrue ("FAIL -- clob is NULL", clob != null);
Writer clobWriter = clob.setCharacterStream(1L);
clobWriter.write(testchar);
clobWriter.close();
PreparedStatement stmt4 = prepareStatement(
"UPDATE testBlobX1 SET c = ? WHERE a = 1");
stmt4.setClob(1, clob);
stmt4.executeUpdate();
stmt4.close();
rs3.close();
rs3 = stmt3.executeQuery();
assertTrue("FAIL -- clob not found", rs3.next());
clob = rs3.getClob(1);
long new_length = clob.length();
assertEquals("FAIL -- wrong clob length", 1, new_length);
// Check contents ...
Reader lStream = clob.getCharacterStream();
char clobchar = (char) lStream.read();
assertEquals("FAIL - fetched Clob and original contents do " +
"not match", testchar, clobchar);
lStream.close();
rs3.close();
stmt3.close();
}
/**
* Run with DerbyNetClient only.
* Embedded Clob/Blob.setXXXStream() methods are not implemented.
*/
public static Test suite() {
BaseTestSuite ts = new BaseTestSuite ("LobStreamsTest");
ts.addTest(TestConfiguration.defaultSuite (LobStreamsTest.class));
// JSR169 does not have support for encryption
if (JDBC.vmSupportsJDBC3()) {
BaseTestSuite encSuite =
new BaseTestSuite ("LobStreamsTest:encrypted");
encSuite.addTestSuite (LobStreamsTest.class);
ts.addTest(Decorator.encryptedDatabase (encSuite));
}
return ts;
}
//method to ensure that buffer is filled if there is any data in stream
private int readBytesFromStream (byte [] b, InputStream is)
throws IOException {
int read = 0;
while (read < b.length) {
int ret = is.read (b, read, b.length - read);
if (ret < 0) {
if (read == 0) {
return ret;
}
else {
break;
}
}
read += ret;
}
return read;
}
private boolean compareLob2File(
InputStream fStream,
InputStream lStream) throws Exception
{
byte[] fByte = new byte[1024];
byte[] lByte = new byte[1024];
int lLength = 0, fLength = 0;
String fString, lString;
do {
fLength = readBytesFromStream (fByte, fStream);
lLength = readBytesFromStream (lByte, lStream);
if (!java.util.Arrays.equals(fByte, lByte))
return false;
} while (fLength > 0 && lLength > 0);
fStream.close();
lStream.close();
return true;
}
private boolean compareClobReader2CharArray(
char[] cArray,
Reader charReader) throws Exception
{
char[] clobChars = new char[cArray.length];
int readChars = 0;
int totalCharsRead = 0;
do {
readChars = charReader.read(clobChars, totalCharsRead, cArray.length - totalCharsRead);
if (readChars != -1)
totalCharsRead += readChars;
} while (readChars != -1 && totalCharsRead < cArray.length);
charReader.close();
if (!java.util.Arrays.equals(cArray, clobChars))
return false;
return true;
}
}