From fe167860386a1c35322509f6f182a8b57dd8a58b Mon Sep 17 00:00:00 2001 From: Orange flavored banana <106858113+moonwhistle@users.noreply.github.com> Date: Wed, 12 Nov 2025 13:51:41 +0900 Subject: [PATCH 1/4] feat(jdbc): implements `setAsciiStream`, `setBinaryStream` methods (int, InputStream, long) --- .../tech/turso/jdbc4/JDBC4PreparedStatement.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java index 5b38658cd..744e1c00b 100644 --- a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java +++ b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java @@ -22,6 +22,7 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.RowId; import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLXML; import java.sql.Time; import java.sql.Timestamp; @@ -435,12 +436,14 @@ public final class JDBC4PreparedStatement extends JDBC4Statement implements Prep @Override public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { - // TODO + requireLengthIsPositiveInt(length); + setAsciiStream(parameterIndex, x, (int) length); } @Override public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { - // TODO + requireLengthIsPositiveInt(length); + setBinaryStream(parameterIndex, x, (int) length); } @Override @@ -449,6 +452,13 @@ public final class JDBC4PreparedStatement extends JDBC4Statement implements Prep // TODO } + private void 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"); + } + } + @Override public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { // TODO From 8d56c624469e2e786cd9a76b9518c7f1a4730162 Mon Sep 17 00:00:00 2001 From: Orange flavored banana <106858113+moonwhistle@users.noreply.github.com> Date: Wed, 12 Nov 2025 14:02:15 +0900 Subject: [PATCH 2/4] test(jdbc): test `setAsciiStream`, `setBinaryStream` methods (int, InputStream, long) --- .../jdbc4/JDBC4PreparedStatementTest.java | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4PreparedStatementTest.java b/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4PreparedStatementTest.java index 939220e90..b5b388b06 100644 --- a/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4PreparedStatementTest.java +++ b/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4PreparedStatementTest.java @@ -594,6 +594,159 @@ class JDBC4PreparedStatementTest { assertThrows(SQLException.class, () -> stmt.setUnicodeStream(1, stream, -5)); } + @Test + void testSetAsciiStream_longLength_insert_and_select() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); + + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + + String text = "test"; + byte[] bytes = text.getBytes(StandardCharsets.US_ASCII); + InputStream stream = new ByteArrayInputStream(bytes); + + stmt.setAsciiStream(1, stream, (long) bytes.length); + stmt.execute(); + + PreparedStatement stmt2 = connection.prepareStatement("SELECT col FROM test"); + ResultSet rs = stmt2.executeQuery(); + + assertTrue(rs.next()); + assertEquals(text, rs.getString(1)); + } + + @Test + void testSetAsciiStream_longLength_nullStream() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); + + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + + stmt.setAsciiStream(1, null, 0L); + stmt.execute(); + + PreparedStatement stmt2 = connection.prepareStatement("SELECT col FROM test"); + ResultSet rs = stmt2.executeQuery(); + + assertTrue(rs.next()); + assertNull(rs.getString(1)); + } + + @Test + void testSetAsciiStream_longLength_emptyStream() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); + + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + InputStream empty = new ByteArrayInputStream(new byte[0]); + + stmt.setAsciiStream(1, empty, 0L); + stmt.execute(); + + PreparedStatement stmt2 = connection.prepareStatement("SELECT col FROM test"); + ResultSet rs = stmt2.executeQuery(); + + assertTrue(rs.next()); + assertEquals("", rs.getString(1)); + } + + @Test + void testSetAsciiStream_longLength_negative() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); + + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + InputStream stream = new ByteArrayInputStream("test".getBytes(StandardCharsets.US_ASCII)); + + assertThrows(SQLFeatureNotSupportedException.class, () -> stmt.setAsciiStream(1, stream, -1L)); + } + + @Test + void testSetAsciiStream_longLength_overflow() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); + + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + InputStream stream = new ByteArrayInputStream("test".getBytes(StandardCharsets.US_ASCII)); + + assertThrows( + SQLFeatureNotSupportedException.class, + () -> stmt.setAsciiStream(1, stream, (long) Integer.MAX_VALUE + 1)); + } + + @Test + void testSetBinaryStream_longLength_insert_and_select() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); + + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + + byte[] data = {1, 2, 3, 4, 5}; + InputStream stream = new ByteArrayInputStream(data); + + stmt.setBinaryStream(1, stream, (long) data.length); + stmt.execute(); + + PreparedStatement stmt2 = connection.prepareStatement("SELECT col FROM test"); + ResultSet rs = stmt2.executeQuery(); + + assertTrue(rs.next()); + assertArrayEquals(data, rs.getBytes(1)); + } + + @Test + void testSetBinaryStream_longLength_nullStream() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); + + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + + stmt.setBinaryStream(1, null, 0L); + stmt.execute(); + + PreparedStatement stmt2 = connection.prepareStatement("SELECT col FROM test"); + ResultSet rs = stmt2.executeQuery(); + + assertTrue(rs.next()); + assertNull(rs.getBytes(1)); + } + + @Test + void testSetBinaryStream_longLength_emptyStream() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); + + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + InputStream empty = new ByteArrayInputStream(new byte[0]); + + stmt.setBinaryStream(1, empty, 0L); + stmt.execute(); + + PreparedStatement stmt2 = connection.prepareStatement("SELECT col FROM test"); + ResultSet rs = stmt2.executeQuery(); + + assertTrue(rs.next()); + + byte[] result = rs.getBytes(1); + assertNotNull(result); + assertEquals(0, result.length); + assertArrayEquals(new byte[0], result); + } + + @Test + void testSetBinaryStream_longLength_negative() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); + + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + InputStream stream = new ByteArrayInputStream(new byte[] {1, 2, 3}); + + assertThrows(SQLFeatureNotSupportedException.class, () -> stmt.setBinaryStream(1, stream, -1L)); + } + + @Test + void testSetBinaryStream_longLength_overflow() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); + + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + InputStream stream = new ByteArrayInputStream(new byte[] {1, 2, 3}); + + assertThrows( + SQLFeatureNotSupportedException.class, + () -> stmt.setBinaryStream(1, stream, (long) Integer.MAX_VALUE + 1)); + } + @Test void execute_insert_should_return_number_of_inserted_elements() throws Exception { connection.prepareStatement("CREATE TABLE test (col INTEGER)").execute(); From 28cd56d4810850ab0d12deeafbf2e99936bb9a5b Mon Sep 17 00:00:00 2001 From: Orange flavored banana <106858113+moonwhistle@users.noreply.github.com> Date: Wed, 12 Nov 2025 14:42:32 +0900 Subject: [PATCH 3/4] test(jdbc): implements `setAsciiStream`, `setBinaryStream` methods (int, InputStream) --- .../turso/jdbc4/JDBC4PreparedStatement.java | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java index 744e1c00b..04862b68c 100644 --- a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java +++ b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java @@ -461,12 +461,51 @@ public final class JDBC4PreparedStatement extends JDBC4Statement implements Prep @Override public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { - // TODO + requireNonNull(this.statement); + if (x == null) { + this.statement.bindNull(parameterIndex); + return; + } + byte[] data = readBytes(x); + String ascii = new String(data, StandardCharsets.US_ASCII); + this.statement.bindText(parameterIndex, ascii); } @Override public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { - // TODO + requireNonNull(this.statement); + if (x == null) { + this.statement.bindNull(parameterIndex); + return; + } + byte[] data = readBytes(x); + this.statement.bindBlob(parameterIndex, data); + } + + /** + * Reads all bytes from the given input stream. + * + * @param x the input stream to read + * @return a byte array containing the data + * @throws SQLException if an I/O error occurs while reading + */ + private byte[] readBytes(InputStream x) throws SQLException { + try { + int firstByte = x.read(); + if (firstByte == -1) { + return new byte[0]; + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.write(firstByte); + byte[] buffer = new byte[8192]; + int bytesRead; + while ((bytesRead = x.read(buffer)) > 0) { + baos.write(buffer, 0, bytesRead); + } + return baos.toByteArray(); + } catch (IOException e) { + throw new SQLException("Error reading InputStream", e); + } } @Override From b701154e3b51780abcce4c8d67b77777b25513cb Mon Sep 17 00:00:00 2001 From: Orange flavored banana <106858113+moonwhistle@users.noreply.github.com> Date: Wed, 12 Nov 2025 14:54:40 +0900 Subject: [PATCH 4/4] test(jdbc): test `setAsciiStream`, `setBinaryStream` methods (int, InputStream) --- .../jdbc4/JDBC4PreparedStatementTest.java | 118 ++++++++++++++++-- 1 file changed, 106 insertions(+), 12 deletions(-) diff --git a/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4PreparedStatementTest.java b/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4PreparedStatementTest.java index b5b388b06..6dc8fbe89 100644 --- a/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4PreparedStatementTest.java +++ b/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4PreparedStatementTest.java @@ -395,7 +395,7 @@ class JDBC4PreparedStatementTest { } @Test - void testSetAsciiStream_insert_and_select() throws SQLException { + void testSetAsciiStream_intLength_insert_and_select() throws SQLException { connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); @@ -415,7 +415,7 @@ class JDBC4PreparedStatementTest { } @Test - void testSetAsciiStream_nullStream() throws SQLException { + void testSetAsciiStream_intLength_nullStream() throws SQLException { connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); @@ -431,7 +431,7 @@ class JDBC4PreparedStatementTest { } @Test - void testSetAsciiStream_emptyStream() throws SQLException { + void testSetAsciiStream_intLength_emptyStream() throws SQLException { connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); @@ -448,7 +448,7 @@ class JDBC4PreparedStatementTest { } @Test - void testSetAsciiStream_negativeLength() throws SQLException { + void testSetAsciiStream_intLength_negativeLength() throws SQLException { connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); @@ -461,7 +461,7 @@ class JDBC4PreparedStatementTest { } @Test - void testSetBinaryStream_insert_and_select() throws SQLException { + void testSetBinaryStream_intLength_insert_and_select() throws SQLException { connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); @@ -480,7 +480,7 @@ class JDBC4PreparedStatementTest { } @Test - void testSetBinaryStream_nullStream() throws SQLException { + void testSetBinaryStream_intLength_nullStream() throws SQLException { connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); @@ -496,7 +496,7 @@ class JDBC4PreparedStatementTest { } @Test - void testSetBinaryStream_emptyStream() throws SQLException { + void testSetBinaryStream_intLength_emptyStream() throws SQLException { connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); @@ -517,7 +517,7 @@ class JDBC4PreparedStatementTest { } @Test - void testSetBinaryStream_negativeLength() throws SQLException { + void testSetBinaryStream_intLength_negativeLength() throws SQLException { connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); @@ -529,7 +529,7 @@ class JDBC4PreparedStatementTest { } @Test - void testSetUnicodeStream_insert_and_select() throws SQLException { + void testSetUnicodeStream_intLength_insert_and_select() throws SQLException { connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); @@ -549,7 +549,7 @@ class JDBC4PreparedStatementTest { } @Test - void testSetUnicodeStream_nullStream() throws SQLException { + void testSetUnicodeStream_intLength_nullStream() throws SQLException { connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); @@ -565,7 +565,7 @@ class JDBC4PreparedStatementTest { } @Test - void testSetUnicodeStream_emptyStream() throws SQLException { + void testSetUnicodeStream_intLength_emptyStream() throws SQLException { connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); @@ -582,7 +582,7 @@ class JDBC4PreparedStatementTest { } @Test - void testSetUnicodeStream_negativeLength() throws SQLException { + void testSetUnicodeStream_intLength_negativeLength() throws SQLException { connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); @@ -747,6 +747,100 @@ class JDBC4PreparedStatementTest { () -> stmt.setBinaryStream(1, stream, (long) Integer.MAX_VALUE + 1)); } + @Test + void testSetAsciiStream_noLength_insert_and_select() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + + String text = "test"; + byte[] bytes = text.getBytes(StandardCharsets.US_ASCII); + InputStream stream = new ByteArrayInputStream(bytes); + + stmt.setAsciiStream(1, stream); + stmt.execute(); + + PreparedStatement stmt2 = connection.prepareStatement("SELECT col FROM test"); + ResultSet rs = stmt2.executeQuery(); + + assertTrue(rs.next()); + assertEquals(text, rs.getString(1)); + } + + @Test + void testSetAsciiStream_noLength_nullStream() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + + stmt.setAsciiStream(1, null); + stmt.execute(); + + ResultSet rs = connection.prepareStatement("SELECT col FROM test").executeQuery(); + assertTrue(rs.next()); + assertNull(rs.getString(1)); + } + + @Test + void testSetAsciiStream_noLength_emptyStream() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + + InputStream empty = new ByteArrayInputStream(new byte[0]); + stmt.setAsciiStream(1, empty); + stmt.execute(); + + ResultSet rs = connection.prepareStatement("SELECT col FROM test").executeQuery(); + assertTrue(rs.next()); + assertEquals("", rs.getString(1)); + } + + @Test + void testSetBinaryStream_noLength_insert_and_select() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + + byte[] data = {1, 2, 3, 4, 5}; + InputStream stream = new ByteArrayInputStream(data); + + stmt.setBinaryStream(1, stream); + stmt.execute(); + + PreparedStatement stmt2 = connection.prepareStatement("SELECT col FROM test"); + ResultSet rs = stmt2.executeQuery(); + + assertTrue(rs.next()); + assertArrayEquals(data, rs.getBytes(1)); + } + + @Test + void testSetBinaryStream_noLength_nullStream() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + + stmt.setBinaryStream(1, null); + stmt.execute(); + + ResultSet rs = connection.prepareStatement("SELECT col FROM test").executeQuery(); + assertTrue(rs.next()); + assertNull(rs.getBytes(1)); + } + + @Test + void testSetBinaryStream_noLength_emptyStream() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + + InputStream empty = new ByteArrayInputStream(new byte[0]); + stmt.setBinaryStream(1, empty); + stmt.execute(); + + ResultSet rs = connection.prepareStatement("SELECT col FROM test").executeQuery(); + assertTrue(rs.next()); + + byte[] result = rs.getBytes(1); + assertNotNull(result); + assertEquals(0, result.length); + } + @Test void execute_insert_should_return_number_of_inserted_elements() throws Exception { connection.prepareStatement("CREATE TABLE test (col INTEGER)").execute();