#include "result_serializer.hpp"
#include "duckdb/common/extra_type_info.hpp"
#include "duckdb/common/types/uuid.hpp"
#include
namespace duckdb {
#define YY_APPEND_FAIL(success) \
if (!success) { \
throw SerializationException("Failed to append in " __FILE__, __LINE__); \
}
void ResultSerializer::SerializeInternal(QueryResult &query_result, yyjson_mut_val *append_root,
const bool values_as_array) {
auto chunk = query_result.Fetch();
auto names = query_result.names;
auto types = query_result.types;
while (chunk) {
SerializeChunk(*chunk, names, types, append_root, values_as_array);
chunk = query_result.Fetch();
}
}
void ResultSerializer::SerializeChunk(const DataChunk &chunk, vector &names, vector &types,
yyjson_mut_val *append_root, const bool values_as_array) {
D_ASSERT(yyjson_mut_is_arr(append_root));
const auto row_count = chunk.size();
for (idx_t row_idx = 0; row_idx < row_count; row_idx++) {
// Which itself contains an object
yyjson_mut_val *obj;
if (values_as_array) {
obj = SerializeRowAsArray(chunk, row_idx, types);
} else {
obj = SerializeRowAsObject(chunk, row_idx, names, types);
}
YY_APPEND_FAIL(yyjson_mut_arr_append(append_root, obj));
}
}
yyjson_mut_val *ResultSerializer::SerializeRowAsArray(const DataChunk &chunk, const idx_t row_idx,
vector &types) {
const auto column_count = chunk.ColumnCount();
auto obj = yyjson_mut_arr(doc);
for (idx_t col_idx = 0; col_idx < column_count; col_idx++) {
auto value = chunk.GetValue(col_idx, row_idx);
auto &type = types[col_idx];
SerializeValue(obj, value, nullptr, type);
}
return obj;
}
yyjson_mut_val *ResultSerializer::SerializeRowAsObject(const DataChunk &chunk, const idx_t row_idx,
vector &names, vector &types) {
const auto column_count = chunk.ColumnCount();
auto obj = yyjson_mut_obj(doc);
for (idx_t col_idx = 0; col_idx < column_count; col_idx++) {
auto value = chunk.GetValue(col_idx, row_idx);
auto &type = types[col_idx];
SerializeValue(obj, value, names[col_idx], type);
}
return obj;
}
void ResultSerializer::SerializeValue( // NOLINT(*-no-recursion)
yyjson_mut_val *parent, const Value &value, optional_ptr name, const LogicalType &type) {
yyjson_mut_val *val = nullptr;
if (value.IsNull()) {
goto null_handle;
}
switch (type.id()) {
case LogicalTypeId::SQLNULL:
null_handle:
val = yyjson_mut_null(doc);
break;
case LogicalTypeId::BOOLEAN:
val = yyjson_mut_bool(doc, value.GetValue());
break;
case LogicalTypeId::TINYINT:
case LogicalTypeId::SMALLINT:
case LogicalTypeId::INTEGER:
case LogicalTypeId::BIGINT:
case LogicalTypeId::INTEGER_LITERAL:
val = yyjson_mut_int(doc, value.GetValue());
break;
case LogicalTypeId::UTINYINT:
case LogicalTypeId::USMALLINT:
case LogicalTypeId::UINTEGER:
case LogicalTypeId::UBIGINT:
val = yyjson_mut_uint(doc, value.GetValue());
break;
// format to big numbers as strings
case LogicalTypeId::UHUGEINT: {
const uhugeint_t uHugeIntNumber = value.GetValue();
val = yyjson_mut_strcpy(doc, uHugeIntNumber.ToString().c_str());
break;
}
case LogicalTypeId::HUGEINT: {
const hugeint_t hugeIntNumber = value.GetValue();
val = yyjson_mut_strcpy(doc, hugeIntNumber.ToString().c_str());
break;
}
case LogicalTypeId::FLOAT:
case LogicalTypeId::DOUBLE:
case LogicalTypeId::DECIMAL: {
const auto real_val = value.GetValue();
if (std::isnan(real_val) || std::isinf(real_val)) {
if (set_invalid_values_to_null) {
goto null_handle;
} else {
const auto castedValue = value.DefaultCastAs(LogicalTypeId::VARCHAR).GetValue();
val = yyjson_mut_strcpy(doc, castedValue.c_str());
break;
}
} else {
val = yyjson_mut_real(doc, real_val);
break;
}
}
// Data + time
case LogicalTypeId::DATE:
case LogicalTypeId::TIME:
case LogicalTypeId::TIMESTAMP_SEC:
case LogicalTypeId::TIMESTAMP_MS:
case LogicalTypeId::TIMESTAMP:
case LogicalTypeId::TIMESTAMP_NS:
case LogicalTypeId::TIMESTAMP_TZ:
case LogicalTypeId::TIME_TZ:
case LogicalTypeId::TIME_NS:
// Enum
case LogicalTypeId::ENUM:
// Strings
case LogicalTypeId::CHAR:
case LogicalTypeId::VARCHAR:
case LogicalTypeId::STRING_LITERAL:
val = yyjson_mut_strcpy(doc, value.GetValue().c_str());
break;
case LogicalTypeId::BIGNUM:
val = yyjson_mut_strcpy(doc, value.DefaultCastAs(LogicalTypeId::VARCHAR).GetValue().c_str());
break;
// UUID
case LogicalTypeId::UUID: {
const auto uuid_int = value.GetValue();
const auto uuid = UUID::ToString(uuid_int);
val = yyjson_mut_strcpy(doc, uuid.c_str());
break;
}
// Weird special types that are just serialized to string
case LogicalTypeId::INTERVAL:
// TODO perhaps base64 encode blob?
case LogicalTypeId::BLOB:
case LogicalTypeId::BIT:
val = yyjson_mut_strcpy(doc, value.ToString().c_str());
break;
case LogicalTypeId::UNION: {
auto &union_val = UnionValue::GetValue(value);
SerializeValue(parent, union_val, name, union_val.type());
return;
}
case LogicalTypeId::ARRAY:
case LogicalTypeId::LIST: {
const auto get_children = LogicalTypeId::LIST == type.id() ? ListValue::GetChildren : ArrayValue::GetChildren;
auto &children = get_children(value);
val = yyjson_mut_arr(doc);
for (auto &child : children) {
SerializeValue(val, child, nullptr, child.type());
}
break;
}
case LogicalTypeId::STRUCT: {
const auto &children = StructValue::GetChildren(value);
const auto &type_info = value.type().AuxInfo()->Cast();
auto all_keys_are_empty = true;
for (uint64_t idx = 0; idx < children.size(); ++idx) {
if (!type_info.child_types[idx].first.empty()) {
all_keys_are_empty = false;
break;
}
}
// Unnamed struct -> just create tuples
if (all_keys_are_empty) {
val = yyjson_mut_arr(doc);
for (auto &child : children) {
SerializeValue(val, child, nullptr, child.type());
}
} else {
val = yyjson_mut_obj(doc);
for (uint64_t idx = 0; idx < children.size(); ++idx) {
string struct_name = type_info.child_types[idx].first;
SerializeValue(val, children[idx], struct_name, type_info.child_types[idx].second);
}
}
break;
}
// Not implemented types
case LogicalTypeId::MAP: {
auto &children = ListValue::GetChildren(value);
val = yyjson_mut_obj(doc);
for (auto &item : children) {
auto &key_value = StructValue::GetChildren(item);
D_ASSERT(key_value.size() == 2);
auto key_str = key_value[0].GetValue();
SerializeValue(val, key_value[1], key_str, key_value[1].type());
}
break;
}
// Unsupported types
case LogicalTypeId::TABLE:
case LogicalTypeId::POINTER:
case LogicalTypeId::VALIDITY:
case LogicalTypeId::AGGREGATE_STATE:
case LogicalTypeId::LAMBDA:
case LogicalTypeId::ANY:
case LogicalTypeId::UNKNOWN:
case LogicalTypeId::INVALID:
case LogicalTypeId::TEMPLATE:
case LogicalTypeId::VARIANT:
if (set_invalid_values_to_null) {
goto null_handle;
}
throw InvalidTypeException("Type " + type.ToString() + " not supported");
}
if (!val) {
throw SerializationException("Could not serialize value of type " + type.ToString());
}
if (!name) {
YY_APPEND_FAIL(yyjson_mut_arr_append(parent, val));
} else {
yyjson_mut_val *key = yyjson_mut_strcpy(doc, name->c_str());
D_ASSERT(key);
YY_APPEND_FAIL(yyjson_mut_obj_add(parent, key, val));
}
}
} // namespace duckdb