From 2096f7aa00a4f4c5561ca242d373c9f1f75cc7d2 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 23 Nov 2015 11:26:50 -0800 Subject: [PATCH 1/3] latest from object store branch --- src/object-store/object_store.cpp | 21 +- src/object-store/object_store.hpp | 11 +- src/object-store/results.cpp | 326 +++++++++++++++++++++++++++--- src/object-store/results.hpp | 159 +++++++++++++-- src/object-store/shared_realm.cpp | 11 +- src/object-store/shared_realm.hpp | 42 ++-- 6 files changed, 485 insertions(+), 85 deletions(-) diff --git a/src/object-store/object_store.cpp b/src/object-store/object_store.cpp index 3e32b98c..3aed85c5 100644 --- a/src/object-store/object_store.cpp +++ b/src/object-store/object_store.cpp @@ -30,6 +30,7 @@ using namespace realm; +namespace { const char * const c_metadataTableName = "metadata"; const char * const c_versionColumnName = "version"; const size_t c_versionColumnIndex = 0; @@ -42,8 +43,8 @@ const size_t c_primaryKeyPropertyNameColumnIndex = 1; const size_t c_zeroRowIndex = 0; -const std::string c_object_table_prefix = "class_"; -const size_t c_object_table_prefix_length = c_object_table_prefix.length(); +const char c_object_table_prefix[] = "class_"; +} const uint64_t ObjectStore::NotVersioned = std::numeric_limits::max(); @@ -119,15 +120,15 @@ void ObjectStore::set_primary_key_for_object(Group *group, StringData object_typ } } -std::string ObjectStore::object_type_for_table_name(const std::string &table_name) { - if (table_name.size() >= c_object_table_prefix_length && table_name.compare(0, c_object_table_prefix_length, c_object_table_prefix) == 0) { - return table_name.substr(c_object_table_prefix_length, table_name.length() - c_object_table_prefix_length); +StringData ObjectStore::object_type_for_table_name(StringData table_name) { + if (table_name.begins_with(c_object_table_prefix)) { + return table_name.substr(sizeof(c_object_table_prefix) - 1); } - return std::string(); + return StringData(); } -std::string ObjectStore::table_name_for_object_type(const std::string &object_type) { - return c_object_table_prefix + object_type; +std::string ObjectStore::table_name_for_object_type(StringData object_type) { + return std::string(c_object_table_prefix) + object_type.data(); } TableRef ObjectStore::table_for_object_type(Group *group, StringData object_type) { @@ -138,7 +139,7 @@ ConstTableRef ObjectStore::table_for_object_type(const Group *group, StringData return group->get_table(table_name_for_object_type(object_type)); } -TableRef ObjectStore::table_for_object_type_create_if_needed(Group *group, const StringData &object_type, bool &created) { +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); } @@ -494,7 +495,7 @@ void ObjectStore::validate_primary_column_uniqueness(const Group *group, Schema } } -void ObjectStore::delete_data_for_object(Group *group, const StringData &object_type) { +void ObjectStore::delete_data_for_object(Group *group, StringData object_type) { TableRef table = table_for_object_type(group, object_type); if (table) { group->remove_table(table->get_index_in_group()); diff --git a/src/object-store/object_store.hpp b/src/object-store/object_store.hpp index e038604c..d41ec3fd 100644 --- a/src/object-store/object_store.hpp +++ b/src/object-store/object_store.hpp @@ -19,7 +19,6 @@ #ifndef REALM_OBJECT_STORE_HPP #define REALM_OBJECT_STORE_HPP -#include "schema.hpp" #include "object_schema.hpp" #include "property.hpp" @@ -30,6 +29,7 @@ namespace realm { class ObjectSchemaValidationException; + class Schema; class ObjectStore { public: @@ -68,11 +68,14 @@ namespace realm { static Schema schema_from_group(const Group *group); // deletes the table for the given type - static void delete_data_for_object(Group *group, const StringData &object_type); + static void delete_data_for_object(Group *group, StringData object_type); // indicates if this group contains any objects static bool is_empty(const Group *group); + static std::string table_name_for_object_type(StringData class_name); + static StringData object_type_for_table_name(StringData table_name); + private: // set a new schema version static void set_schema_version(Group *group, uint64_t version); @@ -102,9 +105,7 @@ namespace realm { // must be in write transaction to set static void set_primary_key_for_object(Group *group, StringData object_type, StringData primary_key); - static TableRef table_for_object_type_create_if_needed(Group *group, const StringData &object_type, bool &created); - static std::string table_name_for_object_type(const std::string &class_name); - static std::string object_type_for_table_name(const std::string &table_name); + static TableRef table_for_object_type_create_if_needed(Group *group, StringData object_type, bool &created); // returns if any indexes were changed static bool update_indexes(Group *group, Schema &schema); diff --git a/src/object-store/results.cpp b/src/object-store/results.cpp index 1977e5be..f361e6f5 100644 --- a/src/object-store/results.cpp +++ b/src/object-store/results.cpp @@ -3,42 +3,318 @@ */ #include "results.hpp" -#import + +#include using namespace realm; -Results::Results(SharedRealm &r, ObjectSchema &o, Query q, SortOrder s) : - realm(r), object_schema(o), backing_query(q), table_view(backing_query.find_all()) +#ifdef __has_cpp_attribute +#define REALM_HAS_CCP_ATTRIBUTE(attr) __has_cpp_attribute(attr) +#else +#define REALM_HAS_CCP_ATTRIBUTE(attr) 0 +#endif + +#if REALM_HAS_CCP_ATTRIBUTE(clang::fallthrough) +#define REALM_FALLTHROUGH [[clang::fallthrough]] +#else +#define REALM_FALLTHROUGH +#endif + +Results::Results(SharedRealm r, Query q, SortOrder s) +: m_realm(std::move(r)) +, m_query(std::move(q)) +, m_table(m_query.get_table().get()) +, m_sort(std::move(s)) +, m_mode(Mode::Query) { - setSort(std::move(s)); +} + +Results::Results(SharedRealm r, Table& table) +: m_realm(std::move(r)) +, m_table(&table) +, m_mode(Mode::Table) +{ +} + +void Results::validate_read() const +{ + if (m_realm) + m_realm->verify_thread(); + if (m_table && !m_table->is_attached()) + throw InvalidatedException(); +} + +void Results::validate_write() const +{ + validate_read(); + if (!m_realm || !m_realm->is_in_transaction()) + throw InvalidTransactionException("Must be in a write transaction"); } size_t Results::size() { - verify_attached(); - return table_view.size(); -} - -void Results::setSort(SortOrder s) -{ - sort_order = std::make_unique(std::move(s)); - table_view.sort(sort_order->columnIndices, sort_order->ascending); -} - -Row Results::get(std::size_t row_ndx) -{ - verify_attached(); - if (row_ndx >= table_view.size()) { - throw std::out_of_range(std::string("Index ") + std::to_string(row_ndx) + " is outside of range 0..." + - std::to_string(table_view.size()) + "."); + validate_read(); + switch (m_mode) { + case Mode::Empty: return 0; + case Mode::Table: return m_table->size(); + case Mode::Query: return m_query.count(); + case Mode::TableView: + update_tableview(); + return m_table_view.size(); } - return table_view.get(row_ndx); + REALM_UNREACHABLE(); } -void Results::verify_attached() +RowExpr Results::get(size_t row_ndx) { - if (!table_view.is_attached()) { - throw std::runtime_error("Tableview is not attached"); + validate_read(); + switch (m_mode) { + case Mode::Empty: break; + case Mode::Table: + if (row_ndx < m_table->size()) + return m_table->get(row_ndx); + break; + case Mode::Query: + case Mode::TableView: + update_tableview(); + if (row_ndx < m_table_view.size()) + return m_table_view.get(row_ndx); + break; } - table_view.sync_if_needed(); + + throw OutOfBoundsIndexException{row_ndx, size()}; +} + +util::Optional Results::first() +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + return none; + case Mode::Table: + return m_table->size() == 0 ? util::none : util::make_optional(m_table->front()); + case Mode::Query: + update_tableview(); + REALM_FALLTHROUGH; + case Mode::TableView: + return m_table_view.size() == 0 ? util::none : util::make_optional(m_table_view.front()); + } + REALM_UNREACHABLE(); +} + +util::Optional Results::last() +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + return none; + case Mode::Table: + return m_table->size() == 0 ? util::none : util::make_optional(m_table->back()); + case Mode::Query: + update_tableview(); + REALM_FALLTHROUGH; + case Mode::TableView: + return m_table_view.size() == 0 ? util::none : util::make_optional(m_table_view.back()); + } + REALM_UNREACHABLE(); +} + +void Results::update_tableview() +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + case Mode::Table: + return; + case Mode::Query: + m_table_view = m_query.find_all(); + if (m_sort) { + m_table_view.sort(m_sort.columnIndices, m_sort.ascending); + } + m_mode = Mode::TableView; + break; + case Mode::TableView: + m_table_view.sync_if_needed(); + break; + } +} + +size_t Results::index_of(Row const& row) +{ + validate_read(); + if (!row) { + throw DetatchedAccessorException{}; + } + if (m_table && row.get_table() != m_table) { + throw IncorrectTableException{ + ObjectStore::object_type_for_table_name(m_table->get_name()), + ObjectStore::object_type_for_table_name(row.get_table()->get_name())}; + } + return index_of(row.get_index()); +} + +size_t Results::index_of(size_t row_ndx) +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + return not_found; + case Mode::Table: + return row_ndx; + case Mode::Query: + if (!m_sort) + return m_query.count(row_ndx, row_ndx + 1) ? m_query.count(0, row_ndx) : not_found; + REALM_FALLTHROUGH; + case Mode::TableView: + update_tableview(); + return m_table_view.find_by_source_ndx(row_ndx); + } + REALM_UNREACHABLE(); +} + +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) +{ + validate_read(); + if (!m_table) + return none; + if (column > m_table->get_column_count()) + throw OutOfBoundsIndexException{column, m_table->get_column_count()}; + + auto do_agg = [&](auto const& getter) -> util::Optional { + switch (m_mode) { + case Mode::Empty: + return none; + case Mode::Table: + if (return_none_for_empty && m_table->size() == 0) + return none; + return util::Optional(getter(*m_table)); + case Mode::Query: + case Mode::TableView: + this->update_tableview(); + if (return_none_for_empty && m_table_view.size() == 0) + return none; + return util::Optional(getter(m_table_view)); + } + REALM_UNREACHABLE(); + }; + + switch (m_table->get_column_type(column)) + { + case type_DateTime: return do_agg(agg_datetime); + case type_Double: return do_agg(agg_double); + case type_Float: return do_agg(agg_float); + case type_Int: return do_agg(agg_int); + default: + throw UnsupportedColumnTypeException{column, m_table}; + } +} + +util::Optional Results::max(size_t column) +{ + return aggregate(column, true, + [=](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); }); +} + +util::Optional Results::min(size_t column) +{ + return aggregate(column, true, + [=](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); }); +} + +util::Optional Results::sum(size_t column) +{ + return aggregate(column, false, + [=](auto const& table) { return table.sum_int(column); }, + [=](auto const& table) { return table.sum_float(column); }, + [=](auto const& table) { return table.sum_double(column); }, + [=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table}; }); +} + +util::Optional Results::average(size_t column) +{ + return aggregate(column, true, + [=](auto const& table) { return table.average_int(column); }, + [=](auto const& table) { return table.average_float(column); }, + [=](auto const& table) { return table.average_double(column); }, + [=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table}; }); +} + +void Results::clear() +{ + switch (m_mode) { + case Mode::Empty: + return; + case Mode::Table: + validate_write(); + m_table->clear(); + break; + case Mode::Query: + // Not using Query:remove() because building the tableview and + // clearing it is actually significantly faster + case Mode::TableView: + validate_write(); + update_tableview(); + m_table_view.clear(); + break; + } +} + +Query Results::get_query() const +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + case Mode::Query: + case Mode::TableView: + return m_query; + case Mode::Table: + return m_table->where(); + } + REALM_UNREACHABLE(); +} + +TableView Results::get_tableview() +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + return {}; + case Mode::Query: + case Mode::TableView: + update_tableview(); + return m_table_view; + case Mode::Table: + return m_table->where().find_all(); + } + REALM_UNREACHABLE(); +} + +StringData Results::get_object_type() const noexcept +{ + return ObjectStore::object_type_for_table_name(m_table->get_name()); +} + +Results Results::sort(realm::SortOrder&& sort) const +{ + return Results(m_realm, get_query(), std::move(sort)); +} + +Results Results::filter(Query&& q) const +{ + return Results(m_realm, get_query().and_query(std::move(q)), get_sort()); +} + +Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table) { + column_index = column; + column_name = table->get_column_name(column); + column_type = table->get_column_type(column); } diff --git a/src/object-store/results.hpp b/src/object-store/results.hpp index fff37124..bf9761a3 100644 --- a/src/object-store/results.hpp +++ b/src/object-store/results.hpp @@ -5,35 +5,150 @@ #ifndef REALM_RESULTS_HPP #define REALM_RESULTS_HPP -#import "shared_realm.hpp" -#import +#include "shared_realm.hpp" + +#include +#include +#include namespace realm { - struct SortOrder { - std::vector columnIndices; - std::vector ascending; +template class BasicRowExpr; +using RowExpr = BasicRowExpr; +class Mixed; - explicit operator bool() const { - return !columnIndices.empty(); - } +struct SortOrder { + std::vector columnIndices; + std::vector ascending; + + explicit operator bool() const + { + return !columnIndices.empty(); + } +}; + +class Results { +public: + // Results can be either be backed by nothing, a thin wrapper around a table, + // or a wrapper around a query and a sort order which creates and updates + // the tableview as needed + Results() = default; + Results(SharedRealm r, Table& table); + Results(SharedRealm r, Query q, SortOrder s = {}); + + // Results is copyable and moveable + Results(Results const&) = default; + Results(Results&&) = default; + Results& operator=(Results const&) = default; + Results& operator=(Results&&) = default; + + // Get a query which will match the same rows as is contained in this Results + // Returned query will not be valid if the current mode is Empty + Query get_query() const; + + // Get the currently applied sort order for this Results + SortOrder const& get_sort() const noexcept { return m_sort; } + + // Get a tableview containing the same rows as this Results + TableView get_tableview(); + + // Get the object type which will be returned by get() + StringData get_object_type() const noexcept; + + // Get the size of this results + // Can be either O(1) or O(N) depending on the state of things + size_t size(); + + // Get the row accessor for the given index + // Throws OutOfBoundsIndexException if index >= size() + RowExpr get(size_t index); + + // Get a row accessor for the first/last row, or none if the results are empty + // More efficient than calling size()+get() + util::Optional first(); + util::Optional last(); + + // Get the first index of the given row in this results, or not_found + // Throws DetachedAccessorException if row is not attached + // Throws IncorrectTableException if row belongs to a different table + size_t index_of(size_t row_ndx); + size_t index_of(Row const& row); + + // Delete all of the rows in this Results from the Realm + // size() will always be zero afterwards + // Throws InvalidTransactionException if not in a write transaction + void clear(); + + // Create a new Results by further filtering or sorting this Results + Results filter(Query&& q) const; + Results sort(SortOrder&& sort) const; + + // 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 OutOfBoundsIndexException for an out-of-bounds column + util::Optional max(size_t column); + util::Optional min(size_t column); + util::Optional average(size_t column); + util::Optional sum(size_t column); + + enum class Mode { + Empty, // Backed by nothing (for missing tables) + Table, // Backed directly by a Table + Query, // Backed by a query that has not yet been turned into a TableView + TableView // Backed by a TableView created from a Query + }; + // Get the currrent mode of the Results + // Ideally this would not be public but it's needed for some KVO stuff + Mode get_mode() const { return m_mode; } + + // The Results object has been invalidated (due to the Realm being invalidated) + // All non-noexcept functions can throw this + struct InvalidatedException {}; + + // The input index parameter was out of bounds + struct OutOfBoundsIndexException { + size_t requested; + size_t valid_count; }; - static SortOrder s_defaultSort = {{}, {}}; + // The input Row object is not attached + struct DetatchedAccessorException { }; - struct Results { - Results(SharedRealm &r, ObjectSchema &o, Query q, SortOrder s = s_defaultSort); - size_t size(); - Row get(std::size_t row_ndx); - void verify_attached(); - - SharedRealm realm; - ObjectSchema &object_schema; - Query backing_query; - TableView table_view; - std::unique_ptr sort_order; - - void setSort(SortOrder s); + // The input Row object belongs to a different table + struct IncorrectTableException { + StringData expected; + StringData actual; }; + + // The requested aggregate operation is not supported for the column type + struct UnsupportedColumnTypeException { + size_t column_index; + StringData column_name; + DataType column_type; + + UnsupportedColumnTypeException(size_t column, const Table* table); + }; + +private: + SharedRealm m_realm; + Query m_query; + TableView m_table_view; + Table* m_table = nullptr; + SortOrder m_sort; + + Mode m_mode = Mode::Empty; + + void validate_read() const; + void validate_write() const; + + void update_tableview(); + + template + util::Optional aggregate(size_t column, bool return_none_for_empty, + Int agg_int, Float agg_float, + Double agg_double, DateTime agg_datetime); +}; } #endif /* REALM_RESULTS_HPP */ diff --git a/src/object-store/shared_realm.cpp b/src/object-store/shared_realm.cpp index 9c0a4e1e..28bb81b6 100644 --- a/src/object-store/shared_realm.cpp +++ b/src/object-store/shared_realm.cpp @@ -47,6 +47,8 @@ Realm::Config::Config(const Config& c) } } +Realm::Config::Config() = default; +Realm::Config::Config(Config&&) = default; Realm::Config::~Config() = default; Realm::Config& Realm::Config::operator=(realm::Realm::Config const& c) @@ -237,7 +239,14 @@ static void check_read_write(Realm *realm) void Realm::verify_thread() const { if (m_thread_id != std::this_thread::get_id()) { - throw IncorrectThreadException("Realm accessed from incorrect thread."); + throw IncorrectThreadException(); + } +} + +void Realm::verify_in_write() const +{ + if (!is_in_transaction()) { + throw InvalidTransactionException("Cannot modify persisted objects outside of a write transaction."); } } diff --git a/src/object-store/shared_realm.hpp b/src/object-store/shared_realm.hpp index 81b14bf7..3a31d878 100644 --- a/src/object-store/shared_realm.hpp +++ b/src/object-store/shared_realm.hpp @@ -19,7 +19,6 @@ #ifndef REALM_REALM_HPP #define REALM_REALM_HPP -#include #include #include #include @@ -56,8 +55,8 @@ namespace realm { MigrationFunction migration_function; - Config() = default; - Config(Config&&) = default; + Config(); + Config(Config&&); Config(const Config& c); ~Config(); @@ -100,6 +99,11 @@ namespace realm { std::thread::id thread_id() const { return m_thread_id; } void verify_thread() const; + void verify_in_write() const; + + // Close this Realm and remove it from the cache. Continuing to use a + // Realm after closing it will produce undefined behavior. + void close(); // Close this Realm and remove it from the cache. Continuing to use a // Realm after closing it will produce undefined behavior. @@ -145,11 +149,9 @@ namespace realm { std::mutex m_mutex; }; - class RealmFileException : public std::runtime_error - { - public: - enum class Kind - { + class RealmFileException : public std::runtime_error { + public: + enum class Kind { /** Thrown for any I/O related exception scenarios when a realm is opened. */ AccessError, /** Thrown if the user does not have permission to open or create @@ -169,32 +171,28 @@ namespace realm { Kind kind() const { return m_kind; } const std::string& path() const { return m_path; } - private: + private: Kind m_kind; std::string m_path; }; - class MismatchedConfigException : public std::runtime_error - { - public: + class MismatchedConfigException : public std::runtime_error { + public: MismatchedConfigException(std::string message) : std::runtime_error(message) {} }; - class InvalidTransactionException : public std::runtime_error - { - public: + class InvalidTransactionException : public std::runtime_error { + public: InvalidTransactionException(std::string message) : std::runtime_error(message) {} }; - class IncorrectThreadException : public std::runtime_error - { - public: - IncorrectThreadException(std::string message) : std::runtime_error(message) {} + class IncorrectThreadException : public std::runtime_error { + public: + IncorrectThreadException() : std::runtime_error("Realm accessed from incorrect thread.") {} }; - class UnitializedRealmException : public std::runtime_error - { - public: + class UnitializedRealmException : public std::runtime_error { + public: UnitializedRealmException(std::string message) : std::runtime_error(message) {} }; } From 39e3b017a48387b5f1e02fdf21de7c4b1d944b06 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 25 Nov 2015 19:57:15 -0800 Subject: [PATCH 2/3] integrate new results class --- src/RJSResults.cpp | 11 ++++---- src/object-store/object_accessor.hpp | 1 + src/object-store/results.cpp | 29 +++++++++----------- src/object-store/results.hpp | 41 ++++++++++++++++++++-------- src/object-store/shared_realm.hpp | 4 --- 5 files changed, 49 insertions(+), 37 deletions(-) diff --git a/src/RJSResults.cpp b/src/RJSResults.cpp index b7483de9..b0d1d709 100644 --- a/src/RJSResults.cpp +++ b/src/RJSResults.cpp @@ -22,7 +22,9 @@ JSValueRef ResultsGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef return JSValueMakeNumber(ctx, size); } - return RJSObjectCreate(ctx, Object(results->realm, results->object_schema, results->get(RJSValidatedPositiveIndex(indexStr)))); + return RJSObjectCreate(ctx, Object(results->get_realm(), + results->object_schema, + results->get(RJSValidatedPositiveIndex(indexStr)))); } catch (std::out_of_range &exp) { // getters for nonexistent properties in JS should always return undefined @@ -61,7 +63,7 @@ bool ResultsSetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef proper void ResultsPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames) { Results *results = RJSGetInternal(object); char str[32]; - for (int i = 0; i < results->table_view.size(); i++) { + for (int i = 0; i < results->size(); i++) { sprintf(str, "%i", i); JSStringRef name = JSStringCreateWithUTF8CString(str); JSPropertyNameAccumulatorAddName(propertyNames, name); @@ -74,7 +76,7 @@ JSValueRef SortByProperty(JSContextRef ctx, JSObjectRef function, JSObjectRef th Results *results = RJSGetInternal(thisObject); RJSValidateArgumentRange(argumentCount, 1, 2); std::string propName = RJSValidatedStringForValue(ctx, arguments[0]); - Property *prop = results->object_schema.property_for_name(propName); + const Property *prop = results->object_schema.property_for_name(propName); if (!prop) { throw std::runtime_error("Property '" + propName + "' does not exist on object type '" + results->object_schema.name + "'"); } @@ -84,8 +86,7 @@ JSValueRef SortByProperty(JSContextRef ctx, JSObjectRef function, JSObjectRef th ascending = JSValueToBoolean(ctx, arguments[1]); } - SortOrder sort = {{prop->table_column}, {ascending}}; - results->setSort(sort); + *results = results->sort({{prop->table_column}, {ascending}}); } catch (std::exception &exp) { if (jsException) { diff --git a/src/object-store/object_accessor.hpp b/src/object-store/object_accessor.hpp index dfae7c16..ffa3edb6 100644 --- a/src/object-store/object_accessor.hpp +++ b/src/object-store/object_accessor.hpp @@ -7,6 +7,7 @@ #include #include "shared_realm.hpp" +#include "schema.hpp" #include "list.hpp" namespace realm { diff --git a/src/object-store/results.cpp b/src/object-store/results.cpp index f361e6f5..2f466476 100644 --- a/src/object-store/results.cpp +++ b/src/object-store/results.cpp @@ -20,19 +20,21 @@ using namespace realm; #define REALM_FALLTHROUGH #endif -Results::Results(SharedRealm r, Query q, SortOrder s) +Results::Results(SharedRealm r, const ObjectSchema &o, Query q, SortOrder s) : m_realm(std::move(r)) , m_query(std::move(q)) , m_table(m_query.get_table().get()) , m_sort(std::move(s)) , m_mode(Mode::Query) +, object_schema(o) { } -Results::Results(SharedRealm r, Table& table) +Results::Results(SharedRealm r, const ObjectSchema &o, Table& table) : m_realm(std::move(r)) , m_table(&table) , m_mode(Mode::Table) +, object_schema(o) { } @@ -146,9 +148,9 @@ size_t Results::index_of(Row const& row) throw DetatchedAccessorException{}; } if (m_table && row.get_table() != m_table) { - throw IncorrectTableException{ - ObjectStore::object_type_for_table_name(m_table->get_name()), - ObjectStore::object_type_for_table_name(row.get_table()->get_name())}; + throw IncorrectTableException(object_schema.name, + ObjectStore::object_type_for_table_name(row.get_table()->get_name()), + "Attempting to get the index of a Row of the wrong type"); } return index_of(row.get_index()); } @@ -298,23 +300,18 @@ TableView Results::get_tableview() REALM_UNREACHABLE(); } -StringData Results::get_object_type() const noexcept -{ - return ObjectStore::object_type_for_table_name(m_table->get_name()); -} - Results Results::sort(realm::SortOrder&& sort) const { - return Results(m_realm, get_query(), std::move(sort)); + return Results(m_realm, object_schema, get_query(), std::move(sort)); } Results Results::filter(Query&& q) const { - return Results(m_realm, get_query().and_query(std::move(q)), get_sort()); + return Results(m_realm, object_schema, get_query().and_query(std::move(q)), get_sort()); } -Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table) { - column_index = column; - column_name = table->get_column_name(column); - column_type = table->get_column_type(column); +Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table) : + column_index(column), column_name(table->get_column_name(column)), column_type(table->get_column_type(column)), + std::runtime_error((std::string)"Operation not supported on '" + table->get_column_name(column).data() + "' columns") +{ } diff --git a/src/object-store/results.hpp b/src/object-store/results.hpp index bf9761a3..53ee6815 100644 --- a/src/object-store/results.hpp +++ b/src/object-store/results.hpp @@ -32,8 +32,8 @@ public: // or a wrapper around a query and a sort order which creates and updates // the tableview as needed Results() = default; - Results(SharedRealm r, Table& table); - Results(SharedRealm r, Query q, SortOrder s = {}); + Results(SharedRealm r, const ObjectSchema &o, Table& table); + Results(SharedRealm r, const ObjectSchema &o, Query q, SortOrder s = {}); // Results is copyable and moveable Results(Results const&) = default; @@ -41,6 +41,12 @@ public: Results& operator=(Results const&) = default; Results& operator=(Results&&) = default; + // Get the Realm + SharedRealm get_realm() const { return m_realm; } + + // Object schema describing the vendored object type + ObjectSchema object_schema; + // Get a query which will match the same rows as is contained in this Results // Returned query will not be valid if the current mode is Empty Query get_query() const; @@ -52,7 +58,7 @@ public: TableView get_tableview(); // Get the object type which will be returned by get() - StringData get_object_type() const noexcept; + StringData get_object_type() const noexcept { return object_schema.name; } // Get the size of this results // Can be either O(1) or O(N) depending on the state of things @@ -104,25 +110,36 @@ public: // The Results object has been invalidated (due to the Realm being invalidated) // All non-noexcept functions can throw this - struct InvalidatedException {}; + struct InvalidatedException : public std::runtime_error + { + InvalidatedException() : std::runtime_error("Access to invalidated Results objects") {} + }; // The input index parameter was out of bounds - struct OutOfBoundsIndexException { - size_t requested; - size_t valid_count; + struct OutOfBoundsIndexException : public std::out_of_range + { + OutOfBoundsIndexException(size_t r, size_t c) : requested(r), valid_count(c), + std::out_of_range((std::string)"Requested index " + std::to_string(r) + + " greater than max " + std::to_string(c)) {} + const size_t requested; + const size_t valid_count; }; // The input Row object is not attached - struct DetatchedAccessorException { }; + struct DetatchedAccessorException : public std::runtime_error { + DetatchedAccessorException() : std::runtime_error("Atempting to access an invalid object") {} + }; // The input Row object belongs to a different table - struct IncorrectTableException { - StringData expected; - StringData actual; + struct IncorrectTableException : public std::runtime_error { + IncorrectTableException(StringData e, StringData a, const std::string &error) : + expected(e), actual(a), std::runtime_error(error) {} + const StringData expected; + const StringData actual; }; // The requested aggregate operation is not supported for the column type - struct UnsupportedColumnTypeException { + struct UnsupportedColumnTypeException : public std::runtime_error { size_t column_index; StringData column_name; DataType column_type; diff --git a/src/object-store/shared_realm.hpp b/src/object-store/shared_realm.hpp index 3a31d878..0503282a 100644 --- a/src/object-store/shared_realm.hpp +++ b/src/object-store/shared_realm.hpp @@ -105,10 +105,6 @@ namespace realm { // Realm after closing it will produce undefined behavior. void close(); - // Close this Realm and remove it from the cache. Continuing to use a - // Realm after closing it will produce undefined behavior. - void close(); - ~Realm(); private: From 8a00a2f92a4be564d4d17912fb73c37278ae1b51 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 25 Nov 2015 19:59:51 -0800 Subject: [PATCH 3/3] call results constructor taking a table when no query is provided --- src/RJSResults.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RJSResults.cpp b/src/RJSResults.cpp index b0d1d709..2f990d14 100644 --- a/src/RJSResults.cpp +++ b/src/RJSResults.cpp @@ -102,7 +102,7 @@ JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, std::string cl if (object_schema == realm->config().schema->end()) { throw std::runtime_error("Object type '" + className + "' not present in Realm."); } - return RJSWrapObject(ctx, RJSResultsClass(), new Results(realm, *object_schema, table->where())); + return RJSWrapObject(ctx, RJSResultsClass(), new Results(realm, *object_schema, *table)); }