mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-23 09:54:26 +01:00
Merge 'bindings/java: implement stream binding methods (int, InputStream, int) in JDBC4PreparedStatement' from Orange banana
## Purpose * Implement `setAsciiStream(int, InputStream, int)`, `setUnicodeStream(int, InputStream, int)`, and `setBinaryStream(int, InputStream, int)` methods in JDBC4PreparedStatemen ## Changes * `setAsciiStream(int, InputStream, int)`: Reads ASCII bytes, converts to `String` using `US_ASCII` and binds with `bindText()`. * `setUnicodeStream(int, InputStream, int)`: Reads bytes as `UTF-8` encoded text and binds with `bindText()`. * `setBinaryStream(int, InputStream, int)`: Reads raw bytes and binds with `bindBlob()`. * Added consistent error handling and validation * null stream - `bindNull()` * Negative length - throws `SQLException` * Empty stream - Empty String or Empty Array * I/O errors - throw `SQLException` * Ensures consistency between `setXxxStream` and `getXxxStream` methods, so data written and read use the same encoding. ## Related Issue * #615 Reviewed-by: Kim Seon Woo (@seonWKim) Closes #3917
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user