ladybird/Userland/Libraries/LibSQL/Value.cpp
kleines Filmröllchen effcd080ca Userland: Remove remaining users of Duration::now_realtime()
This is a clear sign that they want to use a UnixDateTime instead.

This also adds support for placing durations and date times into SQL
databases via their millisecond offset to UTC.
2023-05-24 23:18:07 +02:00

907 lines
27 KiB
C++

/*
* Copyright (c) 2021, Jan de Visser <jan@de-visser.net>
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/NumericLimits.h>
#include <LibIPC/Decoder.h>
#include <LibIPC/Encoder.h>
#include <LibSQL/AST/AST.h>
#include <LibSQL/Serializer.h>
#include <LibSQL/TupleDescriptor.h>
#include <LibSQL/Value.h>
namespace SQL {
// We use the upper 4 bits of the encoded type to store extra information about the type. This
// includes if the value is null, and the encoded size of any integer type. Of course, this encoding
// only works if the SQL type itself fits in the lower 4 bits.
enum class SQLTypeWithCount {
#undef __ENUMERATE_SQL_TYPE
#define __ENUMERATE_SQL_TYPE(name, type) type,
ENUMERATE_SQL_TYPES(__ENUMERATE_SQL_TYPE)
#undef __ENUMERATE_SQL_TYPE
Count,
};
static_assert(to_underlying(SQLTypeWithCount::Count) <= 0x0f, "Too many SQL types for current encoding");
// Adding to this list is fine, but changing the order of any value here will result in LibSQL
// becoming unable to read existing .db files. If the order must absolutely be changed, be sure
// to bump Heap::VERSION.
enum class TypeData : u8 {
Null = 1 << 4,
Int8 = 2 << 4,
Int16 = 3 << 4,
Int32 = 4 << 4,
Int64 = 5 << 4,
Uint8 = 6 << 4,
Uint16 = 7 << 4,
Uint32 = 8 << 4,
Uint64 = 9 << 4,
};
template<typename Callback>
static decltype(auto) downsize_integer(Integer auto value, Callback&& callback)
{
if constexpr (IsSigned<decltype(value)>) {
if (AK::is_within_range<i8>(value))
return callback(static_cast<i8>(value), TypeData::Int8);
if (AK::is_within_range<i16>(value))
return callback(static_cast<i16>(value), TypeData::Int16);
if (AK::is_within_range<i32>(value))
return callback(static_cast<i32>(value), TypeData::Int32);
return callback(value, TypeData::Int64);
} else {
if (AK::is_within_range<u8>(value))
return callback(static_cast<i8>(value), TypeData::Uint8);
if (AK::is_within_range<u16>(value))
return callback(static_cast<i16>(value), TypeData::Uint16);
if (AK::is_within_range<u32>(value))
return callback(static_cast<i32>(value), TypeData::Uint32);
return callback(value, TypeData::Uint64);
}
}
template<typename Callback>
static decltype(auto) downsize_integer(Value const& value, Callback&& callback)
{
VERIFY(value.is_int());
if (value.value().has<i64>())
return downsize_integer(value.value().get<i64>(), forward<Callback>(callback));
return downsize_integer(value.value().get<u64>(), forward<Callback>(callback));
}
template<typename Callback>
static ResultOr<Value> perform_integer_operation(Value const& lhs, Value const& rhs, Callback&& callback)
{
VERIFY(lhs.is_int());
VERIFY(rhs.is_int());
if (lhs.value().has<i64>()) {
if (auto rhs_value = rhs.to_int<i64>(); rhs_value.has_value())
return callback(lhs.to_int<i64>().value(), rhs_value.value());
} else {
if (auto rhs_value = rhs.to_int<u64>(); rhs_value.has_value())
return callback(lhs.to_int<u64>().value(), rhs_value.value());
}
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow };
}
Value::Value(SQLType type)
: m_type(type)
{
}
Value::Value(DeprecatedString value)
: m_type(SQLType::Text)
, m_value(move(value))
{
}
Value::Value(double value)
{
if (trunc(value) == value) {
if (AK::is_within_range<i64>(value)) {
m_type = SQLType::Integer;
m_value = static_cast<i64>(value);
return;
}
if (AK::is_within_range<u64>(value)) {
m_type = SQLType::Integer;
m_value = static_cast<u64>(value);
return;
}
}
m_type = SQLType::Float;
m_value = value;
}
Value::Value(NonnullRefPtr<TupleDescriptor> descriptor, Vector<Value> values)
: m_type(SQLType::Tuple)
, m_value(TupleValue { move(descriptor), move(values) })
{
}
Value::Value(Value const& other)
: m_type(other.m_type)
, m_value(other.m_value)
{
}
Value::Value(Value&& other)
: m_type(other.m_type)
, m_value(move(other.m_value))
{
}
Value::Value(Duration duration)
: m_type(SQLType::Integer)
, m_value(duration.to_milliseconds())
{
}
Value::Value(UnixDateTime time)
: Value(time.offset_to_epoch())
{
}
Value::~Value() = default;
ResultOr<Value> Value::create_tuple(NonnullRefPtr<TupleDescriptor> descriptor)
{
Vector<Value> values;
TRY(values.try_resize(descriptor->size()));
for (size_t i = 0; i < descriptor->size(); ++i)
values[i].m_type = descriptor->at(i).type;
return Value { move(descriptor), move(values) };
}
ResultOr<Value> Value::create_tuple(Vector<Value> values)
{
auto descriptor = TRY(infer_tuple_descriptor(values));
return Value { move(descriptor), move(values) };
}
SQLType Value::type() const
{
return m_type;
}
StringView Value::type_name() const
{
switch (type()) {
#undef __ENUMERATE_SQL_TYPE
#define __ENUMERATE_SQL_TYPE(name, type) \
case SQLType::type: \
return name##sv;
ENUMERATE_SQL_TYPES(__ENUMERATE_SQL_TYPE)
#undef __ENUMERATE_SQL_TYPE
default:
VERIFY_NOT_REACHED();
}
}
bool Value::is_type_compatible_with(SQLType other_type) const
{
switch (type()) {
case SQLType::Null:
return false;
case SQLType::Integer:
case SQLType::Float:
return other_type == SQLType::Integer || other_type == SQLType::Float;
default:
break;
}
return type() == other_type;
}
bool Value::is_null() const
{
return !m_value.has_value();
}
bool Value::is_int() const
{
return m_value.has_value() && (m_value->has<i64>() || m_value->has<u64>());
}
DeprecatedString Value::to_deprecated_string() const
{
if (is_null())
return "(null)"sv;
return m_value->visit(
[](DeprecatedString const& value) -> DeprecatedString { return value; },
[](Integer auto value) -> DeprecatedString { return DeprecatedString::number(value); },
[](double value) -> DeprecatedString { return DeprecatedString::number(value); },
[](bool value) -> DeprecatedString { return value ? "true"sv : "false"sv; },
[](TupleValue const& value) -> DeprecatedString {
StringBuilder builder;
builder.append('(');
builder.join(',', value.values);
builder.append(')');
return builder.to_deprecated_string();
});
}
Optional<double> Value::to_double() const
{
if (is_null())
return {};
return m_value->visit(
[](DeprecatedString const& value) -> Optional<double> { return value.to_double(); },
[](Integer auto value) -> Optional<double> { return static_cast<double>(value); },
[](double value) -> Optional<double> { return value; },
[](bool value) -> Optional<double> { return static_cast<double>(value); },
[](TupleValue const&) -> Optional<double> { return {}; });
}
Optional<bool> Value::to_bool() const
{
if (is_null())
return {};
return m_value->visit(
[](DeprecatedString const& value) -> Optional<bool> {
if (value.equals_ignoring_ascii_case("true"sv) || value.equals_ignoring_ascii_case("t"sv))
return true;
if (value.equals_ignoring_ascii_case("false"sv) || value.equals_ignoring_ascii_case("f"sv))
return false;
return {};
},
[](Integer auto value) -> Optional<bool> { return static_cast<bool>(value); },
[](double value) -> Optional<bool> { return fabs(value) > NumericLimits<double>::epsilon(); },
[](bool value) -> Optional<bool> { return value; },
[](TupleValue const& value) -> Optional<bool> {
for (auto const& element : value.values) {
auto as_bool = element.to_bool();
if (!as_bool.has_value())
return {};
if (!as_bool.value())
return false;
}
return true;
});
}
Optional<Vector<Value>> Value::to_vector() const
{
if (is_null() || (type() != SQLType::Tuple))
return {};
auto const& tuple = m_value->get<TupleValue>();
return tuple.values;
}
Value& Value::operator=(Value value)
{
m_type = value.m_type;
m_value = move(value.m_value);
return *this;
}
Value& Value::operator=(DeprecatedString value)
{
m_type = SQLType::Text;
m_value = move(value);
return *this;
}
Value& Value::operator=(double value)
{
m_type = SQLType::Float;
m_value = value;
return *this;
}
ResultOr<void> Value::assign_tuple(NonnullRefPtr<TupleDescriptor> descriptor)
{
Vector<Value> values;
TRY(values.try_resize(descriptor->size()));
for (size_t i = 0; i < descriptor->size(); ++i)
values[i].m_type = descriptor->at(i).type;
m_type = SQLType::Tuple;
m_value = TupleValue { move(descriptor), move(values) };
return {};
}
ResultOr<void> Value::assign_tuple(Vector<Value> values)
{
if (is_null() || (type() != SQLType::Tuple)) {
auto descriptor = TRY(infer_tuple_descriptor(values));
m_type = SQLType::Tuple;
m_value = TupleValue { move(descriptor), move(values) };
return {};
}
auto& tuple = m_value->get<TupleValue>();
if (values.size() > tuple.descriptor->size())
return Result { SQLCommand::Unknown, SQLErrorCode::InvalidNumberOfValues };
for (size_t i = 0; i < values.size(); ++i) {
if (values[i].type() != tuple.descriptor->at(i).type)
return Result { SQLCommand::Unknown, SQLErrorCode::InvalidType, SQLType_name(values[i].type()) };
}
if (values.size() < tuple.descriptor->size()) {
size_t original_size = values.size();
MUST(values.try_resize(tuple.descriptor->size()));
for (size_t i = original_size; i < values.size(); ++i)
values[i].m_type = tuple.descriptor->at(i).type;
}
m_value = TupleValue { move(tuple.descriptor), move(values) };
return {};
}
size_t Value::length() const
{
if (is_null())
return 0;
// FIXME: This seems to be more of an encoded byte size rather than a length.
return m_value->visit(
[](DeprecatedString const& value) -> size_t { return sizeof(u32) + value.length(); },
[](Integer auto value) -> size_t {
return downsize_integer(value, [](auto integer, auto) {
return sizeof(integer);
});
},
[](double value) -> size_t { return sizeof(value); },
[](bool value) -> size_t { return sizeof(value); },
[](TupleValue const& value) -> size_t {
auto size = value.descriptor->length() + sizeof(u32);
for (auto const& element : value.values)
size += element.length();
return size;
});
}
u32 Value::hash() const
{
if (is_null())
return 0;
return m_value->visit(
[](DeprecatedString const& value) -> u32 { return value.hash(); },
[](Integer auto value) -> u32 {
return downsize_integer(value, [](auto integer, auto) {
if constexpr (sizeof(decltype(integer)) == 8)
return u64_hash(integer);
else
return int_hash(integer);
});
},
[](double) -> u32 { VERIFY_NOT_REACHED(); },
[](bool value) -> u32 { return int_hash(value); },
[](TupleValue const& value) -> u32 {
u32 hash = 0;
for (auto const& element : value.values) {
if (hash == 0)
hash = element.hash();
else
hash = pair_int_hash(hash, element.hash());
}
return hash;
});
}
int Value::compare(Value const& other) const
{
if (is_null())
return -1;
if (other.is_null())
return 1;
return m_value->visit(
[&](DeprecatedString const& value) -> int { return value.view().compare(other.to_deprecated_string()); },
[&](Integer auto value) -> int {
auto casted = other.to_int<IntegerType<decltype(value)>>();
if (!casted.has_value())
return 1;
if (value == *casted)
return 0;
return value < *casted ? -1 : 1;
},
[&](double value) -> int {
auto casted = other.to_double();
if (!casted.has_value())
return 1;
auto diff = value - *casted;
if (fabs(diff) < NumericLimits<double>::epsilon())
return 0;
return diff < 0 ? -1 : 1;
},
[&](bool value) -> int {
auto casted = other.to_bool();
if (!casted.has_value())
return 1;
return value ^ *casted;
},
[&](TupleValue const& value) -> int {
if (other.is_null() || (other.type() != SQLType::Tuple)) {
if (value.values.size() == 1)
return value.values[0].compare(other);
return 1;
}
auto const& other_value = other.m_value->get<TupleValue>();
if (auto result = value.descriptor->compare_ignoring_names(*other_value.descriptor); result != 0)
return 1;
if (value.values.size() != other_value.values.size())
return value.values.size() < other_value.values.size() ? -1 : 1;
for (size_t i = 0; i < value.values.size(); ++i) {
auto result = value.values[i].compare(other_value.values[i]);
if (result == 0)
continue;
if (value.descriptor->at(i).order == Order::Descending)
result = -result;
return result;
}
return 0;
});
}
bool Value::operator==(Value const& value) const
{
return compare(value) == 0;
}
bool Value::operator==(StringView value) const
{
return to_deprecated_string() == value;
}
bool Value::operator==(double value) const
{
return to_double() == value;
}
bool Value::operator!=(Value const& value) const
{
return compare(value) != 0;
}
bool Value::operator<(Value const& value) const
{
return compare(value) < 0;
}
bool Value::operator<=(Value const& value) const
{
return compare(value) <= 0;
}
bool Value::operator>(Value const& value) const
{
return compare(value) > 0;
}
bool Value::operator>=(Value const& value) const
{
return compare(value) >= 0;
}
template<typename Operator>
static Result invalid_type_for_numeric_operator(Operator op)
{
if constexpr (IsSame<Operator, AST::BinaryOperator>)
return { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, BinaryOperator_name(op) };
else if constexpr (IsSame<Operator, AST::UnaryOperator>)
return { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, UnaryOperator_name(op) };
else
static_assert(DependentFalse<Operator>);
}
ResultOr<Value> Value::add(Value const& other) const
{
if (is_int() && other.is_int()) {
return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> {
Checked result { lhs };
result.add(rhs);
if (result.has_overflow())
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow };
return Value { result.value_unchecked() };
});
}
auto lhs = to_double();
auto rhs = other.to_double();
if (!lhs.has_value() || !rhs.has_value())
return invalid_type_for_numeric_operator(AST::BinaryOperator::Plus);
return Value { lhs.value() + rhs.value() };
}
ResultOr<Value> Value::subtract(Value const& other) const
{
if (is_int() && other.is_int()) {
return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> {
Checked result { lhs };
result.sub(rhs);
if (result.has_overflow())
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow };
return Value { result.value_unchecked() };
});
}
auto lhs = to_double();
auto rhs = other.to_double();
if (!lhs.has_value() || !rhs.has_value())
return invalid_type_for_numeric_operator(AST::BinaryOperator::Minus);
return Value { lhs.value() - rhs.value() };
}
ResultOr<Value> Value::multiply(Value const& other) const
{
if (is_int() && other.is_int()) {
return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> {
Checked result { lhs };
result.mul(rhs);
if (result.has_overflow())
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow };
return Value { result.value_unchecked() };
});
}
auto lhs = to_double();
auto rhs = other.to_double();
if (!lhs.has_value() || !rhs.has_value())
return invalid_type_for_numeric_operator(AST::BinaryOperator::Multiplication);
return Value { lhs.value() * rhs.value() };
}
ResultOr<Value> Value::divide(Value const& other) const
{
auto lhs = to_double();
auto rhs = other.to_double();
if (!lhs.has_value() || !rhs.has_value())
return invalid_type_for_numeric_operator(AST::BinaryOperator::Division);
if (rhs == 0.0)
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow };
return Value { lhs.value() / rhs.value() };
}
ResultOr<Value> Value::modulo(Value const& other) const
{
if (!is_int() || !other.is_int())
return invalid_type_for_numeric_operator(AST::BinaryOperator::Modulo);
return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> {
Checked result { lhs };
result.mod(rhs);
if (result.has_overflow())
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow };
return Value { result.value_unchecked() };
});
}
ResultOr<Value> Value::negate() const
{
if (type() == SQLType::Integer) {
auto value = to_int<i64>();
if (!value.has_value())
return invalid_type_for_numeric_operator(AST::UnaryOperator::Minus);
return Value { value.value() * -1 };
}
if (type() == SQLType::Float)
return Value { -to_double().value() };
return invalid_type_for_numeric_operator(AST::UnaryOperator::Minus);
}
ResultOr<Value> Value::shift_left(Value const& other) const
{
if (!is_int() || !other.is_int())
return invalid_type_for_numeric_operator(AST::BinaryOperator::ShiftLeft);
return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> {
using LHS = decltype(lhs);
using RHS = decltype(rhs);
static constexpr auto max_shift = static_cast<RHS>(sizeof(LHS) * 8);
if (rhs < 0 || rhs >= max_shift)
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow };
return Value { lhs << rhs };
});
}
ResultOr<Value> Value::shift_right(Value const& other) const
{
if (!is_int() || !other.is_int())
return invalid_type_for_numeric_operator(AST::BinaryOperator::ShiftRight);
return perform_integer_operation(*this, other, [](auto lhs, auto rhs) -> ResultOr<Value> {
using LHS = decltype(lhs);
using RHS = decltype(rhs);
static constexpr auto max_shift = static_cast<RHS>(sizeof(LHS) * 8);
if (rhs < 0 || rhs >= max_shift)
return Result { SQLCommand::Unknown, SQLErrorCode::IntegerOverflow };
return Value { lhs >> rhs };
});
}
ResultOr<Value> Value::bitwise_or(Value const& other) const
{
if (!is_int() || !other.is_int())
return invalid_type_for_numeric_operator(AST::BinaryOperator::BitwiseOr);
return perform_integer_operation(*this, other, [](auto lhs, auto rhs) {
return Value { lhs | rhs };
});
}
ResultOr<Value> Value::bitwise_and(Value const& other) const
{
if (!is_int() || !other.is_int())
return invalid_type_for_numeric_operator(AST::BinaryOperator::BitwiseAnd);
return perform_integer_operation(*this, other, [](auto lhs, auto rhs) {
return Value { lhs & rhs };
});
}
ResultOr<Value> Value::bitwise_not() const
{
if (!is_int())
return invalid_type_for_numeric_operator(AST::UnaryOperator::BitwiseNot);
return downsize_integer(*this, [](auto value, auto) {
return Value { ~value };
});
}
static u8 encode_type_flags(Value const& value)
{
auto type_flags = to_underlying(value.type());
if (value.is_null()) {
type_flags |= to_underlying(TypeData::Null);
} else if (value.is_int()) {
downsize_integer(value, [&](auto, auto type_data) {
type_flags |= to_underlying(type_data);
});
}
return type_flags;
}
void Value::serialize(Serializer& serializer) const
{
auto type_flags = encode_type_flags(*this);
serializer.serialize<u8>(type_flags);
if (is_null())
return;
if (is_int()) {
downsize_integer(*this, [&](auto integer, auto) {
serializer.serialize(integer);
});
return;
}
m_value->visit(
[&](TupleValue const& value) {
serializer.serialize<TupleDescriptor>(*value.descriptor);
serializer.serialize(static_cast<u32>(value.values.size()));
for (auto const& element : value.values)
serializer.serialize<Value>(element);
},
[&](auto const& value) { serializer.serialize(value); });
}
void Value::deserialize(Serializer& serializer)
{
auto type_flags = serializer.deserialize<u8>();
auto type_data = static_cast<TypeData>(type_flags & 0xf0);
m_type = static_cast<SQLType>(type_flags & 0x0f);
if (type_data == TypeData::Null)
return;
switch (m_type) {
case SQLType::Null:
VERIFY_NOT_REACHED();
case SQLType::Text:
m_value = serializer.deserialize<DeprecatedString>();
break;
case SQLType::Integer:
switch (type_data) {
case TypeData::Int8:
m_value = static_cast<i64>(serializer.deserialize<i8>(0));
break;
case TypeData::Int16:
m_value = static_cast<i64>(serializer.deserialize<i16>(0));
break;
case TypeData::Int32:
m_value = static_cast<i64>(serializer.deserialize<i32>(0));
break;
case TypeData::Int64:
m_value = static_cast<i64>(serializer.deserialize<i64>(0));
break;
case TypeData::Uint8:
m_value = static_cast<u64>(serializer.deserialize<u8>(0));
break;
case TypeData::Uint16:
m_value = static_cast<u64>(serializer.deserialize<u16>(0));
break;
case TypeData::Uint32:
m_value = static_cast<u64>(serializer.deserialize<u32>(0));
break;
case TypeData::Uint64:
m_value = static_cast<u64>(serializer.deserialize<u64>(0));
break;
default:
VERIFY_NOT_REACHED();
}
break;
case SQLType::Float:
m_value = serializer.deserialize<double>(0.0);
break;
case SQLType::Boolean:
m_value = serializer.deserialize<bool>(false);
break;
case SQLType::Tuple: {
auto descriptor = serializer.adopt_and_deserialize<TupleDescriptor>();
auto size = serializer.deserialize<u32>();
Vector<Value> values;
values.ensure_capacity(size);
for (size_t i = 0; i < size; ++i)
values.unchecked_append(serializer.deserialize<Value>());
m_value = TupleValue { move(descriptor), move(values) };
break;
}
}
}
TupleElementDescriptor Value::descriptor() const
{
return { "", "", "", type(), Order::Ascending };
}
ResultOr<NonnullRefPtr<TupleDescriptor>> Value::infer_tuple_descriptor(Vector<Value> const& values)
{
auto descriptor = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) SQL::TupleDescriptor));
TRY(descriptor->try_ensure_capacity(values.size()));
for (auto const& element : values)
descriptor->unchecked_append({ ""sv, ""sv, ""sv, element.type(), Order::Ascending });
return descriptor;
}
}
template<>
ErrorOr<void> IPC::encode(Encoder& encoder, SQL::Value const& value)
{
auto type_flags = encode_type_flags(value);
TRY(encoder.encode(type_flags));
if (value.is_null())
return {};
switch (value.type()) {
case SQL::SQLType::Null:
return {};
case SQL::SQLType::Text:
return encoder.encode(value.to_deprecated_string());
case SQL::SQLType::Integer:
return SQL::downsize_integer(value, [&](auto integer, auto) {
return encoder.encode(integer);
});
case SQL::SQLType::Float:
return encoder.encode(value.to_double().value());
case SQL::SQLType::Boolean:
return encoder.encode(value.to_bool().value());
case SQL::SQLType::Tuple:
return encoder.encode(value.to_vector().value());
}
VERIFY_NOT_REACHED();
}
template<>
ErrorOr<SQL::Value> IPC::decode(Decoder& decoder)
{
auto type_flags = TRY(decoder.decode<u8>());
auto type_data = static_cast<SQL::TypeData>(type_flags & 0xf0);
auto type = static_cast<SQL::SQLType>(type_flags & 0x0f);
if (type_data == SQL::TypeData::Null)
return SQL::Value { type };
switch (type) {
case SQL::SQLType::Null:
return SQL::Value {};
case SQL::SQLType::Text:
return SQL::Value { TRY(decoder.decode<DeprecatedString>()) };
case SQL::SQLType::Integer:
switch (type_data) {
case SQL::TypeData::Int8:
return SQL::Value { TRY(decoder.decode<i8>()) };
case SQL::TypeData::Int16:
return SQL::Value { TRY(decoder.decode<i16>()) };
case SQL::TypeData::Int32:
return SQL::Value { TRY(decoder.decode<i32>()) };
case SQL::TypeData::Int64:
return SQL::Value { TRY(decoder.decode<i64>()) };
case SQL::TypeData::Uint8:
return SQL::Value { TRY(decoder.decode<u8>()) };
case SQL::TypeData::Uint16:
return SQL::Value { TRY(decoder.decode<u16>()) };
case SQL::TypeData::Uint32:
return SQL::Value { TRY(decoder.decode<u32>()) };
case SQL::TypeData::Uint64:
return SQL::Value { TRY(decoder.decode<u64>()) };
default:
break;
}
break;
case SQL::SQLType::Float:
return SQL::Value { TRY(decoder.decode<double>()) };
case SQL::SQLType::Boolean:
return SQL::Value { TRY(decoder.decode<bool>()) };
case SQL::SQLType::Tuple: {
auto tuple = TRY(decoder.decode<Vector<SQL::Value>>());
auto value = SQL::Value::create_tuple(move(tuple));
if (value.is_error())
return Error::from_errno(to_underlying(value.error().error()));
return value.release_value();
}
}
VERIFY_NOT_REACHED();
}