blob: 7728f4b9dfe2f6b5325ce9c544e11fb271c37a02 [file] [log] [blame]
/*
*
* Derby - Class org.apache.derbyTesting.functionTests.tests.jdbcapi.ClobUpdatableReaderTest
*
* 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.Reader;
import java.io.StringReader;
import java.io.Writer;
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.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.Decorator;
import org.apache.derbyTesting.junit.TestConfiguration;
/**
* Test class to test <code>UpdateableReader</code> for <code>Clob</code> in
* embedded driver.
*/
public class ClobUpdatableReaderTest extends BaseJDBCTestCase {
private final String dummy = "This is a new String";
public ClobUpdatableReaderTest (String name) {
super (name);
}
/**
* Test updating a large clob
*/
public void testUpdateableStoreReader () throws Exception {
getConnection().setAutoCommit (false);
PreparedStatement ps = prepareStatement ("insert into updateClob " +
"(id , data) values (? ,?)");
ps.setInt (1, 2);
StringBuffer sb = new StringBuffer ();
String base = "SampleSampleSample";
for (int i = 0; i < 100000; i++) {
sb.append (base);
}
//insert a large enough data to ensure stream is created in dvd
ps.setCharacterStream (2, new StringReader (sb.toString()),
sb.length());
ps.execute();
ps.close();
Statement stmt = createStatement ();
ResultSet rs = stmt.executeQuery("select data from " +
"updateClob where id = 2");
rs.next();
Clob clob = rs.getClob (1);
rs.close();
stmt.close();
assertEquals (sb.length(), clob.length());
Reader r = clob.getCharacterStream();
String newString = "this is a new string";
//access reader before modifying the clob
long l = r.skip (100);
clob.setString (1001, newString);
//l chars are already skipped
long toSkip = 1000 - l;
while (toSkip > 0) {
long skipped = r.skip (toSkip);
toSkip -= skipped;
}
char [] newdata = new char [newString.length()];
int len = r.read(newdata);
assertEquals ("updated not reflected", newString,
new String (newdata, 0, len));
r.close();
}
/**
* Tests updates on reader.
*/
public void testUpdateableReader () throws Exception {
getConnection().setAutoCommit (false);
PreparedStatement ps = prepareStatement ("insert into updateClob " +
"(id , data) values (? ,?)");
ps.setInt (1, 1);
StringBuffer sb = new StringBuffer ();
String base = "SampleSampleSample";
for (int i = 0; i < 100; i++) {
sb.append (base);
}
ps.setCharacterStream (2, new StringReader (sb.toString()),
sb.length());
ps.execute();
ps.close();
Statement stmt = createStatement ();
ResultSet rs = stmt.executeQuery("select data from " +
"updateClob where id = 1");
rs.next();
Clob clob = rs.getClob (1);
rs.close();
stmt.close();
assertEquals (sb.length(), clob.length());
Reader r = clob.getCharacterStream();
char [] clobData = new char [sb.length()];
r.read (clobData);
assertEquals ("mismatch from inserted string",
String.valueOf (clobData), sb.toString());
r.close();
//update before gettting the reader
clob.setString (50, dummy);
r = clob.getCharacterStream();
r.skip (49);
char [] newChars = new char [dummy.length()];
r.read (newChars);
assertEquals ("update not reflected", dummy,
String.valueOf (newChars));
//update again and see if stream is refreshed
clob.setString (75, dummy);
r.skip (75 - 50 - dummy.length());
char [] testChars = new char [dummy.length()];
r.read (testChars);
assertEquals ("update not reflected", dummy,
String.valueOf (newChars));
r.close();
//try inserting some unicode string
String unicodeStr = getUnicodeString();
clob.setString (50, unicodeStr);
char [] utf16Chars = new char [unicodeStr.length()];
r = clob.getCharacterStream();
r.skip(49);
r.read(utf16Chars);
assertEquals ("update not reflected", unicodeStr,
String.valueOf (utf16Chars));
r.close();
Writer w = clob.setCharacterStream (1);
//write enough data to switch the data to file
r = clob.getCharacterStream ();
for (int i = 0; i < 10000; i++) {
w.write (dummy);
}
w.close();
clob.setString (500, unicodeStr);
r.skip (499);
char [] unicodeChars = new char [unicodeStr.length()];
r.read (unicodeChars);
assertEquals ("update not reflected", unicodeStr,
String.valueOf (unicodeChars));
}
/**
* Tests that the Clob can handle multiple streams and the length call
* multiplexed.
* <p>
* This test was written after bug DERBY-2806 was reported, where getting
* the length of the Clob after fetching a stream from it would exhaust
* the stream and cause the next read to return -1.
* <p>
* The test is written to work on a Clob that operates on streams from
* the store, which currently means that it must be over a certain size
* and that no modifying methods can be called on it.
*/
public void testMultiplexedOperationProblem()
throws IOException, SQLException {
getConnection().setAutoCommit(false);
int length = 266000;
PreparedStatement ps = prepareStatement(
"insert into updateClob (id, data) values (?,?)");
ps.setInt(1, length);
ps.setCharacterStream(2, new LoopingAlphabetReader(length), length);
assertEquals(1, ps.executeUpdate());
ps.close();
PreparedStatement psFetchClob = prepareStatement(
"select data from updateClob where id = ?");
psFetchClob.setInt(1, length);
ResultSet rs = psFetchClob.executeQuery();
assertTrue("No Clob of length " + length + " in database", rs.next());
Clob clob = rs.getClob(1);
assertEquals(length, clob.length());
Reader r = clob.getCharacterStream();
int lastReadChar = r.read();
lastReadChar = assertCorrectChar(lastReadChar, r.read());
lastReadChar = assertCorrectChar(lastReadChar, r.read());
assertEquals(length, clob.length());
// Must be bigger than internal buffers might be.
int nextChar;
for (int i = 2; i < 160000; i++) {
nextChar = r.read();
// Check manually to report position where it fails.
if (nextChar == -1) {
fail("Failed at position " + i + ", stream should not be" +
" exhausted now");
}
lastReadChar = assertCorrectChar(lastReadChar, nextChar);
}
lastReadChar = assertCorrectChar(lastReadChar, r.read());
lastReadChar = assertCorrectChar(lastReadChar, r.read());
InputStream ra = clob.getAsciiStream();
assertEquals(length, clob.length());
int lastReadAscii = ra.read();
lastReadAscii = assertCorrectChar(lastReadAscii, ra.read());
lastReadAscii = assertCorrectChar(lastReadAscii, ra.read());
assertEquals(length, clob.length());
lastReadAscii = assertCorrectChar(lastReadAscii, ra.read());
lastReadChar = assertCorrectChar(lastReadChar, r.read());
// Close resources.
r.close();
ra.close();
rs.close();
psFetchClob.close();
}
/**
* Asserts that the two specified characters follow each other in the
* modern latin lowercase alphabet.
*/
private int assertCorrectChar(int prevChar, int nextChar)
throws IOException {
assertTrue("Reached EOF unexpectedly", nextChar != -1);
if (nextChar < 97 && nextChar > 122) {
fail("Char out of range: " + nextChar);
}
if (prevChar < 97 && prevChar > 122) {
fail("Char out of range: " + prevChar);
}
if (prevChar > -1) {
// Work with modern latin lowercase: 97 - 122
if (prevChar == 122) {
assertTrue(prevChar + " -> " + nextChar,
nextChar == 97);
} else {
assertTrue(prevChar + " -> " + nextChar,
nextChar == prevChar +1);
}
}
return nextChar;
}
/**
* Generates a (static) string containing various Unicode characters.
*
* @return a string with ASCII and non-ASCII characters
*/
private String getUnicodeString () {
char[] fill = new char[4];
fill[0] = 'd'; // 1 byte UTF8 character (ASCII)
fill[1] = '\u03a9'; // 2 byte UTF8 character (Greek)
fill[2] = '\u0e14'; // 3 byte UTF8 character (Thai)
fill[3] = 'j'; // 1 byte UTF8 character (ASCII)
StringBuffer sb = new StringBuffer ();
for (int i = 0; i < 4; i++) {
sb.append (fill);
}
return sb.toString();
}
/**
* Setup the test.
*
* @throws SQLException if database access fails
*/
public void setUp() throws Exception {
Statement stmt = createStatement ();
stmt.execute ("create table updateClob " +
"(id integer primary key, data clob)");
stmt.close();
commit();
}
public static Test suite() {
BaseTestSuite ts = new BaseTestSuite("ClobUpdatableReaderTest");
ts.addTest(TestConfiguration.defaultSuite(
ClobUpdatableReaderTest.class));
BaseTestSuite encSuite =
new BaseTestSuite("ClobUpdatableReaderTest:encrypted");
encSuite.addTestSuite (ClobUpdatableReaderTest.class);
ts.addTest(Decorator.encryptedDatabase (encSuite));
return ts;
}
/**
* Cleans up the database.
*/
protected void tearDown() throws java.lang.Exception {
rollback();
Statement stmt = createStatement ();
stmt.execute ("drop table updateClob");
stmt.close();
commit();
super.tearDown();
}
}