1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 23:38:12 +00:00

LibSQL: Redesign Value implementation and add new types

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).
This commit is contained in:
Jan de Visser 2021-07-17 07:02:28 -04:00 committed by Andreas Kling
parent a5e28f2897
commit b74721e604
9 changed files with 1724 additions and 428 deletions

View file

@ -6,14 +6,290 @@
#pragma once
#include <AK/Badge.h>
#include <AK/ByteBuffer.h>
#include <AK/Function.h>
#include <AK/ScopeGuard.h>
#include <AK/String.h>
#include <AK/Variant.h>
#include <LibSQL/Serialize.h>
#include <LibSQL/TupleDescriptor.h>
#include <LibSQL/Type.h>
#include <string.h>
namespace SQL {
class Value;
class BaseImpl {
public:
explicit BaseImpl(SQLType type = SQLType::Null)
: m_type(type)
{
}
[[nodiscard]] SQLType type() const { return m_type; }
[[nodiscard]] String type_name() const { return SQLType_name(type()); }
private:
SQLType m_type { SQLType::Null };
};
class NullImpl : public BaseImpl {
public:
explicit NullImpl()
: BaseImpl(SQLType::Null)
{
}
[[nodiscard]] static bool is_null() { return true; }
[[nodiscard]] static String to_string() { return "(null)"; }
[[nodiscard]] static Optional<int> to_int() { return {}; }
[[nodiscard]] static Optional<double> to_double() { return {}; }
[[nodiscard]] static Optional<bool> to_bool() { return {}; }
[[nodiscard]] static bool to_vector(Vector<Value>&) { return false; }
static void assign(Value const&) { }
static void assign_string(String const&) { }
static void assign_int(int) { }
static void assign_double(double) { }
static void assign_bool(bool) { }
static void assign_vector(Vector<Value> const&) { }
[[nodiscard]] static size_t length() { return 0; }
[[nodiscard]] static bool can_cast(Value const&);
[[nodiscard]] static int compare(Value const&);
static void serialize(ByteBuffer&) { }
static void deserialize(ByteBuffer&, size_t&) { }
[[nodiscard]] static u32 hash() { return 0; }
};
template<typename T>
class Impl : public BaseImpl {
public:
[[nodiscard]] bool is_null() const
{
return !m_value.has_value();
}
[[nodiscard]] T const& value() const
{
VERIFY(m_value.has_value());
return m_value.value();
}
[[nodiscard]] size_t length() const
{
return sizeof(T);
}
void serialize(ByteBuffer& buffer) const
{
serialize_to(buffer, value());
}
void deserialize(ByteBuffer& buffer, size_t& at_offset)
{
T value;
deserialize_from(buffer, at_offset, value);
m_value = value;
}
protected:
explicit Impl(SQLType sql_type)
: BaseImpl(sql_type)
{
}
Optional<T> m_value {};
};
class TextImpl : public Impl<String> {
public:
explicit TextImpl()
: Impl(SQLType::Text)
{
}
[[nodiscard]] String to_string() const;
[[nodiscard]] Optional<int> to_int() const;
[[nodiscard]] Optional<double> to_double() const;
[[nodiscard]] Optional<bool> to_bool() const;
[[nodiscard]] static bool to_vector(Vector<Value>&) { return false; }
void assign(Value const&);
void assign_string(String const&);
void assign_int(int);
void assign_double(double);
void assign_bool(bool);
void assign_vector(Vector<Value> const&) { m_value = {}; }
[[nodiscard]] size_t length() const;
[[nodiscard]] static bool can_cast(Value const&) { return true; }
[[nodiscard]] int compare(Value const& other) const;
[[nodiscard]] u32 hash() const;
};
class IntegerImpl : public Impl<int> {
public:
IntegerImpl()
: Impl(SQLType::Integer)
{
}
[[nodiscard]] String to_string() const;
[[nodiscard]] Optional<int> to_int() const;
[[nodiscard]] Optional<double> to_double() const;
[[nodiscard]] Optional<bool> to_bool() const;
[[nodiscard]] static bool to_vector(Vector<Value>&) { return false; }
void assign(Value const&);
void assign_string(String const&);
void assign_int(int);
void assign_double(double);
void assign_bool(bool);
void assign_vector(Vector<Value> const&) { m_value = {}; }
[[nodiscard]] static bool can_cast(Value const&);
[[nodiscard]] int compare(Value const& other) const;
[[nodiscard]] u32 hash() const;
};
class FloatImpl : public Impl<double> {
public:
explicit FloatImpl()
: Impl(SQLType::Float)
{
}
[[nodiscard]] String to_string() const;
[[nodiscard]] Optional<int> to_int() const;
[[nodiscard]] Optional<double> to_double() const;
[[nodiscard]] static Optional<bool> to_bool() { return {}; }
[[nodiscard]] static bool to_vector(Vector<Value>&) { return false; }
void assign(Value const&);
void assign_string(String const&);
void assign_int(int);
void assign_double(double);
void assign_bool(bool) { m_value = {}; }
void assign_vector(Vector<Value> const&) { m_value = {}; }
[[nodiscard]] static bool can_cast(Value const&);
[[nodiscard]] int compare(Value const& other) const;
// Using floats in hash functions is a bad idea. Let's disable that for now.
[[nodiscard]] static u32 hash() { VERIFY_NOT_REACHED(); }
};
class BooleanImpl : public Impl<bool> {
public:
explicit BooleanImpl()
: Impl(SQLType::Boolean)
{
}
[[nodiscard]] String to_string() const;
[[nodiscard]] Optional<int> to_int() const;
[[nodiscard]] static Optional<double> to_double();
[[nodiscard]] Optional<bool> to_bool() const;
[[nodiscard]] static bool to_vector(Vector<Value>&) { return false; }
void assign(Value const&);
void assign_string(String const&);
void assign_int(int);
void assign_double(double);
void assign_bool(bool);
void assign_vector(Vector<Value> const&) { m_value = {}; }
[[nodiscard]] static bool can_cast(Value const&);
[[nodiscard]] int compare(Value const& other) const;
[[nodiscard]] u32 hash() const;
};
using BaseTypeImpl = Variant<NullImpl, TextImpl, IntegerImpl, FloatImpl, BooleanImpl>;
class ContainerValueImpl : public Impl<Vector<BaseTypeImpl>> {
public:
virtual ~ContainerValueImpl() = default;
[[nodiscard]] String to_string() const;
[[nodiscard]] static Optional<int> to_int() { return {}; }
[[nodiscard]] static Optional<double> to_double() { return {}; }
[[nodiscard]] static Optional<bool> to_bool() { return {}; }
[[nodiscard]] bool to_vector(Vector<Value>&) const;
void assign_string(String const&) { m_value = {}; }
void assign_int(int) { m_value = {}; }
void assign_double(double) { m_value = {}; }
void assign_bool(bool) { m_value = {}; }
void assign_vector(Vector<Value> const&);
[[nodiscard]] u32 hash() const;
virtual bool validate_before_assignment(Vector<Value> const&) { return true; }
virtual bool validate(BaseTypeImpl const&) { return true; }
virtual bool validate_after_assignment() { return true; }
[[nodiscard]] Vector<String> to_string_vector() const;
[[nodiscard]] size_t size() const { return is_null() ? 0 : value().size(); }
bool append(Value const&);
bool append(BaseTypeImpl const& value);
void serialize_values(ByteBuffer& buffer) const;
void deserialize_values(ByteBuffer&, size_t& at_offset);
protected:
explicit ContainerValueImpl(SQLType sql_type, Optional<size_t> const& max_size = {})
: Impl(sql_type)
, m_max_size(max_size)
{
}
Optional<size_t> m_max_size {};
};
class TupleImpl : public ContainerValueImpl {
public:
explicit TupleImpl(NonnullRefPtr<TupleDescriptor> const& descriptor, bool is_null = true)
: ContainerValueImpl(SQLType::Tuple, is_null)
, m_descriptor(descriptor)
{
m_max_size = m_descriptor->size();
}
explicit TupleImpl()
: ContainerValueImpl(SQLType::Tuple, {})
{
}
void assign(Value const&);
[[nodiscard]] size_t length() const;
[[nodiscard]] bool can_cast(Value const&) const;
[[nodiscard]] int compare(Value const& other) const;
virtual bool validate(BaseTypeImpl const&) override;
virtual bool validate_after_assignment() override;
void serialize(ByteBuffer& buffer) const;
void deserialize(ByteBuffer& buffer, size_t&);
private:
RefPtr<TupleDescriptor> m_descriptor;
};
class ArrayImpl : public ContainerValueImpl {
public:
explicit ArrayImpl(SQLType element_type, Optional<size_t> const& max_size = {})
: ContainerValueImpl(SQLType::Array, max_size)
, m_element_type(element_type)
{
}
explicit ArrayImpl()
: ContainerValueImpl(SQLType::Array, {})
, m_element_type(SQLType::Null)
{
}
void assign(Value const&);
[[nodiscard]] size_t length() const;
[[nodiscard]] bool can_cast(Value const&) const;
[[nodiscard]] int compare(Value const& other) const;
void serialize(ByteBuffer& buffer) const;
void deserialize(ByteBuffer& buffer, size_t&);
virtual bool validate(BaseTypeImpl const&) override;
private:
SQLType m_element_type { SQLType::Text };
};
using ValueTypeImpl = Variant<NullImpl, TextImpl, IntegerImpl, FloatImpl, BooleanImpl, TupleImpl, ArrayImpl>;
/**
* A `Value` is an atomic piece of SQL data. A `Value` has a basic type
* (Text/String, Integer, Float, etc). Richer types are implemented in higher
@ -21,88 +297,105 @@ namespace SQL {
*/
class Value {
public:
explicit Value(SQLType sql_type = SQLType::Text);
Value(SQLType sql_type, ByteBuffer& buffer, size_t& offset);
Value(Value const& other);
~Value();
Value(Value&) = default;
Value(Value const&) = default;
static Value const& null();
explicit Value(SQLType sql_type = SQLType::Null);
Value& operator=(Value&& other) noexcept
template<typename... Ts>
explicit Value(Variant<Ts...> impl)
: m_impl(impl)
{
(*this) = other;
return (*this);
}
enum SetImplementation {
SetImplementationSingleton
};
template<typename I>
Value(SetImplementation, I&& impl)
{
m_impl.set<I>(forward<I>(impl));
}
Value(SQLType, Value const&);
Value(SQLType, String const&);
Value(SQLType, char const*);
Value(SQLType, int);
Value(SQLType, double);
Value(SQLType, bool);
explicit Value(String const&);
explicit Value(char const*);
explicit Value(int);
explicit Value(double);
explicit Value(bool);
~Value() = default;
[[nodiscard]] bool is_null() const;
[[nodiscard]] SQLType type() const;
[[nodiscard]] String type_name() const;
[[nodiscard]] BaseTypeImpl downcast_to_basetype() const;
template<typename Impl>
Impl const& get_impl(Badge<Impl>) const { return m_impl.get<Impl>(); }
[[nodiscard]] String to_string() const;
[[nodiscard]] Optional<int> to_int() const;
[[nodiscard]] Optional<u32> to_u32() const;
[[nodiscard]] Optional<double> to_double() const;
[[nodiscard]] Optional<bool> to_bool() const;
[[nodiscard]] Optional<Vector<Value>> to_vector() const;
explicit operator String() const;
explicit operator int() const;
explicit operator u32() const;
explicit operator double() const;
explicit operator bool() const;
void assign(Value const& other_value);
void assign(String const& string_value);
void assign(int const& int_value);
void assign(double const& double_value);
void assign(bool const& bool_value);
void assign(Vector<Value> const& values);
Value& operator=(Value const& other);
Value& operator=(String const&);
Value& operator=(String&& string)
{
operator=(string);
return *this;
}
Value& operator=(char const*);
Value& operator=(int);
Value& operator=(u32);
Value& operator=(double);
Value& set_null();
Value& operator=(bool);
Value& operator=(Vector<Value> const&);
Optional<String> to_string() const;
explicit operator String() const;
Optional<int> to_int() const;
explicit operator int() const;
Optional<double> to_double() const;
explicit operator double() const;
Optional<u32> to_u32() const;
explicit operator u32() const;
[[nodiscard]] SQLType type() const { return m_type; }
[[nodiscard]] const char* type_name() const { return m_type_name(); }
[[nodiscard]] size_t size() const { return m_size(); }
[[nodiscard]] int compare(Value const& other) const { return m_compare(other); }
[[nodiscard]] bool is_null() const { return m_is_null; }
[[nodiscard]] size_t length() const;
[[nodiscard]] u32 hash() const;
[[nodiscard]] bool can_cast(Value const&) const;
[[nodiscard]] u32 hash() const { return (is_null()) ? 0 : m_hash(); }
void serialize_to(ByteBuffer&) const;
void deserialize(ByteBuffer&, size_t&);
bool operator==(Value const& other) const { return m_compare(other) == 0; }
bool operator==(String const& other) const;
bool operator==(int other) const;
bool operator==(double other) const;
bool operator!=(Value const& other) const { return m_compare(other) != 0; }
bool operator<(Value const& other) const { return m_compare(other) < 0; }
bool operator<=(Value const& other) const { return m_compare(other) <= 0; }
bool operator>(Value const& other) const { return m_compare(other) > 0; }
bool operator>=(Value const& other) const { return m_compare(other) >= 0; }
[[nodiscard]] int compare(Value const&) const;
bool operator==(Value const&) const;
bool operator==(String const&) const;
bool operator==(int) const;
bool operator==(double) const;
bool operator!=(Value const&) const;
bool operator<(Value const&) const;
bool operator<=(Value const&) const;
bool operator>(Value const&) const;
bool operator>=(Value const&) const;
void serialize(ByteBuffer& buffer) const
{
VERIFY(!is_null());
m_serialize(buffer);
}
static Value const& null();
static Value create_tuple(NonnullRefPtr<TupleDescriptor> const&);
static Value create_array(SQLType element_type, Optional<size_t> const& max_size = {});
static Value deserialize_from(ByteBuffer&, size_t&);
private:
void setup(SQLType sql_type);
void setup_text();
void setup_int();
void setup_float();
void setup(SQLType type);
Function<Optional<String>()> m_to_string;
Function<Optional<int>()> m_to_int;
Function<Optional<double>()> m_to_double;
Function<void(Value const&)> m_assign_value;
Function<void(String const&)> m_assign_string;
Function<void(int)> m_assign_int;
Function<void(double)> m_assign_double;
Function<int(Value const&)> m_compare;
Function<void(ByteBuffer&)> m_serialize;
Function<void(ByteBuffer&, size_t& offset)> m_deserialize;
Function<size_t()> m_size;
Function<const char*()> m_type_name;
Function<bool(Value const&)> m_can_cast;
Function<u32()> m_hash;
SQLType m_type { SQLType::Text };
bool m_is_null { true };
Variant<String, int, double> m_impl;
ValueTypeImpl m_impl {};
};
}