mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-26 14:28:49 +00:00
The implemtation of the Value class was based on lambda member variables implementing type-dependent behaviour. This was done to ensure that Values can be used as stack-only objects; the simplest alternative, virtual methods, forces them onto the heap. The problem with the the lambda approach is that it bloats the Values (which are supposed to be lightweight objects) quite considerably, because every object contains more than a dozen function pointers. The solution to address both problems (we want Values to be able to live on the stack and be as lightweight as possible) chosen here is to encapsulate type-dependent behaviour and state in an implementation class, and let the Value be an AK::Variant of those implementation classes. All methods of Value are now basically straight delegates to the implementation object using the Variant::visit method. One issue complicating matters is the addition of two aggregate types, Tuple and Array, which each contain a Vector of Values. At this point Tuples and Arrays (and potential future aggregate types) can't contain these aggregate types. This is limiting and needs to be addressed. Another area that needs attention is the nomenclature of things; it's a bit of a tangle of 'ValueBlahBlah' and 'ImplBlahBlah'. It makes sense right now I think but admit we probably can do better. Other things included here: - Added the Boolean and Null types (and Tuple and Array, see above). - to_string now always succeeds and returns a String instead of an Optional. This had some impact on other sources. - Added a lot of tests. - Started moving the serialization mechanism more towards where I want it to be, i.e. a 'DataSerializer' object which just takes serialization and deserialization requests and knows for example how to store long strings out-of-line. One last remark: There is obviously a naming clash between the Tuple class and the Tuple Value type. This is intentional; I plan to make the Tuple class a subclass of Value (and hence Key and Row as well).
992 lines
21 KiB
C++
992 lines
21 KiB
C++
/*
|
|
* Copyright (c) 2021, Jan de Visser <jan@de-visser.net>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibSQL/Serialize.h>
|
|
#include <LibSQL/Value.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
namespace SQL {
|
|
|
|
Value::Value(SQLType sql_type)
|
|
{
|
|
setup(sql_type);
|
|
}
|
|
|
|
void Value::setup(SQLType type)
|
|
{
|
|
switch (type) {
|
|
#undef __ENUMERATE_SQL_TYPE
|
|
#define __ENUMERATE_SQL_TYPE(name, cardinal, type, impl, size) \
|
|
case SQLType::type: \
|
|
m_impl.set<type##Impl>(type##Impl()); \
|
|
break;
|
|
ENUMERATE_SQL_TYPES(__ENUMERATE_SQL_TYPE)
|
|
#undef __ENUMERATE_SQL_TYPE
|
|
default:
|
|
VERIFY_NOT_REACHED();
|
|
}
|
|
}
|
|
|
|
Value::Value(SQLType sql_type, Value const& value)
|
|
: Value(sql_type)
|
|
{
|
|
assign(value);
|
|
}
|
|
|
|
Value::Value(SQLType sql_type, String const& string)
|
|
: Value(sql_type)
|
|
{
|
|
assign(string);
|
|
}
|
|
|
|
Value::Value(SQLType sql_type, char const* string)
|
|
: Value(sql_type)
|
|
{
|
|
assign(String(string));
|
|
}
|
|
|
|
Value::Value(SQLType sql_type, int integer)
|
|
: Value(sql_type)
|
|
{
|
|
assign(integer);
|
|
}
|
|
|
|
Value::Value(SQLType sql_type, double dbl)
|
|
: Value(sql_type)
|
|
{
|
|
assign(dbl);
|
|
}
|
|
|
|
Value::Value(SQLType sql_type, bool boolean)
|
|
: Value(sql_type)
|
|
{
|
|
assign(boolean);
|
|
}
|
|
|
|
Value::Value(String const& string)
|
|
: Value(SQLType::Text)
|
|
{
|
|
assign(string);
|
|
}
|
|
|
|
Value::Value(char const* string)
|
|
: Value(SQLType::Text)
|
|
{
|
|
assign(String(string));
|
|
}
|
|
|
|
Value::Value(int integer)
|
|
: Value(SQLType::Integer)
|
|
{
|
|
assign(integer);
|
|
}
|
|
|
|
Value::Value(double dbl)
|
|
: Value(SQLType::Float)
|
|
{
|
|
assign(dbl);
|
|
}
|
|
|
|
Value::Value(bool boolean)
|
|
: Value(SQLType::Boolean)
|
|
{
|
|
assign(boolean);
|
|
}
|
|
|
|
Value Value::create_tuple(NonnullRefPtr<TupleDescriptor> const& tuple_descriptor)
|
|
{
|
|
return Value(Value::SetImplementationSingleton, TupleImpl(tuple_descriptor));
|
|
}
|
|
|
|
Value Value::create_array(SQLType element_type, Optional<size_t> const& max_size)
|
|
{
|
|
return Value(Value::SetImplementationSingleton, ArrayImpl(element_type, max_size));
|
|
}
|
|
|
|
Value const& Value::null()
|
|
{
|
|
static Value s_null(SQLType::Null);
|
|
return s_null;
|
|
}
|
|
|
|
bool Value::is_null() const
|
|
{
|
|
return m_impl.visit([&](auto& impl) { return impl.is_null(); });
|
|
}
|
|
|
|
SQLType Value::type() const
|
|
{
|
|
return m_impl.visit([&](auto& impl) { return impl.type(); });
|
|
}
|
|
|
|
String Value::type_name() const
|
|
{
|
|
return m_impl.visit([&](auto& impl) { return impl.type_name(); });
|
|
}
|
|
|
|
BaseTypeImpl Value::downcast_to_basetype() const
|
|
{
|
|
return m_impl.downcast<NullImpl, TextImpl, IntegerImpl, FloatImpl, BooleanImpl>();
|
|
}
|
|
|
|
String Value::to_string() const
|
|
{
|
|
if (is_null())
|
|
return "(null)";
|
|
return m_impl.visit([&](auto& impl) { return impl.to_string(); });
|
|
}
|
|
|
|
Optional<int> Value::to_int() const
|
|
{
|
|
if (is_null())
|
|
return {};
|
|
return m_impl.visit([&](auto& impl) { return impl.to_int(); });
|
|
}
|
|
|
|
Optional<u32> Value::to_u32() const
|
|
{
|
|
if (is_null())
|
|
return {};
|
|
auto ret = to_int();
|
|
if (ret.has_value())
|
|
return static_cast<u32>(ret.value());
|
|
return {};
|
|
}
|
|
|
|
Optional<double> Value::to_double() const
|
|
{
|
|
if (is_null())
|
|
return {};
|
|
return m_impl.visit([&](auto& impl) { return impl.to_double(); });
|
|
}
|
|
|
|
Optional<bool> Value::to_bool() const
|
|
{
|
|
if (is_null())
|
|
return {};
|
|
return m_impl.visit([&](auto& impl) { return impl.to_bool(); });
|
|
}
|
|
|
|
Optional<Vector<Value>> Value::to_vector() const
|
|
{
|
|
if (is_null())
|
|
return {};
|
|
Vector<Value> vector;
|
|
if (m_impl.visit([&](auto& impl) { return impl.to_vector(vector); }))
|
|
return vector;
|
|
else
|
|
return {};
|
|
}
|
|
|
|
Value::operator String() const
|
|
{
|
|
return to_string();
|
|
}
|
|
|
|
Value::operator int() const
|
|
{
|
|
auto i = to_int();
|
|
VERIFY(i.has_value());
|
|
return i.value();
|
|
}
|
|
|
|
Value::operator u32() const
|
|
{
|
|
auto i = to_u32();
|
|
VERIFY(i.has_value());
|
|
return i.value();
|
|
}
|
|
|
|
Value::operator double() const
|
|
{
|
|
auto d = to_double();
|
|
VERIFY(d.has_value());
|
|
return d.value();
|
|
}
|
|
|
|
Value::operator bool() const
|
|
{
|
|
auto b = to_bool();
|
|
VERIFY(b.has_value());
|
|
return b.value();
|
|
}
|
|
|
|
void Value::assign(Value const& other_value)
|
|
{
|
|
m_impl.visit([&](auto& impl) { impl.assign(other_value); });
|
|
}
|
|
|
|
void Value::assign(String const& string_value)
|
|
{
|
|
m_impl.visit([&](auto& impl) { impl.assign_string(string_value); });
|
|
}
|
|
|
|
void Value::assign(int const& int_value)
|
|
{
|
|
m_impl.visit([&](auto& impl) { impl.assign_int(int_value); });
|
|
}
|
|
|
|
void Value::assign(double const& double_value)
|
|
{
|
|
m_impl.visit([&](auto& impl) { impl.assign_double(double_value); });
|
|
}
|
|
|
|
void Value::assign(bool const& bool_value)
|
|
{
|
|
m_impl.visit([&](auto& impl) { impl.assign_bool(bool_value); });
|
|
}
|
|
|
|
void Value::assign(Vector<Value> const& values)
|
|
{
|
|
m_impl.visit([&](auto& impl) { impl.assign_vector(values); });
|
|
}
|
|
|
|
Value& Value::operator=(Value const& other)
|
|
{
|
|
if (this != &other) {
|
|
if (other.is_null()) {
|
|
assign(null());
|
|
} else {
|
|
VERIFY(can_cast(other));
|
|
assign(other);
|
|
}
|
|
}
|
|
return (*this);
|
|
}
|
|
|
|
Value& Value::operator=(String const& value)
|
|
{
|
|
assign(value);
|
|
return (*this);
|
|
}
|
|
|
|
Value& Value::operator=(char const* value)
|
|
{
|
|
assign(String(value));
|
|
return (*this);
|
|
}
|
|
|
|
Value& Value::operator=(int value)
|
|
{
|
|
assign(value);
|
|
return (*this);
|
|
}
|
|
|
|
Value& Value::operator=(u32 value)
|
|
{
|
|
assign(static_cast<int>(value));
|
|
return (*this);
|
|
}
|
|
|
|
Value& Value::operator=(double value)
|
|
{
|
|
assign(value);
|
|
return (*this);
|
|
}
|
|
|
|
Value& Value::operator=(bool value)
|
|
{
|
|
assign(value);
|
|
return (*this);
|
|
}
|
|
|
|
Value& Value::operator=(Vector<Value> const& vector)
|
|
{
|
|
assign(vector);
|
|
return (*this);
|
|
}
|
|
|
|
size_t Value::length() const
|
|
{
|
|
return m_impl.visit([&](auto& impl) { return impl.length(); });
|
|
}
|
|
|
|
u32 Value::hash() const
|
|
{
|
|
return (is_null()) ? 0u : m_impl.visit([&](auto& impl) { return impl.hash(); });
|
|
}
|
|
|
|
bool Value::can_cast(Value const& other_value) const
|
|
{
|
|
if (type() == other_value.type())
|
|
return true;
|
|
return m_impl.visit([&](auto& impl) { return impl.can_cast(other_value); });
|
|
}
|
|
|
|
int Value::compare(Value const& other) const
|
|
{
|
|
if (is_null())
|
|
return -1;
|
|
if (other.is_null())
|
|
return 1;
|
|
return m_impl.visit([&](auto& impl) { return impl.compare(other); });
|
|
}
|
|
|
|
bool Value::operator==(Value const& other) const
|
|
{
|
|
return compare(other) == 0;
|
|
}
|
|
|
|
bool Value::operator==(String const& string_value) const
|
|
{
|
|
return to_string() == string_value;
|
|
}
|
|
|
|
bool Value::operator==(int int_value) const
|
|
{
|
|
auto i = to_int();
|
|
if (!i.has_value())
|
|
return false;
|
|
return i.value() == int_value;
|
|
}
|
|
|
|
bool Value::operator==(double double_value) const
|
|
{
|
|
auto d = to_double();
|
|
if (!d.has_value())
|
|
return false;
|
|
return d.value() == double_value;
|
|
}
|
|
|
|
bool Value::operator!=(Value const& other) const
|
|
{
|
|
return compare(other) != 0;
|
|
}
|
|
|
|
bool Value::operator<(Value const& other) const
|
|
{
|
|
return compare(other) < 0;
|
|
}
|
|
|
|
bool Value::operator<=(Value const& other) const
|
|
{
|
|
return compare(other) <= 0;
|
|
}
|
|
|
|
bool Value::operator>(Value const& other) const
|
|
{
|
|
return compare(other) > 0;
|
|
}
|
|
|
|
bool Value::operator>=(Value const& other) const
|
|
{
|
|
return compare(other) >= 0;
|
|
}
|
|
|
|
void Value::serialize_to(ByteBuffer& buffer) const
|
|
{
|
|
u8 type_flags = (u8)type();
|
|
if (is_null())
|
|
type_flags |= (u8)SQLType::Null;
|
|
SQL::serialize_to(buffer, type_flags);
|
|
if (!is_null())
|
|
m_impl.visit([&](auto& impl) { impl.serialize(buffer); });
|
|
}
|
|
|
|
void Value::deserialize(ByteBuffer& buffer, size_t& offset_at)
|
|
{
|
|
m_impl.visit([&](auto& impl) { impl.deserialize(buffer, offset_at); });
|
|
}
|
|
|
|
Value Value::deserialize_from(ByteBuffer& buffer, size_t& at_offset)
|
|
{
|
|
u8 type_flags;
|
|
SQL::deserialize_from(buffer, at_offset, type_flags);
|
|
bool is_null = false;
|
|
if ((type_flags & (u8)SQLType::Null) && (type_flags != (u8)SQLType::Null)) {
|
|
type_flags &= ~((u8)SQLType::Null);
|
|
is_null = true;
|
|
}
|
|
auto type = (SQLType)type_flags;
|
|
VERIFY(!is_null || (type != SQLType::Tuple && type != SQLType::Array));
|
|
Value ret(type);
|
|
if (!is_null) {
|
|
ret.deserialize(buffer, at_offset);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
bool NullImpl::can_cast(Value const& value)
|
|
{
|
|
return value.is_null();
|
|
}
|
|
|
|
int NullImpl::compare(Value const& other)
|
|
{
|
|
return other.type() == SQLType::Null;
|
|
}
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
String TextImpl::to_string() const
|
|
{
|
|
return value();
|
|
}
|
|
|
|
Optional<int> TextImpl::to_int() const
|
|
{
|
|
if (!m_value.has_value())
|
|
return {};
|
|
return value().to_int();
|
|
}
|
|
|
|
Optional<double> TextImpl::to_double() const
|
|
{
|
|
if (!m_value.has_value())
|
|
return {};
|
|
char* end_ptr;
|
|
double ret = strtod(value().characters(), &end_ptr);
|
|
if (end_ptr == value().characters()) {
|
|
return {};
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Optional<bool> TextImpl::to_bool() const
|
|
{
|
|
if (!m_value.has_value())
|
|
return {};
|
|
if (value().equals_ignoring_case("true") || value().equals_ignoring_case("t"))
|
|
return true;
|
|
if (value().equals_ignoring_case("false") || value().equals_ignoring_case("f"))
|
|
return false;
|
|
return {};
|
|
}
|
|
|
|
void TextImpl::assign(Value const& other_value)
|
|
{
|
|
if (other_value.type() == SQLType::Null) {
|
|
m_value = {};
|
|
} else {
|
|
m_value = other_value.to_string();
|
|
}
|
|
}
|
|
|
|
void TextImpl::assign_string(String const& string_value)
|
|
{
|
|
m_value = string_value;
|
|
}
|
|
|
|
void TextImpl::assign_int(int int_value)
|
|
{
|
|
m_value = String::number(int_value);
|
|
}
|
|
|
|
void TextImpl::assign_double(double double_value)
|
|
{
|
|
m_value = String::number(double_value);
|
|
}
|
|
|
|
void TextImpl::assign_bool(bool bool_value)
|
|
{
|
|
m_value = (bool_value) ? "true" : "false";
|
|
}
|
|
|
|
size_t TextImpl::length() const
|
|
{
|
|
return (is_null()) ? 0 : sizeof(u32) + min(value().length(), 64) + 1;
|
|
}
|
|
|
|
int TextImpl::compare(Value const& other) const
|
|
{
|
|
if (is_null())
|
|
return -1;
|
|
auto s1 = value();
|
|
auto s2 = other.to_string();
|
|
if (s1 == s2)
|
|
return 0;
|
|
return (s1 < s2) ? -1 : 1;
|
|
}
|
|
|
|
u32 TextImpl::hash() const
|
|
{
|
|
return value().hash();
|
|
}
|
|
|
|
String IntegerImpl::to_string() const
|
|
{
|
|
return String::formatted("{}", value());
|
|
}
|
|
|
|
Optional<int> IntegerImpl::to_int() const
|
|
{
|
|
return value();
|
|
}
|
|
|
|
Optional<double> IntegerImpl::to_double() const
|
|
{
|
|
return static_cast<double>(value());
|
|
}
|
|
|
|
Optional<bool> IntegerImpl::to_bool() const
|
|
{
|
|
return value() != 0;
|
|
}
|
|
|
|
void IntegerImpl::assign(Value const& other_value)
|
|
{
|
|
auto i = other_value.to_int();
|
|
if (!i.has_value())
|
|
m_value = {};
|
|
else
|
|
m_value = i.value();
|
|
}
|
|
|
|
void IntegerImpl::assign_string(String const& string_value)
|
|
{
|
|
auto i = string_value.to_int();
|
|
if (!i.has_value())
|
|
m_value = {};
|
|
else
|
|
m_value = i.value();
|
|
}
|
|
|
|
void IntegerImpl::assign_int(int int_value)
|
|
{
|
|
m_value = int_value;
|
|
}
|
|
|
|
void IntegerImpl::assign_double(double double_value)
|
|
{
|
|
m_value = static_cast<int>(round(double_value));
|
|
}
|
|
|
|
void IntegerImpl::assign_bool(bool bool_value)
|
|
{
|
|
m_value = (bool_value) ? 1 : 0;
|
|
}
|
|
|
|
bool IntegerImpl::can_cast(Value const& other_value)
|
|
{
|
|
return other_value.to_int().has_value();
|
|
}
|
|
|
|
int IntegerImpl::compare(Value const& other) const
|
|
{
|
|
auto casted = other.to_int();
|
|
if (!casted.has_value()) {
|
|
return 1;
|
|
}
|
|
return value() - casted.value();
|
|
}
|
|
|
|
u32 IntegerImpl::hash() const
|
|
{
|
|
return int_hash(value());
|
|
}
|
|
|
|
String FloatImpl::to_string() const
|
|
{
|
|
return String::formatted("{}", value());
|
|
}
|
|
|
|
Optional<int> FloatImpl::to_int() const
|
|
{
|
|
return static_cast<int>(round(value()));
|
|
}
|
|
|
|
Optional<double> FloatImpl::to_double() const
|
|
{
|
|
return value();
|
|
}
|
|
|
|
void FloatImpl::assign(Value const& other_value)
|
|
{
|
|
auto i = other_value.to_double();
|
|
if (!i.has_value())
|
|
m_value = {};
|
|
else
|
|
m_value = i.value();
|
|
}
|
|
|
|
void FloatImpl::assign_string(String const& string_value)
|
|
{
|
|
char* end_ptr;
|
|
auto dbl = strtod(string_value.characters(), &end_ptr);
|
|
if (end_ptr == string_value.characters())
|
|
m_value = {};
|
|
else
|
|
m_value = dbl;
|
|
}
|
|
|
|
void FloatImpl::assign_int(int int_value)
|
|
{
|
|
m_value = int_value;
|
|
}
|
|
|
|
void FloatImpl::assign_double(double double_value)
|
|
{
|
|
m_value = double_value;
|
|
}
|
|
|
|
bool FloatImpl::can_cast(Value const& other_value)
|
|
{
|
|
return other_value.to_double().has_value();
|
|
}
|
|
|
|
int FloatImpl::compare(Value const& other) const
|
|
{
|
|
auto casted = other.to_double();
|
|
if (!casted.has_value()) {
|
|
return 1;
|
|
}
|
|
auto diff = value() - casted.value();
|
|
return (diff < NumericLimits<double>::epsilon()) ? 0 : ((diff > 0) ? 1 : -1);
|
|
}
|
|
|
|
String BooleanImpl::to_string() const
|
|
{
|
|
return (value()) ? "true" : "false";
|
|
}
|
|
|
|
Optional<int> BooleanImpl::to_int() const
|
|
{
|
|
return (value()) ? 1 : 0;
|
|
}
|
|
|
|
Optional<double> BooleanImpl::to_double()
|
|
{
|
|
return {};
|
|
}
|
|
|
|
Optional<bool> BooleanImpl::to_bool() const
|
|
{
|
|
return value();
|
|
}
|
|
|
|
void BooleanImpl::assign(Value const& other_value)
|
|
{
|
|
auto b = other_value.to_bool();
|
|
if (!b.has_value())
|
|
m_value = {};
|
|
else
|
|
m_value = b.value();
|
|
}
|
|
|
|
void BooleanImpl::assign_string(String const& string_value)
|
|
{
|
|
return assign(Value(string_value));
|
|
}
|
|
|
|
void BooleanImpl::assign_int(int int_value)
|
|
{
|
|
m_value = (int_value != 0);
|
|
}
|
|
|
|
void BooleanImpl::assign_double(double)
|
|
{
|
|
m_value = {};
|
|
}
|
|
|
|
void BooleanImpl::assign_bool(bool bool_value)
|
|
{
|
|
m_value = bool_value;
|
|
}
|
|
|
|
bool BooleanImpl::can_cast(Value const& other_value)
|
|
{
|
|
return other_value.to_bool().has_value();
|
|
}
|
|
|
|
int BooleanImpl::compare(Value const& other) const
|
|
{
|
|
auto casted = other.to_bool();
|
|
if (!casted.has_value()) {
|
|
return 1;
|
|
}
|
|
return value() ^ casted.value(); // xor - zero if both true or both false, 1 otherwise.
|
|
}
|
|
|
|
u32 BooleanImpl::hash() const
|
|
{
|
|
return int_hash(value());
|
|
}
|
|
|
|
void ContainerValueImpl::assign_vector(Vector<Value> const& vector_values)
|
|
{
|
|
if (!validate_before_assignment(vector_values)) {
|
|
m_value = {};
|
|
return;
|
|
}
|
|
m_value = Vector<BaseTypeImpl>();
|
|
for (auto& value : vector_values) {
|
|
if (!append(value)) {
|
|
m_value = {};
|
|
return;
|
|
}
|
|
}
|
|
if (!validate_after_assignment())
|
|
m_value = {};
|
|
}
|
|
|
|
bool ContainerValueImpl::to_vector(Vector<Value>& vector) const
|
|
{
|
|
vector.clear();
|
|
for (auto& value : value()) {
|
|
vector.empend(Value(value));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Vector<String> ContainerValueImpl::to_string_vector() const
|
|
{
|
|
Vector<String> ret;
|
|
for (auto& value : value()) {
|
|
ret.append(Value(value).to_string());
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
String ContainerValueImpl::to_string() const
|
|
{
|
|
StringBuilder builder;
|
|
builder.append("(");
|
|
StringBuilder joined;
|
|
joined.join(", ", to_string_vector());
|
|
builder.append(joined.string_view());
|
|
builder.append(")");
|
|
return builder.build();
|
|
}
|
|
|
|
u32 ContainerValueImpl::hash() const
|
|
{
|
|
u32 ret = 0u;
|
|
for (auto& value : value()) {
|
|
Value v(value);
|
|
// This is an extension of the pair_int_hash function from AK/HashFunctions.h:
|
|
if (!ret)
|
|
ret = v.hash();
|
|
else
|
|
ret = int_hash((ret * 209) ^ (v.hash() * 413));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool ContainerValueImpl::append(Value const& value)
|
|
{
|
|
if (value.type() == SQLType::Tuple || value.type() == SQLType::Array)
|
|
return false;
|
|
return append(value.downcast_to_basetype());
|
|
}
|
|
|
|
bool ContainerValueImpl::append(BaseTypeImpl const& impl)
|
|
{
|
|
if (m_max_size.has_value() && (size() >= m_max_size.value()))
|
|
return false;
|
|
if (!validate(impl))
|
|
return false;
|
|
m_value.value().empend(impl);
|
|
return true;
|
|
}
|
|
|
|
void ContainerValueImpl::serialize_values(ByteBuffer& buffer) const
|
|
{
|
|
serialize_to(buffer, (u32)size());
|
|
for (auto& value : value()) {
|
|
Value(value).serialize_to(buffer);
|
|
}
|
|
}
|
|
|
|
void ContainerValueImpl::deserialize_values(ByteBuffer& buffer, size_t& at_offset)
|
|
{
|
|
u32 sz;
|
|
deserialize_from(buffer, at_offset, sz);
|
|
m_value = Vector<BaseTypeImpl>();
|
|
for (auto ix = 0u; ix < sz; ix++) {
|
|
append(Value::deserialize_from(buffer, at_offset));
|
|
}
|
|
}
|
|
|
|
void TupleImpl::assign(Value const& other)
|
|
{
|
|
if (other.type() != SQLType::Tuple) {
|
|
m_value = {};
|
|
return;
|
|
}
|
|
auto& other_impl = other.get_impl<TupleImpl>({});
|
|
auto other_descriptor = other_impl.m_descriptor;
|
|
if (m_descriptor && other_descriptor && m_descriptor->compare_ignoring_names(*other_descriptor)) {
|
|
m_value = {};
|
|
return;
|
|
}
|
|
assign_vector(other.to_vector().value());
|
|
}
|
|
|
|
size_t TupleImpl::length() const
|
|
{
|
|
return (m_descriptor) ? m_descriptor->data_length() : 0;
|
|
}
|
|
|
|
bool TupleImpl::can_cast(Value const& other_value) const
|
|
{
|
|
if (other_value.type() != SQLType::Tuple)
|
|
return false;
|
|
return (m_descriptor == other_value.get_impl<TupleImpl>({}).m_descriptor);
|
|
}
|
|
|
|
int TupleImpl::compare(Value const& other) const
|
|
{
|
|
if (other.type() != SQLType::Tuple)
|
|
return 1;
|
|
|
|
auto& other_impl = other.get_impl<TupleImpl>({});
|
|
if (m_descriptor && other_impl.m_descriptor && m_descriptor->compare_ignoring_names(*other_impl.m_descriptor))
|
|
return 1;
|
|
|
|
auto other_values = other_impl.value();
|
|
if (size() != other_impl.size())
|
|
return (int)value().size() - (int)other_impl.size();
|
|
for (auto ix = 0u; ix < value().size(); ix++) {
|
|
auto ret = Value(value()[ix]).compare(Value(other_impl.value()[ix]));
|
|
if (ret != 0) {
|
|
if (m_descriptor && (ix < m_descriptor->size()) && (*m_descriptor)[ix].order == Order::Descending)
|
|
ret = -ret;
|
|
return ret;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void TupleImpl::serialize(ByteBuffer& buffer) const
|
|
{
|
|
if (m_descriptor) {
|
|
serialize_to(buffer, (u32)m_descriptor->size());
|
|
for (auto& tuple_element : *m_descriptor) {
|
|
u8 elem_type = (u8)tuple_element.type;
|
|
serialize_to(buffer, elem_type);
|
|
u8 elem_order = (u8)tuple_element.order;
|
|
serialize_to(buffer, elem_order);
|
|
}
|
|
} else {
|
|
serialize_to(buffer, (u32)-1);
|
|
}
|
|
serialize_values(buffer);
|
|
}
|
|
|
|
void TupleImpl::deserialize(ByteBuffer& buffer, size_t& at_offset)
|
|
{
|
|
u32 sz;
|
|
deserialize_from(buffer, at_offset, sz);
|
|
if (sz != (u32)-1) {
|
|
NonnullRefPtr<TupleDescriptor> serialized_descriptor = adopt_ref(*new TupleDescriptor);
|
|
for (auto ix = 0u; ix < sz; ix++) {
|
|
u8 elem_type, elem_order;
|
|
deserialize_from(buffer, at_offset, elem_type);
|
|
deserialize_from(buffer, at_offset, elem_order);
|
|
serialized_descriptor->empend("", (SQLType)elem_type, (Order)elem_order);
|
|
}
|
|
m_descriptor = serialized_descriptor;
|
|
m_max_size = m_descriptor->size();
|
|
} else {
|
|
m_descriptor = nullptr;
|
|
m_max_size = {};
|
|
}
|
|
deserialize_values(buffer, at_offset);
|
|
}
|
|
|
|
bool TupleImpl::validate(BaseTypeImpl const& value)
|
|
{
|
|
if (!m_descriptor)
|
|
return true;
|
|
auto required_type = (*m_descriptor)[size()].type;
|
|
return Value(value).type() == required_type;
|
|
}
|
|
|
|
bool TupleImpl::validate_after_assignment()
|
|
{
|
|
if (!m_descriptor)
|
|
return true;
|
|
for (auto ix = value().size(); ix < m_descriptor->size(); ++ix) {
|
|
auto required_type = (*m_descriptor)[ix].type;
|
|
append(Value(required_type));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ArrayImpl::assign(Value const& other)
|
|
{
|
|
if (other.type() != SQLType::Array) {
|
|
m_value = {};
|
|
return;
|
|
}
|
|
auto& other_impl = other.get_impl<ArrayImpl>({});
|
|
if (m_max_size != other_impl.m_max_size || m_element_type != other_impl.m_element_type) {
|
|
m_value = {};
|
|
return;
|
|
}
|
|
assign_vector(other.to_vector().value());
|
|
}
|
|
|
|
size_t ArrayImpl::length() const
|
|
{
|
|
size_t ret = 0;
|
|
for (auto& value : value()) {
|
|
ret += Value(value).length();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool ArrayImpl::can_cast(Value const& other_value) const
|
|
{
|
|
if (other_value.type() != SQLType::Array)
|
|
return false;
|
|
auto& other_impl = other_value.get_impl<ArrayImpl>({});
|
|
return (m_max_size != other_impl.m_max_size || m_element_type != other_impl.m_element_type);
|
|
}
|
|
|
|
int ArrayImpl::compare(Value const& other) const
|
|
{
|
|
if (other.type() != SQLType::Array)
|
|
return 1;
|
|
auto other_impl = other.get_impl<ArrayImpl>({});
|
|
if (other_impl.m_element_type != m_element_type)
|
|
return 1;
|
|
if (other_impl.m_max_size.has_value() && m_max_size.has_value() && other_impl.m_max_size != m_max_size)
|
|
return (int)m_max_size.value() - (int)other_impl.m_max_size.value();
|
|
if (size() != other_impl.size())
|
|
return (int)size() - (int)other_impl.size();
|
|
for (auto ix = 0u; ix < size(); ix++) {
|
|
auto ret = Value(value()[ix]).compare(Value(other_impl.value()[ix]));
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void ArrayImpl::serialize(ByteBuffer& buffer) const
|
|
{
|
|
serialize_to(buffer, (u8)m_element_type);
|
|
if (m_max_size.has_value())
|
|
serialize_to(buffer, (u32)m_max_size.value());
|
|
else
|
|
serialize_to(buffer, (u32)0);
|
|
serialize_values(buffer);
|
|
}
|
|
|
|
void ArrayImpl::deserialize(ByteBuffer& buffer, size_t& at_offset)
|
|
{
|
|
u8 elem_type;
|
|
deserialize_from(buffer, at_offset, elem_type);
|
|
m_element_type = (SQLType)elem_type;
|
|
u32 max_sz;
|
|
deserialize_from(buffer, at_offset, max_sz);
|
|
if (max_sz)
|
|
m_max_size = max_sz;
|
|
else
|
|
m_max_size = {};
|
|
deserialize_values(buffer, at_offset);
|
|
}
|
|
|
|
bool ArrayImpl::validate(BaseTypeImpl const& impl)
|
|
{
|
|
return Value(impl).type() == m_element_type;
|
|
}
|
|
|
|
}
|