提交 5c8b3888 authored 作者: Thomas Mueller's avatar Thomas Mueller

Connection-created Clob and Blob objects can now be filled using…

Connection-created Clob and Blob objects can now be filled using Clob.setCharacterStream(1), Clob.setString(1, s), Blob.setBytes(1, x), Blob.setBinaryStream(1). Issue 235.
上级 13ce9951
...@@ -7,16 +7,21 @@ ...@@ -7,16 +7,21 @@
package org.h2.jdbc; package org.h2.jdbc;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.sql.Blob; import java.sql.Blob;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.message.TraceObject; import org.h2.message.TraceObject;
import org.h2.util.CallThread;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -25,7 +30,7 @@ import org.h2.value.Value; ...@@ -25,7 +30,7 @@ import org.h2.value.Value;
*/ */
public class JdbcBlob extends TraceObject implements Blob { public class JdbcBlob extends TraceObject implements Blob {
private Value value; Value value;
private JdbcConnection conn; private JdbcConnection conn;
/** /**
...@@ -117,16 +122,24 @@ public class JdbcBlob extends TraceObject implements Blob { ...@@ -117,16 +122,24 @@ public class JdbcBlob extends TraceObject implements Blob {
} }
/** /**
* [Not supported] Sets some bytes of the object. * Fills the Blob. This is only supported for new, empty Blob objects that
* were created with Connection.createBlob(). The position
* must be 1, meaning the whole Blob data is set.
* *
* @param pos the write position * @param pos where to start writing (the first byte is at position 1)
* @param bytes the bytes to set * @param bytes the bytes to set
* @return how many bytes have been written * @return the length of the added data
* @throws SQLException
*/ */
public int setBytes(long pos, byte[] bytes) throws SQLException { public int setBytes(long pos, byte[] bytes) throws SQLException {
debugCode("setBytes("+pos+", bytes);"); try {
throw unsupported("LOB update"); if (pos != 1) {
throw DbException.getInvalidValueException("pos", pos);
}
value = conn.createBlob(new ByteArrayInputStream(bytes), -1);
return bytes.length;
} catch (Exception e) {
throw logAndConvert(e);
}
} }
/** /**
...@@ -160,14 +173,45 @@ public class JdbcBlob extends TraceObject implements Blob { ...@@ -160,14 +173,45 @@ public class JdbcBlob extends TraceObject implements Blob {
} }
/** /**
* [Not supported] Returns an output stream. * Get a writer to update the Blob. This is only supported for new, empty
* Blob objects that were created with Connection.createBlob(). The Blob is
* created in a separate thread, and the object is only updated when
* OutputStream.close() is called. The position must be 1, meaning the whole
* Blob data is set.
* *
* @param pos where to start writing * @param pos where to start writing (the first byte is at position 1)
* @return the output stream to write into * @return an output stream
* @throws SQLException
*/ */
public OutputStream setBinaryStream(long pos) throws SQLException { public OutputStream setBinaryStream(long pos) throws SQLException {
throw unsupported("LOB update"); try {
if (pos != 1) {
throw DbException.getInvalidValueException("pos", pos);
}
if (value.getPrecision() != 0) {
throw DbException.getInvalidValueException("length", value.getPrecision());
}
final JdbcConnection c = conn;
final PipedInputStream in = new PipedInputStream();
final CallThread<Value> call = new CallThread<Value>() {
public Value call() {
return c.createBlob(in, -1);
}
};
PipedOutputStream out = new PipedOutputStream(in) {
public void close() throws IOException {
super.close();
try {
value = call.get();
} catch (Exception e) {
throw DbException.convertToIOException(e);
}
}
};
call.execute();
return new BufferedOutputStream(out);
} catch (Exception e) {
throw logAndConvert(e);
}
} }
/** /**
......
...@@ -6,24 +6,25 @@ ...@@ -6,24 +6,25 @@
*/ */
package org.h2.jdbc; package org.h2.jdbc;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader;
import java.io.Writer; import java.io.Writer;
import java.sql.Clob; import java.sql.Clob;
import java.sql.NClob;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.message.TraceObject; import org.h2.message.TraceObject;
import org.h2.util.CallThread;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.value.Value; import org.h2.value.Value;
//## Java 1.6 begin ##
import java.sql.NClob;
//## Java 1.6 end ##
/** /**
* Represents a CLOB value. * Represents a CLOB value.
*/ */
...@@ -33,7 +34,7 @@ public class JdbcClob extends TraceObject implements Clob ...@@ -33,7 +34,7 @@ public class JdbcClob extends TraceObject implements Clob
//## Java 1.6 end ## //## Java 1.6 end ##
{ {
private Value value; Value value;
private JdbcConnection conn; private JdbcConnection conn;
/** /**
...@@ -126,10 +127,48 @@ public class JdbcClob extends TraceObject implements Clob ...@@ -126,10 +127,48 @@ public class JdbcClob extends TraceObject implements Clob
} }
/** /**
* [Not supported] Returns a writer starting from a given position. * Get a writer to update the Clob. This is only supported for new, empty
* Clob objects that were created with Connection.createClob() or
* createNClob(). The Clob is created in a separate thread, and the object
* is only updated when Writer.close() is called. The position must be 1,
* meaning the whole Clob data is set.
*
* @param pos where to start writing (the first character is at position 1)
* @return a writer
*/ */
public Writer setCharacterStream(long pos) throws SQLException { public Writer setCharacterStream(long pos) throws SQLException {
throw unsupported("LOB update"); try {
if (pos != 1) {
throw DbException.getInvalidValueException("pos", pos);
}
if (value.getPrecision() != 0) {
throw DbException.getInvalidValueException("length", value.getPrecision());
}
final JdbcConnection c = conn;
// PipedReader / PipedWriter are a lot slower
// than PipedInputStream / PipedOutputStream
// (Sun/Oracle Java 1.6.0_20)
final PipedInputStream in = new PipedInputStream();
final CallThread<Value> call = new CallThread<Value>() {
public Value call() {
return c.createClob(IOUtils.getReader(in), -1);
}
};
PipedOutputStream out = new PipedOutputStream(in) {
public void close() throws IOException {
super.close();
try {
value = call.get();
} catch (Exception e) {
throw DbException.convertToIOException(e);
}
}
};
call.execute();
return IOUtils.getBufferedWriter(out);
} catch (Exception e) {
throw logAndConvert(e);
}
} }
/** /**
...@@ -144,10 +183,10 @@ public class JdbcClob extends TraceObject implements Clob ...@@ -144,10 +183,10 @@ public class JdbcClob extends TraceObject implements Clob
debugCode("getSubString(" + pos + ", " + length + ");"); debugCode("getSubString(" + pos + ", " + length + ");");
checkClosed(); checkClosed();
if (pos < 1) { if (pos < 1) {
throw DbException.getInvalidValueException("pos", "" + pos); throw DbException.getInvalidValueException("pos", pos);
} }
if (length < 0) { if (length < 0) {
throw DbException.getInvalidValueException("length", "" + length); throw DbException.getInvalidValueException("length", length);
} }
StringBuilder buff = new StringBuilder(Math.min(4096, length)); StringBuilder buff = new StringBuilder(Math.min(4096, length));
Reader reader = value.getReader(); Reader reader = value.getReader();
...@@ -170,10 +209,24 @@ public class JdbcClob extends TraceObject implements Clob ...@@ -170,10 +209,24 @@ public class JdbcClob extends TraceObject implements Clob
} }
/** /**
* [Not supported] Sets a substring. * Fills the Clob. This is only supported for new, empty Clob objects that
* were created with Connection.createClob() or createNClob(). The position
* must be 1, meaning the whole Clob data is set.
*
* @param pos where to start writing (the first character is at position 1)
* @param str the string to add
* @return the length of the added text
*/ */
public int setString(long pos, String str) throws SQLException { public int setString(long pos, String str) throws SQLException {
throw unsupported("LOB update"); try {
if (pos != 1) {
throw DbException.getInvalidValueException("pos", pos);
}
value = conn.createClob(new StringReader(str), -1);
return str.length();
} catch (Exception e) {
throw logAndConvert(e);
}
} }
/** /**
......
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.jdbc;
import java.io.OutputStream;
import java.io.Writer;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Random;
import org.h2.test.TestBase;
/**
* Test the Blob, Clob, and NClob implementations.
*/
public class TestLob extends TestBase {
private Connection conn;
private Statement stat;
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
// System.setProperty("h2.lobInDatabase", "true");
TestBase.createCaller().init().test();
}
public void test() throws Exception {
deleteDb("lob");
conn = getConnection("lob");
stat = conn.createStatement();
stat.execute("create table test(id int, x blob)");
testBlob(0);
testBlob(1);
testBlob(100);
testBlob(100000);
stat.execute("drop table test");
stat.execute("create table test(id int, x clob)");
testClob(0);
testClob(1);
testClob(100);
testClob(100000);
conn.close();
}
private void testBlob(int length) throws Exception {
Random r = new Random(length);
byte[] data = new byte[length];
r.nextBytes(data);
Blob b = conn.createBlob();
OutputStream out = b.setBinaryStream(1);
out.write(data, 0, data.length);
out.close();
stat.execute("delete from test");
PreparedStatement prep = conn.prepareStatement("insert into test values(?, ?)");
prep.setInt(1, 1);
prep.setBlob(2, b);
prep.execute();
prep.setInt(1, 2);
b = conn.createBlob();
b.setBytes(1, data);
prep.setBlob(2, b);
prep.execute();
ResultSet rs;
rs = stat.executeQuery("select * from test");
rs.next();
Blob b2 = rs.getBlob(2);
assertEquals(length, b2.length());
byte[] bytes = b.getBytes(1, length);
byte[] bytes2 = b2.getBytes(1, length);
assertEquals(bytes, bytes2);
rs.next();
b2 = rs.getBlob(2);
assertEquals(length, b2.length());
bytes2 = b2.getBytes(1, length);
assertEquals(bytes, bytes2);
}
private void testClob(int length) throws Exception {
Random r = new Random(length);
char[] data = new char[length];
// Unicode problem:
// The UCS code values 0xd800-0xdfff (UTF-16 surrogates)
// as well as 0xfffe and 0xffff (UCS non-characters)
// should not appear in conforming UTF-8 streams.
// (String.getBytes("UTF-8") only returns 1 byte for 0xd800-0xdfff)
for (int i = 0; i < length; i++) {
char c;
do {
c = (char) r.nextInt();
} while (c >= 0xd800 && c <= 0xdfff);
data[i] = c;
}
Clob c = conn.createClob();
Writer out = c.setCharacterStream(1);
out.write(data, 0, data.length);
out.close();
stat.execute("delete from test");
PreparedStatement prep = conn.prepareStatement("insert into test values(?, ?)");
prep.setInt(1, 1);
prep.setClob(2, c);
prep.execute();
c = conn.createClob();
c.setString(1, new String(data));
prep.setInt(1, 2);
prep.setClob(2, c);
prep.execute();
ResultSet rs;
rs = stat.executeQuery("select * from test");
rs.next();
Clob c2 = rs.getClob(2);
assertEquals(length, c2.length());
String s = c.getSubString(1, length);
String s2 = c2.getSubString(1, length);
rs.next();
c2 = rs.getClob(2);
assertEquals(length, c2.length());
s2 = c2.getSubString(1, length);
assertEquals(s, s2);
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论