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 015667893..5b38658cd 100644 --- a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java +++ b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java @@ -2,11 +2,14 @@ package tech.turso.jdbc4; import static java.util.Objects.requireNonNull; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; import java.net.URL; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; @@ -164,17 +167,88 @@ public final class JDBC4PreparedStatement extends JDBC4Statement implements Prep @Override public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { - // TODO + requireNonNull(this.statement); + if (x == null) { + this.statement.bindNull(parameterIndex); + return; + } + if (length < 0) { + throw new SQLException("setAsciiStream length must be non-negative"); + } + if (length == 0) { + this.statement.bindText(parameterIndex, ""); + return; + } + try { + byte[] buffer = new byte[length]; + int offset = 0; + int read; + while (offset < length && (read = x.read(buffer, offset, length - offset)) > 0) { + offset += read; + } + String ascii = new String(buffer, 0, offset, StandardCharsets.US_ASCII); + this.statement.bindText(parameterIndex, ascii); + } catch (IOException e) { + throw new SQLException("Error reading ASCII stream", e); + } } @Override public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { - // TODO + requireNonNull(this.statement); + if (x == null) { + this.statement.bindNull(parameterIndex); + return; + } + if (length < 0) { + throw new SQLException("setUnicodeStream length must be non-negative"); + } + if (length == 0) { + this.statement.bindText(parameterIndex, ""); + return; + } + try { + byte[] buffer = new byte[length]; + int offset = 0; + int read; + while (offset < length && (read = x.read(buffer, offset, length - offset)) > 0) { + offset += read; + } + String text = new String(buffer, 0, offset, StandardCharsets.UTF_8); + this.statement.bindText(parameterIndex, text); + } catch (IOException e) { + throw new SQLException("Error reading Unicode stream", e); + } } @Override public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { - // TODO + requireNonNull(this.statement); + if (x == null) { + this.statement.bindNull(parameterIndex); + return; + } + if (length < 0) { + throw new SQLException("setBinaryStream length must be non-negative"); + } + if (length == 0) { + this.statement.bindBlob(parameterIndex, new byte[0]); + return; + } + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + byte[] buffer = new byte[8192]; + int bytesRead; + int totalRead = 0; + while (totalRead < length + && (bytesRead = x.read(buffer, 0, Math.min(buffer.length, length - totalRead))) > 0) { + baos.write(buffer, 0, bytesRead); + totalRead += bytesRead; + } + byte[] data = baos.toByteArray(); + this.statement.bindBlob(parameterIndex, data); + } catch (IOException e) { + throw new SQLException("Error reading binary stream", e); + } } @Override 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 0a5c23097..939220e90 100644 --- a/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4PreparedStatementTest.java +++ b/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4PreparedStatementTest.java @@ -3,9 +3,15 @@ package tech.turso.jdbc4; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; import java.sql.*; import java.util.Properties; import org.junit.jupiter.api.BeforeEach; @@ -388,6 +394,206 @@ class JDBC4PreparedStatementTest { new java.math.BigDecimal(decimalText).stripTrailingZeros()); } + @Test + void testSetAsciiStream_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, 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_nullStream() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); + + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + + stmt.setAsciiStream(1, null, 0); + stmt.execute(); + + PreparedStatement stmt2 = connection.prepareStatement("SELECT col FROM test"); + ResultSet rs = stmt2.executeQuery(); + + assertTrue(rs.next()); + assertNull(rs.getString(1)); + } + + @Test + void testSetAsciiStream_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, 10); + stmt.execute(); + + PreparedStatement stmt2 = connection.prepareStatement("SELECT col FROM test"); + ResultSet rs = stmt2.executeQuery(); + + assertTrue(rs.next()); + assertEquals("", rs.getString(1)); + } + + @Test + void testSetAsciiStream_negativeLength() 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); + + assertThrows(SQLException.class, () -> stmt.setAsciiStream(1, stream, -1)); + } + + @Test + void testSetBinaryStream_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, 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_nullStream() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); + + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + + stmt.setBinaryStream(1, null, 0); + stmt.execute(); + + PreparedStatement stmt2 = connection.prepareStatement("SELECT col FROM test"); + ResultSet rs = stmt2.executeQuery(); + + assertTrue(rs.next()); + assertNull(rs.getBytes(1)); + } + + @Test + void testSetBinaryStream_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, 10); + 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_negativeLength() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); + + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + + byte[] data = {1, 2, 3}; + InputStream stream = new ByteArrayInputStream(data); + + assertThrows(SQLException.class, () -> stmt.setBinaryStream(1, stream, -1)); + } + + @Test + void testSetUnicodeStream_insert_and_select() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); + + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + + String text = "안녕하세요😊 Hello🌏"; + byte[] bytes = text.getBytes(StandardCharsets.UTF_8); + InputStream stream = new ByteArrayInputStream(bytes); + + stmt.setUnicodeStream(1, stream, 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 testSetUnicodeStream_nullStream() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); + + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + + stmt.setUnicodeStream(1, null, 0); + stmt.execute(); + + PreparedStatement stmt2 = connection.prepareStatement("SELECT col FROM test"); + ResultSet rs = stmt2.executeQuery(); + + assertTrue(rs.next()); + assertNull(rs.getString(1)); + } + + @Test + void testSetUnicodeStream_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.setUnicodeStream(1, empty, 10); + stmt.execute(); + + PreparedStatement stmt2 = connection.prepareStatement("SELECT col FROM test"); + ResultSet rs = stmt2.executeQuery(); + + assertTrue(rs.next()); + assertEquals("", rs.getString(1)); + } + + @Test + void testSetUnicodeStream_negativeLength() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col TEXT)").execute(); + + PreparedStatement stmt = connection.prepareStatement("INSERT INTO test (col) VALUES (?)"); + + String text = "테스트"; + byte[] bytes = text.getBytes(StandardCharsets.UTF_8); + InputStream stream = new ByteArrayInputStream(bytes); + + assertThrows(SQLException.class, () -> stmt.setUnicodeStream(1, stream, -5)); + } + @Test void execute_insert_should_return_number_of_inserted_elements() throws Exception { connection.prepareStatement("CREATE TABLE test (col INTEGER)").execute();