mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-23 08:00:20 +00:00
effcd080ca
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.
907 lines
27 KiB
C++
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();
|
|
}
|