summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibSQL/Value.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Libraries/LibSQL/Value.cpp')
-rw-r--r--Userland/Libraries/LibSQL/Value.cpp542
1 files changed, 359 insertions, 183 deletions
diff --git a/Userland/Libraries/LibSQL/Value.cpp b/Userland/Libraries/LibSQL/Value.cpp
index 5f36618993..86f2ecf94e 100644
--- a/Userland/Libraries/LibSQL/Value.cpp
+++ b/Userland/Libraries/LibSQL/Value.cpp
@@ -12,38 +12,115 @@
#include <LibSQL/Serializer.h>
#include <LibSQL/TupleDescriptor.h>
#include <LibSQL/Value.h>
-#include <math.h>
#include <string.h>
namespace SQL {
-Value::Value(SQLType type)
- : m_type(type)
+// 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::current_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));
}
-Value::Value(DeprecatedString value)
- : m_type(SQLType::Text)
- , m_value(move(value))
+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(int value)
- : m_type(SQLType::Integer)
- , m_value(value)
+Value::Value(SQLType type)
+ : m_type(type)
{
}
-Value::Value(u32 value)
- : m_type(SQLType::Integer)
- , m_value(static_cast<int>(value)) // FIXME: Handle signed overflow.
+Value::Value(DeprecatedString value)
+ : m_type(SQLType::Text)
+ , m_value(move(value))
{
}
Value::Value(double value)
- : m_type(SQLType::Float)
- , m_value(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)
@@ -122,6 +199,11 @@ 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())
@@ -129,7 +211,7 @@ DeprecatedString Value::to_deprecated_string() const
return m_value->visit(
[](DeprecatedString const& value) -> DeprecatedString { return value; },
- [](int value) -> DeprecatedString { return DeprecatedString::number(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 {
@@ -143,33 +225,6 @@ DeprecatedString Value::to_deprecated_string() const
});
}
-Optional<int> Value::to_int() const
-{
- if (is_null())
- return {};
-
- return m_value->visit(
- [](DeprecatedString const& value) -> Optional<int> { return value.to_int(); },
- [](int value) -> Optional<int> { return value; },
- [](double value) -> Optional<int> {
- if (value > static_cast<double>(NumericLimits<int>::max()))
- return {};
- if (value < static_cast<double>(NumericLimits<int>::min()))
- return {};
- return static_cast<int>(round(value));
- },
- [](bool value) -> Optional<int> { return static_cast<int>(value); },
- [](TupleValue const&) -> Optional<int> { return {}; });
-}
-
-Optional<u32> Value::to_u32() const
-{
- // FIXME: Handle negative values.
- if (auto result = to_int(); result.has_value())
- return static_cast<u32>(result.value());
- return {};
-}
-
Optional<double> Value::to_double() const
{
if (is_null())
@@ -184,7 +239,7 @@ Optional<double> Value::to_double() const
return {};
return result;
},
- [](int value) -> Optional<double> { return static_cast<double>(value); },
+ [](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 {}; });
@@ -203,7 +258,7 @@ Optional<bool> Value::to_bool() const
return false;
return {};
},
- [](int value) -> Optional<bool> { return static_cast<bool>(value); },
+ [](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> {
@@ -242,20 +297,6 @@ Value& Value::operator=(DeprecatedString value)
return *this;
}
-Value& Value::operator=(int value)
-{
- m_type = SQLType::Integer;
- m_value = value;
- return *this;
-}
-
-Value& Value::operator=(u32 value)
-{
- m_type = SQLType::Integer;
- m_value = static_cast<int>(value); // FIXME: Handle signed overflow.
- return *this;
-}
-
Value& Value::operator=(double value)
{
m_type = SQLType::Float;
@@ -318,7 +359,11 @@ size_t Value::length() const
// 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(); },
- [](int value) -> size_t { return sizeof(value); },
+ [](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 {
@@ -338,7 +383,14 @@ u32 Value::hash() const
return m_value->visit(
[](DeprecatedString const& value) -> u32 { return value.hash(); },
- [](int value) -> u32 { return int_hash(value); },
+ [](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 {
@@ -364,8 +416,8 @@ int Value::compare(Value const& other) const
return m_value->visit(
[&](DeprecatedString const& value) -> int { return value.view().compare(other.to_deprecated_string()); },
- [&](int value) -> int {
- auto casted = other.to_int();
+ [&](Integer auto value) -> int {
+ auto casted = other.to_int<IntegerType<decltype(value)>>();
if (!casted.has_value())
return 1;
@@ -427,16 +479,6 @@ bool Value::operator==(StringView value) const
return to_deprecated_string() == value;
}
-bool Value::operator==(int value) const
-{
- return to_int() == value;
-}
-
-bool Value::operator==(u32 value) const
-{
- return to_u32() == value;
-}
-
bool Value::operator==(double value) const
{
return to_double() == value;
@@ -467,133 +509,218 @@ bool Value::operator>=(Value const& value) const
return compare(value) >= 0;
}
-static Result invalid_type_for_numeric_operator(AST::BinaryOperator op)
+template<typename Operator>
+static Result invalid_type_for_numeric_operator(Operator op)
{
- return { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, BinaryOperator_name(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 (auto double_maybe = to_double(); double_maybe.has_value()) {
- if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value())
- return Value(double_maybe.value() + other_double_maybe.value());
- if (auto int_maybe = other.to_int(); int_maybe.has_value())
- return Value(double_maybe.value() + (double)int_maybe.value());
- } else if (auto int_maybe = to_int(); int_maybe.has_value()) {
- if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value())
- return Value(other_double_maybe.value() + (double)int_maybe.value());
- if (auto other_int_maybe = other.to_int(); other_int_maybe.has_value())
- return Value(int_maybe.value() + other_int_maybe.value());
+ 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() };
+ });
}
- return invalid_type_for_numeric_operator(AST::BinaryOperator::Plus);
+
+ 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 (auto double_maybe = to_double(); double_maybe.has_value()) {
- if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value())
- return Value(double_maybe.value() - other_double_maybe.value());
- if (auto int_maybe = other.to_int(); int_maybe.has_value())
- return Value(double_maybe.value() - (double)int_maybe.value());
- } else if (auto int_maybe = to_int(); int_maybe.has_value()) {
- if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value())
- return Value((double)int_maybe.value() - other_double_maybe.value());
- if (auto other_int_maybe = other.to_int(); other_int_maybe.has_value())
- return Value(int_maybe.value() - other_int_maybe.value());
+ 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() };
+ });
}
- return invalid_type_for_numeric_operator(AST::BinaryOperator::Minus);
+
+ 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 (auto double_maybe = to_double(); double_maybe.has_value()) {
- if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value())
- return Value(double_maybe.value() * other_double_maybe.value());
- if (auto int_maybe = other.to_int(); int_maybe.has_value())
- return Value(double_maybe.value() * (double)int_maybe.value());
- } else if (auto int_maybe = to_int(); int_maybe.has_value()) {
- if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value())
- return Value((double)int_maybe.value() * other_double_maybe.value());
- if (auto other_int_maybe = other.to_int(); other_int_maybe.has_value())
- return Value(int_maybe.value() * other_int_maybe.value());
+ 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() };
+ });
}
- return invalid_type_for_numeric_operator(AST::BinaryOperator::Multiplication);
+
+ 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
{
- if (auto double_maybe = to_double(); double_maybe.has_value()) {
- if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value())
- return Value(double_maybe.value() / other_double_maybe.value());
- if (auto int_maybe = other.to_int(); int_maybe.has_value())
- return Value(double_maybe.value() / (double)int_maybe.value());
- } else if (auto int_maybe = to_int(); int_maybe.has_value()) {
- if (auto other_double_maybe = other.to_double(); other_double_maybe.has_value())
- return Value((double)int_maybe.value() / other_double_maybe.value());
- if (auto other_int_maybe = other.to_int(); other_int_maybe.has_value())
- return Value(int_maybe.value() / other_int_maybe.value());
- }
- return invalid_type_for_numeric_operator(AST::BinaryOperator::Division);
+ 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
{
- auto int_maybe_1 = to_int();
- auto int_maybe_2 = other.to_int();
- if (!int_maybe_1.has_value() || !int_maybe_2.has_value())
+ if (!is_int() || !other.is_int())
return invalid_type_for_numeric_operator(AST::BinaryOperator::Modulo);
- return Value(int_maybe_1.value() % int_maybe_2.value());
+
+ 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
{
- auto u32_maybe = to_u32();
- auto num_bytes_maybe = other.to_int();
- if (!u32_maybe.has_value() || !num_bytes_maybe.has_value())
+ if (!is_int() || !other.is_int())
return invalid_type_for_numeric_operator(AST::BinaryOperator::ShiftLeft);
- return Value(u32_maybe.value() << num_bytes_maybe.value());
+
+ 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
{
- auto u32_maybe = to_u32();
- auto num_bytes_maybe = other.to_int();
- if (!u32_maybe.has_value() || !num_bytes_maybe.has_value())
+ if (!is_int() || !other.is_int())
return invalid_type_for_numeric_operator(AST::BinaryOperator::ShiftRight);
- return Value(u32_maybe.value() >> num_bytes_maybe.value());
+
+ 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
{
- auto u32_maybe_1 = to_u32();
- auto u32_maybe_2 = other.to_u32();
- if (!u32_maybe_1.has_value() || !u32_maybe_2.has_value())
+ if (!is_int() || !other.is_int())
return invalid_type_for_numeric_operator(AST::BinaryOperator::BitwiseOr);
- return Value(u32_maybe_1.value() | u32_maybe_2.value());
+
+ return perform_integer_operation(*this, other, [](auto lhs, auto rhs) {
+ return Value { lhs | rhs };
+ });
}
ResultOr<Value> Value::bitwise_and(Value const& other) const
{
- auto u32_maybe_1 = to_u32();
- auto u32_maybe_2 = other.to_u32();
- if (!u32_maybe_1.has_value() || !u32_maybe_2.has_value())
+ if (!is_int() || !other.is_int())
return invalid_type_for_numeric_operator(AST::BinaryOperator::BitwiseAnd);
- return Value(u32_maybe_1.value() & u32_maybe_2.value());
+
+ return perform_integer_operation(*this, other, [](auto lhs, auto rhs) {
+ return Value { lhs & rhs };
+ });
}
-static constexpr auto sql_type_null_as_flag = static_cast<u8>(SQLType::Null);
+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 };
+ });
+}
-void Value::serialize(Serializer& serializer) const
+static u8 encode_type_flags(Value const& value)
{
- auto type_flags = static_cast<u8>(type());
- if (is_null())
- type_flags |= sql_type_null_as_flag;
+ 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);
@@ -608,16 +735,11 @@ void Value::serialize(Serializer& serializer) const
void Value::deserialize(Serializer& serializer)
{
auto type_flags = serializer.deserialize<u8>();
- bool has_value = true;
- if ((type_flags & sql_type_null_as_flag) && (type_flags != sql_type_null_as_flag)) {
- type_flags &= ~sql_type_null_as_flag;
- has_value = false;
- }
-
- m_type = static_cast<SQLType>(type_flags);
+ auto type_data = static_cast<TypeData>(type_flags & 0xf0);
+ m_type = static_cast<SQLType>(type_flags & 0x0f);
- if (!has_value)
+ if (type_data == TypeData::Null)
return;
switch (m_type) {
@@ -628,7 +750,35 @@ void Value::deserialize(Serializer& serializer)
m_value = serializer.deserialize<DeprecatedString>();
break;
case SQLType::Integer:
- m_value = serializer.deserialize<int>(0);
+ 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;
+ }
break;
case SQLType::Float:
m_value = serializer.deserialize<double>(0.0);
@@ -673,11 +823,9 @@ ResultOr<NonnullRefPtr<TupleDescriptor>> Value::infer_tuple_descriptor(Vector<Va
template<>
bool IPC::encode(Encoder& encoder, SQL::Value const& value)
{
- auto type_flags = to_underlying(value.type());
- if (value.is_null())
- type_flags |= SQL::sql_type_null_as_flag;
-
+ auto type_flags = encode_type_flags(value);
encoder << type_flags;
+
if (value.is_null())
return true;
@@ -688,7 +836,9 @@ bool IPC::encode(Encoder& encoder, SQL::Value const& value)
encoder << value.to_deprecated_string();
break;
case SQL::SQLType::Integer:
- encoder << value.to_int().value();
+ SQL::downsize_integer(value, [&](auto integer, auto) {
+ encoder << integer;
+ });
break;
case SQL::SQLType::Float:
encoder << value.to_double().value();
@@ -704,46 +854,72 @@ bool IPC::encode(Encoder& encoder, SQL::Value const& value)
return true;
}
+template<typename T>
+static ErrorOr<void> decode_scalar(IPC::Decoder& decoder, SQL::Value& value)
+{
+ T decoded {};
+ TRY(decoder.decode(decoded));
+ value = move(decoded);
+ return {};
+}
+
template<>
ErrorOr<void> IPC::decode(Decoder& decoder, SQL::Value& value)
{
- UnderlyingType<SQL::SQLType> type_flags;
+ u8 type_flags { 0 };
TRY(decoder.decode(type_flags));
- if ((type_flags & SQL::sql_type_null_as_flag) && (type_flags != SQL::sql_type_null_as_flag)) {
- type_flags &= ~SQL::sql_type_null_as_flag;
+ auto type_data = static_cast<SQL::TypeData>(type_flags & 0xf0);
+ auto type = static_cast<SQL::SQLType>(type_flags & 0x0f);
- value = SQL::Value(static_cast<SQL::SQLType>(type_flags));
+ if (type_data == SQL::TypeData::Null) {
+ value = SQL::Value(type);
return {};
}
- switch (static_cast<SQL::SQLType>(type_flags)) {
+ switch (type) {
case SQL::SQLType::Null:
break;
- case SQL::SQLType::Text: {
- DeprecatedString text;
- TRY(decoder.decode(text));
- value = move(text);
+ case SQL::SQLType::Text:
+ TRY(decode_scalar<DeprecatedString>(decoder, value));
break;
- }
- case SQL::SQLType::Integer: {
- int number { 0 };
- TRY(decoder.decode(number));
- value = number;
+ case SQL::SQLType::Integer:
+ switch (type_data) {
+ case SQL::TypeData::Int8:
+ TRY(decode_scalar<i8>(decoder, value));
+ break;
+ case SQL::TypeData::Int16:
+ TRY(decode_scalar<i16>(decoder, value));
+ break;
+ case SQL::TypeData::Int32:
+ TRY(decode_scalar<i32>(decoder, value));
+ break;
+ case SQL::TypeData::Int64:
+ TRY(decode_scalar<i64>(decoder, value));
+ break;
+ case SQL::TypeData::Uint8:
+ TRY(decode_scalar<u8>(decoder, value));
+ break;
+ case SQL::TypeData::Uint16:
+ TRY(decode_scalar<u16>(decoder, value));
+ break;
+ case SQL::TypeData::Uint32:
+ TRY(decode_scalar<u32>(decoder, value));
+ break;
+ case SQL::TypeData::Uint64:
+ TRY(decode_scalar<u64>(decoder, value));
+ break;
+ default:
+ VERIFY_NOT_REACHED();
+ break;
+ }
break;
- }
- case SQL::SQLType::Float: {
- double number { 0.0 };
- TRY(decoder.decode(number));
- value = number;
+ case SQL::SQLType::Float:
+ TRY(decode_scalar<double>(decoder, value));
break;
- }
- case SQL::SQLType::Boolean: {
- bool boolean { false };
- TRY(decoder.decode(boolean));
- value = boolean;
+ case SQL::SQLType::Boolean:
+ TRY(decode_scalar<bool>(decoder, value));
break;
- }
case SQL::SQLType::Tuple: {
Vector<SQL::Value> tuple;
TRY(decoder.decode(tuple));