diff --git a/src/main/java/io/roastedroot/sqlite4j/jdbc3/JDBC3PreparedStatement.java b/src/main/java/io/roastedroot/sqlite4j/jdbc3/JDBC3PreparedStatement.java index 3ea1def79..6376edfd4 100644 --- a/src/main/java/io/roastedroot/sqlite4j/jdbc3/JDBC3PreparedStatement.java +++ b/src/main/java/io/roastedroot/sqlite4j/jdbc3/JDBC3PreparedStatement.java @@ -279,20 +279,11 @@ private byte[] readBytes(InputStream istream, int length) throws SQLException { throw new SQLException("Error reading stream. Length should be non-negative"); } - byte[] bytes = new byte[length]; - try { - int bytesRead; - int totalBytesRead = 0; - - while (totalBytesRead < length) { - bytesRead = istream.read(bytes, totalBytesRead, length - totalBytesRead); - if (bytesRead == -1) { - throw new IOException("End of stream has been reached"); - } - totalBytesRead += bytesRead; + byte[] bytes = istream.readNBytes(length); + if (bytes.length < length) { + throw new IOException("End of stream has been reached"); } - return bytes; } catch (IOException cause) { SQLException exception = new SQLException("Error reading stream"); @@ -308,6 +299,7 @@ private byte[] readBytes(InputStream istream, int length) throws SQLException { public void setBinaryStream(int pos, InputStream istream, int length) throws SQLException { if (istream == null && length == 0) { setBytes(pos, null); + return; } setBytes(pos, readBytes(istream, length)); @@ -326,6 +318,7 @@ public void setAsciiStream(int pos, InputStream istream, int length) throws SQLE public void setUnicodeStream(int pos, InputStream istream, int length) throws SQLException { if (istream == null && length == 0) { setString(pos, null); + return; } try { @@ -463,13 +456,14 @@ public void setString(int pos, String value) throws SQLException { */ public void setCharacterStream(int pos, Reader reader, int length) throws SQLException { try { - // copy chars from reader to StringBuffer - StringBuffer sb = new StringBuffer(); + // copy chars from reader to StringBuilder + StringBuilder sb = new StringBuilder(); char[] cbuf = new char[8192]; int cnt; - while ((cnt = reader.read(cbuf)) > 0) { + while ((cnt = reader.read(cbuf, 0, Math.min(length, cbuf.length))) > 0) { sb.append(cbuf, 0, cnt); + length -= cnt; } // set as string diff --git a/src/main/java/io/roastedroot/sqlite4j/jdbc4/JDBC4PreparedStatement.java b/src/main/java/io/roastedroot/sqlite4j/jdbc4/JDBC4PreparedStatement.java index 23cbeeda8..bc423f237 100644 --- a/src/main/java/io/roastedroot/sqlite4j/jdbc4/JDBC4PreparedStatement.java +++ b/src/main/java/io/roastedroot/sqlite4j/jdbc4/JDBC4PreparedStatement.java @@ -2,6 +2,8 @@ import io.roastedroot.sqlite4j.SQLiteConnection; import io.roastedroot.sqlite4j.jdbc3.JDBC3PreparedStatement; +import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.sql.NClob; @@ -25,6 +27,25 @@ public JDBC4PreparedStatement(SQLiteConnection conn, String sql) throws SQLExcep super(conn, sql); } + private static int requireLengthIsPositiveInt(long length) + throws SQLFeatureNotSupportedException { + if (length > Integer.MAX_VALUE || length < 0) { + throw new SQLFeatureNotSupportedException( + "Data must have a length between 0 and Integer.MAX_VALUE"); + } + return (int) length; + } + + private byte[] readBytes(InputStream istream) throws SQLException { + try { + return istream.readAllBytes(); + } catch (IOException cause) { + SQLException exception = new SQLException("Error reading stream"); + exception.initCause(cause); + throw exception; + } + } + // JDBC 4 public void setRowId(int parameterIndex, RowId x) throws SQLException { // TODO Support this @@ -32,35 +53,33 @@ public void setRowId(int parameterIndex, RowId x) throws SQLException { } public void setNString(int parameterIndex, String value) throws SQLException { - // TODO Support this - throw new SQLFeatureNotSupportedException(); + setString(parameterIndex, value); } public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { - // TODO Support this - throw new SQLFeatureNotSupportedException(); + setCharacterStream(parameterIndex, value, length); } public void setNClob(int parameterIndex, NClob value) throws SQLException { - // TODO Support this - throw new SQLFeatureNotSupportedException(); + if (value == null) { + setString(parameterIndex, null); + } else { + setCharacterStream(parameterIndex, value.getCharacterStream()); + } } public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { - // TODO Support this - throw new SQLFeatureNotSupportedException(); + setCharacterStream(parameterIndex, reader, requireLengthIsPositiveInt(length)); } public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { - // TODO Support this - throw new SQLFeatureNotSupportedException(); + setBinaryStream(parameterIndex, inputStream, requireLengthIsPositiveInt(length)); } public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { - // TODO Support this - throw new SQLFeatureNotSupportedException(); + setClob(parameterIndex, reader, length); } public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { @@ -69,54 +88,45 @@ public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException } public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { - // TODO Support this - throw new SQLFeatureNotSupportedException(); + setAsciiStream(parameterIndex, x, requireLengthIsPositiveInt(length)); } public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { - // TODO Support this - throw new SQLFeatureNotSupportedException(); + setBinaryStream(parameterIndex, x, requireLengthIsPositiveInt(length)); } public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { - // TODO Support this - throw new SQLFeatureNotSupportedException(); + setCharacterStream(parameterIndex, reader, requireLengthIsPositiveInt(length)); } public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { - // TODO Support this - throw new SQLFeatureNotSupportedException(); + byte[] bytes = readBytes(x); + setAsciiStream(parameterIndex, new ByteArrayInputStream(bytes), bytes.length); } public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { - // TODO Support this - throw new SQLFeatureNotSupportedException(); + setBytes(parameterIndex, readBytes(x)); } public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { - // TODO Support this - throw new SQLFeatureNotSupportedException(); + setCharacterStream(parameterIndex, reader, Integer.MAX_VALUE); } public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { - // TODO Support this - throw new SQLFeatureNotSupportedException(); + setCharacterStream(parameterIndex, value); } public void setClob(int parameterIndex, Reader reader) throws SQLException { - // TODO Support this - throw new SQLFeatureNotSupportedException(); + setCharacterStream(parameterIndex, reader, Integer.MAX_VALUE); } public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { - // TODO Support this - throw new SQLFeatureNotSupportedException(); + setBytes(parameterIndex, readBytes(inputStream)); } public void setNClob(int parameterIndex, Reader reader) throws SQLException { - // TODO Support this - throw new SQLFeatureNotSupportedException(); + setClob(parameterIndex, reader); } } diff --git a/src/test/java/io/roastedroot/sqlite4j/PrepStmtTest.java b/src/test/java/io/roastedroot/sqlite4j/PrepStmtTest.java index 637b44c30..e34aa4c49 100644 --- a/src/test/java/io/roastedroot/sqlite4j/PrepStmtTest.java +++ b/src/test/java/io/roastedroot/sqlite4j/PrepStmtTest.java @@ -7,6 +7,7 @@ import static org.assertj.core.data.Offset.offset; import java.io.ByteArrayInputStream; +import java.io.StringReader; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.sql.Connection; @@ -177,6 +178,73 @@ public void stringRS() throws SQLException { rs.close(); } + @Test + public void clobRS() throws SQLException { + String name = "Gandhi"; + PreparedStatement prep = conn.prepareStatement("select ?;"); + prep.setClob(1, new StringReader(name)); + ResultSet rs = prep.executeQuery(); + assertThat(prep.getUpdateCount()).isEqualTo(-1); + assertThat(rs.next()).isTrue(); + assertThat(rs.getString(1)).isEqualTo(name); + assertThat(rs.next()).isFalse(); + rs.close(); + } + + @Test + public void blobRS() throws SQLException { + String name = "Gandhi"; + PreparedStatement prep = conn.prepareStatement("select ?;"); + prep.setBlob(1, new ByteArrayInputStream(name.getBytes(StandardCharsets.UTF_8))); + ResultSet rs = prep.executeQuery(); + assertThat(prep.getUpdateCount()).isEqualTo(-1); + assertThat(rs.next()).isTrue(); + assertThat(rs.getBytes(1)).isEqualTo(name.getBytes(StandardCharsets.UTF_8)); + assertThat(rs.next()).isFalse(); + rs.close(); + } + + @Test + public void nullStreamsRS() throws SQLException { + PreparedStatement prep = conn.prepareStatement("select ?, ?;"); + prep.setBinaryStream(1, null, 0); + prep.setAsciiStream(2, null, 0); + ResultSet rs = prep.executeQuery(); + assertThat(rs.next()).isTrue(); + assertThat(rs.getBytes(1)).isNull(); + assertThat(rs.getString(2)).isNull(); + assertThat(rs.next()).isFalse(); + rs.close(); + } + + @Test + public void characterAndBinaryStreamRS() throws SQLException { + String text = "Stream content"; + PreparedStatement prep = conn.prepareStatement("select ?, ?;"); + prep.setCharacterStream(1, new StringReader(text)); + prep.setBinaryStream(2, new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8))); + ResultSet rs = prep.executeQuery(); + assertThat(rs.next()).isTrue(); + assertThat(rs.getString(1)).isEqualTo(text); + assertThat(rs.getBytes(2)).isEqualTo(text.getBytes(StandardCharsets.UTF_8)); + assertThat(rs.next()).isFalse(); + rs.close(); + } + + @Test + public void nstringAndNClobRS() throws SQLException { + String text = "Unicode text \u00e9\u00e8\u00e0"; + PreparedStatement prep = conn.prepareStatement("select ?, ?;"); + prep.setNString(1, text); + prep.setNClob(2, new StringReader(text)); + ResultSet rs = prep.executeQuery(); + assertThat(rs.next()).isTrue(); + assertThat(rs.getString(1)).isEqualTo(text); + assertThat(rs.getString(2)).isEqualTo(text); + assertThat(rs.next()).isFalse(); + rs.close(); + } + @Test public void finalizePrep() throws SQLException { conn.prepareStatement("select null;");