Skip to content

Commit 3e688d2

Browse files
committed
Support typed configuration options
This change adds support for all primitive types (and additionally for a list of strings) for configuration values that can be passed to `DriverManager.getConnection(url, conf)`. To be able to handle config in a separate compilation unit, cached references and common utilities are moved to separate files. There are a few more check added to the Java references collection code, but it still kept largely the same. Additionally the `file_system` field of the `DBConfig` is filled manually, it was necessary because this field is used for checks when incoming file-system related options are set. Testing: new test added that sets typed config values and checks that they are set with `current_setting()`. Fixes: duckdb#137
1 parent a179e86 commit 3e688d2

File tree

12 files changed

+687
-378
lines changed

12 files changed

+687
-378
lines changed

CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,13 @@ if(MSVC)
145145
set(DUCKDB_SYSTEM_LIBS ${DUCKDB_SYSTEM_LIBS} ws2_32 rstrtmgr bcrypt)
146146
endif()
147147

148-
add_library(duckdb_java SHARED src/jni/duckdb_java.cpp src/jni/functions.cpp ${DUCKDB_SRC_FILES})
148+
add_library(duckdb_java SHARED
149+
src/jni/config.cpp
150+
src/jni/duckdb_java.cpp
151+
src/jni/functions.cpp
152+
src/jni/refs.cpp
153+
src/jni/util.cpp
154+
${DUCKDB_SRC_FILES})
149155
target_compile_options(duckdb_java PRIVATE -fexceptions)
150156
target_link_libraries(duckdb_java duckdb-native )
151157
target_link_libraries(duckdb_java ${DUCKDB_SYSTEM_LIBS})

CMakeLists.txt.in

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,13 @@ if(MSVC)
145145
set(DUCKDB_SYSTEM_LIBS ${DUCKDB_SYSTEM_LIBS} ws2_32 rstrtmgr bcrypt)
146146
endif()
147147

148-
add_library(duckdb_java SHARED src/jni/duckdb_java.cpp src/jni/functions.cpp ${DUCKDB_SRC_FILES})
148+
add_library(duckdb_java SHARED
149+
src/jni/config.cpp
150+
src/jni/duckdb_java.cpp
151+
src/jni/functions.cpp
152+
src/jni/refs.cpp
153+
src/jni/util.cpp
154+
${DUCKDB_SRC_FILES})
149155
target_compile_options(duckdb_java PRIVATE -fexceptions)
150156
target_link_libraries(duckdb_java duckdb-native ${LIBRARY_FILES})
151157
target_link_libraries(duckdb_java ${DUCKDB_SYSTEM_LIBS})

scripts/format.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@
1111
if args.check:
1212
template += ['--dry-run', '--Werror']
1313

14-
for name in ['src/jni/duckdb_java.cpp'] + glob('src/**/*.java', recursive=True):
14+
hpp_files = set(glob('src/jni/*.hpp'))
15+
hpp_files.remove('src/jni/functions.hpp')
16+
cpp_files = set(glob('src/jni/*.cpp'))
17+
cpp_files.remove('src/jni/functions.cpp')
18+
java_files = set(glob('src/**/*.java', recursive=True))
19+
20+
for name in [*hpp_files] + [*cpp_files] + [*java_files]:
1521
print('Formatting', name)
1622
check_call(template + [name])
1723

