latest from object store branch

This commit is contained in:
Ari Lazier 2015-11-23 11:26:50 -08:00
parent d5f56540b7
commit e05ec4ea83
6 changed files with 485 additions and 85 deletions

View File

@ -30,6 +30,7 @@
using namespace realm; using namespace realm;
namespace {
const char * const c_metadataTableName = "metadata"; const char * const c_metadataTableName = "metadata";
const char * const c_versionColumnName = "version"; const char * const c_versionColumnName = "version";
const size_t c_versionColumnIndex = 0; const size_t c_versionColumnIndex = 0;
@ -42,8 +43,8 @@ const size_t c_primaryKeyPropertyNameColumnIndex = 1;
const size_t c_zeroRowIndex = 0; const size_t c_zeroRowIndex = 0;
const std::string c_object_table_prefix = "class_"; const char c_object_table_prefix[] = "class_";
const size_t c_object_table_prefix_length = c_object_table_prefix.length(); }
const uint64_t ObjectStore::NotVersioned = std::numeric_limits<uint64_t>::max(); const uint64_t ObjectStore::NotVersioned = std::numeric_limits<uint64_t>::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) { StringData ObjectStore::object_type_for_table_name(StringData 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) { if (table_name.begins_with(c_object_table_prefix)) {
return table_name.substr(c_object_table_prefix_length, table_name.length() - c_object_table_prefix_length); 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) { std::string ObjectStore::table_name_for_object_type(StringData object_type) {
return c_object_table_prefix + object_type; return std::string(c_object_table_prefix) + object_type.data();
} }
TableRef ObjectStore::table_for_object_type(Group *group, StringData object_type) { 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)); 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); 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); TableRef table = table_for_object_type(group, object_type);
if (table) { if (table) {
group->remove_table(table->get_index_in_group()); group->remove_table(table->get_index_in_group());

View File

@ -19,7 +19,6 @@
#ifndef REALM_OBJECT_STORE_HPP #ifndef REALM_OBJECT_STORE_HPP
#define REALM_OBJECT_STORE_HPP #define REALM_OBJECT_STORE_HPP
#include "schema.hpp"
#include "object_schema.hpp" #include "object_schema.hpp"
#include "property.hpp" #include "property.hpp"
@ -30,6 +29,7 @@
namespace realm { namespace realm {
class ObjectSchemaValidationException; class ObjectSchemaValidationException;
class Schema;
class ObjectStore { class ObjectStore {
public: public:
@ -68,11 +68,14 @@ namespace realm {
static Schema schema_from_group(const Group *group); static Schema schema_from_group(const Group *group);
// deletes the table for the given type // 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 // indicates if this group contains any objects
static bool is_empty(const Group *group); 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: private:
// set a new schema version // set a new schema version
static void set_schema_version(Group *group, uint64_t version); static void set_schema_version(Group *group, uint64_t version);
@ -102,9 +105,7 @@ namespace realm {
// must be in write transaction to set // must be in write transaction to set
static void set_primary_key_for_object(Group *group, StringData object_type, StringData primary_key); 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 TableRef table_for_object_type_create_if_needed(Group *group, 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);
// returns if any indexes were changed // returns if any indexes were changed
static bool update_indexes(Group *group, Schema &schema); static bool update_indexes(Group *group, Schema &schema);

View File

@ -3,42 +3,318 @@
*/ */
#include "results.hpp" #include "results.hpp"
#import <stdexcept>
#include <stdexcept>
using namespace realm; using namespace realm;
Results::Results(SharedRealm &r, ObjectSchema &o, Query q, SortOrder s) : #ifdef __has_cpp_attribute
realm(r), object_schema(o), backing_query(q), table_view(backing_query.find_all()) #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() size_t Results::size()
{ {
verify_attached(); validate_read();
return table_view.size(); switch (m_mode) {
} case Mode::Empty: return 0;
case Mode::Table: return m_table->size();
void Results::setSort(SortOrder s) case Mode::Query: return m_query.count();
{ case Mode::TableView:
sort_order = std::make_unique<SortOrder>(std::move(s)); update_tableview();
table_view.sort(sort_order->columnIndices, sort_order->ascending); return m_table_view.size();
}
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()) + ".");
} }
return table_view.get(row_ndx); REALM_UNREACHABLE();
} }
void Results::verify_attached() RowExpr Results::get(size_t row_ndx)
{ {
if (!table_view.is_attached()) { validate_read();
throw std::runtime_error("Tableview is not attached"); 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<RowExpr> 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<RowExpr> 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<typename Int, typename Float, typename Double, typename DateTime>
util::Optional<Mixed> 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<Mixed> {
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<Mixed>(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<Mixed>(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<Mixed> 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<Mixed> 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<Mixed> 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<Mixed> 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);
} }

View File

@ -5,35 +5,150 @@
#ifndef REALM_RESULTS_HPP #ifndef REALM_RESULTS_HPP
#define REALM_RESULTS_HPP #define REALM_RESULTS_HPP
#import "shared_realm.hpp" #include "shared_realm.hpp"
#import <realm/table_view.hpp>
#include <realm/table_view.hpp>
#include <realm/table.hpp>
#include <realm/util/optional.hpp>
namespace realm { namespace realm {
struct SortOrder { template<typename T> class BasicRowExpr;
std::vector<size_t> columnIndices; using RowExpr = BasicRowExpr<Table>;
std::vector<bool> ascending; class Mixed;
explicit operator bool() const { struct SortOrder {
return !columnIndices.empty(); std::vector<size_t> columnIndices;
} std::vector<bool> 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<RowExpr> first();
util::Optional<RowExpr> 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<Mixed> max(size_t column);
util::Optional<Mixed> min(size_t column);
util::Optional<Mixed> average(size_t column);
util::Optional<Mixed> 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 { // The input Row object belongs to a different table
Results(SharedRealm &r, ObjectSchema &o, Query q, SortOrder s = s_defaultSort); struct IncorrectTableException {
size_t size(); StringData expected;
Row get(std::size_t row_ndx); StringData actual;
void verify_attached();
SharedRealm realm;
ObjectSchema &object_schema;
Query backing_query;
TableView table_view;
std::unique_ptr<SortOrder> sort_order;
void setSort(SortOrder s);
}; };
// 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<typename Int, typename Float, typename Double, typename DateTime>
util::Optional<Mixed> 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 */ #endif /* REALM_RESULTS_HPP */

View File

@ -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::~Config() = default;
Realm::Config& Realm::Config::operator=(realm::Realm::Config const& c) 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 void Realm::verify_thread() const
{ {
if (m_thread_id != std::this_thread::get_id()) { 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.");
} }
} }

View File

@ -19,7 +19,6 @@
#ifndef REALM_REALM_HPP #ifndef REALM_REALM_HPP
#define REALM_REALM_HPP #define REALM_REALM_HPP
#include <map>
#include <memory> #include <memory>
#include <thread> #include <thread>
#include <vector> #include <vector>
@ -56,8 +55,8 @@ namespace realm {
MigrationFunction migration_function; MigrationFunction migration_function;
Config() = default; Config();
Config(Config&&) = default; Config(Config&&);
Config(const Config& c); Config(const Config& c);
~Config(); ~Config();
@ -100,6 +99,11 @@ namespace realm {
std::thread::id thread_id() const { return m_thread_id; } std::thread::id thread_id() const { return m_thread_id; }
void verify_thread() const; 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 // Close this Realm and remove it from the cache. Continuing to use a
// Realm after closing it will produce undefined behavior. // Realm after closing it will produce undefined behavior.
@ -145,11 +149,9 @@ namespace realm {
std::mutex m_mutex; std::mutex m_mutex;
}; };
class RealmFileException : public std::runtime_error class RealmFileException : public std::runtime_error {
{ public:
public: enum class Kind {
enum class Kind
{
/** Thrown for any I/O related exception scenarios when a realm is opened. */ /** Thrown for any I/O related exception scenarios when a realm is opened. */
AccessError, AccessError,
/** Thrown if the user does not have permission to open or create /** Thrown if the user does not have permission to open or create
@ -169,32 +171,28 @@ namespace realm {
Kind kind() const { return m_kind; } Kind kind() const { return m_kind; }
const std::string& path() const { return m_path; } const std::string& path() const { return m_path; }
private: private:
Kind m_kind; Kind m_kind;
std::string m_path; std::string m_path;
}; };
class MismatchedConfigException : public std::runtime_error class MismatchedConfigException : public std::runtime_error {
{ public:
public:
MismatchedConfigException(std::string message) : std::runtime_error(message) {} MismatchedConfigException(std::string message) : std::runtime_error(message) {}
}; };
class InvalidTransactionException : public std::runtime_error class InvalidTransactionException : public std::runtime_error {
{ public:
public:
InvalidTransactionException(std::string message) : std::runtime_error(message) {} InvalidTransactionException(std::string message) : std::runtime_error(message) {}
}; };
class IncorrectThreadException : public std::runtime_error class IncorrectThreadException : public std::runtime_error {
{ public:
public: IncorrectThreadException() : std::runtime_error("Realm accessed from incorrect thread.") {}
IncorrectThreadException(std::string message) : std::runtime_error(message) {}
}; };
class UnitializedRealmException : public std::runtime_error class UnitializedRealmException : public std::runtime_error {
{ public:
public:
UnitializedRealmException(std::string message) : std::runtime_error(message) {} UnitializedRealmException(std::string message) : std::runtime_error(message) {}
}; };
} }