Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding fuller implementation of ParameterMetadata #114

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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