src/jni/config.cpp

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#include "config.hpp"
2+
3+
#include "duckdb/common/virtual_file_system.hpp"
4+
#include "refs.hpp"
5+
#include "util.hpp"
6+
7+
#include <stdexcept>
8+
#include <string>
9+
10+
static duckdb::Value jobj_to_value(JNIEnv *env, const std::string &key, jobject jval) {
11+
// On the right in comments are all types that are currently present
12+
// in DuckDB config.
13+
if (nullptr == jval) {
14+
return duckdb::Value();
15+
16+
} else if (env->IsInstanceOf(jval, J_Bool)) { // BOOLEAN
17+
jboolean val = env->CallBooleanMethod(jval, J_Bool_booleanValue);
18+
check_java_exception_and_rethrow(env);
19+
return duckdb::Value::BOOLEAN(val);
20+
21+
} else if (env->IsInstanceOf(jval, J_Byte)) { // UBIGINT
22+
jbyte val = env->CallByteMethod(jval, J_Byte_byteValue);
23+
check_java_exception_and_rethrow(env);
24+
return duckdb::Value::TINYINT(val);
25+
26+
} else if (env->IsInstanceOf(jval, J_Short)) { // UBIGINT
27+
jshort val = env->CallShortMethod(jval, J_Short_shortValue);
28+
check_java_exception_and_rethrow(env);
29+
return duckdb::Value::SMALLINT(val);
30+
31+
} else if (env->IsInstanceOf(jval, J_Int)) { // UBIGINT
32+
jint val = env->CallIntMethod(jval, J_Int_intValue);
33+
check_java_exception_and_rethrow(env);
34+
return duckdb::Value::INTEGER(val);
35+
36+
} else if (env->IsInstanceOf(jval, J_Long)) { // UBIGINT
37+
jlong val = env->CallLongMethod(jval, J_Long_longValue);
38+
check_java_exception_and_rethrow(env);
39+
return duckdb::Value::BIGINT(val);
40+
41+
} else if (env->IsInstanceOf(jval, J_Float)) { // FLOAT
42+
jfloat val = env->CallFloatMethod(jval, J_Float_floatValue);
43+
check_java_exception_and_rethrow(env);
44+
return duckdb::Value::FLOAT(val);
45+
46+
} else if (env->IsInstanceOf(jval, J_Double)) { // DOUBLE
47+
jdouble val = env->CallDoubleMethod(jval, J_Double_doubleValue);
48+
check_java_exception_and_rethrow(env);
49+
return duckdb::Value::DOUBLE(val);
50+
51+
} else if (env->IsInstanceOf(jval, J_String)) { // VARCHAR
52+
std::string val = jstring_to_string(env, reinterpret_cast<jstring>(jval));
53+
return duckdb::Value(val);
54+
55+
} else if (env->IsInstanceOf(jval, J_List)) { // VARCHAR[]
56+
jobject iterator = env->CallObjectMethod(jval, J_List_iterator);
57+
check_java_exception_and_rethrow(env);
58+
59+
duckdb::vector<duckdb::Value> vec;
60+
while (env->CallBooleanMethod(iterator, J_Iterator_hasNext)) {
61+
check_java_exception_and_rethrow(env);
62+
jobject list_entry = env->CallObjectMethod(iterator, J_Iterator_next);
63+
check_java_exception_and_rethrow(env);
64+
// all list entries are coalesced to string
65+
jstring jstr = reinterpret_cast<jstring>(env->CallObjectMethod(list_entry, J_Object_toString));
66+
check_java_exception_and_rethrow(env);
67+
std::string sval = jstring_to_string(env, jstr);
68+
duckdb::Value val(std::move(sval));
69+
vec.push_back(std::move(val));
70+
}
71+
return duckdb::Value::LIST(duckdb::LogicalType::VARCHAR, std::move(vec));
72+
73+
} else {
74+
// coalesce to string the entry with an unknown type
75+
jstring jstr = reinterpret_cast<jstring>(env->CallObjectMethod(jval, J_Object_toString));
76+
check_java_exception_and_rethrow(env);
77+
std::string str = jstring_to_string(env, jstr);
78+
return duckdb::Value(str);
79+
}
80+
}
81+
82+
std::unique_ptr<duckdb::DBConfig> create_db_config(JNIEnv *env, jboolean read_only, jobject java_config) {
83+
auto config = std::unique_ptr<duckdb::DBConfig>(new duckdb::DBConfig());
84+
// Required for setting like 'allowed_directories' that use
85+
// file separator when checking the property value.
86+
config->file_system = duckdb::make_uniq<duckdb::VirtualFileSystem>();
87+
config->SetOptionByName("duckdb_api", "java");
88+
config->AddExtensionOption(
89+
"jdbc_stream_results",
90+
"Whether to stream results. Only one ResultSet on a connection can be open at once when true",
91+
duckdb::LogicalType::BOOLEAN);
92+
if (read_only) {
93+
config->options.access_mode = duckdb::AccessMode::READ_ONLY;
94+
}
95+
jobject entry_set = env->CallObjectMethod(java_config, J_Map_entrySet);
96+
check_java_exception_and_rethrow(env);
97+
jobject iterator = env->CallObjectMethod(entry_set, J_Set_iterator);
98+
check_java_exception_and_rethrow(env);
99+
100+
while (env->CallBooleanMethod(iterator, J_Iterator_hasNext)) {
101+
check_java_exception_and_rethrow(env);
102+
jobject pair = env->CallObjectMethod(iterator, J_Iterator_next);
103+
check_java_exception_and_rethrow(env);
104+
jobject key = env->CallObjectMethod(pair, J_Entry_getKey);
105+
check_java_exception_and_rethrow(env);
106+
jobject value = env->CallObjectMethod(pair, J_Entry_getValue);
107+
check_java_exception_and_rethrow(env);
108+
109+
jstring key_jstr = reinterpret_cast<jstring>(env->CallObjectMethod(key, J_Object_toString));
110+
check_java_exception_and_rethrow(env);
111+
std::string key_str = jstring_to_string(env, key_jstr);
112+
113+
duckdb::Value dvalue = jobj_to_value(env, key_str, value);
114+
115+
try {
116+
config->SetOptionByName(key_str, dvalue);
117+
} catch (const std::exception &e) {
118+
duckdb::ErrorData error(e);
119+
throw duckdb::CatalogException("Failed to set configuration option \"%s\", error: %s", key_str,
120+
error.RawMessage());
121+
}
122+
}
123+
124+
return config;
125+
}

src/jni/config.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#pragma once
2+
3+
#include "duckdb.hpp"
4+
5+
#include <jni.h>
6+
#include <memory>
7+
8+
std::unique_ptr<duckdb::DBConfig> create_db_config(JNIEnv *env, jboolean read_only, jobject java_config);

0 commit comments

Comments
 (0)