ladybird/Userland/Libraries/LibSQL/Value.cpp
Jan de Visser 85a84b0794 LibSQL: Introduce Serializer as a mediator between Heap and client code
Classes reading and writing to the data heap would communicate directly
with the Heap object, and transfer ByteBuffers back and forth with it.
This makes things like caching and locking hard. Therefore all data
persistence activity will be funneled through a Serializer object which
in turn submits it to the Heap.

Introducing this unfortunately resulted in a huge amount of churn, in
which a number of smaller refactorings got caught up as well.
2021-08-21 22:03:30 +02:00

989 lines
21 KiB
C++

/*
* Copyright (c) 2021, Jan de Visser <jan@de-visser.net>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibSQL/Serializer.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(Serializer& serializer) const
{
u8 type_flags = (u8)type();
if (is_null())
type_flags |= (u8)SQLType::Null;
serializer.serialize<u8>(type_flags);
if (!is_null())
m_impl.visit([&](auto& impl) { serializer.serialize(impl); });
}
void Value::deserialize(Serializer& serializer)
{
auto type_flags = serializer.deserialize<u8>();
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));
setup(type);
if (!is_null) {
m_impl.visit([&](auto& impl) { impl.deserialize(serializer); });
}
}
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) + value().length();
}
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 (!validate(impl))
return false;
m_value.value().empend(impl);
return true;
}
void ContainerValueImpl::serialize_values(Serializer& serializer) const
{
serializer.serialize((u32)size());
for (auto& impl : value()) {
serializer.serialize<Value>(Value(impl));
}
}
void ContainerValueImpl::deserialize_values(Serializer& serializer)
{
auto sz = serializer.deserialize<u32>();
m_value = Vector<BaseTypeImpl>();
for (auto ix = 0u; ix < sz; ix++) {
append(serializer.deserialize<Value>());
}
}
size_t ContainerValueImpl::length() const
{
size_t len = sizeof(u32);
for (auto& impl : value()) {
len += Value(impl).length();
}
return len;
}
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->length() + ContainerValueImpl::length();
}
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(Serializer& serializer) const
{
serializer.serialize<TupleDescriptor>(*m_descriptor);
serialize_values(serializer);
}
void TupleImpl::deserialize(Serializer& serializer)
{
m_descriptor = serializer.adopt_and_deserialize<TupleDescriptor>();
deserialize_values(serializer);
}
void TupleImpl::infer_descriptor()
{
if (!m_descriptor) {
m_descriptor = adopt_ref(*new TupleDescriptor);
m_descriptor_inferred = true;
}
}
void TupleImpl::extend_descriptor(Value const& value)
{
VERIFY(m_descriptor_inferred);
m_descriptor->empend("", value.type(), Order::Ascending);
}
bool TupleImpl::validate_before_assignment(Vector<Value> const& values)
{
if (m_descriptor_inferred)
m_descriptor = nullptr;
if (!m_descriptor) {
infer_descriptor();
if (values.size() > m_descriptor->size()) {
for (auto ix = m_descriptor->size(); ix < values.size(); ix++) {
extend_descriptor(values[ix]);
}
}
}
return true;
}
bool TupleImpl::validate(BaseTypeImpl const& value)
{
if (!m_descriptor)
infer_descriptor();
if (m_descriptor_inferred && (this->value().size() == m_descriptor->size()))
extend_descriptor(Value(value));
if (m_descriptor->size() == this->value().size())
return false;
auto required_type = (*m_descriptor)[this->value().size()].type;
return Value(value).type() == required_type;
}
bool TupleImpl::validate_after_assignment()
{
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
{
return sizeof(u8) + sizeof(u32) + ContainerValueImpl::length();
}
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(Serializer& serializer) const
{
serializer.serialize((u8)m_element_type);
if (m_max_size.has_value())
serializer.serialize((u32)m_max_size.value());
else
serializer.serialize((u32)0);
serialize_values(serializer);
}
void ArrayImpl::deserialize(Serializer& serializer)
{
m_element_type = (SQLType)serializer.deserialize<u8>();
auto max_sz = serializer.deserialize<u32>();
if (max_sz)
m_max_size = max_sz;
else
m_max_size = {};
deserialize_values(serializer);
}
bool ArrayImpl::validate(BaseTypeImpl const& impl)
{
if (m_max_size.has_value() && (size() >= m_max_size.value()))
return false;
return Value(impl).type() == m_element_type;
}
}