Skip to content

Commit b1efe7a

Browse files
committed
Fuller implementation of ParameterMetadata
1 parent 77c787a commit b1efe7a

File tree

5 files changed

+161
-26
lines changed

5 files changed

+161
-26
lines changed

src/jni/duckdb_java.cpp

+32-8
Original file line numberDiff line numberDiff line change
@@ -269,9 +269,9 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
269269
J_DuckResultSetMeta = (jclass)env->NewGlobalRef(tmpLocalRef);
270270
env->DeleteLocalRef(tmpLocalRef);
271271

272-
J_DuckResultSetMeta_init =
273-
env->GetMethodID(J_DuckResultSetMeta, "<init>",
274-
"(II[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V");
272+
J_DuckResultSetMeta_init = env->GetMethodID(J_DuckResultSetMeta, "<init>",
273+
"(II[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/"
274+
"lang/String;[Ljava/lang/String;[Ljava/lang/String;)V");
275275

276276
tmpLocalRef = env->FindClass("org/duckdb/DuckDBVector");
277277
J_DuckVector = (jclass)env->NewGlobalRef(tmpLocalRef);
@@ -782,7 +782,8 @@ static std::string type_to_jduckdb_type(LogicalType logical_type) {
782782
}
783783

784784
static jobject build_meta(JNIEnv *env, size_t column_count, size_t n_param, const duckdb::vector<string> &names,
785-
const duckdb::vector<LogicalType> &types, StatementProperties properties) {
785+
const duckdb::vector<LogicalType> &types, StatementProperties properties,
786+
const duckdb::vector<LogicalType> &param_types) {
786787
auto name_array = env->NewObjectArray(column_count, J_String, nullptr);
787788
auto type_array = env->NewObjectArray(column_count, J_String, nullptr);
788789
auto type_detail_array = env->NewObjectArray(column_count, J_String, nullptr);
@@ -802,10 +803,26 @@ static jobject build_meta(JNIEnv *env, size_t column_count, size_t n_param, cons
802803
env->NewStringUTF(type_to_jduckdb_type(types[col_idx]).c_str()));
803804
}
804805

806+
auto param_type_array = env->NewObjectArray(n_param, J_String, nullptr);
807+
auto param_type_detail_array = env->NewObjectArray(n_param, J_String, nullptr);
808+
809+
for (idx_t param_idx = 0; param_idx < n_param; param_idx++) {
810+
std::string param_name;
811+
if (param_types[param_idx].id() == LogicalTypeId::ENUM) {
812+
param_name = "ENUM";
813+
} else {
814+
param_name = param_types[param_idx].ToString();
815+
}
816+
817+
env->SetObjectArrayElement(param_type_array, param_idx, env->NewStringUTF(param_name.c_str()));
818+
env->SetObjectArrayElement(param_type_detail_array, param_idx,
819+
env->NewStringUTF(type_to_jduckdb_type(param_types[param_idx]).c_str()));
820+
}
821+
805822
auto return_type = env->NewStringUTF(StatementReturnTypeToString(properties.return_type).c_str());
806823

807824
return env->NewObject(J_DuckResultSetMeta, J_DuckResultSetMeta_init, n_param, column_count, name_array, type_array,
808-
type_detail_array, return_type);
825+
type_detail_array, return_type, param_type_array, param_type_detail_array);
809826
}
810827

811828
jobject _duckdb_jdbc_query_result_meta(JNIEnv *env, jclass, jobject res_ref_buf) {
@@ -815,9 +832,11 @@ jobject _duckdb_jdbc_query_result_meta(JNIEnv *env, jclass, jobject res_ref_buf)
815832
}
816833
auto &result = res_ref->res;
817834

818-
auto n_param = -1; // no params now
835+
auto n_param = 0; // no params now
836+
duckdb::vector<LogicalType> param_types(n_param);
819837

820-
return build_meta(env, result->ColumnCount(), n_param, result->names, result->types, result->properties);
838+
return build_meta(env, result->ColumnCount(), n_param, result->names, result->types, result->properties,
839+
param_types);
821840
}
822841

823842
jobject _duckdb_jdbc_prepared_statement_meta(JNIEnv *env, jclass, jobject stmt_ref_buf) {
@@ -829,9 +848,14 @@ jobject _duckdb_jdbc_prepared_statement_meta(JNIEnv *env, jclass, jobject stmt_r
829848

830849
auto &stmt = stmt_ref->stmt;
831850
auto n_param = stmt->named_param_map.size();
851+
auto expected_parameter_types = stmt->GetExpectedParameterTypes();
852+
duckdb::vector<LogicalType> param_types(n_param);
853+
for (auto &it : stmt->named_param_map) {
854+
param_types[it.second - 1] = expected_parameter_types[it.first];
855+
}
832856

833857
return build_meta(env, stmt->ColumnCount(), n_param, stmt->GetNames(), stmt->GetTypes(),
834-
stmt->GetStatementProperties());
858+
stmt->GetStatementProperties(), param_types);
835859
}
836860

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

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

+32
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,38 @@ public static void test_multiple_statements_exception() throws Exception {
10321032
}
10331033
}
10341034

1035+
public static void test_parameter_metadata() throws Exception {
1036+
Connection conn = DriverManager.getConnection(JDBC_URL);
1037+
Statement stmt = conn.createStatement();
1038+
stmt.execute("CREATE TABLE q (id DECIMAL(3,0), dec16 DECIMAL(4,1), dec32 DECIMAL(9,4), dec64 DECIMAL(18,7), "
1039+
+ "dec128 DECIMAL(38,10), int INTEGER, uint UINTEGER)");
1040+
PreparedStatement ps1 = conn.prepareStatement(
1041+
"INSERT INTO q (id, dec16, dec32, dec64, dec128, int, uint) VALUES (?, ?, ?, ?, ?, ?, ?)");
1042+
ParameterMetaData meta = ps1.getParameterMetaData();
1043+
assertEquals(3, meta.getPrecision(1));
1044+
assertEquals(0, meta.getScale(1));
1045+
assertEquals(4, meta.getPrecision(2));
1046+
assertEquals(1, meta.getScale(2));
1047+
assertEquals(9, meta.getPrecision(3));
1048+
assertEquals(4, meta.getScale(3));
1049+
assertEquals(18, meta.getPrecision(4));
1050+
assertEquals(7, meta.getScale(4));
1051+
assertEquals(38, meta.getPrecision(5));
1052+
assertEquals(10, meta.getScale(5));
1053+
assertEquals(0, meta.getPrecision(6));
1054+
assertEquals(0, meta.getScale(6));
1055+
assertEquals(0, meta.getPrecision(7));
1056+
assertEquals(0, meta.getScale(7));
1057+
1058+
assertTrue(meta.isSigned(5));
1059+
assertTrue(meta.isSigned(6));
1060+
assertFalse(meta.isSigned(7));
1061+
1062+
assertTrue(BigDecimal.class.getName().equals(meta.getParameterClassName(1)));
1063+
assertTrue(Integer.class.getName().equals(meta.getParameterClassName(6)));
1064+
assertTrue(Long.class.getName().equals(meta.getParameterClassName(7)));
1065+
}
1066+
10351067
public static void test_bigdecimal() throws Exception {
10361068
Connection conn = DriverManager.getConnection(JDBC_URL);
10371069
Statement stmt = conn.createStatement();

0 commit comments

Comments
 (0)