Merge branch 'master' into sk-node

* master:
  add keypath tests
  pr feedback, changelog
  typo
  bug fix
  tests
  support keypath comparisons for types that support it
  support for null queries
  don't require optional or link properties when creating objects
  [0.11.1] Bump version
  Update CHANGELOG with 0.11.1 fixes
  Update changelog script
  Remove changes_available implementation that asserts
  Get correct path to adb from Android Studio
  add some simple tests for Realm.write
  Update apps to use React Native 0.22
This commit is contained in:
Scott Kyle 2016-04-18 14:19:06 -07:00
commit 4731610a58
17 changed files with 293 additions and 54 deletions

View File

@ -1,3 +1,20 @@
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
* 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

View File

@ -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:../.."
}
}

View File

@ -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:../.."

View File

@ -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": [

View File

@ -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
}

View File

@ -11,13 +11,18 @@ allprojects {
repositories {
mavenLocal()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$projectDir/../../react-native/android"
}
}
}
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) {
@ -51,5 +56,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
}

28
scripts/changelog-header.sh Normal file → Executable file
View File

@ -1,14 +1,22 @@
empty_section=$(cat <<EOS
#!/bin/bash
set -e
set -o pipefail
CHANGELOG=$(cat <<EOF
x.x.x Release notes (yyyy-MM-dd)
=============================================================
### API breaking changes
* None.
### Breaking changes
* None
### Enhancements
* None.
* None
### Bugfixes
* None.
EOS)
changelog=$(cat CHANGELOG.md)
echo "$empty_section" > CHANGELOG.md
echo >> CHANGELOG.md
echo "$changelog" >> CHANGELOG.md
* None
$(cat CHANGELOG.md)
EOF
)
echo "$CHANGELOG" > CHANGELOG.md

View File

@ -881,7 +881,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;
@ -942,7 +942,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;
@ -987,7 +987,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";
@ -1009,7 +1009,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";
@ -1080,7 +1080,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;
@ -1128,7 +1128,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";

View File

@ -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);

View File

@ -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 >

View File

@ -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) {}
};

View File

@ -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<Link>(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<int>(argExpr.s));
@ -438,6 +422,103 @@ void do_add_comparison_to_query(Query &query, const Schema &schema, const Object
}
}
}
template<typename T>
void do_add_null_comparison_to_query(Query &query, Predicate::Operator op, const PropertyExpression &expr, Arguments &args)
{
Columns<T> column = expr.table_getter()->template column<T>(expr.prop->table_column);
switch (op) {
case Predicate::Operator::NotEqual:
query.and_query(column != realm::null());
break;
case Predicate::Operator::Equal:
query.and_query(column == realm::null());
break;
default:
throw std::runtime_error("Only 'equal' and 'not equal' operators supported when comparing against 'null'.");
}
}
template<>
void do_add_null_comparison_to_query<Binary>(Query &query, Predicate::Operator op, const PropertyExpression &expr, Arguments &args)
{
precondition(expr.indexes.empty(), "KeyPath queries not supported for data comparisons.");
Columns<Binary> column = expr.table_getter()->template column<Binary>(expr.prop->table_column);
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;
default:
throw std::runtime_error("Only 'equal' and 'not equal' operators supported when comparing against 'null'.");
}
}
template<>
void do_add_null_comparison_to_query<Link>(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:
// for not equal we negate the query and then fallthrough
query.Not();
case Predicate::Operator::Equal:
query.and_query(query.get_table()->column<Link>(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)
{
auto type = expr.prop->type;
switch (type) {
case PropertyTypeBool:
do_add_null_comparison_to_query<bool>(query, cmp.op, expr, args);
break;
case PropertyTypeDate:
do_add_null_comparison_to_query<DateTime>(query, cmp.op, expr, args);
break;
case PropertyTypeDouble:
do_add_null_comparison_to_query<Double>(query, cmp.op, expr, args);
break;
case PropertyTypeFloat:
do_add_null_comparison_to_query<Float>(query, cmp.op, expr, args);
break;
case PropertyTypeInt:
do_add_null_comparison_to_query<Int>(query, cmp.op, expr, args);
break;
case PropertyTypeString:
do_add_null_comparison_to_query<String>(query, cmp.op, expr, args);
break;
case PropertyTypeData:
do_add_null_comparison_to_query<Binary>(query, cmp.op, expr, args);
break;
case PropertyTypeObject:
do_add_null_comparison_to_query<Link>(query, cmp.op, expr, args);
break;
case PropertyTypeArray:
throw std::runtime_error((std::string)"Comparing Lists to 'null' is not supported");
break;
default: {
throw std::runtime_error((std::string)"Object type " + string_for_property_type(type) + " not supported");
}
}
}
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<int>(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 +527,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");

View File

@ -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,62 @@
["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", [1], "OptionalTypesObject", "null == intCol"],
["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"],
["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"]
]
}
}

View File

@ -146,6 +146,9 @@ module.exports = BaseTest.extend({
},
testKeyPathQueries: function() {
runQuerySuite(testCases.keyPathTests);
},
testOptionalQueries: function() {
runQuerySuite(testCases.optionalTests);
}
});

View File

@ -138,8 +138,32 @@ module.exports = BaseTest.extend({
});
},
testRealmCreate: function() {
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.TestObject]});
TestCase.assertThrows(function() {
realm.create('TestObject', {doubleCol: 1});
@ -154,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,
@ -188,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',

View File

@ -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"
}
}
}

View File

@ -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:../..",