Skip to content

Commit

Permalink
Fuller implementation of ParameterMetadata
Browse files Browse the repository at this point in the history
  • Loading branch information
eramitmittal committed Dec 20, 2024
1 parent 7319174 commit 325eacb
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 26 deletions.
40 changes: 32 additions & 8 deletions src/jni/duckdb_java.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,9 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
J_DuckResultSetMeta = (jclass)env->NewGlobalRef(tmpLocalRef);
env->DeleteLocalRef(tmpLocalRef);

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

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

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

auto param_type_array = env->NewObjectArray(n_param, J_String, nullptr);
auto param_type_detail_array = env->NewObjectArray(n_param, J_String, nullptr);

for (idx_t param_idx = 0; param_idx < n_param; param_idx++) {
std::string param_name;
if (param_types[param_idx].id() == LogicalTypeId::ENUM) {
param_name = "ENUM";
} else {
param_name = param_types[param_idx].ToString();
}

env->SetObjectArrayElement(param_type_array, param_idx, env->NewStringUTF(param_name.c_str()));
env->SetObjectArrayElement(param_type_detail_array, param_idx,
env->NewStringUTF(type_to_jduckdb_type(param_types[param_idx]).c_str()));
}

auto return_type = env->NewStringUTF(StatementReturnTypeToString(properties.return_type).c_str());

return env->NewObject(J_DuckResultSetMeta, J_DuckResultSetMeta_init, n_param, column_count, name_array, type_array,
type_detail_array, return_type);
type_detail_array, return_type, param_type_array, param_type_detail_array);
}

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

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

return build_meta(env, result->ColumnCount(), n_param, result->names, result->types, result->properties);
return build_meta(env, result->ColumnCount(), n_param, result->names, result->types, result->properties,
param_types);
}

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

auto &stmt = stmt_ref->stmt;
auto n_param = stmt->named_param_map.size();
auto expected_parameter_types = stmt->GetExpectedParameterTypes();
duckdb::vector<LogicalType> param_types(n_param);
for (auto &it : stmt->named_param_map) {
param_types[it.second - 1] = expected_parameter_types[it.first];
}

return build_meta(env, stmt->ColumnCount(), n_param, stmt->GetNames(), stmt->GetTypes(),
stmt->GetStatementProperties());
stmt->GetStatementProperties(), param_types);
}

jobject ProcessVector(JNIEnv *env, Connection *conn_ref, Vector &vec, idx_t row_count);
Expand Down
60 changes: 46 additions & 14 deletions src/main/java/org/duckdb/DuckDBParameterMetaData.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@

import java.sql.ParameterMetaData;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;

