From c9c941aac946bfab02192efa02613bb533b21414 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Thu, 10 Mar 2016 15:16:31 -0800 Subject: [PATCH 01/15] Update apps to use React Native 0.22 React is now a peer dependency, which means we need to add it to our package.json for npm v3 to work properly. --- examples/ReactExample/package.json | 3 ++- examples/ReactNativeBenchmarks/package.json | 3 ++- react-native/android/build.gradle | 7 +++++-- react-native/android/publish_android_template | 6 +++++- tests/react-test-app/android/build.gradle | 6 +++--- tests/react-test-app/package.json | 3 ++- 6 files changed, 19 insertions(+), 9 deletions(-) diff --git a/examples/ReactExample/package.json b/examples/ReactExample/package.json index e24eb959..371d7ea5 100644 --- a/examples/ReactExample/package.json +++ b/examples/ReactExample/package.json @@ -6,7 +6,8 @@ "start": "react-native start" }, "dependencies": { - "react-native": "^0.21.0", + "react": "^0.14.5", + "react-native": "^0.22.0", "realm": "file:../.." } } diff --git a/examples/ReactNativeBenchmarks/package.json b/examples/ReactNativeBenchmarks/package.json index db0a88e1..a46a20d5 100644 --- a/examples/ReactNativeBenchmarks/package.json +++ b/examples/ReactNativeBenchmarks/package.json @@ -6,7 +6,8 @@ "start": "react-native start" }, "dependencies": { - "react-native": "^0.21.0", + "react": "^0.14.5", + "react-native": "^0.22.0", "react-native-sqlite-storage": "^2.1.3", "react-native-store": "^0.4.1", "realm": "file:../.." diff --git a/react-native/android/build.gradle b/react-native/android/build.gradle index 22d2dc21..a4bcdee1 100644 --- a/react-native/android/build.gradle +++ b/react-native/android/build.gradle @@ -12,6 +12,10 @@ allprojects { repositories { mavenLocal() jcenter() + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + url "$projectDir/../../tests/react-test-app/node_modules/react-native/android" + } } } @@ -287,9 +291,8 @@ afterEvaluate { project -> } } - dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'org.nanohttpd:nanohttpd:2.2.0' - compile 'com.facebook.react:react-native:0.20.+' + compile 'com.facebook.react:react-native:+' // From node_modules } diff --git a/react-native/android/publish_android_template b/react-native/android/publish_android_template index 0fc1ef6b..9031fb2c 100644 --- a/react-native/android/publish_android_template +++ b/react-native/android/publish_android_template @@ -11,6 +11,10 @@ allprojects { repositories { mavenLocal() jcenter() + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + url "$projectDir/../../react-native/android" + } } } @@ -51,5 +55,5 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'org.nanohttpd:nanohttpd:2.2.0' - compile 'com.facebook.react:react-native:0.20.+' + compile 'com.facebook.react:react-native:+' // From node_modules } diff --git a/tests/react-test-app/android/build.gradle b/tests/react-test-app/android/build.gradle index 77b8e24e..403a0075 100644 --- a/tests/react-test-app/android/build.gradle +++ b/tests/react-test-app/android/build.gradle @@ -6,7 +6,6 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:1.3.1' - classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -17,8 +16,9 @@ allprojects { repositories { mavenLocal() jcenter() - jcenter { - url "http://dl.bintray.com/mkonicek/maven" + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + url "$projectDir/../../node_modules/react-native/android" } } } diff --git a/tests/react-test-app/package.json b/tests/react-test-app/package.json index c60c7b0d..64031733 100644 --- a/tests/react-test-app/package.json +++ b/tests/react-test-app/package.json @@ -6,7 +6,8 @@ "start": "react-native start" }, "dependencies": { - "react-native": "^0.21.0", + "react": "^0.14.5", + "react-native": "^0.22.0", "react-native-fs": "^1.1.0", "xmlbuilder": "^4.2.1", "realm": "file:../..", From 9d0b890923db9220e36a025a069183a73bf5fd4f Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 29 Mar 2016 09:02:36 -0700 Subject: [PATCH 02/15] add some simple tests for Realm.write --- tests/js/realm-tests.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/js/realm-tests.js b/tests/js/realm-tests.js index 465ec0c5..f201c601 100644 --- a/tests/js/realm-tests.js +++ b/tests/js/realm-tests.js @@ -138,6 +138,30 @@ module.exports = BaseTest.extend({ }); }, + testRealmWrite: function() { + var realm = new Realm({schema: [schemas.IntPrimary, schemas.AllTypes, schemas.TestObject]}); + + // exceptions should be propogated + TestCase.assertThrows(function() { + realm.write(function() { + realm.invalid(); + }); + }); + + // writes should be possible after caught exception + realm.write(function() { + realm.create('TestObject', {doubleCol: 1}); + }); + TestCase.assertEqual(1, realm.objects('TestObject').length); + + realm.write(function() { + // nested transactions not supported + TestCase.assertThrows(function() { + realm.write(function() {}); + }); + }); + }, + testRealmCreate: function() { var realm = new Realm({schema: [schemas.IntPrimary, schemas.AllTypes, schemas.TestObject]}); From e2d1bc45207905f60a4ce512fbe4744b6e6585ec Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Tue, 29 Mar 2016 10:56:51 -0700 Subject: [PATCH 03/15] Get correct path to adb from Android Studio If adb really isn't found (unsure what conditions would cause that to happen), then `false` will be called which will result in that warning message being displayed instead. Fixes #351 --- react-native/android/publish_android_template | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/react-native/android/publish_android_template b/react-native/android/publish_android_template index 0fc1ef6b..79823879 100644 --- a/react-native/android/publish_android_template +++ b/react-native/android/publish_android_template @@ -17,7 +17,8 @@ allprojects { apply plugin: 'com.android.library' task forwardDebugPort(type: Exec) { - commandLine 'adb', 'forward', 'tcp:8082', 'tcp:8082' + def adb = android.getAdbExe()?.toString() ?: 'false' + commandLine adb, 'forward', 'tcp:8082', 'tcp:8082' ignoreExitValue true doLast { if (execResult.getExitValue() != 0) { From 0f9f4500622a0f5c9af59547b6d1d1d0e8943e81 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Tue, 29 Mar 2016 12:58:02 -0700 Subject: [PATCH 04/15] Remove changes_available implementation that asserts This gets called when a Realm changes on another thread, but is called on the correct thread on iOS. It doesn't look like it will be called on Android, so we can unofficially support iOS accessing Realms from multiple bindings for now. Fixes #352 --- src/js_realm.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/js_realm.cpp b/src/js_realm.cpp index 0bca0d23..6648032c 100644 --- a/src/js_realm.cpp +++ b/src/js_realm.cpp @@ -36,9 +36,6 @@ using RJSAccessor = realm::NativeAccessor; class RJSRealmDelegate : public BindingContext { public: - virtual void changes_available() { - assert(0); - } virtual void did_change(std::vector const& observers, std::vector const& invalidated) { notify("change"); } From bbc0ed9d62229bb1d2f367a61629377fcb815bc9 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Tue, 29 Mar 2016 15:26:45 -0700 Subject: [PATCH 05/15] Update changelog script --- scripts/changelog-header.sh | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) mode change 100644 => 100755 scripts/changelog-header.sh diff --git a/scripts/changelog-header.sh b/scripts/changelog-header.sh old mode 100644 new mode 100755 index 00a65acf..548a3eb9 --- a/scripts/changelog-header.sh +++ b/scripts/changelog-header.sh @@ -1,14 +1,22 @@ -empty_section=$(cat < CHANGELOG.md -echo >> CHANGELOG.md -echo "$changelog" >> CHANGELOG.md +* None + +$(cat CHANGELOG.md) +EOF +) + +echo "$CHANGELOG" > CHANGELOG.md From 36ad1ae8401e993acf6709a0ae2a33758ba9f8ec Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Tue, 29 Mar 2016 15:30:15 -0700 Subject: [PATCH 06/15] Update CHANGELOG with 0.11.1 fixes --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86e17392..ec690625 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +0.11.1 Release notes (2016-3-29) +============================================================= +### Bugfixes +* Fix for using Android Studio to build app using Realm +* Fix for sharing Realm between JS and Objective-C/Swift + 0.11.0 Release notes (2016-3-24) ============================================================= ### Breaking changes From 2c0d8f8cef8dcaed379de17352d792837ffdf562 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Tue, 29 Mar 2016 15:30:39 -0700 Subject: [PATCH 07/15] [0.11.1] Bump version --- package.json | 2 +- src/ios/RealmJS.xcodeproj/project.pbxproj | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 8ff2303b..489f59d0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "realm", "description": "Realm is a mobile database: an alternative to SQLite and key-value stores", - "version": "0.11.0", + "version": "0.11.1", "license": "Apache-2.0", "homepage": "https://realm.io", "keywords": [ diff --git a/src/ios/RealmJS.xcodeproj/project.pbxproj b/src/ios/RealmJS.xcodeproj/project.pbxproj index 2110ecdf..129bea40 100644 --- a/src/ios/RealmJS.xcodeproj/project.pbxproj +++ b/src/ios/RealmJS.xcodeproj/project.pbxproj @@ -739,7 +739,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.11.0; + CURRENT_PROJECT_VERSION = 0.11.1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -794,7 +794,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.11.0; + CURRENT_PROJECT_VERSION = 0.11.1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -833,7 +833,7 @@ APPLICATION_EXTENSION_API_ONLY = YES; DEFINES_MODULE = NO; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0.11.0; + DYLIB_CURRENT_VERSION = 0.11.1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -855,7 +855,7 @@ APPLICATION_EXTENSION_API_ONLY = YES; DEFINES_MODULE = NO; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0.11.0; + DYLIB_CURRENT_VERSION = 0.11.1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -926,7 +926,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.11.0; + CURRENT_PROJECT_VERSION = 0.11.1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -968,7 +968,7 @@ APPLICATION_EXTENSION_API_ONLY = YES; DEFINES_MODULE = NO; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0.11.0; + DYLIB_CURRENT_VERSION = 0.11.1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; From 4ac022ecc9d3bd05c3b8e541fdc46a37bd2ee1c2 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 1 Apr 2016 11:00:53 -0700 Subject: [PATCH 08/15] don't require optional or link properties when creating objects --- src/object-store/src/object_accessor.hpp | 13 ++++++++---- tests/js/realm-tests.js | 25 +++++++++++++++++++++--- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/object-store/src/object_accessor.hpp b/src/object-store/src/object_accessor.hpp index a97568dd..61ef0a1d 100644 --- a/src/object-store/src/object_accessor.hpp +++ b/src/object-store/src/object_accessor.hpp @@ -208,10 +208,12 @@ namespace realm { case PropertyTypeArray: { realm::LinkViewRef link_view = m_row.get_linklist(column); link_view->clear(); - size_t count = Accessor::list_size(ctx, value); - for (size_t i = 0; i < count; i++) { - ValueType element = Accessor::list_value_at_index(ctx, value, i); - link_view->add(Accessor::to_object_index(ctx, m_realm, element, property.object_type, try_update)); + if (!Accessor::is_null(ctx, value)) { + size_t count = Accessor::list_size(ctx, value); + for (size_t i = 0; i < count; i++) { + ValueType element = Accessor::list_value_at_index(ctx, value, i); + link_view->add(Accessor::to_object_index(ctx, m_realm, element, property.object_type, try_update)); + } } break; } @@ -310,6 +312,9 @@ namespace realm { if (Accessor::has_default_value_for_property(ctx, realm.get(), object_schema, prop.name)) { object.set_property_value_impl(ctx, prop, Accessor::default_value_for_property(ctx, realm.get(), object_schema, prop.name), try_update); } + else if (prop.is_nullable || prop.type == PropertyTypeArray) { + object.set_property_value_impl(ctx, prop, Accessor::null_value(ctx), try_update); + } else { throw MissingPropertyValueException(object_schema.name, prop.name, "Missing property value for property " + prop.name); diff --git a/tests/js/realm-tests.js b/tests/js/realm-tests.js index f201c601..3e6aec72 100644 --- a/tests/js/realm-tests.js +++ b/tests/js/realm-tests.js @@ -163,7 +163,7 @@ module.exports = BaseTest.extend({ }, testRealmCreate: function() { - var realm = new Realm({schema: [schemas.IntPrimary, schemas.AllTypes, schemas.TestObject]}); + var realm = new Realm({schema: [schemas.TestObject]}); TestCase.assertThrows(function() { realm.create('TestObject', {doubleCol: 1}); @@ -178,8 +178,11 @@ module.exports = BaseTest.extend({ TestCase.assertEqual(objects.length, 2, 'wrong object count'); TestCase.assertEqual(objects[0].doubleCol, 1, 'wrong object property value'); TestCase.assertEqual(objects[1].doubleCol, 2, 'wrong object property value'); + }, + + testRealmCreatePrimaryKey: function() { + var realm = new Realm({schema: [schemas.IntPrimary]}); - // test int primary object realm.write(function() { var obj0 = realm.create('IntPrimaryObject', { primaryCol: 0, @@ -212,8 +215,24 @@ module.exports = BaseTest.extend({ realm.create('IntPrimaryObject', {primaryCol: 0}, true); TestCase.assertEqual(obj0.valueCol, 'newVal0'); }); + }, - // test upsert with all type and string primary object + testRealmCreateOptionals: function() { + var realm = new Realm({schema: [schemas.NullableBasicTypes, schemas.LinkTypes, schemas.TestObject]}); + var basic, links; + realm.write(function() { + basic = realm.create('NullableBasicTypesObject', {}); + links = realm.create('LinkTypesObject', {}); + }); + for (var name in schemas.NullableBasicTypes.properties) { + TestCase.assertEqual(basic[name], null); + } + TestCase.assertEqual(links.objectCol, null); + TestCase.assertEqual(links.arrayCol.length, 0); + }, + + testRealmCreateUpsert: function() { + var realm = new Realm({schema: [schemas.IntPrimary, schemas.AllTypes, schemas.TestObject]}); realm.write(function() { var values = { primaryCol: '0', From 8a77099685a18e16c93fe97ae7e86312cb372fa6 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 11 Apr 2016 13:58:52 -0700 Subject: [PATCH 09/15] support for null queries --- src/object-store/src/parser/parser.cpp | 4 +- src/object-store/src/parser/parser.hpp | 2 +- src/object-store/src/parser/query_builder.cpp | 74 ++++++++++++++----- 3 files changed, 60 insertions(+), 20 deletions(-) diff --git a/src/object-store/src/parser/parser.cpp b/src/object-store/src/parser/parser.cpp index 6819a954..6b981b3e 100644 --- a/src/object-store/src/parser/parser.cpp +++ b/src/object-store/src/parser/parser.cpp @@ -56,6 +56,7 @@ struct number : seq< minus, sor< float_num, hex_num, int_num > > {}; struct true_value : pegtl_istring_t("true") {}; struct false_value : pegtl_istring_t("false") {}; +struct null_value : pegtl_istring_t("null") {}; // key paths struct key_path : list< seq< sor< alpha, one< '_' > >, star< sor< alnum, one< '_', '-' > > > >, one< '.' > > {}; @@ -65,7 +66,7 @@ struct argument_index : plus< digit > {}; struct argument : seq< one< '$' >, must< argument_index > > {}; // expressions and operators -struct expr : sor< dq_string, sq_string, number, argument, true_value, false_value, key_path > {}; +struct expr : sor< dq_string, sq_string, number, argument, true_value, false_value, null_value, key_path > {}; struct case_insensitive : pegtl_istring_t("[c]"){}; struct eq : seq< sor< two< '=' >, one< '=' > >, opt< case_insensitive > >{}; @@ -236,6 +237,7 @@ EXPRESSION_ACTION(key_path, Expression::Type::KeyPath) EXPRESSION_ACTION(number, Expression::Type::Number) EXPRESSION_ACTION(true_value, Expression::Type::True) EXPRESSION_ACTION(false_value, Expression::Type::False) +EXPRESSION_ACTION(null_value, Expression::Type::Null) EXPRESSION_ACTION(argument_index, Expression::Type::Argument) template<> struct action< true_pred > diff --git a/src/object-store/src/parser/parser.hpp b/src/object-store/src/parser/parser.hpp index 2f2ebed2..1e8afef8 100644 --- a/src/object-store/src/parser/parser.hpp +++ b/src/object-store/src/parser/parser.hpp @@ -28,7 +28,7 @@ class Schema; namespace parser { struct Expression { - enum class Type { None, Number, String, KeyPath, Argument, True, False } type; + enum class Type { None, Number, String, KeyPath, Argument, True, False, Null } type; std::string s; Expression(Type t = Type::None, std::string s = "") : type(t), s(s) {} }; diff --git a/src/object-store/src/parser/query_builder.cpp b/src/object-store/src/parser/query_builder.cpp index 69eb8e54..a93f7863 100644 --- a/src/object-store/src/parser/query_builder.cpp +++ b/src/object-store/src/parser/query_builder.cpp @@ -265,22 +265,6 @@ void add_link_constraint_to_query(realm::Query &query, } } -void add_link_constraint_to_query(realm::Query &query, - Predicate::Operator op, - PropertyExpression &prop_expr, - realm::null) { - precondition(prop_expr.indexes.empty(), "KeyPath queries not supported for object comparisons."); - switch (op) { - case Predicate::Operator::NotEqual: - query.Not(); - case Predicate::Operator::Equal: - query.and_query(query.get_table()->column(prop_expr.prop->table_column).is_null()); - break; - default: - throw std::runtime_error("Only 'equal' and 'not equal' operators supported for object comparison."); - } -} - auto link_argument(const PropertyExpression &propExpr, const parser::Expression &argExpr, Arguments &args) { return args.object_index_for_argument(stot(argExpr.s)); @@ -438,6 +422,50 @@ void do_add_comparison_to_query(Query &query, const Schema &schema, const Object } } } + +void do_add_null_comparison_to_query(Query &query, const Schema &schema, const ObjectSchema &object_schema, Predicate::Comparison cmp, const PropertyExpression &expr, Arguments &args) +{ + auto type = expr.prop->type; + switch (type) { + case PropertyTypeObject: + precondition(expr.indexes.empty(), "KeyPath queries not supported for object comparisons."); + switch (cmp.op) { + case Predicate::Operator::NotEqual: + query.Not(); + case Predicate::Operator::Equal: + query.and_query(query.get_table()->column(expr.prop->table_column).is_null()); + break; + default: + throw std::runtime_error("Only 'equal' and 'not equal' operators supported for object comparison."); + } + break; + case PropertyTypeArray: + throw std::runtime_error((std::string)"Comparing Lists to 'null' is not supported"); + break; + default: { + switch (cmp.op) { + case Predicate::Operator::NotEqual: + query.not_equal(expr.prop->table_column, realm::null()); + break; + case Predicate::Operator::Equal: + query.equal(expr.prop->table_column, realm::null()); + break; + default: + throw std::runtime_error("Only 'equal' and 'not equal' operators supported for object comparison."); + } + } + } +} + +bool expression_is_null(const parser::Expression &expr, Arguments &args) { + if (expr.type == parser::Expression::Type::Null) { + return true; + } + else if (expr.type == parser::Expression::Type::Argument) { + return args.is_argument_null(stot(expr.s)); + } + return false; +} void add_comparison_to_query(Query &query, const Predicate &pred, Arguments &args, const Schema &schema, const std::string &type) { @@ -446,11 +474,21 @@ void add_comparison_to_query(Query &query, const Predicate &pred, Arguments &arg auto object_schema = schema.find(type); if (t0 == parser::Expression::Type::KeyPath && t1 != parser::Expression::Type::KeyPath) { PropertyExpression expr(query, schema, object_schema, cmpr.expr[0].s); - do_add_comparison_to_query(query, schema, *object_schema, cmpr, expr, expr, cmpr.expr[1], args); + if (expression_is_null(cmpr.expr[1], args)) { + do_add_null_comparison_to_query(query, schema, *object_schema, cmpr, expr, args); + } + else { + do_add_comparison_to_query(query, schema, *object_schema, cmpr, expr, expr, cmpr.expr[1], args); + } } else if (t0 != parser::Expression::Type::KeyPath && t1 == parser::Expression::Type::KeyPath) { PropertyExpression expr(query, schema, object_schema, cmpr.expr[1].s); - do_add_comparison_to_query(query, schema, *object_schema, cmpr, expr, cmpr.expr[0], expr, args); + if (expression_is_null(cmpr.expr[0], args)) { + do_add_null_comparison_to_query(query, schema, *object_schema, cmpr, expr, args); + } + else { + do_add_comparison_to_query(query, schema, *object_schema, cmpr, expr, cmpr.expr[0], expr, args); + } } else { throw std::runtime_error("Predicate expressions must compare a keypath and another keypath or a constant value"); From 3a174161d73ce8135b1fd0a781f3510e93360803 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 11 Apr 2016 15:03:47 -0700 Subject: [PATCH 10/15] support keypath comparisons for types that support it --- src/object-store/src/parser/query_builder.cpp | 70 ++++++++++++++++--- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/src/object-store/src/parser/query_builder.cpp b/src/object-store/src/parser/query_builder.cpp index a93f7863..eea6b8d2 100644 --- a/src/object-store/src/parser/query_builder.cpp +++ b/src/object-store/src/parser/query_builder.cpp @@ -422,11 +422,68 @@ void do_add_comparison_to_query(Query &query, const Schema &schema, const Object } } } + +template +void do_add_null_comparison_to_query(Query &query, Predicate::Operator op, const PropertyExpression &expr, Arguments &args) +{ + static realm::null null = realm::null(); + Columns column = expr.table_getter()->template column(expr.prop->table_column); + + switch (op) { + case Predicate::Operator::NotEqual: + query.and_query(column != null); + case Predicate::Operator::Equal: + query.and_query(column == null); + break; + default: + throw std::runtime_error("Only 'equal' and 'not equal' operators supported for object comparison."); + } +} -void do_add_null_comparison_to_query(Query &query, const Schema &schema, const ObjectSchema &object_schema, Predicate::Comparison cmp, const PropertyExpression &expr, Arguments &args) +template<> +void do_add_null_comparison_to_query(Query &query, Predicate::Operator op, const PropertyExpression &expr, Arguments &args) +{ + static realm::null null = realm::null(); + Columns column = expr.table_getter()->template column(expr.prop->table_column); + + switch (op) { + case Predicate::Operator::NotEqual: + query.not_equal(expr.prop->table_column, null); + case Predicate::Operator::Equal: + query.equal(expr.prop->table_column, null); + break; + default: + throw std::runtime_error("Only 'equal' and 'not equal' operators supported for object comparison."); + } +} + +void do_add_null_comparison_to_query(Query &query, const Schema &schema, const ObjectSchema &object_schema, Predicate::Comparison cmp, + const PropertyExpression &expr, Arguments &args) { auto type = expr.prop->type; switch (type) { + case PropertyTypeBool: + do_add_null_comparison_to_query(query, cmp.op, expr, args); + break; + case PropertyTypeDate: + do_add_null_comparison_to_query(query, cmp.op, expr, args); + break; + case PropertyTypeDouble: + do_add_null_comparison_to_query(query, cmp.op, expr, args); + break; + case PropertyTypeFloat: + do_add_null_comparison_to_query(query, cmp.op, expr, args); + break; + case PropertyTypeInt: + do_add_null_comparison_to_query(query, cmp.op, expr, args); + break; + case PropertyTypeString: + do_add_null_comparison_to_query(query, cmp.op, expr, args); + break; + case PropertyTypeData: + precondition(expr.indexes.empty(), "KeyPath queries not supported for data comparisons."); + do_add_null_comparison_to_query(query, cmp.op, expr, args); + break; case PropertyTypeObject: precondition(expr.indexes.empty(), "KeyPath queries not supported for object comparisons."); switch (cmp.op) { @@ -443,16 +500,7 @@ void do_add_null_comparison_to_query(Query &query, const Schema &schema, const O throw std::runtime_error((std::string)"Comparing Lists to 'null' is not supported"); break; default: { - switch (cmp.op) { - case Predicate::Operator::NotEqual: - query.not_equal(expr.prop->table_column, realm::null()); - break; - case Predicate::Operator::Equal: - query.equal(expr.prop->table_column, realm::null()); - break; - default: - throw std::runtime_error("Only 'equal' and 'not equal' operators supported for object comparison."); - } + throw std::runtime_error((std::string)"Object type " + string_for_property_type(type) + " not supported"); } } } From b542fe8c7b9e22423b61a49fb77b203ddd0444dd Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 11 Apr 2016 16:01:51 -0700 Subject: [PATCH 11/15] tests --- src/object-store/src/parser/query_builder.cpp | 43 +++++++++-------- src/object-store/tests/query.json | 46 +++++++++++++++++++ tests/js/query-tests.js | 3 ++ 3 files changed, 72 insertions(+), 20 deletions(-) diff --git a/src/object-store/src/parser/query_builder.cpp b/src/object-store/src/parser/query_builder.cpp index eea6b8d2..a0746f65 100644 --- a/src/object-store/src/parser/query_builder.cpp +++ b/src/object-store/src/parser/query_builder.cpp @@ -426,37 +426,50 @@ void do_add_comparison_to_query(Query &query, const Schema &schema, const Object template void do_add_null_comparison_to_query(Query &query, Predicate::Operator op, const PropertyExpression &expr, Arguments &args) { - static realm::null null = realm::null(); Columns column = expr.table_getter()->template column(expr.prop->table_column); - switch (op) { case Predicate::Operator::NotEqual: - query.and_query(column != null); + query.and_query(column != realm::null()); case Predicate::Operator::Equal: - query.and_query(column == null); + query.and_query(column == realm::null()); break; default: - throw std::runtime_error("Only 'equal' and 'not equal' operators supported for object comparison."); + throw std::runtime_error("Only 'equal' and 'not equal' operators supported when comapring against 'null'."); } } template<> void do_add_null_comparison_to_query(Query &query, Predicate::Operator op, const PropertyExpression &expr, Arguments &args) { - static realm::null null = realm::null(); + precondition(expr.indexes.empty(), "KeyPath queries not supported for data comparisons."); Columns column = expr.table_getter()->template column(expr.prop->table_column); - switch (op) { case Predicate::Operator::NotEqual: - query.not_equal(expr.prop->table_column, null); + query.not_equal(expr.prop->table_column, realm::null()); case Predicate::Operator::Equal: - query.equal(expr.prop->table_column, null); + query.equal(expr.prop->table_column, realm::null()); + break; + default: + throw std::runtime_error("Only 'equal' and 'not equal' operators supported when comapring against 'null'."); + } +} + +template<> +void do_add_null_comparison_to_query(Query &query, Predicate::Operator op, const PropertyExpression &expr, Arguments &args) +{ + precondition(expr.indexes.empty(), "KeyPath queries not supported for object comparisons."); + switch (op) { + case Predicate::Operator::NotEqual: + query.Not(); + case Predicate::Operator::Equal: + query.and_query(query.get_table()->column(expr.prop->table_column).is_null()); break; default: throw std::runtime_error("Only 'equal' and 'not equal' operators supported for object comparison."); } } + void do_add_null_comparison_to_query(Query &query, const Schema &schema, const ObjectSchema &object_schema, Predicate::Comparison cmp, const PropertyExpression &expr, Arguments &args) { @@ -481,20 +494,10 @@ void do_add_null_comparison_to_query(Query &query, const Schema &schema, const O do_add_null_comparison_to_query(query, cmp.op, expr, args); break; case PropertyTypeData: - precondition(expr.indexes.empty(), "KeyPath queries not supported for data comparisons."); do_add_null_comparison_to_query(query, cmp.op, expr, args); break; case PropertyTypeObject: - precondition(expr.indexes.empty(), "KeyPath queries not supported for object comparisons."); - switch (cmp.op) { - case Predicate::Operator::NotEqual: - query.Not(); - case Predicate::Operator::Equal: - query.and_query(query.get_table()->column(expr.prop->table_column).is_null()); - break; - default: - throw std::runtime_error("Only 'equal' and 'not equal' operators supported for object comparison."); - } + do_add_null_comparison_to_query(query, cmp.op, expr, args); break; case PropertyTypeArray: throw std::runtime_error((std::string)"Comparing Lists to 'null' is not supported"); diff --git a/src/object-store/tests/query.json b/src/object-store/tests/query.json index e4edd4de..22ef1c72 100644 --- a/src/object-store/tests/query.json +++ b/src/object-store/tests/query.json @@ -245,6 +245,9 @@ ["QueryCount", 1, "LinkObject", "linkCol == $0", [0, "linkCol"]], ["QueryCount", 1, "LinkObject", "$0 == linkCol", [1, "linkCol"]], ["QueryCount", 2, "LinkObject", "linkCol != $0", [0, "linkCol"]], + ["QueryCount", 1, "LinkObject", "linkCol = null"], + ["QueryCount", 2, "LinkObject", "linkCol != NULL"], + ["QueryCount", 1, "LinkObject", "linkCol = $0", null], ["QueryThrows", "LinkObject", "linkCol > $0", [0, "linkCol"]], ["QueryThrows", "LinkObject", "intCol = $0", [0, "linkCol"]] @@ -317,6 +320,49 @@ ["ObjectSet", [1], "LinkTypesObject", "linkLink.basicLink.floatCol == 0.1"], ["ObjectSet", [1, 3], "LinkTypesObject", "linkLink.basicLink.floatCol > 0"] ] +}, + +"optionalTests" : { + "schema" : [ + { + "name": "OptionalTypesObject", + "primaryKey": "primaryKey", + "properties": [ + { "name": "primaryKey", "type": "int" }, + { "name": "intCol", "type": "int", "optional": true }, + { "name": "floatCol", "type": "float", "optional": true }, + { "name": "doubleCol", "type": "double", "optional": true }, + { "name": "stringCol", "type": "string", "optional": true }, + { "name": "dateCol", "type": "date", "optional": true }, + { "name": "dataCol", "type": "data", "optional": true } + ] + }, + { + "name": "LinkTypesObject", + "primaryKey": "primaryKey", + "properties": [ + { "name": "primaryKey", "type": "int" }, + { "name": "basicLink", "type": "object", "objectType": "OptionalTypesObject" } + ] + }], + "objects": [ + { "type": "LinkTypesObject", "value": [0, [0, 1, 0.1, 0.001, "1", 1, [1, 10, 100]]] }, + { "type": "LinkTypesObject", "value": [1, [1, null, null, null, null, null, null]] } + ], + "tests": [ + ["ObjectSet", [1], "OptionalTypesObject", "intCol == null"], + ["ObjectSet", [0], "OptionalTypesObject", "intCol != null"], + ["ObjectSet", [1], "OptionalTypesObject", "floatCol == null"], + ["ObjectSet", [0], "OptionalTypesObject", "floatCol != null"], + ["ObjectSet", [1], "OptionalTypesObject", "doubleCol == null"], + ["ObjectSet", [0], "OptionalTypesObject", "doubleCol != null"], + ["ObjectSet", [1], "OptionalTypesObject", "stringCol == null"], + ["ObjectSet", [0], "OptionalTypesObject", "stringCol != null"], + ["ObjectSet", [1], "OptionalTypesObject", "dateCol == null"], + ["ObjectSet", [0], "OptionalTypesObject", "dateCol != null"], + ["ObjectSet", [1], "OptionalTypesObject", "dataCol == null"], + ["ObjectSet", [0], "OptionalTypesObject", "dataCol != null"] + ] } } diff --git a/tests/js/query-tests.js b/tests/js/query-tests.js index c4cc4d57..3f5f2ace 100644 --- a/tests/js/query-tests.js +++ b/tests/js/query-tests.js @@ -146,6 +146,9 @@ module.exports = BaseTest.extend({ }, testKeyPathQueries: function() { runQuerySuite(testCases.keyPathTests); + }, + testOptionalQueries: function() { + runQuerySuite(testCases.optionalTests); } }); From 321ddc39e170564cc3e487d4b2408c725aea6cb1 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 11 Apr 2016 16:03:36 -0700 Subject: [PATCH 12/15] bug fix --- src/object-store/src/parser/query_builder.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/object-store/src/parser/query_builder.cpp b/src/object-store/src/parser/query_builder.cpp index a0746f65..877fa167 100644 --- a/src/object-store/src/parser/query_builder.cpp +++ b/src/object-store/src/parser/query_builder.cpp @@ -430,6 +430,7 @@ void do_add_null_comparison_to_query(Query &query, Predicate::Operator op, const switch (op) { case Predicate::Operator::NotEqual: query.and_query(column != realm::null()); + break; case Predicate::Operator::Equal: query.and_query(column == realm::null()); break; @@ -446,6 +447,7 @@ void do_add_null_comparison_to_query(Query &query, Predicate::Operator o switch (op) { case Predicate::Operator::NotEqual: query.not_equal(expr.prop->table_column, realm::null()); + break; case Predicate::Operator::Equal: query.equal(expr.prop->table_column, realm::null()); break; From 9f4b9410a5e2a4ff7d2fe8c2a6309f28bb8906fe Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 11 Apr 2016 16:37:18 -0700 Subject: [PATCH 13/15] typo --- src/object-store/src/parser/query_builder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/object-store/src/parser/query_builder.cpp b/src/object-store/src/parser/query_builder.cpp index 877fa167..5cd7ca49 100644 --- a/src/object-store/src/parser/query_builder.cpp +++ b/src/object-store/src/parser/query_builder.cpp @@ -435,7 +435,7 @@ void do_add_null_comparison_to_query(Query &query, Predicate::Operator op, const query.and_query(column == realm::null()); break; default: - throw std::runtime_error("Only 'equal' and 'not equal' operators supported when comapring against 'null'."); + throw std::runtime_error("Only 'equal' and 'not equal' operators supported when comparing against 'null'."); } } @@ -452,7 +452,7 @@ void do_add_null_comparison_to_query(Query &query, Predicate::Operator o query.equal(expr.prop->table_column, realm::null()); break; default: - throw std::runtime_error("Only 'equal' and 'not equal' operators supported when comapring against 'null'."); + throw std::runtime_error("Only 'equal' and 'not equal' operators supported when comparing against 'null'."); } } From 1afa844aa43652b289a1459eea0b35854f3167c8 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 12 Apr 2016 08:13:29 -0700 Subject: [PATCH 14/15] pr feedback, changelog --- CHANGELOG.md | 11 +++++++++++ src/object-store/src/parser/query_builder.cpp | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec690625..79745316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +x.x.x Release notes (yyyy-MM-dd) +============================================================= +### Breaking changes +* None + +### Enhancements +* Support for queries comparing optional properties to `null` + +### Bugfixes +* None + 0.11.1 Release notes (2016-3-29) ============================================================= ### Bugfixes diff --git a/src/object-store/src/parser/query_builder.cpp b/src/object-store/src/parser/query_builder.cpp index 5cd7ca49..6984af85 100644 --- a/src/object-store/src/parser/query_builder.cpp +++ b/src/object-store/src/parser/query_builder.cpp @@ -462,6 +462,7 @@ void do_add_null_comparison_to_query(Query &query, Predicate::Operator op, precondition(expr.indexes.empty(), "KeyPath queries not supported for object comparisons."); switch (op) { case Predicate::Operator::NotEqual: + // for not equal we negate the query and then fallthrough query.Not(); case Predicate::Operator::Equal: query.and_query(query.get_table()->column(expr.prop->table_column).is_null()); @@ -471,7 +472,6 @@ void do_add_null_comparison_to_query(Query &query, Predicate::Operator op, } } - void do_add_null_comparison_to_query(Query &query, const Schema &schema, const ObjectSchema &object_schema, Predicate::Comparison cmp, const PropertyExpression &expr, Arguments &args) { From 4aed9135b8000e2ce0b507f8707c3d8f0e0d0445 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 12 Apr 2016 11:01:33 -0700 Subject: [PATCH 15/15] add keypath tests --- src/object-store/tests/query.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/object-store/tests/query.json b/src/object-store/tests/query.json index 22ef1c72..6cbbee55 100644 --- a/src/object-store/tests/query.json +++ b/src/object-store/tests/query.json @@ -351,6 +351,7 @@ ], "tests": [ ["ObjectSet", [1], "OptionalTypesObject", "intCol == null"], + ["ObjectSet", [1], "OptionalTypesObject", "null == intCol"], ["ObjectSet", [0], "OptionalTypesObject", "intCol != null"], ["ObjectSet", [1], "OptionalTypesObject", "floatCol == null"], ["ObjectSet", [0], "OptionalTypesObject", "floatCol != null"], @@ -361,7 +362,19 @@ ["ObjectSet", [1], "OptionalTypesObject", "dateCol == null"], ["ObjectSet", [0], "OptionalTypesObject", "dateCol != null"], ["ObjectSet", [1], "OptionalTypesObject", "dataCol == null"], - ["ObjectSet", [0], "OptionalTypesObject", "dataCol != null"] + ["ObjectSet", [0], "OptionalTypesObject", "dataCol != null"], + + ["ObjectSet", [1], "LinkTypesObject", "basicLink.intCol == null"], + ["ObjectSet", [0], "LinkTypesObject", "basicLink.intCol != null"], + ["ObjectSet", [1], "LinkTypesObject", "basicLink.floatCol == null"], + ["ObjectSet", [0], "LinkTypesObject", "basicLink.floatCol != null"], + ["ObjectSet", [1], "LinkTypesObject", "basicLink.doubleCol == null"], + ["ObjectSet", [0], "LinkTypesObject", "basicLink.doubleCol != null"], + ["ObjectSet", [1], "LinkTypesObject", "basicLink.stringCol == null"], + ["ObjectSet", [0], "LinkTypesObject", "basicLink.stringCol != null"], + ["ObjectSet", [1], "LinkTypesObject", "basicLink.dateCol == null"], + ["ObjectSet", [0], "LinkTypesObject", "basicLink.dateCol != null"], + ["QueryThrows", "LinkTypesObject", "basicLink.dataCol == null"] ] }