From f45bf88bb59b5baa0db5ca628d319fc5c9e91c41 Mon Sep 17 00:00:00 2001 From: Alex Kasko Date: Fri, 24 Apr 2026 12:08:26 +0100 Subject: [PATCH] Support GEOMETRY in Appender This PR adds support for appending to `GEOMETRY` columns. `GEOMETRY` values need to be passed as `byte[]` blobs in Well-Known Binary (WKB) format. Fixes: #671 --- src/main/java/org/duckdb/DuckDBAppender.java | 4 ++- src/main/java/org/duckdb/DuckDBBindings.java | 6 +++- src/test/java/org/duckdb/TestAppender.java | 35 ++++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/duckdb/DuckDBAppender.java b/src/main/java/org/duckdb/DuckDBAppender.java index 38371ae2b..7626ce3a0 100644 --- a/src/main/java/org/duckdb/DuckDBAppender.java +++ b/src/main/java/org/duckdb/DuckDBAppender.java @@ -41,6 +41,7 @@ public class DuckDBAppender implements AutoCloseable { supportedTypes.add(DUCKDB_TYPE_VARCHAR.typeId); supportedTypes.add(DUCKDB_TYPE_BLOB.typeId); + supportedTypes.add(DUCKDB_TYPE_GEOMETRY.typeId); supportedTypes.add(DUCKDB_TYPE_DATE.typeId); supportedTypes.add(DUCKDB_TYPE_TIME.typeId); @@ -71,7 +72,8 @@ public class DuckDBAppender implements AutoCloseable { private static final CAPIType[] timestampMicrosTypes = new CAPIType[] {DUCKDB_TYPE_TIMESTAMP, DUCKDB_TYPE_TIMESTAMP_TZ}; private static final CAPIType[] collectionTypes = new CAPIType[] {DUCKDB_TYPE_ARRAY, DUCKDB_TYPE_LIST}; - private static final CAPIType[] varlenTypes = new CAPIType[] {DUCKDB_TYPE_VARCHAR, DUCKDB_TYPE_BLOB}; + private static final CAPIType[] varlenTypes = + new CAPIType[] {DUCKDB_TYPE_VARCHAR, DUCKDB_TYPE_BLOB, DUCKDB_TYPE_GEOMETRY}; private static final CAPIType[] varcharOrEnumTypes = new CAPIType[] {DUCKDB_TYPE_VARCHAR, DUCKDB_TYPE_ENUM}; private static final int STRING_MAX_INLINE_BYTES = 12; diff --git a/src/main/java/org/duckdb/DuckDBBindings.java b/src/main/java/org/duckdb/DuckDBBindings.java index 85826c721..18264648d 100644 --- a/src/main/java/org/duckdb/DuckDBBindings.java +++ b/src/main/java/org/duckdb/DuckDBBindings.java @@ -356,7 +356,11 @@ enum CAPIType { // enum type, only useful as logical type DUCKDB_TYPE_STRING_LITERAL(37), // enum type, only useful as logical type - DUCKDB_TYPE_INTEGER_LITERAL(38); + DUCKDB_TYPE_INTEGER_LITERAL(38), + // duckdb_time_ns (nanoseconds) + // DUCKDB_TYPE_TIME_NS = 39, + // WKB blob + DUCKDB_TYPE_GEOMETRY(40, 16); final int typeId; final long widthBytes; diff --git a/src/test/java/org/duckdb/TestAppender.java b/src/test/java/org/duckdb/TestAppender.java index 01d4b4e9a..d404aa24c 100644 --- a/src/test/java/org/duckdb/TestAppender.java +++ b/src/test/java/org/duckdb/TestAppender.java @@ -991,4 +991,39 @@ public static void test_lots_appender_concurrent_flush() throws Exception { } } } + + public static void test_appender_basic_geometry() throws Exception { + byte[] geometryBytes = + new byte[] {1, 1, 0, 0, 0, -51, -52, -52, -52, -52, -116, 68, 64, -102, -103, -103, -103, -103, 25, 69, 64}; + try (DuckDBConnection conn = DriverManager.getConnection(JDBC_URL).unwrap(DuckDBConnection.class); + Statement stmt = conn.createStatement()) { + stmt.execute("CREATE TABLE tab1 (col1 INT, col2 GEOMETRY)"); + stmt.execute("INSERT INTO tab1 VALUES (0, 'POINT(41.1 42.2)'::GEOMETRY)"); + try (PreparedStatement ps = conn.prepareStatement("INSERT INTO tab1 VALUES(?, ST_GeomFromWKB(?))")) { + ps.setInt(1, 1); + ps.setBytes(2, geometryBytes); + ps.execute(); + } + try (DuckDBAppender appender = conn.createAppender("tab1")) { + appender.beginRow().append(2).append(geometryBytes).endRow().flush(); + } + try (ResultSet rs = stmt.executeQuery("FROM tab1 ORDER BY col1")) { + for (int i = 0; i < 3; i++) { + assertTrue(rs.next()); + assertEquals(rs.getInt(1), i); + byte[] bytes = rs.getBytes(2); + List list = new ArrayList<>(); + for (byte b : bytes) { + list.add(b); + } + List expected = new ArrayList<>(); + for (byte b : geometryBytes) { + expected.add(b); + } + assertListsEqual(expected, list); + } + assertFalse(rs.next()); + } + } + } }