Merge pull request #339 from realm/al-indexed

Support for indexed properties
This commit is contained in:
Ari Lazier 2016-03-21 13:27:23 -07:00
commit 7e9df63777
26 changed files with 150 additions and 86 deletions

View File

@ -8,6 +8,7 @@
* Add common Array methods to List and Results * Add common Array methods to List and Results
* Accept constructor in create() and objects() methods * Accept constructor in create() and objects() methods
* Support relative paths when opening realms * Support relative paths when opening realms
* Support for indexed bool, string, date, and int properties
### Bugfixes ### Bugfixes
* Fix for crash on Android when initializing the Realm module * Fix for crash on Android when initializing the Realm module

View File

@ -150,6 +150,8 @@ Realm.defaultPath;
* @property {any} [default] - The default value for this property on creation when not * @property {any} [default] - The default value for this property on creation when not
* otherwise specified. * otherwise specified.
* @property {boolean} [optional] - Signals if this property may be assigned `null` or `undefined`. * @property {boolean} [optional] - Signals if this property may be assigned `null` or `undefined`.
* @property {boolean} [indexed] - Signals if this property should be indexed. Only supported for
* `"string"`, `"int"`, and `"bool"` properties.
*/ */
/** /**

View File

@ -41,6 +41,7 @@
"scripts": { "scripts": {
"get-version": "echo $npm_package_version", "get-version": "echo $npm_package_version",
"set-version": "scripts/set-version.sh", "set-version": "scripts/set-version.sh",
"get-core-version": "scripts/download-core.sh --version",
"jsdoc": "rm -rf docs/output && jsdoc -c docs/conf.json", "jsdoc": "rm -rf docs/output && jsdoc -c docs/conf.json",
"lint": "eslint", "lint": "eslint",
"test": "scripts/test.sh", "test": "scripts/test.sh",

View File

@ -27,8 +27,8 @@ import org.apache.tools.ant.filters.ReplaceTokens
// We download various C++ open-source dependencies into downloads. // We download various C++ open-source dependencies into downloads.
// We then copy both the downloaded code and our custom makefiles and headers into third-party-ndk. // We then copy both the downloaded code and our custom makefiles and headers into third-party-ndk.
// After that we build native code from src/main/jni with module path pointing at third-party-ndk. // After that we build native code from src/main/jni with module path pointing at third-party-ndk.
ext.coreVersion = '0.96.2'
ext.coreVersion = "npm --silent run get-core-version".execute().text.trim()
def currentVersion = "npm --silent run get-version".execute().text.trim() def currentVersion = "npm --silent run get-version".execute().text.trim()
def downloadsDir = new File("$projectDir/downloads") def downloadsDir = new File("$projectDir/downloads")
def jscDownloadDir = new File("$projectDir/src/main/jni/jsc") def jscDownloadDir = new File("$projectDir/src/main/jni/jsc")

View File

@ -3,7 +3,7 @@
set -e set -e
set -o pipefail set -o pipefail
: ${REALM_CORE_VERSION:=0.96.2} # set to "current" to always use the current build : ${REALM_CORE_VERSION:=0.97.1} # set to "current" to always use the current build
# Start current working directory at the root of the project. # Start current working directory at the root of the project.
cd "$(dirname "$0")/.." cd "$(dirname "$0")/.."
@ -46,6 +46,11 @@ check_release_notes() {
grep -Fqi "$REALM_CORE_VERSION RELEASE NOTES" "$@" grep -Fqi "$REALM_CORE_VERSION RELEASE NOTES" "$@"
} }
if [[ $1 = "--version" ]]; then
echo $REALM_CORE_VERSION
exit 0
fi
if [ ! -e core ]; then if [ ! -e core ]; then
download_core download_core
elif [ -d core -a -d ../realm-core -a ! -L core ]; then elif [ -d core -a -d ../realm-core -a ! -L core ]; then

View File

@ -2,7 +2,8 @@
set -e set -e
set -o pipefail set -o pipefail
export REALM_CORE_VERSION=$(./scripts/download-core.sh --version)
echo "Core Version: $REALM_CORE_VERSION"
cd "$(dirname "$0")/.." cd "$(dirname "$0")/.."
if [ -n "$REALM_BUILD_ANDROID" ]; then if [ -n "$REALM_BUILD_ANDROID" ]; then

View File

@ -47,6 +47,7 @@ JSObjectRef RJSSchemaCreate(JSContextRef ctx, Schema &schema) {
static inline Property RJSParseProperty(JSContextRef ctx, JSValueRef propertyAttributes, std::string propertyName, ObjectDefaults &objectDefaults) { static inline Property RJSParseProperty(JSContextRef ctx, JSValueRef propertyAttributes, std::string propertyName, ObjectDefaults &objectDefaults) {
static JSStringRef defaultString = JSStringCreateWithUTF8CString("default"); static JSStringRef defaultString = JSStringCreateWithUTF8CString("default");
static JSStringRef indexedString = JSStringCreateWithUTF8CString("indexed");
static JSStringRef typeString = JSStringCreateWithUTF8CString("type"); static JSStringRef typeString = JSStringCreateWithUTF8CString("type");
static JSStringRef objectTypeString = JSStringCreateWithUTF8CString("objectType"); static JSStringRef objectTypeString = JSStringCreateWithUTF8CString("objectType");
static JSStringRef optionalString = JSStringCreateWithUTF8CString("optional"); static JSStringRef optionalString = JSStringCreateWithUTF8CString("optional");
@ -123,6 +124,11 @@ static inline Property RJSParseProperty(JSContextRef ctx, JSValueRef propertyAtt
JSValueProtect(ctx, defaultValue); JSValueProtect(ctx, defaultValue);
objectDefaults.emplace(prop.name, defaultValue); objectDefaults.emplace(prop.name, defaultValue);
} }
JSValueRef indexedValue = RJSValidatedPropertyValue(ctx, propertyObject, indexedString);
if (!JSValueIsUndefined(ctx, indexedValue)) {
prop.is_indexed = JSValueToBoolean(ctx, indexedValue);
}
} }
return prop; return prop;

View File

@ -7,7 +7,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake")
include(CompilerFlags) include(CompilerFlags)
include(RealmCore) include(RealmCore)
set(REALM_CORE_VERSION "0.96.2" CACHE STRING "") set(REALM_CORE_VERSION "0.97.0" CACHE STRING "")
use_realm_core(${REALM_CORE_VERSION}) use_realm_core(${REALM_CORE_VERSION})
include_directories(${REALM_CORE_INCLUDE_DIR} src external/pegtl) include_directories(${REALM_CORE_INCLUDE_DIR} src external/pegtl)

View File

@ -11,7 +11,7 @@ Cross-platform code used accross bindings. Binding developers can choose to use
The object store's build system currently only suports building for OS X. The object store itself can build for all Apple The object store's build system currently only suports building for OS X. The object store itself can build for all Apple
platforms when integrated into a binding. platforms when integrated into a binding.
1. Install CMake. You can download an installer for OS X from the [CMake download page], or install via [Homebrew](http://brew.sh): 1. Install CMake. You can download an installer for OS X from the [CMake download page](https://cmake.org/download/), or install via [Homebrew](http://brew.sh):
``` ```
brew install cmake brew install cmake
``` ```

View File

@ -159,7 +159,7 @@ void AsyncQuery::run()
if (m_initial_run_complete) { if (m_initial_run_complete) {
// Make an empty tableview from the query to get the table version, since // Make an empty tableview from the query to get the table version, since
// Query doesn't expose it // Query doesn't expose it
if (m_query->find_all(0, 0, 0).outside_version() == m_handed_over_table_version) { if (m_query->find_all(0, 0, 0).sync_if_needed() == m_handed_over_table_version) {
return; return;
} }
} }
@ -181,7 +181,7 @@ void AsyncQuery::prepare_handover()
REALM_ASSERT(m_tv.is_in_sync()); REALM_ASSERT(m_tv.is_in_sync());
m_initial_run_complete = true; m_initial_run_complete = true;
m_handed_over_table_version = m_tv.outside_version(); m_handed_over_table_version = m_tv.sync_if_needed();
m_tv_handover = m_sg->export_for_handover(m_tv, MutableSourcePayload::Move); m_tv_handover = m_sg->export_for_handover(m_tv, MutableSourcePayload::Move);
// detach the TableView as we won't need it again and keeping it around // detach the TableView as we won't need it again and keeping it around
@ -231,7 +231,7 @@ bool AsyncQuery::deliver(SharedGroup& sg, std::exception_ptr err)
m_tv_handover->version = m_sg_version; m_tv_handover->version = m_sg_version;
Results::Internal::set_table_view(*m_target_results, Results::Internal::set_table_view(*m_target_results,
std::move(*sg.import_from_handover(std::move(m_tv_handover)))); std::move(*sg.import_from_handover(std::move(m_tv_handover))));
m_delievered_table_version = m_handed_over_table_version; m_delivered_table_version = m_handed_over_table_version;
} }
REALM_ASSERT(!m_tv_handover); REALM_ASSERT(!m_tv_handover);
@ -259,8 +259,8 @@ std::function<void (std::exception_ptr)> AsyncQuery::next_callback()
std::lock_guard<std::mutex> callback_lock(m_callback_mutex); std::lock_guard<std::mutex> callback_lock(m_callback_mutex);
for (++m_callback_index; m_callback_index < m_callbacks.size(); ++m_callback_index) { for (++m_callback_index; m_callback_index < m_callbacks.size(); ++m_callback_index) {
auto& callback = m_callbacks[m_callback_index]; auto& callback = m_callbacks[m_callback_index];
if (m_error || callback.delivered_version != m_delievered_table_version) { if (m_error || callback.delivered_version != m_delivered_table_version) {
callback.delivered_version = m_delievered_table_version; callback.delivered_version = m_delivered_table_version;
return callback.fn; return callback.fn;
} }
} }

View File

@ -97,7 +97,7 @@ private:
SharedGroup* m_sg = nullptr; SharedGroup* m_sg = nullptr;
uint_fast64_t m_handed_over_table_version = -1; uint_fast64_t m_handed_over_table_version = -1;
uint_fast64_t m_delievered_table_version = -1; uint_fast64_t m_delivered_table_version = -1;
// Iteration variable for looping over callbacks // Iteration variable for looping over callbacks
// remove_callback() updates this when needed // remove_callback() updates this when needed

View File

@ -21,7 +21,7 @@
#include <future> #include <future>
namespace realm { namespace realm {
class ClientHistory; class Replication;
namespace _impl { namespace _impl {
class RealmCoordinator; class RealmCoordinator;
@ -38,7 +38,7 @@ private:
RealmCoordinator& m_parent; RealmCoordinator& m_parent;
// A shared group used to listen for changes // A shared group used to listen for changes
std::unique_ptr<ClientHistory> m_history; std::unique_ptr<Replication> m_history;
SharedGroup m_sg; SharedGroup m_sg;
// The listener thread // The listener thread

View File

@ -385,7 +385,7 @@ void RealmCoordinator::move_new_queries_to_main()
void RealmCoordinator::advance_helper_shared_group_to_latest() void RealmCoordinator::advance_helper_shared_group_to_latest()
{ {
if (m_new_queries.empty()) { if (m_new_queries.empty()) {
LangBindHelper::advance_read(*m_query_sg, *m_query_history); LangBindHelper::advance_read(*m_query_sg);
return; return;
} }
@ -397,14 +397,13 @@ void RealmCoordinator::advance_helper_shared_group_to_latest()
// Import all newly added queries to our helper SG // Import all newly added queries to our helper SG
for (auto& query : m_new_queries) { for (auto& query : m_new_queries) {
LangBindHelper::advance_read(*m_advancer_sg, *m_advancer_history, query->version()); LangBindHelper::advance_read(*m_advancer_sg, query->version());
query->attach_to(*m_advancer_sg); query->attach_to(*m_advancer_sg);
} }
// Advance both SGs to the newest version // Advance both SGs to the newest version
LangBindHelper::advance_read(*m_advancer_sg, *m_advancer_history); LangBindHelper::advance_read(*m_advancer_sg);
LangBindHelper::advance_read(*m_query_sg, *m_query_history, LangBindHelper::advance_read(*m_query_sg, m_advancer_sg->get_version_of_current_transaction());
m_advancer_sg->get_version_of_current_transaction());
// Transfer all new queries over to the main SG // Transfer all new queries over to the main SG
for (auto& query : m_new_queries) { for (auto& query : m_new_queries) {
@ -421,7 +420,6 @@ void RealmCoordinator::advance_to_ready(Realm& realm)
decltype(m_queries) queries; decltype(m_queries) queries;
auto& sg = Realm::Internal::get_shared_group(realm); auto& sg = Realm::Internal::get_shared_group(realm);
auto& history = Realm::Internal::get_history(realm);
auto get_query_version = [&] { auto get_query_version = [&] {
for (auto& query : m_queries) { for (auto& query : m_queries) {
@ -440,8 +438,8 @@ void RealmCoordinator::advance_to_ready(Realm& realm)
} }
// no async queries; just advance to latest // no async queries; just advance to latest
if (version.version == 0) { if (version.version == std::numeric_limits<uint_fast64_t>::max()) {
transaction::advance(sg, history, realm.m_binding_context.get()); transaction::advance(sg, realm.m_binding_context.get());
return; return;
} }
@ -453,14 +451,14 @@ void RealmCoordinator::advance_to_ready(Realm& realm)
while (true) { while (true) {
// Advance to the ready version without holding any locks because it // Advance to the ready version without holding any locks because it
// may end up calling user code (in did_change() notifications) // may end up calling user code (in did_change() notifications)
transaction::advance(sg, history, realm.m_binding_context.get(), version); transaction::advance(sg, realm.m_binding_context.get(), version);
// Reacquire the lock and recheck the query version, as the queries may // Reacquire the lock and recheck the query version, as the queries may
// have advanced to a later version while we didn't hold the lock. If // have advanced to a later version while we didn't hold the lock. If
// so, we need to release the lock and re-advance // so, we need to release the lock and re-advance
std::lock_guard<std::mutex> lock(m_query_mutex); std::lock_guard<std::mutex> lock(m_query_mutex);
version = get_query_version(); version = get_query_version();
if (version.version == 0) if (version.version == std::numeric_limits<uint_fast64_t>::max())
return; return;
if (version != sg.get_version_of_current_transaction()) if (version != sg.get_version_of_current_transaction())
continue; continue;

View File

@ -25,7 +25,7 @@
namespace realm { namespace realm {
class AsyncQueryCallback; class AsyncQueryCallback;
class ClientHistory; class Replication;
class Results; class Results;
class Schema; class Schema;
class SharedGroup; class SharedGroup;
@ -65,10 +65,10 @@ public:
// Should only be called in test code, as continuing to use the previously // Should only be called in test code, as continuing to use the previously
// cached instances will have odd results // cached instances will have odd results
static void clear_cache(); static void clear_cache();
// Clears all caches on existing coordinators // Clears all caches on existing coordinators
static void clear_all_caches(); static void clear_all_caches();
// Explicit constructor/destructor needed for the unique_ptrs to forward-declared types // Explicit constructor/destructor needed for the unique_ptrs to forward-declared types
RealmCoordinator(); RealmCoordinator();
~RealmCoordinator(); ~RealmCoordinator();
@ -102,13 +102,13 @@ private:
// SharedGroup used for actually running async queries // SharedGroup used for actually running async queries
// Will have a read transaction iff m_queries is non-empty // Will have a read transaction iff m_queries is non-empty
std::unique_ptr<ClientHistory> m_query_history; std::unique_ptr<Replication> m_query_history;
std::unique_ptr<SharedGroup> m_query_sg; std::unique_ptr<SharedGroup> m_query_sg;
// SharedGroup used to advance queries in m_new_queries to the main shared // SharedGroup used to advance queries in m_new_queries to the main shared
// group's transaction version // group's transaction version
// Will have a read transaction iff m_new_queries is non-empty // Will have a read transaction iff m_new_queries is non-empty
std::unique_ptr<ClientHistory> m_advancer_history; std::unique_ptr<Replication> m_advancer_history;
std::unique_ptr<SharedGroup> m_advancer_sg; std::unique_ptr<SharedGroup> m_advancer_sg;
std::exception_ptr m_async_error; std::exception_ptr m_async_error;

View File

@ -432,23 +432,21 @@ public:
namespace realm { namespace realm {
namespace _impl { namespace _impl {
namespace transaction { namespace transaction {
void advance(SharedGroup& sg, ClientHistory& history, BindingContext* context, void advance(SharedGroup& sg, BindingContext* context, SharedGroup::VersionID version)
SharedGroup::VersionID version)
{ {
TransactLogObserver(context, sg, [&](auto&&... args) { TransactLogObserver(context, sg, [&](auto&&... args) {
LangBindHelper::advance_read(sg, history, std::move(args)...); LangBindHelper::advance_read(sg, std::move(args)...);
}, true); }, true);
} }
void begin(SharedGroup& sg, ClientHistory& history, BindingContext* context, void begin(SharedGroup& sg, BindingContext* context, bool validate_schema_changes)
bool validate_schema_changes)
{ {
TransactLogObserver(context, sg, [&](auto&&... args) { TransactLogObserver(context, sg, [&](auto&&... args) {
LangBindHelper::promote_to_write(sg, history, std::move(args)...); LangBindHelper::promote_to_write(sg, std::move(args)...);
}, validate_schema_changes); }, validate_schema_changes);
} }
void commit(SharedGroup& sg, ClientHistory&, BindingContext* context) void commit(SharedGroup& sg, BindingContext* context)
{ {
LangBindHelper::commit_and_continue_as_read(sg); LangBindHelper::commit_and_continue_as_read(sg);
@ -457,10 +455,10 @@ void commit(SharedGroup& sg, ClientHistory&, BindingContext* context)
} }
} }
void cancel(SharedGroup& sg, ClientHistory& history, BindingContext* context) void cancel(SharedGroup& sg, BindingContext* context)
{ {
TransactLogObserver(context, sg, [&](auto&&... args) { TransactLogObserver(context, sg, [&](auto&&... args) {
LangBindHelper::rollback_and_continue_as_read(sg, history, std::move(args)...); LangBindHelper::rollback_and_continue_as_read(sg, std::move(args)...);
}, false); }, false);
} }

View File

@ -24,27 +24,26 @@
namespace realm { namespace realm {
class BindingContext; class BindingContext;
class SharedGroup; class SharedGroup;
class ClientHistory;
namespace _impl { namespace _impl {
namespace transaction { namespace transaction {
// Advance the read transaction version, with change notifications sent to delegate // Advance the read transaction version, with change notifications sent to delegate
// Must not be called from within a write transaction. // Must not be called from within a write transaction.
void advance(SharedGroup& sg, ClientHistory& history, BindingContext* binding_context, void advance(SharedGroup& sg, BindingContext* binding_context,
SharedGroup::VersionID version=SharedGroup::VersionID{}); SharedGroup::VersionID version=SharedGroup::VersionID{});
// Begin a write transaction // Begin a write transaction
// If the read transaction version is not up to date, will first advance to the // If the read transaction version is not up to date, will first advance to the
// most recent read transaction and sent notifications to delegate // most recent read transaction and sent notifications to delegate
void begin(SharedGroup& sg, ClientHistory& history, BindingContext* binding_context, void begin(SharedGroup& sg, BindingContext* binding_context,
bool validate_schema_changes=true); bool validate_schema_changes=true);
// Commit a write transaction // Commit a write transaction
void commit(SharedGroup& sg, ClientHistory& history, BindingContext* binding_context); void commit(SharedGroup& sg, BindingContext* binding_context);
// Cancel a write transaction and roll back all changes, with change notifications // Cancel a write transaction and roll back all changes, with change notifications
// for reverting to the old values sent to delegate // for reverting to the old values sent to delegate
void cancel(SharedGroup& sg, ClientHistory& history, BindingContext* binding_context); void cancel(SharedGroup& sg, BindingContext* binding_context);
} // namespace transaction } // namespace transaction
} // namespace _impl } // namespace _impl
} // namespace realm } // namespace realm

View File

@ -26,7 +26,7 @@
namespace realm { namespace realm {
class Group; class Group;
class Property; struct Property;
class ObjectSchema { class ObjectSchema {
public: public:

View File

@ -52,7 +52,7 @@ namespace realm {
// determines if a realm with the given old schema needs non-migration // determines if a realm with the given old schema needs non-migration
// changes to make it compatible with the given target schema // changes to make it compatible with the given target schema
static bool needs_update(Schema const& old_schema, Schema const& schema); static bool needs_update(Schema const& old_schema, Schema const& schema);
// updates a Realm from old_schema to the given target schema, creating and updating tables as needed // updates a Realm from old_schema to the given target schema, creating and updating tables as needed
// passed in target schema is updated with the correct column mapping // passed in target schema is updated with the correct column mapping
// optionally runs migration function if schema is out of date // optionally runs migration function if schema is out of date

View File

@ -112,7 +112,7 @@ struct ParserState
{ {
return group_stack.back(); return group_stack.back();
} }
Predicate *last_predicate() Predicate *last_predicate()
{ {
Predicate *pred = current_group(); Predicate *pred = current_group();
@ -121,12 +121,12 @@ struct ParserState
} }
return pred; return pred;
} }
void add_predicate_to_current_group(Predicate::Type type) void add_predicate_to_current_group(Predicate::Type type)
{ {
current_group()->cpnd.sub_predicates.emplace_back(type, negate_next); current_group()->cpnd.sub_predicates.emplace_back(type, negate_next);
negate_next = false; negate_next = false;
if (current_group()->cpnd.sub_predicates.size() > 1) { if (current_group()->cpnd.sub_predicates.size() > 1) {
if (next_type == Predicate::Type::Or) { if (next_type == Predicate::Type::Or) {
apply_or(); apply_or();
@ -136,10 +136,10 @@ struct ParserState
} }
} }
} }
bool negate_next = false; bool negate_next = false;
Predicate::Type next_type = Predicate::Type::And; Predicate::Type next_type = Predicate::Type::And;
void add_expression(Expression && exp) void add_expression(Expression && exp)
{ {
Predicate *current = last_predicate(); Predicate *current = last_predicate();
@ -151,32 +151,32 @@ struct ParserState
last_predicate()->cmpr.expr[0] = std::move(exp); last_predicate()->cmpr.expr[0] = std::move(exp);
} }
} }
void apply_or() void apply_or()
{ {
Predicate *group = current_group(); Predicate *group = current_group();
if (group->type == Predicate::Type::Or) { if (group->type == Predicate::Type::Or) {
return; return;
} }
// convert to OR // convert to OR
group->type = Predicate::Type::Or; group->type = Predicate::Type::Or;
if (group->cpnd.sub_predicates.size() > 2) { if (group->cpnd.sub_predicates.size() > 2) {
// split the current group into an AND group ORed with the last subpredicate // split the current group into an AND group ORed with the last subpredicate
Predicate new_sub(Predicate::Type::And); Predicate new_sub(Predicate::Type::And);
new_sub.cpnd.sub_predicates = std::move(group->cpnd.sub_predicates); new_sub.cpnd.sub_predicates = std::move(group->cpnd.sub_predicates);
group->cpnd.sub_predicates = { new_sub, std::move(new_sub.cpnd.sub_predicates.back()) }; group->cpnd.sub_predicates = { new_sub, std::move(new_sub.cpnd.sub_predicates.back()) };
group->cpnd.sub_predicates[0].cpnd.sub_predicates.pop_back(); group->cpnd.sub_predicates[0].cpnd.sub_predicates.pop_back();
} }
} }
void apply_and() void apply_and()
{ {
if (current_group()->type == Predicate::Type::And) { if (current_group()->type == Predicate::Type::And) {
return; return;
} }
auto &sub_preds = current_group()->cpnd.sub_predicates; auto &sub_preds = current_group()->cpnd.sub_predicates;
auto second_last = sub_preds.end() - 2; auto second_last = sub_preds.end() - 2;
if (second_last->type == Predicate::Type::And && !second_last->negate) { if (second_last->type == Predicate::Type::And && !second_last->negate) {

View File

@ -462,7 +462,7 @@ void update_query_with_predicate(Query &query, const Predicate &pred, Arguments
if (pred.negate) { if (pred.negate) {
query.Not(); query.Not();
} }
switch (pred.type) { switch (pred.type) {
case Predicate::Type::And: case Predicate::Type::And:
query.group(); query.group();
@ -474,7 +474,7 @@ void update_query_with_predicate(Query &query, const Predicate &pred, Arguments
} }
query.end_group(); query.end_group();
break; break;
case Predicate::Type::Or: case Predicate::Type::Or:
query.group(); query.group();
for (auto &sub : pred.cpnd.sub_predicates) { for (auto &sub : pred.cpnd.sub_predicates) {
@ -486,7 +486,7 @@ void update_query_with_predicate(Query &query, const Predicate &pred, Arguments
} }
query.end_group(); query.end_group();
break; break;
case Predicate::Type::Comparison: { case Predicate::Type::Comparison: {
add_comparison_to_query(query, pred, arguments, schema, type); add_comparison_to_query(query, pred, arguments, schema, type);
break; break;
@ -494,11 +494,11 @@ void update_query_with_predicate(Query &query, const Predicate &pred, Arguments
case Predicate::Type::True: case Predicate::Type::True:
query.and_query(std::unique_ptr<realm::Expression>(new TrueExpression)); query.and_query(std::unique_ptr<realm::Expression>(new TrueExpression));
break; break;
case Predicate::Type::False: case Predicate::Type::False:
query.and_query(std::unique_ptr<realm::Expression>(new FalseExpression)); query.and_query(std::unique_ptr<realm::Expression>(new FalseExpression));
break; break;
default: default:
throw std::runtime_error("Invalid predicate type"); throw std::runtime_error("Invalid predicate type");
} }
@ -507,7 +507,7 @@ void update_query_with_predicate(Query &query, const Predicate &pred, Arguments
void apply_predicate(Query &query, const Predicate &predicate, Arguments &arguments, const Schema &schema, const std::string &objectType) void apply_predicate(Query &query, const Predicate &predicate, Arguments &arguments, const Schema &schema, const std::string &objectType)
{ {
update_query_with_predicate(query, predicate, arguments, schema, objectType); update_query_with_predicate(query, predicate, arguments, schema, objectType);
// Test the constructed query in core // Test the constructed query in core
std::string validateMessage = query.validate(); std::string validateMessage = query.validate();
precondition(validateMessage.empty(), validateMessage.c_str()); precondition(validateMessage.empty(), validateMessage.c_str());

View File

@ -55,6 +55,7 @@ namespace realm {
size_t table_column = -1; size_t table_column = -1;
bool requires_index() const { return is_primary || is_indexed; } bool requires_index() const { return is_primary || is_indexed; }
bool is_indexable() const { return type == PropertyTypeInt || type == PropertyTypeBool || type == PropertyTypeString || type == PropertyTypeDate; }
}; };
static inline const char *string_for_property_type(PropertyType type) { static inline const char *string_for_property_type(PropertyType type) {

View File

@ -83,10 +83,10 @@ public:
// Get the Realm // Get the Realm
SharedRealm get_realm() const { return m_realm; } SharedRealm get_realm() const { return m_realm; }
// Object schema describing the vendored object type // Object schema describing the vendored object type
const ObjectSchema &get_object_schema() const { return *m_object_schema; } const ObjectSchema &get_object_schema() const { return *m_object_schema; }
// Get a query which will match the same rows as is contained in this Results // 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 // Returned query will not be valid if the current mode is Empty
Query get_query() const; Query get_query() const;

View File

@ -89,7 +89,7 @@ void Schema::validate() const
// check indexable // check indexable
if (prop.is_indexed) { if (prop.is_indexed) {
if (prop.type != PropertyTypeString && prop.type != PropertyTypeInt) { if (!prop.is_indexable()) {
exceptions.emplace_back(PropertyTypeNotIndexableException(object.name, prop)); exceptions.emplace_back(PropertyTypeNotIndexableException(object.name, prop));
} }
} }

View File

@ -72,7 +72,7 @@ Realm::Realm(Config config)
} }
void Realm::open_with_config(const Config& config, void Realm::open_with_config(const Config& config,
std::unique_ptr<ClientHistory>& history, std::unique_ptr<Replication>& history,
std::unique_ptr<SharedGroup>& shared_group, std::unique_ptr<SharedGroup>& shared_group,
std::unique_ptr<Group>& read_only_group) std::unique_ptr<Group>& read_only_group)
{ {
@ -211,9 +211,8 @@ void Realm::update_schema(std::unique_ptr<Schema> schema, uint64_t version)
} }
read_group(); read_group();
transaction::begin(*m_shared_group, *m_history, m_binding_context.get(), transaction::begin(*m_shared_group, m_binding_context.get(),
/* error on schema changes */ false); /* error on schema changes */ false);
m_in_transaction = true;
struct WriteTransactionGuard { struct WriteTransactionGuard {
Realm& realm; Realm& realm;
@ -289,20 +288,27 @@ void Realm::verify_in_write() const
} }
} }
bool Realm::is_in_transaction() const noexcept
{
if (!m_shared_group) {
return false;
}
return m_shared_group->get_transact_stage() == SharedGroup::transact_Writing;
}
void Realm::begin_transaction() void Realm::begin_transaction()
{ {
check_read_write(this); check_read_write(this);
verify_thread(); verify_thread();
if (m_in_transaction) { if (is_in_transaction()) {
throw InvalidTransactionException("The Realm is already in a write transaction"); throw InvalidTransactionException("The Realm is already in a write transaction");
} }
// make sure we have a read transaction // make sure we have a read transaction
read_group(); read_group();
transaction::begin(*m_shared_group, *m_history, m_binding_context.get()); transaction::begin(*m_shared_group, m_binding_context.get());
m_in_transaction = true;
} }
void Realm::commit_transaction() void Realm::commit_transaction()
@ -310,12 +316,11 @@ void Realm::commit_transaction()
check_read_write(this); check_read_write(this);
verify_thread(); verify_thread();
if (!m_in_transaction) { if (!is_in_transaction()) {
throw InvalidTransactionException("Can't commit a non-existing write transaction"); throw InvalidTransactionException("Can't commit a non-existing write transaction");
} }
m_in_transaction = false; transaction::commit(*m_shared_group, m_binding_context.get());
transaction::commit(*m_shared_group, *m_history, m_binding_context.get());
m_coordinator->send_commit_notifications(); m_coordinator->send_commit_notifications();
} }
@ -324,18 +329,19 @@ void Realm::cancel_transaction()
check_read_write(this); check_read_write(this);
verify_thread(); verify_thread();
if (!m_in_transaction) { if (!is_in_transaction()) {
throw InvalidTransactionException("Can't cancel a non-existing write transaction"); throw InvalidTransactionException("Can't cancel a non-existing write transaction");
} }
m_in_transaction = false; transaction::cancel(*m_shared_group, m_binding_context.get());
transaction::cancel(*m_shared_group, *m_history, m_binding_context.get());
} }
void Realm::invalidate() void Realm::invalidate()
{ {
verify_thread(); verify_thread();
if (m_in_transaction) { check_read_write(this);
if (is_in_transaction()) {
cancel_transaction(); cancel_transaction();
} }
if (!m_group) { if (!m_group) {
@ -353,7 +359,7 @@ bool Realm::compact()
if (m_config.read_only) { if (m_config.read_only) {
throw InvalidTransactionException("Can't compact a read-only Realm"); throw InvalidTransactionException("Can't compact a read-only Realm");
} }
if (m_in_transaction) { if (is_in_transaction()) {
throw InvalidTransactionException("Can't compact a Realm within a write transaction"); throw InvalidTransactionException("Can't compact a Realm within a write transaction");
} }
@ -395,7 +401,7 @@ bool Realm::refresh()
check_read_write(this); check_read_write(this);
// can't be any new changes if we're in a write transaction // can't be any new changes if we're in a write transaction
if (m_in_transaction) { if (is_in_transaction()) {
return false; return false;
} }
@ -405,7 +411,7 @@ bool Realm::refresh()
} }
if (m_group) { if (m_group) {
transaction::advance(*m_shared_group, *m_history, m_binding_context.get()); transaction::advance(*m_shared_group, m_binding_context.get());
m_coordinator->process_available_async(*this); m_coordinator->process_available_async(*this);
} }
else { else {

View File

@ -32,7 +32,7 @@
namespace realm { namespace realm {
class BindingContext; class BindingContext;
class ClientHistory; class Replication;
class Group; class Group;
class Realm; class Realm;
class RealmDelegate; class RealmDelegate;
@ -114,7 +114,7 @@ namespace realm {
void begin_transaction(); void begin_transaction();
void commit_transaction(); void commit_transaction();
void cancel_transaction(); void cancel_transaction();
bool is_in_transaction() const { return m_in_transaction; } bool is_in_transaction() const noexcept;
bool is_in_read_transaction() const { return !!m_group; } bool is_in_read_transaction() const { return !!m_group; }
bool refresh(); bool refresh();
@ -149,7 +149,6 @@ namespace realm {
// AsyncQuery needs access to the SharedGroup to be able to call the // AsyncQuery needs access to the SharedGroup to be able to call the
// handover functions, which are not very wrappable // handover functions, which are not very wrappable
static SharedGroup& get_shared_group(Realm& realm) { return *realm.m_shared_group; } static SharedGroup& get_shared_group(Realm& realm) { return *realm.m_shared_group; }
static ClientHistory& get_history(Realm& realm) { return *realm.m_history; }
// AsyncQuery needs to be able to access the owning coordinator to // AsyncQuery needs to be able to access the owning coordinator to
// wake up the worker thread when a callback is added, and // wake up the worker thread when a callback is added, and
@ -158,17 +157,16 @@ namespace realm {
}; };
static void open_with_config(const Config& config, static void open_with_config(const Config& config,
std::unique_ptr<ClientHistory>& history, std::unique_ptr<Replication>& history,
std::unique_ptr<SharedGroup>& shared_group, std::unique_ptr<SharedGroup>& shared_group,
std::unique_ptr<Group>& read_only_group); std::unique_ptr<Group>& read_only_group);
private: private:
Config m_config; Config m_config;
std::thread::id m_thread_id = std::this_thread::get_id(); std::thread::id m_thread_id = std::this_thread::get_id();
bool m_in_transaction = false;
bool m_auto_refresh = true; bool m_auto_refresh = true;
std::unique_ptr<ClientHistory> m_history; std::unique_ptr<Replication> m_history;
std::unique_ptr<SharedGroup> m_shared_group; std::unique_ptr<SharedGroup> m_shared_group;
std::unique_ptr<Group> m_read_only_group; std::unique_ptr<Group> m_read_only_group;
@ -231,7 +229,7 @@ namespace realm {
public: public:
UnitializedRealmException(std::string message) : std::runtime_error(message) {} UnitializedRealmException(std::string message) : std::runtime_error(message) {}
}; };
class InvalidEncryptionKeyException : public std::runtime_error { class InvalidEncryptionKeyException : public std::runtime_error {
public: public:
InvalidEncryptionKeyException() : std::runtime_error("Encryption key must be 64 bytes.") {} InvalidEncryptionKeyException() : std::runtime_error("Encryption key must be 64 bytes.") {}

View File

@ -258,6 +258,54 @@ module.exports = BaseTest.extend({
}); });
}, },
testRealmWithIndexedProperties: function() {
var IndexedTypes = {
name: 'IndexedTypesObject',
properties: {
boolCol: {type: 'bool', indexed: true},
intCol: {type: 'int', indexed: true},
stringCol: {type: 'string', indexed: true},
dateCol: {type: 'date', indexed: true},
}
};
var realm = new Realm({schema: [IndexedTypes]});
realm.write(function() {
realm.create('IndexedTypesObject', {boolCol: true, intCol: 1, stringCol: '1', dateCol: new Date(1)});
});
var NotIndexed = {
name: 'NotIndexedObject',
properties: {
floatCol: {type: 'float', indexed: false}
}
};
new Realm({schema: [NotIndexed], path: '1'});
TestCase.assertThrows(function() {
IndexedTypes.properties = { floatCol: {type: 'float', indexed: true} }
new Realm({schema: [IndexedTypes], path: '2'});
});
TestCase.assertThrows(function() {
IndexedTypes.properties = { doubleCol: {type: 'double', indexed: true} }
new Realm({schema: [IndexedTypes], path: '3'});
});
TestCase.assertThrows(function() {
IndexedTypes.properties = { dataCol: {type: 'data', indexed: true} }
new Realm({schema: [IndexedTypes], path: '4'});
});
// primary key
IndexedTypes.primaryKey = 'boolCol';
IndexedTypes.properties = { boolCol: {type: 'bool', indexed: true} }
// Test this doesn't throw
new Realm({schema: [IndexedTypes], path: '5'});
},
testRealmCreateWithDefaults: function() { testRealmCreateWithDefaults: function() {
var realm = new Realm({schema: [schemas.DefaultValues, schemas.TestObject]}); var realm = new Realm({schema: [schemas.DefaultValues, schemas.TestObject]});