Skip to content

Commit 3dcfe85

Browse files
committed
Implement ParameterMetadata
This PR brings in the change that was contributed earlier by `eramitmittal` in duckdb#114 but was not merged. That change is rebased onto the latest `main`with minimal adjustetments. Testing: in addition to the `DECIMAL` test from original PR more tests are added to cover all other column types. Fixes: duckdb#121 Co-authored-by: eramitmittal
1 parent 0f935a6 commit 3dcfe85

9 files changed

+274
-27
lines changed

CMakeLists.txt

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

CMakeLists.txt.in

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ add_library(duckdb_java SHARED
103103
src/jni/duckdb_java.cpp
104104
src/jni/functions.cpp
105105
src/jni/refs.cpp
106+
src/jni/types.cpp
106107
src/jni/util.cpp
107108
${DUCKDB_SRC_FILES})
108109

src/jni/duckdb_java.cpp

+31-5
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,8 @@ void _duckdb_jdbc_free_result(JNIEnv *env, jclass, jobject res_ref_buf) {
400400
}
401401

402402
static jobject build_meta(JNIEnv *env, size_t column_count, size_t n_param, const duckdb::vector<string> &names,
403-
const duckdb::vector<LogicalType> &types, StatementProperties properties) {
403+
const duckdb::vector<LogicalType> &types, StatementProperties properties,
404+
const duckdb::vector<LogicalType> &param_types) {
404405
auto name_array = env->NewObjectArray(column_count, J_String, nullptr);
405406
auto type_array = env->NewObjectArray(column_count, J_String, nullptr);
406407
auto type_detail_array = env->NewObjectArray(column_count, J_String, nullptr);
@@ -420,10 +421,26 @@ static jobject build_meta(JNIEnv *env, size_t column_count, size_t n_param, cons
420421
env->NewStringUTF(type_to_jduckdb_type(types[col_idx]).c_str()));
421422
}
422423

424+
auto param_type_array = env->NewObjectArray(n_param, J_String, nullptr);
425+
auto param_type_detail_array = env->NewObjectArray(n_param, J_String, nullptr);
426+
427+
for (idx_t param_idx = 0; param_idx < n_param; param_idx++) {
428+
std::string param_name;
429+
if (param_types[param_idx].id() == LogicalTypeId::ENUM) {
430+
param_name = "ENUM";
431+
} else {
432+
param_name = param_types[param_idx].ToString();
433+
}
434+
435+
env->SetObjectArrayElement(param_type_array, param_idx, env->NewStringUTF(param_name.c_str()));
436+
env->SetObjectArrayElement(param_type_detail_array, param_idx,
437+
env->NewStringUTF(type_to_jduckdb_type(param_types[param_idx]).c_str()));
438+
}
439+
423440
auto return_type = env->NewStringUTF(StatementReturnTypeToString(properties.return_type).c_str());
424441

425442
return env->NewObject(J_DuckResultSetMeta, J_DuckResultSetMeta_init, n_param, column_count, name_array, type_array,
426-
type_detail_array, return_type);
443+
type_detail_array, return_type, param_type_array, param_type_detail_array);
427444
}
428445

429446
jobject _duckdb_jdbc_query_result_meta(JNIEnv *env, jclass, jobject res_ref_buf) {
@@ -433,9 +450,11 @@ jobject _duckdb_jdbc_query_result_meta(JNIEnv *env, jclass, jobject res_ref_buf)
433450
}
434451
auto &result = res_ref->res;
435452

436-
auto n_param = -1; // no params now
453+
auto n_param = 0; // no params now
454+
duckdb::vector<LogicalType> param_types(n_param);
437455

438-
return build_meta(env, result->ColumnCount(), n_param, result->names, result->types, result->properties);
456+
return build_meta(env, result->ColumnCount(), n_param, result->names, result->types, result->properties,
457+
param_types);
439458
}
440459

441460
jobject _duckdb_jdbc_prepared_statement_meta(JNIEnv *env, jclass, jobject stmt_ref_buf) {
@@ -447,9 +466,16 @@ jobject _duckdb_jdbc_prepared_statement_meta(JNIEnv *env, jclass, jobject stmt_r
447466

448467
auto &stmt = stmt_ref->stmt;
449468
auto n_param = stmt->named_param_map.size();
469+
duckdb::vector<LogicalType> param_types(n_param);
470+
if (n_param > 0) {
471+
auto expected_parameter_types = stmt->GetExpectedParameterTypes();
472+
for (auto &it : stmt->named_param_map) {
473+
param_types[it.second - 1] = expected_parameter_types[it.first];
474+
}
475+
}
450476

451477
return build_meta(env, stmt->ColumnCount(), n_param, stmt->GetNames(), stmt->GetTypes(),
452-
stmt->GetStatementProperties());
478+
stmt->GetStatementProperties(), param_types);
453479
}
454480

455481
jobject ProcessVector(JNIEnv *env, Connection *conn_ref, Vector &vec, idx_t row_count);

src/jni/refs.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,9 @@ void create_refs(JNIEnv *env) {
239239
J_Decimal_longValue = get_method_id(env, J_Decimal, "longValue", "()J");
240240

241241
J_DuckResultSetMeta = make_class_ref(env, "org/duckdb/DuckDBResultSetMetaData");
242-
J_DuckResultSetMeta_init =
243-
get_method_id(env, J_DuckResultSetMeta, "<init>",
244-
"(II[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V");
242+
J_DuckResultSetMeta_init = env->GetMethodID(J_DuckResultSetMeta, "<init>",
243+
"(II[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/"
244+
"lang/String;[Ljava/lang/String;[Ljava/lang/String;)V");
245245

246246
J_DuckVector = make_class_ref(env, "org/duckdb/DuckDBVector");
247247

src/main/java/org/duckdb/DuckDBParameterMetaData.java

+46-14
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,20 @@
22

33
import java.sql.ParameterMetaData;
44
import java.sql.SQLException;
5-
import java.sql.SQLFeatureNotSupportedException;
65

76
public class DuckDBParameterMetaData implements ParameterMetaData {
8-
private DuckDBResultSetMetaData meta;
97

10-
public DuckDBParameterMetaData(DuckDBResultSetMetaData meta) {
11-
this.meta = meta;
8+
private int param_count;
9+
private DuckDBColumnType[] param_types;
10+
private DuckDBColumnTypeMetaData[] param_types_meta;
11+
private String[] param_types_string;
12+
13+
public DuckDBParameterMetaData(int param_count, String[] param_types_string, DuckDBColumnType[] param_types,
14+
DuckDBColumnTypeMetaData[] param_types_meta) {
15+
this.param_count = param_count;
16+
this.param_types_string = param_types_string;
17+
this.param_types = param_types;
18+
this.param_types_meta = param_types_meta;
1219
}
1320

1421
@Override
@@ -23,7 +30,7 @@ public boolean isWrapperFor(Class<?> iface) {
2330

2431
@Override
2532
public int getParameterCount() throws SQLException {
26-
return meta.param_count;
33+
return param_count;
2734
}
2835

2936
@Override
@@ -33,35 +40,60 @@ public int isNullable(int param) throws SQLException {
3340

3441
@Override
3542
public boolean isSigned(int param) throws SQLException {
36-
return true;
43+
if (param > param_count) {
44+
throw new SQLException("Parameter index out of bounds");
45+
}
46+
return DuckDBResultSetMetaData.is_signed(param_types[param - 1]);
3747
}
3848

3949
@Override
4050
public int getPrecision(int param) throws SQLException {
41-
throw new SQLFeatureNotSupportedException("getPrecision");
51+
if (param > param_count) {
52+
throw new SQLException("Parameter index out of bounds");
53+
}
54+
DuckDBColumnTypeMetaData typeMetaData = param_types_meta[param - 1];
55+
if (typeMetaData == null) {
56+
return 0;
57+
}
58+
59+
return typeMetaData.width;
4260
}
4361

4462
@Override
4563
public int getScale(int param) throws SQLException {
46-
throw new SQLFeatureNotSupportedException("getScale");
64+
if (param > param_count) {
65+
throw new SQLException("Parameter index out of bounds");
66+
}
67+
DuckDBColumnTypeMetaData typeMetaData = param_types_meta[param - 1];
68+
if (typeMetaData == null) {
69+
return 0;
70+
}
71+
72+
return typeMetaData.scale;
4773
}
4874

4975
@Override
5076
public int getParameterType(int param) throws SQLException {
51-
// TODO Auto-generated method stub
52-
return 0;
77+
if (param > param_count) {
78+
throw new SQLException("Parameter index out of bounds");
79+
}
80+
return DuckDBResultSetMetaData.type_to_int(param_types[param - 1]);
5381
}
5482

5583
@Override
5684
public String getParameterTypeName(int param) throws SQLException {
57-
// TODO Auto-generated method stub
58-
return null;
85+
if (param > param_count) {
86+
throw new SQLException("Parameter index out of bounds");
87+
}
88+
return param_types_string[param - 1];
5989
}
6090

6191
@Override
6292
public String getParameterClassName(int param) throws SQLException {
63-
// TODO Auto-generated method stub
64-
return null;
93+
if (param > param_count) {
94+
throw new SQLException("Parameter index out of bounds");
95+
}
96+
return DuckDBResultSetMetaData.type_to_javaString(param_types[param - 1]);
6597
}
6698

6799
@Override

src/main/java/org/duckdb/DuckDBPreparedStatement.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ public ParameterMetaData getParameterMetaData() throws SQLException {
233233
if (stmt_ref == null) {
234234
throw new SQLException("Prepare something first");
235235
}
236-
return new DuckDBParameterMetaData(meta);
236+
return meta.param_meta;
237237
}
238238

239239
@Override

src/main/java/org/duckdb/DuckDBResultSetMetaData.java

+50-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.math.BigDecimal;
44
import java.math.BigInteger;
5+
import java.sql.ParameterMetaData;
56
import java.sql.ResultSetMetaData;
67
import java.sql.SQLException;
78
import java.sql.Timestamp;
@@ -17,7 +18,9 @@
1718
public class DuckDBResultSetMetaData implements ResultSetMetaData {
1819

1920
public DuckDBResultSetMetaData(int param_count, int column_count, String[] column_names,
20-
String[] column_types_string, String[] column_types_details, String return_type) {
21+
String[] column_types_string, String[] column_types_details, String return_type,
22+
String[] param_types_string, String[] param_types_details) {
23+
2124
this.param_count = param_count;
2225
this.column_count = column_count;
2326
this.column_names = column_names;
@@ -41,6 +44,26 @@ public DuckDBResultSetMetaData(int param_count, int column_count, String[] colum
4144
}
4245
}
4346
this.column_types_meta = column_types_meta.toArray(new DuckDBColumnTypeMetaData[column_count]);
47+
48+
ArrayList<DuckDBColumnType> param_types_al = new ArrayList<DuckDBColumnType>(param_count);
49+
ArrayList<DuckDBColumnTypeMetaData> param_types_meta_al = new ArrayList<DuckDBColumnTypeMetaData>(param_count);
50+
51+
for (String param_type_string : param_types_string) {
52+
param_types_al.add(TypeNameToType(param_type_string));
53+
}
54+
DuckDBColumnType[] param_types = param_types_al.toArray(new DuckDBColumnType[param_count]);
55+
56+
for (String param_type_detail : param_types_details) {
57+
if (TypeNameToType(param_type_detail) == DuckDBColumnType.DECIMAL) {
58+
param_types_meta_al.add(DuckDBColumnTypeMetaData.parseColumnTypeMetadata(param_type_detail));
59+
} else {
60+
param_types_meta_al.add(null);
61+
}
62+
}
63+
DuckDBColumnTypeMetaData[] param_types_meta =
64+
param_types_meta_al.toArray(new DuckDBColumnTypeMetaData[param_count]);
65+
66+
this.param_meta = new DuckDBParameterMetaData(param_count, param_types_string, param_types, param_types_meta);
4467
}
4568

4669
public static DuckDBColumnType TypeNameToType(String type_name) {
@@ -75,6 +98,7 @@ public static DuckDBColumnType TypeNameToType(String type_name) {
7598
protected DuckDBColumnType[] column_types;
7699
protected DuckDBColumnTypeMetaData[] column_types_meta;
77100
protected final StatementReturnType return_type;
101+
protected ParameterMetaData param_meta;
78102

79103
public StatementReturnType getReturnType() {
80104
return return_type;
@@ -150,7 +174,14 @@ public int getColumnType(int column) throws SQLException {
150174
}
151175

152176
public String getColumnClassName(int column) throws SQLException {
153-
switch (column_types[column - 1]) {
177+
if (column > column_count) {
178+
throw new SQLException("Column index out of bounds");
179+
}
180+
return type_to_javaString(column_types[column - 1]);
181+
}
182+
183+
protected static String type_to_javaString(DuckDBColumnType type) {
184+
switch (type) {
154185
case BOOLEAN:
155186
return Boolean.class.getName();
156187
case TINYINT:
@@ -249,7 +280,23 @@ public boolean isCurrency(int column) throws SQLException {
249280
}
250281

251282
public boolean isSigned(int column) throws SQLException {
252-
return false;
283+
if (column > column_count) {
284+
throw new SQLException("Column index out of bounds");
285+
}
286+
return is_signed(column_types[column - 1]);
287+
}
288+
289+
protected static boolean is_signed(DuckDBColumnType type) {
290+
switch (type) {
291+
case UTINYINT:
292+
case USMALLINT:
293+
case UINTEGER:
294+
case UBIGINT:
295+
case UHUGEINT:
296+
return false;
297+
default:
298+
return true;
299+
}
253300
}
254301

255302
public int getColumnDisplaySize(int column) throws SQLException {

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -4801,7 +4801,8 @@ public static void main(String[] args) throws Exception {
48014801
statusCode = runTests(new String[0], clazz);
48024802
} else {
48034803
// extension installation fails on CI, Spatial test is temporary disabled
4804-
statusCode = runTests(args, TestDuckDBJDBC.class, TestExtensionTypes.class /*, TestSpatial.class */);
4804+
statusCode = runTests(args, TestDuckDBJDBC.class, TestExtensionTypes.class /*, TestSpatial.class */,
4805+
TestParameterMetadata.class);
48054806
}
48064807
System.exit(statusCode);
48074808
}

0 commit comments

Comments
 (0)