Skip to content

Commit 73139ae

Browse files
authored
Merge pull request #168 from staticlibs/spatial_types
Support spatial extension types
2 parents ce82b1d + 97bdc34 commit 73139ae

File tree

9 files changed

+454
-76
lines changed

9 files changed

+454
-76
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,7 @@ add_library(duckdb_java SHARED
560560
src/jni/duckdb_java.cpp
561561
src/jni/functions.cpp
562562
src/jni/refs.cpp
563+
src/jni/types.cpp
563564
src/jni/util.cpp
564565
${DUCKDB_SRC_FILES})
565566

src/jni/duckdb_java.cpp

+3-41
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "duckdb/parser/parsed_data/create_type_info.hpp"
1515
#include "functions.hpp"
1616
#include "refs.hpp"
17+
#include "types.hpp"
1718
#include "util.hpp"
1819

1920
#include <cstdint>
@@ -396,42 +397,6 @@ void _duckdb_jdbc_free_result(JNIEnv *env, jclass, jobject res_ref_buf) {
396397
}
397398
}
398399

399-
static std::string type_to_jduckdb_type(LogicalType logical_type) {
400-
switch (logical_type.id()) {
401-
case LogicalTypeId::DECIMAL: {
402-
403-
uint8_t width = 0;
404-
uint8_t scale = 0;
405-
logical_type.GetDecimalProperties(width, scale);
406-
std::string width_scale = std::to_string(width) + std::string(";") + std::to_string(scale);
407-
408-
auto physical_type = logical_type.InternalType();
409-
switch (physical_type) {
410-
case PhysicalType::INT16: {
411-
string res = std::string("DECIMAL16;") + width_scale;
412-
return res;
413-
}
414-
case PhysicalType::INT32: {
415-
string res = std::string("DECIMAL32;") + width_scale;
416-
return res;
417-
}
418-
case PhysicalType::INT64: {
419-
string res = std::string("DECIMAL64;") + width_scale;
420-
return res;
421-
}
422-
case PhysicalType::INT128: {
423-
string res = std::string("DECIMAL128;") + width_scale;
424-
return res;
425-
}
426-
default:
427-
return std::string("no physical type found");
428-
}
429-
} break;
430-
default:
431-
return logical_type.ToString();
432-
}
433-
}
434-
435400
static jobject build_meta(JNIEnv *env, size_t column_count, size_t n_param, const duckdb::vector<string> &names,
436401
const duckdb::vector<LogicalType> &types, StatementProperties properties) {
437402
auto name_array = env->NewObjectArray(column_count, J_String, nullptr);
@@ -515,6 +480,7 @@ jobjectArray _duckdb_jdbc_fetch(JNIEnv *env, jclass, jobject res_ref_buf, jobjec
515480

516481
return vec_array;
517482
}
483+
518484
jobject ProcessVector(JNIEnv *env, Connection *conn_ref, Vector &vec, idx_t row_count) {
519485
auto type_str = env->NewStringUTF(type_to_jduckdb_type(vec.GetType()).c_str());
520486
// construct nullmask
@@ -530,11 +496,7 @@ jobject ProcessVector(JNIEnv *env, Connection *conn_ref, Vector &vec, idx_t row_
530496
jobject constlen_data = nullptr;
531497
jobjectArray varlen_data = nullptr;
532498

533-
// this allows us to treat aliased (usually extension) types as strings
534-
auto type = vec.GetType();
535-
auto type_id = type.HasAlias() ? LogicalTypeId::UNKNOWN : type.id();
536-
537-
switch (type_id) {
499+
switch (vec.GetType().id()) {
538500
case LogicalTypeId::BOOLEAN:
539501
constlen_data = env->NewDirectByteBuffer(FlatVector::GetData(vec), row_count * sizeof(bool));
540502
break;

src/jni/types.cpp

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#include "types.hpp"
2+
3+
#include <string>
4+
#include <vector>
5+
6+
std::string type_to_jduckdb_type(duckdb::LogicalType logical_type) {
7+
switch (logical_type.id()) {
8+
case duckdb::LogicalTypeId::DECIMAL: {
9+
10+
uint8_t width = 0;
11+
uint8_t scale = 0;
12+
logical_type.GetDecimalProperties(width, scale);
13+
std::string width_scale = std::to_string(width) + std::string(";") + std::to_string(scale);
14+
15+
auto physical_type = logical_type.InternalType();
16+
switch (physical_type) {
17+
case duckdb::PhysicalType::INT16: {
18+
std::string res = std::string("DECIMAL16;") + width_scale;
19+
return res;
20+
}
21+
case duckdb::PhysicalType::INT32: {
22+
std::string res = std::string("DECIMAL32;") + width_scale;
23+
return res;
24+
}
25+
case duckdb::PhysicalType::INT64: {
26+
std::string res = std::string("DECIMAL64;") + width_scale;
27+
return res;
28+
}
29+
case duckdb::PhysicalType::INT128: {
30+
std::string res = std::string("DECIMAL128;") + width_scale;
31+
return res;
32+
}
33+
default:
34+
return std::string("no physical type found");
35+
}
36+
} break;
37+
default:
38+
// JSON requires special handling because it is mapped
39+
// to JsonNode class
40+
if (logical_type.IsJSONType()) {
41+
return logical_type.GetAlias();
42+
}
43+
return duckdb::EnumUtil::ToString(logical_type.id());
44+
}
45+
}

src/jni/types.hpp

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#pragma once
2+
3+
#include "duckdb.hpp"
4+
5+
std::string type_to_jduckdb_type(duckdb::LogicalType logical_type);

src/test/java/org/duckdb/TestDuckDBJDBC.java

+13-26
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,7 @@
1111
import static org.duckdb.DuckDBDriver.DUCKDB_USER_AGENT_PROPERTY;
1212
import static org.duckdb.DuckDBDriver.JDBC_STREAM_RESULTS;
1313
import static org.duckdb.DuckDBTimestamp.localDateTimeFromTimestamp;
14-
import static org.duckdb.test.Assertions.assertEquals;
15-
import static org.duckdb.test.Assertions.assertFalse;
16-
import static org.duckdb.test.Assertions.assertNotNull;
17-
import static org.duckdb.test.Assertions.assertNull;
18-
import static org.duckdb.test.Assertions.assertThrows;
19-
import static org.duckdb.test.Assertions.assertThrowsMaybe;
20-
import static org.duckdb.test.Assertions.assertTrue;
21-
import static org.duckdb.test.Assertions.fail;
14+
import static org.duckdb.test.Assertions.*;
2215
import static org.duckdb.test.Runner.runTests;
2316

2417
import java.math.BigDecimal;
@@ -1302,7 +1295,7 @@ public static void test_lots_of_decimals() throws Exception {
13021295
conn.close();
13031296
}
13041297

1305-
public static void test_big_data() throws Exception {
1298+
public static void test_lots_of_big_data() throws Exception {
13061299
Connection conn = DriverManager.getConnection(JDBC_URL);
13071300
Statement stmt = conn.createStatement();
13081301
int rows = 10000;
@@ -4187,21 +4180,6 @@ private static Map<String, Object> structToMap(DuckDBStruct actual) throws SQLEx
41874180
return result;
41884181
}
41894182

4190-
private static <T> void assertListsEqual(List<T> actual, List<T> expected) throws Exception {
4191-
assertListsEqual(actual, expected, "");
4192-
}
4193-
4194-
private static <T> void assertListsEqual(List<T> actual, List<T> expected, String label) throws Exception {
4195-
assertEquals(actual.size(), expected.size());
4196-
4197-
ListIterator<T> itera = actual.listIterator();
4198-
ListIterator<T> itere = expected.listIterator();
4199-
4200-
while (itera.hasNext()) {
4201-
assertEquals(itera.next(), itere.next(), label);
4202-
}
4203-
}
4204-
42054183
public static void test_cancel() throws Exception {
42064184
ExecutorService service = Executors.newFixedThreadPool(1);
42074185
try (Connection conn = DriverManager.getConnection(JDBC_URL); Statement stmt = conn.createStatement()) {
@@ -4300,7 +4278,7 @@ public static void test_invalid_execute_calls() throws Exception {
43004278
}
43014279
}
43024280

4303-
public static void test_race() throws Exception {
4281+
public static void test_lots_of_races() throws Exception {
43044282
try (Connection connection = DriverManager.getConnection(JDBC_URL)) {
43054283
ExecutorService executorService = Executors.newFixedThreadPool(10);
43064284

@@ -4799,6 +4777,15 @@ public static void test_typed_connection_properties() throws Exception {
47994777
}
48004778

48014779
public static void main(String[] args) throws Exception {
4802-
System.exit(runTests(args, TestDuckDBJDBC.class, TestExtensionTypes.class));
4780+
String arg1 = args.length > 0 ? args[0] : "";
4781+
final int statusCode;
4782+
if (arg1.startsWith("Test")) {
4783+
Class<?> clazz = Class.forName("org.duckdb." + arg1);
4784+
statusCode = runTests(new String[0], clazz);
4785+
} else {
4786+
// extension installation fails on CI, Spatial test is temporary disabled
4787+
statusCode = runTests(args, TestDuckDBJDBC.class, TestExtensionTypes.class /*, TestSpatial.class */);
4788+
}
4789+
System.exit(statusCode);
48034790
}
48044791
}

src/test/java/org/duckdb/TestExtensionTypes.java

+9-8
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,7 @@
33
import static org.duckdb.TestDuckDBJDBC.JDBC_URL;
44
import static org.duckdb.test.Assertions.assertEquals;
55

6-
import java.sql.Connection;
7-
import java.sql.DriverManager;
8-
import java.sql.ResultSet;
9-
import java.sql.ResultSetMetaData;
10-
import java.sql.Statement;
11-
import java.sql.Types;
6+
import java.sql.*;
127

138
public class TestExtensionTypes {
149

@@ -21,8 +16,14 @@ public static void test_extension_type() throws Exception {
2116
try (ResultSet rs = stmt.executeQuery(
2217
"SELECT {\"hello\": 'foo', \"world\": 'bar'}::test_type, '\\xAA'::byte_test_type")) {
2318
rs.next();
24-
assertEquals(rs.getObject(1), "{'hello': foo, 'world': bar}");
25-
assertEquals(rs.getObject(2), "\\xAA");
19+
Struct struct = (Struct) rs.getObject(1);
20+
Object[] attrs = struct.getAttributes();
21+
assertEquals(attrs[0], "foo");
22+
assertEquals(attrs[1], "bar");
23+
Blob blob = rs.getBlob(2);
24+
byte[] bytes = blob.getBytes(1, (int) blob.length());
25+
assertEquals(bytes.length, 1);
26+
assertEquals(bytes[0] & 0xff, 0xaa);
2627
}
2728
}
2829
}

0 commit comments

Comments
 (0)