diff --git a/src/js_object_accessor.hpp b/src/js_object_accessor.hpp index 9234b27f..4bd985be 100644 --- a/src/js_object_accessor.hpp +++ b/src/js_object_accessor.hpp @@ -85,12 +85,15 @@ struct NativeAccessor { static ValueType from_string(ContextType ctx, StringData string) { return Value::from_string(ctx, string.data()); } - static DateTime to_datetime(ContextType ctx, ValueType &value) { + static Timestamp to_timestamp(ContextType ctx, ValueType &value) { ObjectType date = Value::validated_to_date(ctx, value, "Property"); - return DateTime(Value::to_number(ctx, date)); + double milliseconds = Value::to_number(ctx, date); + u_int64_t seconds = milliseconds / 1000; + u_int32_t nanoseconds = ((u_int64_t)milliseconds % 1000) * 1000000; + return Timestamp(seconds, nanoseconds); } - static ValueType from_datetime(ContextType ctx, DateTime dt) { - return Object::create_date(ctx, dt.get_datetime()); + static ValueType from_timestamp(ContextType ctx, Timestamp ts) { + return Object::create_date(ctx, ts.get_seconds() * 1000 + ts.get_nanoseconds() / 1000000); } static bool is_null(ContextType ctx, ValueType &value) { diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 45b6b8c3..8dcc2b73 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -323,13 +323,9 @@ void Realm::constructor(ContextType ctx, ObjectType this_object, size_t argc, SharedRealm realm = realm::Realm::get_shared_realm(config); auto delegate = new RealmDelegate(realm, Context::get_global_context(ctx)); - - if (!realm->m_binding_context) { - realm->m_binding_context.reset(delegate); - } - delegate->m_defaults = std::move(defaults); delegate->m_constructors = std::move(constructors); + realm->m_binding_context.reset(delegate); set_internal>(this_object, new SharedRealm(realm)); } diff --git a/src/object-store/CMake/RealmCore.cmake b/src/object-store/CMake/RealmCore.cmake index 76072544..90256b98 100644 --- a/src/object-store/CMake/RealmCore.cmake +++ b/src/object-store/CMake/RealmCore.cmake @@ -4,6 +4,10 @@ if(${CMAKE_GENERATOR} STREQUAL "Unix Makefiles") set(MAKE_EQUAL_MAKE "MAKE=$(MAKE)") endif() +if(SANITIZE_ADDRESS) + set(MAKEFLAGS "MAKEFLAGS=EXTRA_CFLAGS=-fsanitize=address EXTRA_LDFLAGS=-fsanitize=address") +endif() + if (${CMAKE_VERSION} VERSION_GREATER "3.4.0") set(USES_TERMINAL_BUILD USES_TERMINAL_BUILD 1) endif() @@ -91,7 +95,7 @@ function(clone_and_build_realm_core branch) PREFIX ${core_prefix_directory} BUILD_IN_SOURCE 1 CONFIGURE_COMMAND "" - BUILD_COMMAND ${MAKE_EQUAL_MAKE} sh build.sh build + BUILD_COMMAND export ${MAKEFLAGS} && ${MAKE_EQUAL_MAKE} sh build.sh build INSTALL_COMMAND "" ${USES_TERMINAL_BUILD} ) @@ -109,7 +113,7 @@ function(build_existing_realm_core core_directory) BUILD_IN_SOURCE 1 BUILD_ALWAYS 1 CONFIGURE_COMMAND "" - BUILD_COMMAND ${MAKE_EQUAL_MAKE} sh build.sh build + BUILD_COMMAND export ${MAKEFLAGS} && ${MAKE_EQUAL_MAKE} sh build.sh build INSTALL_COMMAND "" ${USES_TERMINAL_BUILD} ) diff --git a/src/object-store/CMake/Sanitizers.cmake b/src/object-store/CMake/Sanitizers.cmake new file mode 100644 index 00000000..202f1a68 --- /dev/null +++ b/src/object-store/CMake/Sanitizers.cmake @@ -0,0 +1,20 @@ +option(SANITIZE_ADDRESS "build with ASan") +option(SANITIZE_THREAD "build with TSan") +option(SANITIZE_UNDEFINED "build with UBSan") + +if(SANITIZE_ADDRESS) + set(SANITIZER_FLAGS "${SANITIZER_FLAGS} -fsanitize=address") +endif() + +if(SANITIZE_THREAD) + set(SANITIZER_FLAGS "${SANITIZER_FLAGS} -fsanitize=thread") +endif() + +if(SANITIZE_UNDEFINED) + set(SANITIZER_FLAGS "${SANITIZER_FLAGS} -fsanitize=undefined") +endif() + +if(SANITIZE_ADDRESS OR SANITIZE_THREAD OR SANITIZE_UNDEFINED) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS} -fno-omit-frame-pointer") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_FLAGS}") +endif() diff --git a/src/object-store/CMakeLists.txt b/src/object-store/CMakeLists.txt index 0d75308f..b4c55f65 100644 --- a/src/object-store/CMakeLists.txt +++ b/src/object-store/CMakeLists.txt @@ -1,13 +1,15 @@ +cmake_minimum_required(VERSION 3.2.0) + set(CMAKE_BUILD_TYPE Debug CACHE STRING "") project(realm-object-store) -cmake_minimum_required(VERSION 3.2.0) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake") include(CompilerFlags) +include(Sanitizers) include(RealmCore) -set(REALM_CORE_VERSION "0.97.0" CACHE STRING "") +set(REALM_CORE_VERSION "0.100.1" CACHE STRING "") use_realm_core(${REALM_CORE_VERSION}) include_directories(${REALM_CORE_INCLUDE_DIR} src external/pegtl) diff --git a/src/object-store/README.md b/src/object-store/README.md index 6776d5a5..70c11003 100644 --- a/src/object-store/README.md +++ b/src/object-store/README.md @@ -36,6 +36,14 @@ cmake -DREALM_CORE_VERSION=/path/to/realm-core The given core tree will be built as part of the object store build. +### Building with Sanitizers + +The object store can be built using ASan, TSan and/or UBSan by specifying `-DSANITIZE_ADDRESS=1`, `-DSANITIZE_THREAD=1`, or `-DSANITIZE_UNDEFINED=1` when inoking CMake. +Building with ASan requires specifying a path to core with `-DREAM_CORE_VERSION` as core needs to also be built with ASan enabled. + +On OS X, the Xcode-provided copy of Clang only comes with ASan, and using TSan or UBSan requires a custom build of Clang. +If you have installed Clang as an external Xcode toolchain (using the `install-xcode-toolchain` when building LLVM), note that you'll have to specify `-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++` when running `cmake` to stop cmake from being too clever. + ## Testing ``` diff --git a/src/object-store/src/impl/realm_coordinator.cpp b/src/object-store/src/impl/realm_coordinator.cpp index a1e34f8c..3c624c32 100644 --- a/src/object-store/src/impl/realm_coordinator.cpp +++ b/src/object-store/src/impl/realm_coordinator.cpp @@ -152,17 +152,9 @@ RealmCoordinator::~RealmCoordinator() void RealmCoordinator::unregister_realm(Realm* realm) { std::lock_guard lock(m_realm_mutex); - for (size_t i = 0; i < m_weak_realm_notifiers.size(); ++i) { - auto& weak_realm_notifier = m_weak_realm_notifiers[i]; - if (!weak_realm_notifier.expired() && !weak_realm_notifier.is_for_realm(realm)) { - continue; - } - - if (i + 1 < m_weak_realm_notifiers.size()) { - weak_realm_notifier = std::move(m_weak_realm_notifiers.back()); - } - m_weak_realm_notifiers.pop_back(); - } + auto new_end = remove_if(begin(m_weak_realm_notifiers), end(m_weak_realm_notifiers), + [=](auto& notifier) { return notifier.expired() || notifier.is_for_realm(realm); }); + m_weak_realm_notifiers.erase(new_end, end(m_weak_realm_notifiers)); } void RealmCoordinator::clear_cache() diff --git a/src/object-store/src/impl/transact_log_handler.cpp b/src/object-store/src/impl/transact_log_handler.cpp index d99a7ea7..d95aa32d 100644 --- a/src/object-store/src/impl/transact_log_handler.cpp +++ b/src/object-store/src/impl/transact_log_handler.cpp @@ -124,7 +124,8 @@ public: bool set_double(size_t, size_t, double) { return true; } bool set_string(size_t, size_t, StringData) { return true; } bool set_binary(size_t, size_t, BinaryData) { return true; } - bool set_date_time(size_t, size_t, DateTime) { return true; } + bool set_olddatetime(size_t, size_t, OldDateTime) { return true; } + bool set_timestamp(size_t, size_t, Timestamp) { return true; } bool set_table(size_t, size_t) { return true; } bool set_mixed(size_t, size_t, const Mixed&) { return true; } bool set_link(size_t, size_t, size_t, size_t) { return true; } @@ -416,7 +417,8 @@ public: bool set_double(size_t col, size_t row, double) { return mark_dirty(row, col); } bool set_string(size_t col, size_t row, StringData) { return mark_dirty(row, col); } bool set_binary(size_t col, size_t row, BinaryData) { return mark_dirty(row, col); } - bool set_date_time(size_t col, size_t row, DateTime) { return mark_dirty(row, col); } + bool set_olddatetime(size_t col, size_t row, OldDateTime) { return mark_dirty(row, col); } + bool set_timestamp(size_t col, size_t row, Timestamp) { return mark_dirty(row, col); } bool set_table(size_t col, size_t row) { return mark_dirty(row, col); } bool set_mixed(size_t col, size_t row, const Mixed&) { return mark_dirty(row, col); } bool set_link(size_t col, size_t row, size_t, size_t) { return mark_dirty(row, col); } diff --git a/src/object-store/src/object_accessor.hpp b/src/object-store/src/object_accessor.hpp index 3af25193..d79a2f3d 100644 --- a/src/object-store/src/object_accessor.hpp +++ b/src/object-store/src/object_accessor.hpp @@ -87,8 +87,8 @@ namespace realm { static ValueType from_string(ContextType, StringData); static std::string to_binary(ContextType, ValueType &); static ValueType from_binary(ContextType, BinaryData); - static DateTime to_datetime(ContextType, ValueType &); - static ValueType from_datetime(ContextType, DateTime); + static Timestamp to_timestamp(ContextType, ValueType &); + static ValueType from_timestamp(ContextType, Timestamp); static bool is_null(ContextType, ValueType &); static ValueType null_value(ContextType); @@ -197,9 +197,11 @@ namespace realm { case PropertyTypeDouble: m_row.set_double(column, Accessor::to_double(ctx, value)); break; - case PropertyTypeString: - m_row.set_string(column, Accessor::to_string(ctx, value)); + case PropertyTypeString: { + auto string_value = Accessor::to_string(ctx, value); + m_row.set_string(column, string_value); break; + } case PropertyTypeData: m_row.set_binary(column, BinaryData(Accessor::to_binary(ctx, value))); break; @@ -207,7 +209,7 @@ namespace realm { m_row.set_mixed(column, Accessor::to_mixed(ctx, value)); break; case PropertyTypeDate: - m_row.set_datetime(column, Accessor::to_datetime(ctx, value)); + m_row.set_timestamp(column, Accessor::to_timestamp(ctx, value)); break; case PropertyTypeObject: { if (Accessor::is_null(ctx, value)) { @@ -261,7 +263,7 @@ namespace realm { case PropertyTypeAny: throw "Any not supported"; case PropertyTypeDate: - return Accessor::from_datetime(ctx, m_row.get_datetime(column)); + return Accessor::from_timestamp(ctx, m_row.get_timestamp(column)); case PropertyTypeObject: { auto linkObjectSchema = m_realm->config().schema->find(property.object_type); TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), linkObjectSchema->name); @@ -297,7 +299,8 @@ namespace realm { // search for existing object based on primary key type ValueType primary_value = Accessor::dict_value_for_key(ctx, value, object_schema.primary_key); if (primary_prop->type == PropertyTypeString) { - row_index = table->find_first_string(primary_prop->table_column, Accessor::to_string(ctx, primary_value)); + auto primary_string = Accessor::to_string(ctx, primary_value); + row_index = table->find_first_string(primary_prop->table_column, primary_string); } else { row_index = table->find_first_int(primary_prop->table_column, Accessor::to_long(ctx, primary_value)); diff --git a/src/object-store/src/object_schema.cpp b/src/object-store/src/object_schema.cpp index 65dc7e85..f6d7ae2f 100644 --- a/src/object-store/src/object_schema.cpp +++ b/src/object-store/src/object_schema.cpp @@ -17,14 +17,29 @@ //////////////////////////////////////////////////////////////////////////// #include "object_schema.hpp" + #include "object_store.hpp" #include "property.hpp" -#include -#include +#include +#include using namespace realm; +#define ASSERT_PROPERTY_TYPE_VALUE(property, type) \ + static_assert(static_cast(PropertyType##property) == type_##type, \ + "PropertyType and DataType must have the same values") + +ASSERT_PROPERTY_TYPE_VALUE(Int, Int); +ASSERT_PROPERTY_TYPE_VALUE(Bool, Bool); +ASSERT_PROPERTY_TYPE_VALUE(Float, Float); +ASSERT_PROPERTY_TYPE_VALUE(Double, Double); +ASSERT_PROPERTY_TYPE_VALUE(Data, Binary); +ASSERT_PROPERTY_TYPE_VALUE(Date, Timestamp); +ASSERT_PROPERTY_TYPE_VALUE(Any, Mixed); +ASSERT_PROPERTY_TYPE_VALUE(Object, Link); +ASSERT_PROPERTY_TYPE_VALUE(Array, LinkList); + ObjectSchema::~ObjectSchema() = default; ObjectSchema::ObjectSchema(std::string name, std::string primary_key, std::initializer_list properties) diff --git a/src/object-store/src/object_store.cpp b/src/object-store/src/object_store.cpp index 3167cca1..7be70789 100644 --- a/src/object-store/src/object_store.cpp +++ b/src/object-store/src/object_store.cpp @@ -21,7 +21,6 @@ #include "schema.hpp" #include -#include #include #include #include @@ -128,15 +127,18 @@ std::string ObjectStore::table_name_for_object_type(StringData object_type) { } TableRef ObjectStore::table_for_object_type(Group *group, StringData object_type) { - return group->get_table(table_name_for_object_type(object_type)); + auto name = table_name_for_object_type(object_type); + return group->get_table(name); } ConstTableRef ObjectStore::table_for_object_type(const Group *group, StringData object_type) { - return group->get_table(table_name_for_object_type(object_type)); + auto name = table_name_for_object_type(object_type); + return group->get_table(name); } TableRef ObjectStore::table_for_object_type_create_if_needed(Group *group, StringData object_type, bool &created) { - return group->get_or_add_table(table_name_for_object_type(object_type), &created); + auto name = table_name_for_object_type(object_type); + return group->get_or_add_table(name, &created); } static inline bool property_has_changed(Property const& p1, Property const& p2) { @@ -241,7 +243,7 @@ static void copy_property_values(const Property& source, const Property& destina copy_property_values(source, destination, table, &Table::get_binary, &Table::set_binary); break; case PropertyTypeDate: - copy_property_values(source, destination, table, &Table::get_datetime, &Table::set_datetime); + copy_property_values(source, destination, table, &Table::get_timestamp, &Table::set_timestamp); break; default: break; diff --git a/src/object-store/src/parser/query_builder.cpp b/src/object-store/src/parser/query_builder.cpp index 6984af85..f116b9b2 100644 --- a/src/object-store/src/parser/query_builder.cpp +++ b/src/object-store/src/parser/query_builder.cpp @@ -288,14 +288,13 @@ template struct ValueGetter; template -struct ValueGetter { - static Int convert(TableGetter&&, const parser::Expression & value, Arguments &args) +struct ValueGetter { + static Timestamp convert(TableGetter&&, const parser::Expression & value, Arguments &args) { if (value.type != parser::Expression::Type::Argument) { throw std::runtime_error("You must pass in a date argument to compare"); } - DateTime dt = args.datetime_for_argument(stot(value.s)); - return dt.get_datetime(); + return args.timestamp_for_argument(stot(value.s)); } }; @@ -390,8 +389,8 @@ void do_add_comparison_to_query(Query &query, const Schema &schema, const Object value_of_type_for_query(expr.table_getter, rhs, args)); break; case PropertyTypeDate: - add_numeric_constraint_to_query(query, cmp.op, value_of_type_for_query(expr.table_getter, lhs, args), - value_of_type_for_query(expr.table_getter, rhs, args)); + add_numeric_constraint_to_query(query, cmp.op, value_of_type_for_query(expr.table_getter, lhs, args), + value_of_type_for_query(expr.table_getter, rhs, args)); break; case PropertyTypeDouble: add_numeric_constraint_to_query(query, cmp.op, value_of_type_for_query(expr.table_getter, lhs, args), @@ -481,7 +480,7 @@ void do_add_null_comparison_to_query(Query &query, const Schema &schema, const O do_add_null_comparison_to_query(query, cmp.op, expr, args); break; case PropertyTypeDate: - do_add_null_comparison_to_query(query, cmp.op, expr, args); + do_add_null_comparison_to_query(query, cmp.op, expr, args); break; case PropertyTypeDouble: do_add_null_comparison_to_query(query, cmp.op, expr, args); diff --git a/src/object-store/src/parser/query_builder.hpp b/src/object-store/src/parser/query_builder.hpp index 87043ee2..bccc2e13 100644 --- a/src/object-store/src/parser/query_builder.hpp +++ b/src/object-store/src/parser/query_builder.hpp @@ -19,11 +19,11 @@ #ifndef REALM_QUERY_BUILDER_HPP #define REALM_QUERY_BUILDER_HPP -#include -#include #include "parser.hpp" #include "object_accessor.hpp" +#include + namespace realm { class Query; class Schema; @@ -31,10 +31,10 @@ class Schema; namespace query_builder { class Arguments; -void apply_predicate(Query &query, const parser::Predicate &predicate, Arguments &arguments, const Schema &schema, const std::string &objectType); +void apply_predicate(Query &query, const parser::Predicate &predicate, Arguments &arguments, + const Schema &schema, const std::string &objectType); -class Arguments -{ +class Arguments { public: virtual bool bool_for_argument(size_t argument_index) = 0; virtual long long long_for_argument(size_t argument_index) = 0; @@ -42,14 +42,13 @@ class Arguments virtual double double_for_argument(size_t argument_index) = 0; virtual std::string string_for_argument(size_t argument_index) = 0; virtual std::string binary_for_argument(size_t argument_index) = 0; - virtual DateTime datetime_for_argument(size_t argument_index) = 0; + virtual Timestamp timestamp_for_argument(size_t argument_index) = 0; virtual size_t object_index_for_argument(size_t argument_index) = 0; virtual bool is_argument_null(size_t argument_index) = 0; }; template -class ArgumentConverter : public Arguments -{ +class ArgumentConverter : public Arguments { public: ArgumentConverter(ContextType context, std::vector arguments) : m_arguments(arguments), m_ctx(context) {}; @@ -60,7 +59,7 @@ class ArgumentConverter : public Arguments virtual double double_for_argument(size_t argument_index) { return Accessor::to_double(m_ctx, argument_at(argument_index)); } virtual std::string string_for_argument(size_t argument_index) { return Accessor::to_string(m_ctx, argument_at(argument_index)); } virtual std::string binary_for_argument(size_t argument_index) { return Accessor::to_binary(m_ctx, argument_at(argument_index)); } - virtual DateTime datetime_for_argument(size_t argument_index) { return Accessor::to_datetime(m_ctx, argument_at(argument_index)); } + virtual Timestamp timestamp_for_argument(size_t argument_index) { return Accessor::to_timestamp(m_ctx, argument_at(argument_index)); } virtual size_t object_index_for_argument(size_t argument_index) { return Accessor::to_existing_object_index(m_ctx, argument_at(argument_index)); } virtual bool is_argument_null(size_t argument_index) { return Accessor::is_null(m_ctx, argument_at(argument_index)); } diff --git a/src/object-store/src/property.hpp b/src/object-store/src/property.hpp index fa8b0ec3..25a3bda7 100644 --- a/src/object-store/src/property.hpp +++ b/src/object-store/src/property.hpp @@ -23,25 +23,15 @@ namespace realm { enum PropertyType { - /** Integer type: NSInteger, int, long, Int (Swift) */ PropertyTypeInt = 0, - /** Boolean type: BOOL, bool, Bool (Swift) */ PropertyTypeBool = 1, - /** Float type: CGFloat (32bit), float, Float (Swift) */ PropertyTypeFloat = 9, - /** Double type: CGFloat (64bit), double, Double (Swift) */ PropertyTypeDouble = 10, - /** String type: NSString, String (Swift) */ PropertyTypeString = 2, - /** Data type: NSData */ PropertyTypeData = 4, - /** Any type: id, **not supported in Swift** */ - PropertyTypeAny = 6, - /** Date type: NSDate */ - PropertyTypeDate = 7, - /** Object type. See [Realm Models](http://realm.io/docs/cocoa/latest/#models) */ + PropertyTypeAny = 6, // deprecated and will be removed in the future + PropertyTypeDate = 8, PropertyTypeObject = 12, - /** Array type. See [Realm Models](http://realm.io/docs/cocoa/latest/#models) */ PropertyTypeArray = 13, }; @@ -55,7 +45,13 @@ namespace realm { size_t table_column = -1; bool requires_index() const { return is_primary || is_indexed; } - bool is_indexable() const { return type == PropertyTypeInt || type == PropertyTypeBool || type == PropertyTypeString || type == PropertyTypeDate; } + bool is_indexable() const + { + return type == PropertyTypeInt + || type == PropertyTypeBool + || type == PropertyTypeDate + || type == PropertyTypeString; + } }; static inline const char *string_for_property_type(PropertyType type) { diff --git a/src/object-store/src/results.cpp b/src/object-store/src/results.cpp index b600af41..540aac54 100644 --- a/src/object-store/src/results.cpp +++ b/src/object-store/src/results.cpp @@ -223,10 +223,10 @@ size_t Results::index_of(size_t row_ndx) REALM_UNREACHABLE(); } -template +template util::Optional Results::aggregate(size_t column, bool return_none_for_empty, Int agg_int, Float agg_float, - Double agg_double, DateTime agg_datetime) + Double agg_double, Timestamp agg_timestamp) { validate_read(); if (!m_table) @@ -254,7 +254,7 @@ util::Optional Results::aggregate(size_t column, bool return_none_for_emp switch (m_table->get_column_type(column)) { - case type_DateTime: return do_agg(agg_datetime); + case type_Timestamp: return do_agg(agg_timestamp); case type_Double: return do_agg(agg_double); case type_Float: return do_agg(agg_float); case type_Int: return do_agg(agg_int); @@ -269,7 +269,7 @@ util::Optional Results::max(size_t column) [=](auto const& table) { return table.maximum_int(column); }, [=](auto const& table) { return table.maximum_float(column); }, [=](auto const& table) { return table.maximum_double(column); }, - [=](auto const& table) { return table.maximum_datetime(column); }); + [=](auto const& table) { return table.maximum_timestamp(column); }); } util::Optional Results::min(size_t column) @@ -278,7 +278,7 @@ util::Optional Results::min(size_t column) [=](auto const& table) { return table.minimum_int(column); }, [=](auto const& table) { return table.minimum_float(column); }, [=](auto const& table) { return table.minimum_double(column); }, - [=](auto const& table) { return table.minimum_datetime(column); }); + [=](auto const& table) { return table.minimum_timestamp(column); }); } util::Optional Results::sum(size_t column) diff --git a/src/object-store/src/results.hpp b/src/object-store/src/results.hpp index 57767d86..c7501179 100644 --- a/src/object-store/src/results.hpp +++ b/src/object-store/src/results.hpp @@ -134,7 +134,7 @@ public: // Get the min/max/average/sum of the given column // All but sum() returns none when there are zero matching rows // sum() returns 0, except for when it returns none - // Throws UnsupportedColumnTypeException for sum/average on datetime or non-numeric column + // Throws UnsupportedColumnTypeException for sum/average on timestamp or non-numeric column // Throws OutOfBoundsIndexException for an out-of-bounds column util::Optional max(size_t column); util::Optional min(size_t column); @@ -225,10 +225,10 @@ private: void validate_read() const; void validate_write() const; - template + template util::Optional aggregate(size_t column, bool return_none_for_empty, Int agg_int, Float agg_float, - Double agg_double, DateTime agg_datetime); + Double agg_double, Timestamp agg_timestamp); void set_table_view(TableView&& tv); };