From 58098ef9abb475c891933beee2c6e41c2f582124 Mon Sep 17 00:00:00 2001 From: Magnus Persson <1431685+magnusp@users.noreply.github.com> Date: Thu, 22 Oct 2026 00:03:42 +0200 Subject: [PATCH] fix(jdbc): convert getBoolean through integer coercion (#32) --- .../magnusp/libsql/jdbc/LibsqlResultSet.java | 15 ++--- .../jdbc/LibsqlResultSetGetBooleanTest.java | 65 +++++++++++++++++++ 2 files changed, 72 insertions(+), 8 deletions(-) create mode 100644 libsql-jdbc/src/test/java/com/github/magnusp/libsql/jdbc/LibsqlResultSetGetBooleanTest.java diff --git a/libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlResultSet.java b/libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlResultSet.java index 6d58992..54a7ca2 100644 --- a/libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlResultSet.java +++ b/libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlResultSet.java @@ -123,16 +123,15 @@ public String getString(int columnIndex) throws SQLException { }; } + /** + * Converts every storage class through {@link #getInt(int)}, matching sqlite-jdbc. NULL and values + * whose integer coercion is zero are false: non-numeric text such as {@code "true"}, REAL values + * between -1 and 1, and empty or non-numeric blobs. Integers, REAL values truncated to a nonzero + * integer, and text or blob numeric prefixes such as {@code "2"} or {@code x'313233'} are true. + */ @Override public boolean getBoolean(int columnIndex) throws SQLException { - Value val = getCurrentValue(columnIndex); - return switch (val) { - case Value.Null n -> false; - case Value.Integer i -> i.value() != 0; - case Value.Float f -> f.value() != 0.0; - case Value.Text t -> "true".equalsIgnoreCase(t.value()) || "1".equals(t.value()); - case Value.Blob b -> b.bytes().length > 0 && b.bytes()[0] != 0; - }; + return getInt(columnIndex) != 0; } @Override diff --git a/libsql-jdbc/src/test/java/com/github/magnusp/libsql/jdbc/LibsqlResultSetGetBooleanTest.java b/libsql-jdbc/src/test/java/com/github/magnusp/libsql/jdbc/LibsqlResultSetGetBooleanTest.java new file mode 100644 index 0000000..9bba86e --- /dev/null +++ b/libsql-jdbc/src/test/java/com/github/magnusp/libsql/jdbc/LibsqlResultSetGetBooleanTest.java @@ -0,0 +1,65 @@ +package com.github.magnusp.libsql.jdbc; + +import com.github.magnusp.libsql.hrana.codec.Col; +import com.github.magnusp.libsql.hrana.codec.Row; +import com.github.magnusp.libsql.hrana.codec.StmtResult; +import com.github.magnusp.libsql.hrana.codec.Value; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.nio.charset.StandardCharsets; +import java.sql.SQLException; +import java.util.List; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +class LibsqlResultSetGetBooleanTest { + + static Stream storageClasses() { + return Stream.of( + Arguments.of("text 'true'", new Value.Text("true"), false), + Arguments.of("text '0'", new Value.Text("0"), false), + Arguments.of("text '2'", new Value.Text("2"), true), + Arguments.of("real 0.5", new Value.Float(0.5), false), + Arguments.of("real 2.0", new Value.Float(2.0), true), + Arguments.of( + "blob x'313233'", + new Value.Blob("123".getBytes(StandardCharsets.UTF_8)), + true), + Arguments.of("null", new Value.Null(), false)); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("storageClasses") + void getBooleanConvertsThroughIntegerLikeXerial(String name, Value value, boolean expected) + throws SQLException { + LibsqlResultSet rs = resultSet(value); + assertThat(rs.next()).isTrue(); + + assertThat(rs.getBoolean(1)).isEqualTo(expected); + assertThat(rs.getBoolean(1)).isEqualTo(rs.getInt(1) != 0); + } + + @Test + void reproductionValuesMatchSqliteJdbc() throws SQLException { + LibsqlResultSet rs = resultSet( + new Value.Text("true"), + new Value.Text("2"), + new Value.Float(0.5)); + + assertThat(rs.next()).isTrue(); + + assertThat(rs.getBoolean(1)).isFalse(); + assertThat(rs.getBoolean(2)).isTrue(); + assertThat(rs.getBoolean(3)).isFalse(); + } + + private static LibsqlResultSet resultSet(Value... values) { + List cols = Stream.generate(() -> new Col("c", null)).limit(values.length).toList(); + StmtResult result = new StmtResult(cols, List.of(new Row(List.of(values))), 1, null); + return new LibsqlResultSet(null, result); + } +}