Merge pull request #168 from realm/al-os-merge
Integrate new Results class
This commit is contained in:
commit
7ad2786359
|
@ -22,7 +22,9 @@ JSValueRef ResultsGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef
|
||||||
return JSValueMakeNumber(ctx, size);
|
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) {
|
catch (std::out_of_range &exp) {
|
||||||
// getters for nonexistent properties in JS should always return undefined
|
// 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) {
|
void ResultsPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames) {
|
||||||
Results *results = RJSGetInternal<Results *>(object);
|
Results *results = RJSGetInternal<Results *>(object);
|
||||||
char str[32];
|
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);
|
sprintf(str, "%i", i);
|
||||||
JSStringRef name = JSStringCreateWithUTF8CString(str);
|
JSStringRef name = JSStringCreateWithUTF8CString(str);
|
||||||
JSPropertyNameAccumulatorAddName(propertyNames, name);
|
JSPropertyNameAccumulatorAddName(propertyNames, name);
|
||||||
|
@ -74,7 +76,7 @@ JSValueRef SortByProperty(JSContextRef ctx, JSObjectRef function, JSObjectRef th
|
||||||
Results *results = RJSGetInternal<Results *>(thisObject);
|
Results *results = RJSGetInternal<Results *>(thisObject);
|
||||||
RJSValidateArgumentRange(argumentCount, 1, 2);
|
RJSValidateArgumentRange(argumentCount, 1, 2);
|
||||||
std::string propName = RJSValidatedStringForValue(ctx, arguments[0]);
|
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) {
|
if (!prop) {
|
||||||
throw std::runtime_error("Property '" + propName + "' does not exist on object type '" + results->object_schema.name + "'");
|
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]);
|
ascending = JSValueToBoolean(ctx, arguments[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
SortOrder sort = {{prop->table_column}, {ascending}};
|
*results = results->sort({{prop->table_column}, {ascending}});
|
||||||
results->setSort(sort);
|
|
||||||
}
|
}
|
||||||
catch (std::exception &exp) {
|
catch (std::exception &exp) {
|
||||||
if (jsException) {
|
if (jsException) {
|
||||||
|
@ -101,7 +102,7 @@ JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, std::string cl
|
||||||
if (object_schema == realm->config().schema->end()) {
|
if (object_schema == realm->config().schema->end()) {
|
||||||
throw std::runtime_error("Object type '" + className + "' not present in Realm.");
|
throw std::runtime_error("Object type '" + className + "' not present in Realm.");
|
||||||
}
|
}
|
||||||
return RJSWrapObject<Results *>(ctx, RJSResultsClass(), new Results(realm, *object_schema, table->where()));
|
return RJSWrapObject<Results *>(ctx, RJSResultsClass(), new Results(realm, *object_schema, *table));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "shared_realm.hpp"
|
#include "shared_realm.hpp"
|
||||||
|
#include "schema.hpp"
|
||||||
#include "list.hpp"
|
#include "list.hpp"
|
||||||
|
|
||||||
namespace realm {
|
namespace realm {
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -3,42 +3,315 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#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, 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)
|
||||||
{
|
{
|
||||||
setSort(std::move(s));
|
}
|
||||||
|
|
||||||
|
Results::Results(SharedRealm r, const ObjectSchema &o, Table& table)
|
||||||
|
: m_realm(std::move(r))
|
||||||
|
, m_table(&table)
|
||||||
|
, m_mode(Mode::Table)
|
||||||
|
, object_schema(o)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
case Mode::Query: return m_query.count();
|
||||||
|
case Mode::TableView:
|
||||||
|
update_tableview();
|
||||||
|
return m_table_view.size();
|
||||||
|
}
|
||||||
|
REALM_UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Results::setSort(SortOrder s)
|
RowExpr Results::get(size_t row_ndx)
|
||||||
{
|
{
|
||||||
sort_order = std::make_unique<SortOrder>(std::move(s));
|
validate_read();
|
||||||
table_view.sort(sort_order->columnIndices, sort_order->ascending);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
Row Results::get(std::size_t row_ndx)
|
throw OutOfBoundsIndexException{row_ndx, size()};
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Results::verify_attached()
|
util::Optional<RowExpr> Results::first()
|
||||||
{
|
{
|
||||||
if (!table_view.is_attached()) {
|
validate_read();
|
||||||
throw std::runtime_error("Tableview is not attached");
|
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());
|
||||||
}
|
}
|
||||||
table_view.sync_if_needed();
|
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(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());
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
Results Results::sort(realm::SortOrder&& sort) const
|
||||||
|
{
|
||||||
|
return Results(m_realm, object_schema, get_query(), std::move(sort));
|
||||||
|
}
|
||||||
|
|
||||||
|
Results Results::filter(Query&& q) const
|
||||||
|
{
|
||||||
|
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)),
|
||||||
|
std::runtime_error((std::string)"Operation not supported on '" + table->get_column_name(column).data() + "' columns")
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,34 +5,166 @@
|
||||||
#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 {
|
||||||
|
template<typename T> class BasicRowExpr;
|
||||||
|
using RowExpr = BasicRowExpr<Table>;
|
||||||
|
class Mixed;
|
||||||
|
|
||||||
struct SortOrder {
|
struct SortOrder {
|
||||||
std::vector<size_t> columnIndices;
|
std::vector<size_t> columnIndices;
|
||||||
std::vector<bool> ascending;
|
std::vector<bool> ascending;
|
||||||
|
|
||||||
explicit operator bool() const {
|
explicit operator bool() const
|
||||||
|
{
|
||||||
return !columnIndices.empty();
|
return !columnIndices.empty();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static SortOrder s_defaultSort = {{}, {}};
|
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, const ObjectSchema &o, Table& table);
|
||||||
|
Results(SharedRealm r, const ObjectSchema &o, Query q, SortOrder s = {});
|
||||||
|
|
||||||
struct Results {
|
// Results is copyable and moveable
|
||||||
Results(SharedRealm &r, ObjectSchema &o, Query q, SortOrder s = s_defaultSort);
|
Results(Results const&) = default;
|
||||||
|
Results(Results&&) = default;
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 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 { return object_schema.name; }
|
||||||
|
|
||||||
|
// Get the size of this results
|
||||||
|
// Can be either O(1) or O(N) depending on the state of things
|
||||||
size_t size();
|
size_t size();
|
||||||
Row get(std::size_t row_ndx);
|
|
||||||
void verify_attached();
|
|
||||||
|
|
||||||
SharedRealm realm;
|
// Get the row accessor for the given index
|
||||||
ObjectSchema &object_schema;
|
// Throws OutOfBoundsIndexException if index >= size()
|
||||||
Query backing_query;
|
RowExpr get(size_t index);
|
||||||
TableView table_view;
|
|
||||||
std::unique_ptr<SortOrder> sort_order;
|
|
||||||
|
|
||||||
void setSort(SortOrder s);
|
// 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 : public std::runtime_error
|
||||||
|
{
|
||||||
|
InvalidatedException() : std::runtime_error("Access to invalidated Results objects") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The input index parameter was out of bounds
|
||||||
|
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 : 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 : 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 : public std::runtime_error {
|
||||||
|
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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,7 @@ 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
|
// 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 +145,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
|
||||||
|
@ -174,26 +172,22 @@ namespace realm {
|
||||||
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::string message) : std::runtime_error(message) {}
|
IncorrectThreadException() : std::runtime_error("Realm accessed from incorrect thread.") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
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) {}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue