|
1 | 1 | #include "types.hpp" |
2 | 2 |
|
| 3 | +#include "refs.hpp" |
| 4 | +#include "util.hpp" |
| 5 | + |
3 | 6 | #include <string> |
4 | 7 | #include <vector> |
5 | 8 |
|
@@ -43,3 +46,167 @@ std::string type_to_jduckdb_type(duckdb::LogicalType logical_type) { |
43 | 46 | return duckdb::EnumUtil::ToString(logical_type.id()); |
44 | 47 | } |
45 | 48 | } |
| 49 | + |
| 50 | +duckdb::Value create_value_from_bigdecimal(JNIEnv *env, jobject decimal) { |
| 51 | + jint precision = env->CallIntMethod(decimal, J_BigDecimal_precision); |
| 52 | + jint scale = env->CallIntMethod(decimal, J_BigDecimal_scale); |
| 53 | + |
| 54 | + // Java BigDecimal type can have scale that exceeds the precision |
| 55 | + // Which our DECIMAL type does not support (assert(width >= scale)) |
| 56 | + if (scale > precision) { |
| 57 | + precision = scale; |
| 58 | + } |
| 59 | + |
| 60 | + // DECIMAL scale is unsigned, so negative values are not supported |
| 61 | + if (scale < 0) { |
| 62 | + throw duckdb::InvalidInputException("Converting from a BigDecimal with negative scale is not supported"); |
| 63 | + } |
| 64 | + |
| 65 | + duckdb::Value val; |
| 66 | + |
| 67 | + if (precision <= 18) { // normal sizes -> avoid string processing |
| 68 | + jobject no_point_dec = env->CallObjectMethod(decimal, J_BigDecimal_scaleByPowTen, scale); |
| 69 | + jlong result = env->CallLongMethod(no_point_dec, J_BigDecimal_longValue); |
| 70 | + val = duckdb::Value::DECIMAL((int64_t)result, (uint8_t)precision, (uint8_t)scale); |
| 71 | + } else if (precision <= 38) { // larger than int64 -> get string and cast |
| 72 | + jobject str_val = env->CallObjectMethod(decimal, J_BigDecimal_toPlainString); |
| 73 | + auto *str_char = env->GetStringUTFChars((jstring)str_val, 0); |
| 74 | + val = duckdb::Value(str_char); |
| 75 | + val = val.DefaultCastAs(duckdb::LogicalType::DECIMAL(precision, scale)); |
| 76 | + env->ReleaseStringUTFChars((jstring)str_val, str_char); |
| 77 | + } |
| 78 | + |
| 79 | + return val; |
| 80 | +} |
| 81 | + |
| 82 | +static duckdb::Value create_value_from_hugeint(JNIEnv *env, jobject hugeint) { |
| 83 | + jlong lower = env->GetLongField(hugeint, J_HugeInt_lower); |
| 84 | + jlong upper = env->GetLongField(hugeint, J_HugeInt_upper); |
| 85 | + duckdb::hugeint_t hi(upper, lower); |
| 86 | + return duckdb::Value::HUGEINT(std::move(hi)); |
| 87 | +} |
| 88 | + |
| 89 | +static duckdb::Value create_value_from_uuid(JNIEnv *env, jobject param) { |
| 90 | + jlong most_significant = env->CallLongMethod(param, J_UUID_getMostSignificantBits); |
| 91 | + // Account for the following logic in UUID::FromString: |
| 92 | + // Flip the first bit to make `order by uuid` same as `order by uuid::varchar` |
| 93 | + most_significant ^= (std::numeric_limits<int64_t>::min)(); |
| 94 | + jlong least_significant = env->CallLongMethod(param, J_UUID_getLeastSignificantBits); |
| 95 | + duckdb::hugeint_t hi = duckdb::hugeint_t(most_significant, least_significant); |
| 96 | + return duckdb::Value::UUID(std::move(hi)); |
| 97 | +} |
| 98 | + |
| 99 | +static duckdb::Value create_value_from_map(JNIEnv *env, jobject param, duckdb::ClientContext &context) { |
| 100 | + auto typeName = jstring_to_string(env, (jstring)env->CallObjectMethod(param, J_DuckMap_getSQLTypeName)); |
| 101 | + |
| 102 | + duckdb::LogicalType type; |
| 103 | + context.RunFunctionInTransaction([&]() { type = duckdb::TransformStringToLogicalType(typeName, context); }); |
| 104 | + |
| 105 | + auto entrySet = env->CallObjectMethod(param, J_Map_entrySet); |
| 106 | + auto iterator = env->CallObjectMethod(entrySet, J_Set_iterator); |
| 107 | + duckdb::vector<duckdb::Value> entries; |
| 108 | + while (env->CallBooleanMethod(iterator, J_Iterator_hasNext)) { |
| 109 | + auto entry = env->CallObjectMethod(iterator, J_Iterator_next); |
| 110 | + |
| 111 | + auto key = env->CallObjectMethod(entry, J_Entry_getKey); |
| 112 | + auto value = env->CallObjectMethod(entry, J_Entry_getValue); |
| 113 | + D_ASSERT(key); |
| 114 | + D_ASSERT(value); |
| 115 | + |
| 116 | + entries.push_back(duckdb::Value::STRUCT( |
| 117 | + {{"key", to_duckdb_value(env, key, context)}, {"value", to_duckdb_value(env, value, context)}})); |
| 118 | + } |
| 119 | + |
| 120 | + return duckdb::Value::MAP(duckdb::ListType::GetChildType(type), entries); |
| 121 | +} |
| 122 | + |
| 123 | +static duckdb::Value create_value_from_struct(JNIEnv *env, jobject param, duckdb::ClientContext &context) { |
| 124 | + auto typeName = jstring_to_string(env, (jstring)env->CallObjectMethod(param, J_Struct_getSQLTypeName)); |
| 125 | + |
| 126 | + duckdb::LogicalType type; |
| 127 | + context.RunFunctionInTransaction([&]() { type = TransformStringToLogicalType(typeName, context); }); |
| 128 | + |
| 129 | + auto jvalues = (jobjectArray)env->CallObjectMethod(param, J_Struct_getAttributes); |
| 130 | + |
| 131 | + int size = env->GetArrayLength(jvalues); |
| 132 | + |
| 133 | + duckdb::child_list_t<duckdb::Value> values; |
| 134 | + |
| 135 | + for (int i = 0; i < size; i++) { |
| 136 | + auto name = duckdb::StructType::GetChildName(type, i); |
| 137 | + |
| 138 | + auto value = env->GetObjectArrayElement(jvalues, i); |
| 139 | + |
| 140 | + values.emplace_back(name, to_duckdb_value(env, value, context)); |
| 141 | + } |
| 142 | + |
| 143 | + return duckdb::Value::STRUCT(std::move(values)); |
| 144 | +} |
| 145 | + |
| 146 | +static duckdb::Value create_value_from_array(JNIEnv *env, jobject param, duckdb::ClientContext &context) { |
| 147 | + auto typeName = jstring_to_string(env, (jstring)env->CallObjectMethod(param, J_Array_getBaseTypeName)); |
| 148 | + auto jvalues = (jobjectArray)env->CallObjectMethod(param, J_Array_getArray); |
| 149 | + int size = env->GetArrayLength(jvalues); |
| 150 | + |
| 151 | + duckdb::LogicalType type; |
| 152 | + context.RunFunctionInTransaction([&]() { type = TransformStringToLogicalType(typeName, context); }); |
| 153 | + |
| 154 | + duckdb::vector<duckdb::Value> values; |
| 155 | + for (int i = 0; i < size; i++) { |
| 156 | + auto value = env->GetObjectArrayElement(jvalues, i); |
| 157 | + |
| 158 | + values.emplace_back(to_duckdb_value(env, value, context)); |
| 159 | + } |
| 160 | + |
| 161 | + return (duckdb::Value::LIST(type, values)); |
| 162 | +} |
| 163 | + |
| 164 | +duckdb::Value to_duckdb_value(JNIEnv *env, jobject param, duckdb::ClientContext &context) { |
| 165 | + param = env->CallStaticObjectMethod(J_Timestamp, J_Timestamp_valueOf, param); |
| 166 | + |
| 167 | + if (param == nullptr) { |
| 168 | + return (duckdb::Value()); |
| 169 | + } else if (env->IsInstanceOf(param, J_Bool)) { |
| 170 | + return (duckdb::Value::BOOLEAN(env->CallBooleanMethod(param, J_Bool_booleanValue))); |
| 171 | + } else if (env->IsInstanceOf(param, J_Byte)) { |
| 172 | + return (duckdb::Value::TINYINT(env->CallByteMethod(param, J_Byte_byteValue))); |
| 173 | + } else if (env->IsInstanceOf(param, J_Short)) { |
| 174 | + return (duckdb::Value::SMALLINT(env->CallShortMethod(param, J_Short_shortValue))); |
| 175 | + } else if (env->IsInstanceOf(param, J_Int)) { |
| 176 | + return (duckdb::Value::INTEGER(env->CallIntMethod(param, J_Int_intValue))); |
| 177 | + } else if (env->IsInstanceOf(param, J_Long)) { |
| 178 | + return (duckdb::Value::BIGINT(env->CallLongMethod(param, J_Long_longValue))); |
| 179 | + } else if (env->IsInstanceOf(param, J_HugeInt)) { |
| 180 | + return create_value_from_hugeint(env, param); |
| 181 | + } else if (env->IsInstanceOf(param, J_TimestampTZ)) { // Check for subclass before superclass! |
| 182 | + return (duckdb::Value::TIMESTAMPTZ( |
| 183 | + (duckdb::timestamp_tz_t)env->CallLongMethod(param, J_TimestampTZ_getMicrosEpoch))); |
| 184 | + } else if (env->IsInstanceOf(param, J_DuckDBDate)) { |
| 185 | + return (duckdb::Value::DATE((duckdb::date_t)env->CallLongMethod(param, J_DuckDBDate_getDaysSinceEpoch))); |
| 186 | + } else if (env->IsInstanceOf(param, J_DuckDBTime)) { |
| 187 | + return (duckdb::Value::TIME((duckdb::dtime_t)env->CallLongMethod(param, J_Timestamp_getMicrosEpoch))); |
| 188 | + } else if (env->IsInstanceOf(param, J_Timestamp)) { |
| 189 | + return (duckdb::Value::TIMESTAMP((duckdb::timestamp_t)env->CallLongMethod(param, J_Timestamp_getMicrosEpoch))); |
| 190 | + } else if (env->IsInstanceOf(param, J_Float)) { |
| 191 | + return (duckdb::Value::FLOAT(env->CallFloatMethod(param, J_Float_floatValue))); |
| 192 | + } else if (env->IsInstanceOf(param, J_Double)) { |
| 193 | + return (duckdb::Value::DOUBLE(env->CallDoubleMethod(param, J_Double_doubleValue))); |
| 194 | + } else if (env->IsInstanceOf(param, J_BigDecimal)) { |
| 195 | + return create_value_from_bigdecimal(env, param); |
| 196 | + } else if (env->IsInstanceOf(param, J_String)) { |
| 197 | + auto param_string = jstring_to_string(env, (jstring)param); |
| 198 | + return (duckdb::Value(param_string)); |
| 199 | + } else if (env->IsInstanceOf(param, J_ByteArray)) { |
| 200 | + return (duckdb::Value::BLOB_RAW(byte_array_to_string(env, (jbyteArray)param))); |
| 201 | + } else if (env->IsInstanceOf(param, J_UUID)) { |
| 202 | + return create_value_from_uuid(env, param); |
| 203 | + } else if (env->IsInstanceOf(param, J_DuckMap)) { |
| 204 | + return create_value_from_map(env, param, context); |
| 205 | + } else if (env->IsInstanceOf(param, J_Struct)) { |
| 206 | + return create_value_from_struct(env, param, context); |
| 207 | + } else if (env->IsInstanceOf(param, J_Array)) { |
| 208 | + return create_value_from_array(env, param, context); |
| 209 | + } else { |
| 210 | + throw duckdb::InvalidInputException("Unsupported parameter type"); |
| 211 | + } |
| 212 | +} |
0 commit comments