public class DuckDBParameterMetaData implements ParameterMetaData {
private DuckDBResultSetMetaData meta;

public DuckDBParameterMetaData(DuckDBResultSetMetaData meta) {
this.meta = meta;
private int param_count;
private DuckDBColumnType[] param_types;
private DuckDBColumnTypeMetaData[] param_types_meta;
private String[] param_types_string;

public DuckDBParameterMetaData(int param_count, String[] param_types_string, DuckDBColumnType[] param_types,
DuckDBColumnTypeMetaData[] param_types_meta) {
this.param_count = param_count;
this.param_types_string = param_types_string;
this.param_types = param_types;
this.param_types_meta = param_types_meta;
}

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

@Override
public int getParameterCount() throws SQLException {
return meta.param_count;
return param_count;
}

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

@Override
public boolean isSigned(int param) throws SQLException {
return true;
if (param > param_count) {
throw new SQLException("Parameter index out of bounds");
}
return DuckDBResultSetMetaData.is_signed(param_types[param - 1]);
}

@Override
public int getPrecision(int param) throws SQLException {
throw new SQLFeatureNotSupportedException("getPrecision");
if (param > param_count) {
throw new SQLException("Parameter index out of bounds");
}
DuckDBColumnTypeMetaData typeMetaData = param_types_meta[param - 1];
if (typeMetaData == null) {
return 0;
}

return typeMetaData.width;
}

@Override
public int getScale(int param) throws SQLException {
throw new SQLFeatureNotSupportedException("getScale");
if (param > param_count) {
throw new SQLException("Parameter index out of bounds");
}
DuckDBColumnTypeMetaData typeMetaData = param_types_meta[param - 1];
if (typeMetaData == null) {
return 0;
}

return typeMetaData.scale;
}

@Override
public int getParameterType(int param) throws SQLException {
// TODO Auto-generated method stub
return 0;
if (param > param_count) {
throw new SQLException("Parameter index out of bounds");
}
return DuckDBResultSetMetaData.type_to_int(param_types[param - 1]);
}

@Override
public String getParameterTypeName(int param) throws SQLException {
// TODO Auto-generated method stub
return null;
if (param > param_count) {
throw new SQLException("Parameter index out of bounds");
}
return param_types_string[param - 1];
}

@Override
public String getParameterClassName(int param) throws SQLException {
// TODO Auto-generated method stub
return null;
if (param > param_count) {
throw new SQLException("Parameter index out of bounds");
}
return DuckDBResultSetMetaData.type_to_javaString(param_types[param - 1]);
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/duckdb/DuckDBPreparedStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ public ParameterMetaData getParameterMetaData() throws SQLException {
if (stmt_ref == null) {
throw new SQLException("Prepare something first");
}
return new DuckDBParameterMetaData(meta);
return meta.param_meta;
}

@Override
Expand Down
53 changes: 50 additions & 3 deletions src/main/java/org/duckdb/DuckDBResultSetMetaData.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.ParameterMetaData;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
Expand All @@ -17,7 +18,9 @@
public class DuckDBResultSetMetaData implements ResultSetMetaData {

public DuckDBResultSetMetaData(int param_count, int column_count, String[] column_names,
String[] column_types_string, String[] column_types_details, String return_type) {
String[] column_types_string, String[] column_types_details, String return_type,
String[] param_types_string, String[] param_types_details) {

this.param_count = param_count;
this.column_count = column_count;
this.column_names = column_names;
Expand All @@ -41,6 +44,26 @@ public DuckDBResultSetMetaData(int param_count, int column_count, String[] colum
}
}
this.column_types_meta = column_types_meta.toArray(new DuckDBColumnTypeMetaData[column_count]);

ArrayList<DuckDBColumnType> param_types_al = new ArrayList<DuckDBColumnType>(param_count);
ArrayList<DuckDBColumnTypeMetaData> param_types_meta_al = new ArrayList<DuckDBColumnTypeMetaData>(param_count);

for (String param_type_string : param_types_string) {
param_types_al.add(TypeNameToType(param_type_string));
}
DuckDBColumnType[] param_types = param_types_al.toArray(new DuckDBColumnType[param_count]);

for (String param_type_detail : param_types_details) {
if (TypeNameToType(param_type_detail) == DuckDBColumnType.DECIMAL) {
param_types_meta_al.add(DuckDBColumnTypeMetaData.parseColumnTypeMetadata(param_type_detail));
} else {
param_types_meta_al.add(null);
}
}
DuckDBColumnTypeMetaData[] param_types_meta =
param_types_meta_al.toArray(new DuckDBColumnTypeMetaData[param_count]);

this.param_meta = new DuckDBParameterMetaData(param_count, param_types_string, param_types, param_types_meta);
}

public static DuckDBColumnType TypeNameToType(String type_name) {
Expand Down Expand Up @@ -75,6 +98,7 @@ public static DuckDBColumnType TypeNameToType(String type_name) {
protected DuckDBColumnType[] column_types;
protected DuckDBColumnTypeMetaData[] column_types_meta;
protected final StatementReturnType return_type;
protected ParameterMetaData param_meta;

public StatementReturnType getReturnType() {
return return_type;
Expand Down Expand Up @@ -150,7 +174,14 @@ public int getColumnType(int column) throws SQLException {
}

public String getColumnClassName(int column) throws SQLException {
switch (column_types[column - 1]) {
if (column > column_count) {
throw new SQLException("Column index out of bounds");
}
return type_to_javaString(column_types[column - 1]);
}

protected static String type_to_javaString(DuckDBColumnType type) {
switch (type) {
case BOOLEAN:
return Boolean.class.getName();
case TINYINT:
Expand Down Expand Up @@ -249,7 +280,23 @@ public boolean isCurrency(int column) throws SQLException {
}

public boolean isSigned(int column) throws SQLException {
return false;
if (column > column_count) {
throw new SQLException("Column index out of bounds");
}
return is_signed(column_types[column - 1]);
}

protected static boolean is_signed(DuckDBColumnType type) {
switch (type) {
case UTINYINT:
case USMALLINT:
case UINTEGER:
case UBIGINT:
case UHUGEINT:
return false;
default:
return true;
}
}

public int getColumnDisplaySize(int column) throws SQLException {
Expand Down
32 changes: 32 additions & 0 deletions src/test/java/org/duckdb/TestDuckDBJDBC.java
Original file line number Diff line number Diff line change
Expand Up @@ -1032,6 +1032,38 @@ public static void test_multiple_statements_exception() throws Exception {
}
}

public static void test_parameter_metadata() throws Exception {
Connection conn = DriverManager.getConnection(JDBC_URL);
Statement stmt = conn.createStatement();
stmt.execute("CREATE TABLE q (id DECIMAL(3,0), dec16 DECIMAL(4,1), dec32 DECIMAL(9,4), dec64 DECIMAL(18,7), "
+ "dec128 DECIMAL(38,10), int INTEGER, uint UINTEGER)");
PreparedStatement ps1 = conn.prepareStatement(
"INSERT INTO q (id, dec16, dec32, dec64, dec128, int, uint) VALUES (?, ?, ?, ?, ?, ?, ?)");
ParameterMetaData meta = ps1.getParameterMetaData();
assertEquals(3, meta.getPrecision(1));
assertEquals(0, meta.getScale(1));
assertEquals(4, meta.getPrecision(2));
assertEquals(1, meta.getScale(2));
assertEquals(9, meta.getPrecision(3));
assertEquals(4, meta.getScale(3));
assertEquals(18, meta.getPrecision(4));
assertEquals(7, meta.getScale(4));
assertEquals(38, meta.getPrecision(5));
assertEquals(10, meta.getScale(5));
assertEquals(0, meta.getPrecision(6));
assertEquals(0, meta.getScale(6));
assertEquals(0, meta.getPrecision(7));
assertEquals(0, meta.getScale(7));

assertTrue(meta.isSigned(5));
assertTrue(meta.isSigned(6));
assertFalse(meta.isSigned(7));

assertTrue(BigDecimal.class.getName().equals(meta.getParameterClassName(1)));
assertTrue(Integer.class.getName().equals(meta.getParameterClassName(6)));
assertTrue(Long.class.getName().equals(meta.getParameterClassName(7)));
}

public static void test_bigdecimal() throws Exception {
Connection conn = DriverManager.getConnection(JDBC_URL);
Statement stmt = conn.createStatement();
Expand Down

0 comments on commit 325eacb

Please sign in to comment.