diff --git a/RealmJS.xcodeproj/project.pbxproj b/RealmJS.xcodeproj/project.pbxproj index 7074fcb2..9ff6dac2 100644 --- a/RealmJS.xcodeproj/project.pbxproj +++ b/RealmJS.xcodeproj/project.pbxproj @@ -221,8 +221,8 @@ 0270BC7B1B7D020100010E03 /* RealmJSTests.mm */, 0270BC7C1B7D020100010E03 /* RealmTests.js */, 0270BC7D1B7D020100010E03 /* ResultsTests.js */, - 0270BC7E1B7D020100010E03 /* TestCase.js */, 0270BC7F1B7D020100010E03 /* TestObjects.js */, + 0270BC7E1B7D020100010E03 /* TestCase.js */, ); path = RealmJSTests; sourceTree = ""; diff --git a/src/RJSRealm.mm b/src/RJSRealm.mm index e76206ab..4554686f 100644 --- a/src/RJSRealm.mm +++ b/src/RJSRealm.mm @@ -221,7 +221,12 @@ JSValueRef RealmCreateObject(JSContextRef ctx, JSObjectRef function, JSObjectRef object = RJSDictForPropertyArray(ctx, object_schema->second, object); } - return RJSObjectCreate(ctx, Object::create(ctx, sharedRealm, object_schema->second, object, false)); + bool update = false; + if (argumentCount == 3) { + update = RJSValidatedValueToBool(ctx, arguments[2]); + } + + return RJSObjectCreate(ctx, Object::create(ctx, sharedRealm, object_schema->second, object, update)); } catch (std::exception &exp) { if (jsException) { diff --git a/src/RJSSchema.mm b/src/RJSSchema.mm index ff836eac..0fba76df 100644 --- a/src/RJSSchema.mm +++ b/src/RJSSchema.mm @@ -118,6 +118,7 @@ static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef ob ObjectSchema objectSchema; static JSStringRef nameString = JSStringCreateWithUTF8CString("name"); + objectSchema.name = RJSValidatedStringProperty(ctx, objectSchemaObject, nameString); size_t numProperties = RJSValidatedArrayLength(ctx, propertiesObject); @@ -126,6 +127,17 @@ static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef ob objectSchema.properties.emplace_back(RJSParseProperty(ctx, property)); } + static JSStringRef primaryString = JSStringCreateWithUTF8CString("primaryKey"); + JSValueRef primaryValue = RJSValidatedPropertyValue(ctx, objectSchemaObject, primaryString); + if (!JSValueIsUndefined(ctx, primaryValue)) { + objectSchema.primary_key = RJSValidatedStringForValue(ctx, primaryValue); + Property *property = objectSchema.primary_key_property(); + if (!property) { + throw std::runtime_error("Missing primary key property '" + objectSchema.primary_key + "'"); + } + property->is_primary = true; + } + // store prototype if (prototypeObject) { JSValueProtect(ctx, prototypeObject); diff --git a/src/RJSUtil.hpp b/src/RJSUtil.hpp index bf571158..f258325a 100644 --- a/src/RJSUtil.hpp +++ b/src/RJSUtil.hpp @@ -112,6 +112,18 @@ static inline double RJSValidatedValueToNumber(JSContextRef ctx, JSValueRef valu return number; } +static inline bool RJSValidatedValueToBool(JSContextRef ctx, JSValueRef value) { + JSValueRef exception = NULL; + if (!JSValueIsBoolean(ctx, value)) { + throw std::runtime_error("Value is not a boolean"); + } + bool b = JSValueToNumber(ctx, value, &exception); + if (exception) { + throw RJSException(ctx, exception); + } + return b; +} + static inline JSValueRef RJSValidatedPropertyValue(JSContextRef ctx, JSObjectRef object, JSStringRef property) { JSValueRef exception = NULL; JSValueRef propertyValue = JSObjectGetProperty(ctx, object, property, &exception); diff --git a/src/object-store/object_accessor.hpp b/src/object-store/object_accessor.hpp index c7f92022..3e5e3a20 100644 --- a/src/object-store/object_accessor.hpp +++ b/src/object-store/object_accessor.hpp @@ -158,7 +158,7 @@ namespace realm { size_t row_index = realm::not_found; realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object_schema.name); Property *primary_prop = object_schema.primary_key_property(); - if (try_update && primary_prop) { + if (primary_prop) { // search for existing object based on primary key type ValueType primary_value = Accessor::dict_value_for_key(ctx, value, object_schema.primary_key); if (primary_prop->type == PropertyTypeString) { @@ -167,6 +167,10 @@ namespace realm { else { row_index = table->find_first_int(primary_prop->table_column, Accessor::to_long(ctx, primary_value)); } + + if (!try_update && row_index != realm::not_found) { + throw std::runtime_error("Attempting to create an object of type '" + object_schema.name + "' with an exising primary key value."); + } } // if no existing, create row diff --git a/tests/RealmTests.js b/tests/RealmTests.js index 82ce6cf3..82760723 100644 --- a/tests/RealmTests.js +++ b/tests/RealmTests.js @@ -82,6 +82,23 @@ var RealmTests = { 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'); + + // test primary object + realm = new Realm({path: TestUtil.realmPathForFile('intPrimary.realm'), schema: [IntPrimaryObjectSchema, StringPrimaryObjectSchema]}); + realm.write(function() { + var obj0 = realm.create('IntPrimaryObject', [0, 'val0']); + + TestCase.assertThrows(function() { + realm.create('IntPrimaryObject', [0, 'val0']); + }); + realm.create('IntPrimaryObject', [1, 'val1'], true); + var objects = realm.objects('IntPrimaryObject'); + TestCase.assertEqual(objects.length, 2); + + realm.create('IntPrimaryObject', [0, 'newVal0'], true); + TestCase.assertEqual(obj0.valueCol, 'newVal0'); + TestCase.assertEqual(objects.length, 2); + }); }, testRealmDelete: function() { @@ -167,3 +184,4 @@ var RealmTests = { TestCase.assertEqual(notificationCount, 1); }, }; + diff --git a/tests/TestObjects.js b/tests/TestObjects.js index 1154e843..912ccf22 100644 --- a/tests/TestObjects.js +++ b/tests/TestObjects.js @@ -58,3 +58,21 @@ var LinkTypesObjectSchema = { {name: 'arrayCol', type: RealmType.Array, objectType: 'TestObject'}, ] }; + +var IntPrimaryObjectSchema = { + name: 'IntPrimaryObject', + primaryKey: 'primaryCol', + properties: [ + {name: 'primaryCol', type: RealmType.Int}, + {name: 'valueCol', type: RealmType.String}, + ] +}; + +var StringPrimaryObjectSchema = { + name: 'StringPrimaryObject', + primaryKey: 'primaryCol', + properties: [ + {name: 'primaryCol', type: RealmType.String}, + {name: 'valueCol', type: RealmType.String}, + ] +}; \ No newline at end of file