object.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2017 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #include "catch2/catch.hpp"
  19. #include "util/event_loop.hpp"
  20. #include "util/index_helpers.hpp"
  21. #include "util/test_file.hpp"
  22. #include "feature_checks.hpp"
  23. #include "collection_notifications.hpp"
  24. #include "object_accessor.hpp"
  25. #include "property.hpp"
  26. #include "schema.hpp"
  27. #include "impl/realm_coordinator.hpp"
  28. #include "impl/object_accessor_impl.hpp"
  29. #include <realm/group.hpp>
  30. #include <realm/util/any.hpp>
  31. #include <cstdint>
  32. using namespace realm;
  33. namespace {
  34. using AnyDict = std::map<std::string, util::Any>;
  35. using AnyVec = std::vector<util::Any>;
  36. template <class T>
  37. std::vector<T> get_vector(std::initializer_list<T> list)
  38. {
  39. return std::vector<T>(list);
  40. }
  41. }
  42. struct TestContext : CppContext {
  43. std::map<std::string, AnyDict> defaults;
  44. using CppContext::CppContext;
  45. TestContext(TestContext& parent, realm::Property const& prop)
  46. : CppContext(parent, prop)
  47. , defaults(parent.defaults)
  48. { }
  49. util::Optional<util::Any>
  50. default_value_for_property(ObjectSchema const& object, Property const& prop)
  51. {
  52. auto obj_it = defaults.find(object.name);
  53. if (obj_it == defaults.end())
  54. return util::none;
  55. auto prop_it = obj_it->second.find(prop.name);
  56. if (prop_it == obj_it->second.end())
  57. return util::none;
  58. return prop_it->second;
  59. }
  60. void will_change(Object const&, Property const&) {}
  61. void did_change() {}
  62. std::string print(util::Any) { return "not implemented"; }
  63. bool allow_missing(util::Any) { return false; }
  64. };
  65. TEST_CASE("object") {
  66. using namespace std::string_literals;
  67. _impl::RealmCoordinator::assert_no_open_realms();
  68. InMemoryTestFile config;
  69. config.automatic_change_notifications = false;
  70. config.schema = Schema{
  71. {"table", {
  72. {"value 1", PropertyType::Int},
  73. {"value 2", PropertyType::Int},
  74. }},
  75. {"all types", {
  76. {"pk", PropertyType::Int, Property::IsPrimary{true}},
  77. {"bool", PropertyType::Bool},
  78. {"int", PropertyType::Int},
  79. {"float", PropertyType::Float},
  80. {"double", PropertyType::Double},
  81. {"string", PropertyType::String},
  82. {"data", PropertyType::Data},
  83. {"date", PropertyType::Date},
  84. {"object", PropertyType::Object|PropertyType::Nullable, "link target"},
  85. {"bool array", PropertyType::Array|PropertyType::Bool},
  86. {"int array", PropertyType::Array|PropertyType::Int},
  87. {"float array", PropertyType::Array|PropertyType::Float},
  88. {"double array", PropertyType::Array|PropertyType::Double},
  89. {"string array", PropertyType::Array|PropertyType::String},
  90. {"data array", PropertyType::Array|PropertyType::Data},
  91. {"date array", PropertyType::Array|PropertyType::Date},
  92. {"object array", PropertyType::Array|PropertyType::Object, "array target"},
  93. }},
  94. {"all optional types", {
  95. {"pk", PropertyType::Int|PropertyType::Nullable, Property::IsPrimary{true}},
  96. {"bool", PropertyType::Bool|PropertyType::Nullable},
  97. {"int", PropertyType::Int|PropertyType::Nullable},
  98. {"float", PropertyType::Float|PropertyType::Nullable},
  99. {"double", PropertyType::Double|PropertyType::Nullable},
  100. {"string", PropertyType::String|PropertyType::Nullable},
  101. {"data", PropertyType::Data|PropertyType::Nullable},
  102. {"date", PropertyType::Date|PropertyType::Nullable},
  103. {"bool array", PropertyType::Array|PropertyType::Bool|PropertyType::Nullable},
  104. {"int array", PropertyType::Array|PropertyType::Int|PropertyType::Nullable},
  105. {"float array", PropertyType::Array|PropertyType::Float|PropertyType::Nullable},
  106. {"double array", PropertyType::Array|PropertyType::Double|PropertyType::Nullable},
  107. {"string array", PropertyType::Array|PropertyType::String|PropertyType::Nullable},
  108. {"data array", PropertyType::Array|PropertyType::Data|PropertyType::Nullable},
  109. {"date array", PropertyType::Array|PropertyType::Date|PropertyType::Nullable},
  110. }},
  111. {"link target", {
  112. {"value", PropertyType::Int},
  113. }, {
  114. {"origin", PropertyType::LinkingObjects|PropertyType::Array, "all types", "object"},
  115. }},
  116. {"array target", {
  117. {"value", PropertyType::Int},
  118. }},
  119. {"pk after list", {
  120. {"array 1", PropertyType::Array|PropertyType::Object, "array target"},
  121. {"int 1", PropertyType::Int},
  122. {"pk", PropertyType::Int, Property::IsPrimary{true}},
  123. {"int 2", PropertyType::Int},
  124. {"array 2", PropertyType::Array|PropertyType::Object, "array target"},
  125. }},
  126. {"nullable int pk", {
  127. {"pk", PropertyType::Int|PropertyType::Nullable, Property::IsPrimary{true}},
  128. }},
  129. {"nullable string pk", {
  130. {"pk", PropertyType::String|PropertyType::Nullable, Property::IsPrimary{true}},
  131. }},
  132. {"person", {
  133. {"name", PropertyType::String, Property::IsPrimary{true}},
  134. {"age", PropertyType::Int},
  135. {"scores", PropertyType::Array|PropertyType::Int},
  136. {"assistant", PropertyType::Object|PropertyType::Nullable, "person"},
  137. {"team", PropertyType::Array|PropertyType::Object, "person"},
  138. }},
  139. };
  140. config.schema_version = 0;
  141. auto r = Realm::get_shared_realm(config);
  142. auto& coordinator = *_impl::RealmCoordinator::get_coordinator(config.path);
  143. SECTION("add_notification_callback()") {
  144. auto table = r->read_group().get_table("class_table");
  145. auto col_keys = table->get_column_keys();
  146. ObjKeys object_keys({3, 4, 7, 9, 10, 21, 24, 34, 42, 50});
  147. r->begin_transaction();
  148. for (int i = 0; i < 10; ++i)
  149. table->create_object(object_keys[i]).set_all(i);
  150. r->commit_transaction();
  151. auto r2 = coordinator.get_realm();
  152. CollectionChangeSet change;
  153. auto obj = *table->begin();
  154. Object object(r, obj);
  155. auto write = [&](auto&& f) {
  156. r->begin_transaction();
  157. f();
  158. r->commit_transaction();
  159. advance_and_notify(*r);
  160. };
  161. auto require_change = [&] {
  162. auto token = object.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
  163. change = c;
  164. });
  165. advance_and_notify(*r);
  166. return token;
  167. };
  168. auto require_no_change = [&] {
  169. bool first = true;
  170. auto token = object.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  171. REQUIRE(first);
  172. first = false;
  173. });
  174. advance_and_notify(*r);
  175. return token;
  176. };
  177. SECTION("deleting the object sends a change notification") {
  178. auto token = require_change();
  179. write([&] { obj.remove(); });
  180. REQUIRE_INDICES(change.deletions, 0);
  181. }
  182. SECTION("clearing the table sends a change notification") {
  183. auto token = require_change();
  184. write([&] { table->clear(); });
  185. REQUIRE_INDICES(change.deletions, 0);
  186. }
  187. SECTION("clearing the table sends a change notification to the last object") {
  188. obj = table->get_object(table->size() - 1);
  189. object = Object(r, obj);
  190. auto token = require_change();
  191. write([&] { table->clear(); });
  192. REQUIRE_INDICES(change.deletions, 0);
  193. }
  194. SECTION("modifying the object sends a change notification") {
  195. auto token = require_change();
  196. write([&] { obj.set(col_keys[0], 10); });
  197. REQUIRE_INDICES(change.modifications, 0);
  198. REQUIRE(change.columns.size() == 1);
  199. REQUIRE_INDICES(change.columns[col_keys[0].value], 0);
  200. write([&] { obj.set(col_keys[1], 10); });
  201. REQUIRE_INDICES(change.modifications, 0);
  202. REQUIRE(change.columns.size() == 1);
  203. REQUIRE_INDICES(change.columns[col_keys[1].value], 0);
  204. }
  205. SECTION("modifying a different object") {
  206. auto token = require_no_change();
  207. write([&] { table->get_object(1).set(col_keys[0], 10); });
  208. }
  209. SECTION("multiple write transactions") {
  210. auto token = require_change();
  211. auto r2row = r2->read_group().get_table("class_table")->get_object(0);
  212. r2->begin_transaction();
  213. r2row.set(col_keys[0], 1);
  214. r2->commit_transaction();
  215. r2->begin_transaction();
  216. r2row.set(col_keys[1], 2);
  217. r2->commit_transaction();
  218. advance_and_notify(*r);
  219. REQUIRE(change.columns.size() == 2);
  220. REQUIRE_INDICES(change.columns[col_keys[0].value], 0);
  221. REQUIRE_INDICES(change.columns[col_keys[1].value], 0);
  222. }
  223. SECTION("skipping a notification") {
  224. auto token = require_no_change();
  225. write([&] {
  226. obj.set(col_keys[0], 1);
  227. token.suppress_next();
  228. });
  229. }
  230. SECTION("skipping only effects the current transaction even if no notification would occur anyway") {
  231. auto token = require_change();
  232. // would not produce a notification even if it wasn't skipped because no changes were made
  233. write([&] {
  234. token.suppress_next();
  235. });
  236. REQUIRE(change.empty());
  237. // should now produce a notification
  238. write([&] { obj.set(col_keys[0], 1); });
  239. REQUIRE_INDICES(change.modifications, 0);
  240. }
  241. SECTION("add notification callback, remove it, then add another notification callback") {
  242. {
  243. auto token = object.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  244. FAIL("This should never happen");
  245. });
  246. }
  247. auto token = require_change();
  248. write([&] { obj.remove(); });
  249. REQUIRE_INDICES(change.deletions, 0);
  250. }
  251. SECTION("observing deleted object throws") {
  252. write([&] {
  253. obj.remove();
  254. });
  255. REQUIRE_THROWS(require_change());
  256. }
  257. }
  258. TestContext d(r);
  259. auto create = [&](util::Any&& value, CreatePolicy policy = CreatePolicy::ForceCreate) {
  260. r->begin_transaction();
  261. auto obj = Object::create(d, r, *r->schema().find("all types"), value, policy);
  262. r->commit_transaction();
  263. return obj;
  264. };
  265. auto create_sub = [&](util::Any&& value, CreatePolicy policy = CreatePolicy::ForceCreate) {
  266. r->begin_transaction();
  267. auto obj = Object::create(d, r, *r->schema().find("link target"), value, policy);
  268. r->commit_transaction();
  269. return obj;
  270. };
  271. auto create_company = [&](util::Any&& value, CreatePolicy policy = CreatePolicy::ForceCreate) {
  272. r->begin_transaction();
  273. auto obj = Object::create(d, r, *r->schema().find("person"), value, policy);
  274. r->commit_transaction();
  275. return obj;
  276. };
  277. SECTION("create object") {
  278. auto obj = create(AnyDict{
  279. {"pk", INT64_C(1)},
  280. {"bool", true},
  281. {"int", INT64_C(5)},
  282. {"float", 2.2f},
  283. {"double", 3.3},
  284. {"string", "hello"s},
  285. {"data", "olleh"s},
  286. {"date", Timestamp(10, 20)},
  287. {"object", AnyDict{{"value", INT64_C(10)}}},
  288. {"bool array", AnyVec{true, false}},
  289. {"int array", AnyVec{INT64_C(5), INT64_C(6)}},
  290. {"float array", AnyVec{1.1f, 2.2f}},
  291. {"double array", AnyVec{3.3, 4.4}},
  292. {"string array", AnyVec{"a"s, "b"s, "c"s}},
  293. {"data array", AnyVec{"d"s, "e"s, "f"s}},
  294. {"date array", AnyVec{Timestamp(10, 20), Timestamp(30, 40)}},
  295. {"object array", AnyVec{AnyDict{{"value", INT64_C(20)}}}},
  296. });
  297. auto row = obj.obj();
  298. auto link_target = *r->read_group().get_table("class_link target")->begin();
  299. auto table = row.get_table();
  300. auto target_table = link_target.get_table();
  301. auto array_target_table = r->read_group().get_table("class_array target");
  302. REQUIRE(row.get<Int>(table->get_column_key("pk")) == 1);
  303. REQUIRE(row.get<Bool>(table->get_column_key("bool")) == true);
  304. REQUIRE(row.get<Int>(table->get_column_key("int")) == 5);
  305. REQUIRE(row.get<float>(table->get_column_key("float")) == 2.2f);
  306. REQUIRE(row.get<double>(table->get_column_key("double")) == 3.3);
  307. REQUIRE(row.get<String>(table->get_column_key("string")) == "hello");
  308. REQUIRE(row.get<Binary>(table->get_column_key("data")) == BinaryData("olleh", 5));
  309. REQUIRE(row.get<Timestamp>(table->get_column_key("date")) == Timestamp(10, 20));
  310. REQUIRE(row.get<ObjKey>(table->get_column_key("object")) == link_target.get_key());
  311. REQUIRE(link_target.get<Int>(target_table->get_column_key("value")) == 10);
  312. auto check_array = [&](ColKey col, auto... values) {
  313. auto vec = get_vector({values...});
  314. using U = typename decltype(vec)::value_type;
  315. auto list = row.get_list<U>(col);
  316. size_t i = 0;
  317. for (const auto& value : vec) {
  318. CAPTURE(i);
  319. REQUIRE(i < list.size());
  320. REQUIRE(value == list.get(i));
  321. ++i;
  322. }
  323. };
  324. check_array(table->get_column_key("bool array"), true, false);
  325. check_array(table->get_column_key("int array"), INT64_C(5), INT64_C(6));
  326. check_array(table->get_column_key("float array"), 1.1f, 2.2f);
  327. check_array(table->get_column_key("double array"), 3.3, 4.4);
  328. check_array(table->get_column_key("string array"), StringData("a"), StringData("b"), StringData("c"));
  329. check_array(table->get_column_key("data array"), BinaryData("d", 1), BinaryData("e", 1), BinaryData("f", 1));
  330. check_array(table->get_column_key("date array"), Timestamp(10, 20), Timestamp(30, 40));
  331. auto list = row.get_linklist_ptr(table->get_column_key("object array"));
  332. REQUIRE(list->size() == 1);
  333. REQUIRE(list->get_object(0).get<Int>(array_target_table->get_column_key("value")) == 20);
  334. }
  335. SECTION("create uses defaults for missing values") {
  336. d.defaults["all types"] = {
  337. {"bool", true},
  338. {"int", INT64_C(5)},
  339. {"float", 2.2f},
  340. {"double", 3.3},
  341. {"string", "hello"s},
  342. {"data", "olleh"s},
  343. {"date", Timestamp(10, 20)},
  344. {"object", AnyDict{{"value", INT64_C(10)}}},
  345. {"bool array", AnyVec{true, false}},
  346. {"int array", AnyVec{INT64_C(5), INT64_C(6)}},
  347. {"float array", AnyVec{1.1f, 2.2f}},
  348. {"double array", AnyVec{3.3, 4.4}},
  349. {"string array", AnyVec{"a"s, "b"s, "c"s}},
  350. {"data array", AnyVec{"d"s, "e"s, "f"s}},
  351. {"date array", AnyVec{}},
  352. {"object array", AnyVec{AnyDict{{"value", INT64_C(20)}}}},
  353. };
  354. auto obj = create(AnyDict{
  355. {"pk", INT64_C(1)},
  356. {"float", 6.6f},
  357. });
  358. auto row = obj.obj();
  359. auto table = row.get_table();
  360. REQUIRE(row.get<Int>(table->get_column_key("pk")) == 1);
  361. REQUIRE(row.get<Bool>(table->get_column_key("bool")) == true);
  362. REQUIRE(row.get<Int>(table->get_column_key("int")) == 5);
  363. REQUIRE(row.get<float>(table->get_column_key("float")) == 6.6f);
  364. REQUIRE(row.get<double>(table->get_column_key("double")) == 3.3);
  365. REQUIRE(row.get<String>(table->get_column_key("string")) == "hello");
  366. REQUIRE(row.get<Binary>(table->get_column_key("data")) == BinaryData("olleh", 5));
  367. REQUIRE(row.get<Timestamp>(table->get_column_key("date")) == Timestamp(10, 20));
  368. REQUIRE(row.get_listbase_ptr(table->get_column_key("bool array"))->size() == 2);
  369. REQUIRE(row.get_listbase_ptr(table->get_column_key("int array"))->size() == 2);
  370. REQUIRE(row.get_listbase_ptr(table->get_column_key("float array"))->size() == 2);
  371. REQUIRE(row.get_listbase_ptr(table->get_column_key("double array"))->size() == 2);
  372. REQUIRE(row.get_listbase_ptr(table->get_column_key("string array"))->size() == 3);
  373. REQUIRE(row.get_listbase_ptr(table->get_column_key("data array"))->size() == 3);
  374. REQUIRE(row.get_listbase_ptr(table->get_column_key("date array"))->size() == 0);
  375. REQUIRE(row.get_listbase_ptr(table->get_column_key("object array"))->size() == 1);
  376. }
  377. SECTION("create can use defaults for primary key") {
  378. d.defaults["all types"] = {
  379. {"pk", INT64_C(10)},
  380. };
  381. auto obj = create(AnyDict{
  382. {"bool", true},
  383. {"int", INT64_C(5)},
  384. {"float", 2.2f},
  385. {"double", 3.3},
  386. {"string", "hello"s},
  387. {"data", "olleh"s},
  388. {"date", Timestamp(10, 20)},
  389. {"object", AnyDict{{"value", INT64_C(10)}}},
  390. {"array", AnyVector{AnyDict{{"value", INT64_C(20)}}}},
  391. });
  392. auto row = obj.obj();
  393. REQUIRE(row.get<Int>(row.get_table()->get_column_key("pk")) == 10);
  394. }
  395. SECTION("create does not complain about missing values for nullable fields") {
  396. r->begin_transaction();
  397. realm::Object obj;
  398. REQUIRE_NOTHROW(obj = Object::create(d, r, *r->schema().find("all optional types"), util::Any(AnyDict{})));
  399. r->commit_transaction();
  400. REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "pk").has_value());
  401. REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "bool").has_value());
  402. REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "int").has_value());
  403. REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "float").has_value());
  404. REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "double").has_value());
  405. REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "string").has_value());
  406. REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "data").has_value());
  407. REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "date").has_value());
  408. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "bool array")).size() == 0);
  409. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "int array")).size() == 0);
  410. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "float array")).size() == 0);
  411. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "double array")).size() == 0);
  412. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "string array")).size() == 0);
  413. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "data array")).size() == 0);
  414. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "date array")).size() == 0);
  415. }
  416. SECTION("create throws for missing values if there is no default") {
  417. REQUIRE_THROWS(create(AnyDict{
  418. {"pk", INT64_C(1)},
  419. {"float", 6.6f},
  420. }));
  421. }
  422. SECTION("create always sets the PK first") {
  423. AnyDict value{
  424. {"array 1", AnyVector{AnyDict{{"value", INT64_C(1)}}}},
  425. {"array 2", AnyVector{AnyDict{{"value", INT64_C(2)}}}},
  426. {"int 1", INT64_C(0)},
  427. {"int 2", INT64_C(0)},
  428. {"pk", INT64_C(7)},
  429. };
  430. // Core will throw if the list is populated before the PK is set
  431. r->begin_transaction();
  432. REQUIRE_NOTHROW(Object::create(d, r, *r->schema().find("pk after list"), util::Any(value)));
  433. }
  434. SECTION("create with update") {
  435. CollectionChangeSet change;
  436. bool callback_called;
  437. Object obj = create(AnyDict{
  438. {"pk", INT64_C(1)},
  439. {"bool", true},
  440. {"int", INT64_C(5)},
  441. {"float", 2.2f},
  442. {"double", 3.3},
  443. {"string", "hello"s},
  444. {"data", "olleh"s},
  445. {"date", Timestamp(10, 20)},
  446. {"object", AnyDict{{"value", INT64_C(10)}}},
  447. {"bool array", AnyVec{true, false}},
  448. {"int array", AnyVec{INT64_C(5), INT64_C(6)}},
  449. {"float array", AnyVec{1.1f, 2.2f}},
  450. {"double array", AnyVec{3.3, 4.4}},
  451. {"string array", AnyVec{"a"s, "b"s, "c"s}},
  452. {"data array", AnyVec{"d"s, "e"s, "f"s}},
  453. {"date array", AnyVec{}},
  454. {"object array", AnyVec{AnyDict{{"value", INT64_C(20)}}}},
  455. });
  456. auto token = obj.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
  457. change = c;
  458. callback_called = true;
  459. });
  460. advance_and_notify(*r);
  461. create(AnyDict{
  462. {"pk", INT64_C(1)},
  463. {"int", INT64_C(6)},
  464. {"string", "a"s},
  465. }, CreatePolicy::UpdateAll);
  466. callback_called = false;
  467. advance_and_notify(*r);
  468. REQUIRE(callback_called);
  469. REQUIRE_INDICES(change.modifications, 0);
  470. auto row = obj.obj();
  471. auto table = row.get_table();
  472. REQUIRE(row.get<Int>(table->get_column_key("pk")) == 1);
  473. REQUIRE(row.get<Bool>(table->get_column_key("bool")) == true);
  474. REQUIRE(row.get<Int>(table->get_column_key("int")) == 6);
  475. REQUIRE(row.get<float>(table->get_column_key("float")) == 2.2f);
  476. REQUIRE(row.get<double>(table->get_column_key("double")) == 3.3);
  477. REQUIRE(row.get<String>(table->get_column_key("string")) == "a");
  478. REQUIRE(row.get<Binary>(table->get_column_key("data")) == BinaryData("olleh", 5));
  479. REQUIRE(row.get<Timestamp>(table->get_column_key("date")) == Timestamp(10, 20));
  480. }
  481. SECTION("create with update - only with diffs") {
  482. CollectionChangeSet change;
  483. bool callback_called;
  484. AnyDict adam {
  485. {"name", "Adam"s},
  486. {"age", INT64_C(32)},
  487. {"scores", AnyVec{INT64_C(1), INT64_C(2)}},
  488. };
  489. AnyDict brian {
  490. {"name", "Brian"s},
  491. {"age", INT64_C(33)},
  492. };
  493. AnyDict charley {
  494. {"name", "Charley"s},
  495. {"age", INT64_C(34)},
  496. {"team", AnyVec{adam, brian}}
  497. };
  498. AnyDict donald {
  499. {"name", "Donald"s},
  500. {"age", INT64_C(35)},
  501. };
  502. AnyDict eddie {
  503. {"name", "Eddie"s},
  504. {"age", INT64_C(36)},
  505. {"assistant", donald},
  506. {"team", AnyVec{donald, charley}}
  507. };
  508. Object obj = create_company(eddie, CreatePolicy::UpdateAll);
  509. auto table = r->read_group().get_table("class_person");
  510. REQUIRE(table->size() == 5);
  511. Results result(r, table);
  512. auto token = result.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
  513. change = c;
  514. callback_called = true;
  515. });
  516. advance_and_notify(*r);
  517. // First update unconditionally
  518. create_company(eddie, CreatePolicy::UpdateAll);
  519. callback_called = false;
  520. advance_and_notify(*r);
  521. REQUIRE(callback_called);
  522. REQUIRE_INDICES(change.modifications, 0, 1, 2, 3, 4);
  523. // Now, only update where differences (there should not be any diffs - so no update)
  524. create_company(eddie, CreatePolicy::UpdateModified);
  525. REQUIRE(table->size() == 5);
  526. callback_called = false;
  527. advance_and_notify(*r);
  528. REQUIRE(!callback_called);
  529. // Now, only update sub-object)
  530. donald["scores"] = AnyVec{INT64_C(3), INT64_C(4), INT64_C(5)};
  531. // Insert the new donald
  532. eddie["assistant"] = donald;
  533. create_company(eddie, CreatePolicy::UpdateModified);
  534. REQUIRE(table->size() == 5);
  535. callback_called = false;
  536. advance_and_notify(*r);
  537. REQUIRE(callback_called);
  538. REQUIRE_INDICES(change.modifications, 1);
  539. // Shorten list
  540. donald["scores"] = AnyVec{INT64_C(3), INT64_C(4)};
  541. eddie["assistant"] = donald;
  542. create_company(eddie, CreatePolicy::UpdateModified);
  543. REQUIRE(table->size() == 5);
  544. callback_called = false;
  545. advance_and_notify(*r);
  546. REQUIRE(callback_called);
  547. REQUIRE_INDICES(change.modifications, 1);
  548. }
  549. SECTION("create with update - identical sub-object") {
  550. Object sub_obj = create_sub(AnyDict{{"value", INT64_C(10)}});
  551. Object obj = create(AnyDict{
  552. {"pk", INT64_C(1)},
  553. {"bool", true},
  554. {"int", INT64_C(5)},
  555. {"float", 2.2f},
  556. {"double", 3.3},
  557. {"string", "hello"s},
  558. {"data", "olleh"s},
  559. {"date", Timestamp(10, 20)},
  560. {"object", sub_obj},
  561. });
  562. auto obj_table = r->read_group().get_table("class_all types");
  563. Results result(r, obj_table);
  564. bool callback_called;
  565. bool results_callback_called;
  566. bool sub_callback_called;
  567. auto token1 = obj.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  568. callback_called = true;
  569. });
  570. auto token2 = result.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  571. results_callback_called = true;
  572. });
  573. auto token3 = sub_obj.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  574. sub_callback_called = true;
  575. });
  576. advance_and_notify(*r);
  577. auto table = r->read_group().get_table("class_link target");
  578. REQUIRE(table->size() == 1);
  579. create(AnyDict{
  580. {"pk", INT64_C(1)},
  581. {"bool", true},
  582. {"int", INT64_C(5)},
  583. {"float", 2.2f},
  584. {"double", 3.3},
  585. {"string", "hello"s},
  586. {"data", "olleh"s},
  587. {"date", Timestamp(10, 20)},
  588. {"object", AnyDict{{"value", INT64_C(10)}}},
  589. }, CreatePolicy::UpdateModified);
  590. REQUIRE(table->size() == 1);
  591. callback_called = false;
  592. results_callback_called = false;
  593. sub_callback_called = false;
  594. advance_and_notify(*r);
  595. REQUIRE(!callback_called);
  596. REQUIRE(!results_callback_called);
  597. REQUIRE(!sub_callback_called);
  598. // Now change sub object
  599. create(AnyDict{
  600. {"pk", INT64_C(1)},
  601. {"bool", true},
  602. {"int", INT64_C(5)},
  603. {"float", 2.2f},
  604. {"double", 3.3},
  605. {"string", "hello"s},
  606. {"data", "olleh"s},
  607. {"date", Timestamp(10, 20)},
  608. {"object", AnyDict{{"value", INT64_C(11)}}},
  609. }, CreatePolicy::UpdateModified);
  610. callback_called = false;
  611. results_callback_called = false;
  612. sub_callback_called = false;
  613. advance_and_notify(*r);
  614. REQUIRE(!callback_called);
  615. REQUIRE(results_callback_called);
  616. REQUIRE(sub_callback_called);
  617. }
  618. SECTION("create with update - identical array of sub-objects") {
  619. bool callback_called;
  620. auto dict = AnyDict{
  621. {"pk", INT64_C(1)},
  622. {"bool", true},
  623. {"int", INT64_C(5)},
  624. {"float", 2.2f},
  625. {"double", 3.3},
  626. {"string", "hello"s},
  627. {"data", "olleh"s},
  628. {"date", Timestamp(10, 20)},
  629. {"object array", AnyVec{AnyDict{{"value", INT64_C(20)}}, AnyDict{{"value", INT64_C(21)}}}},
  630. };
  631. Object obj = create(dict);
  632. auto obj_table = r->read_group().get_table("class_all types");
  633. Results result(r, obj_table);
  634. auto token1 = result.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  635. callback_called = true;
  636. });
  637. advance_and_notify(*r);
  638. create(dict, CreatePolicy::UpdateModified);
  639. callback_called = false;
  640. advance_and_notify(*r);
  641. REQUIRE(!callback_called);
  642. // Now change list
  643. dict["object array"] = AnyVec{AnyDict{{"value", INT64_C(23)}}};
  644. create(dict, CreatePolicy::UpdateModified);
  645. callback_called = false;
  646. advance_and_notify(*r);
  647. REQUIRE(callback_called);
  648. }
  649. for (auto policy : {CreatePolicy::UpdateAll, CreatePolicy::UpdateModified}) {
  650. SECTION("set existing fields to null with update "s + (policy == CreatePolicy::UpdateModified ? "(diffed)" : "(all)")) {
  651. AnyDict initial_values{
  652. {"pk", INT64_C(1)},
  653. {"bool", true},
  654. {"int", INT64_C(5)},
  655. {"float", 2.2f},
  656. {"double", 3.3},
  657. {"string", "hello"s},
  658. {"data", "olleh"s},
  659. {"date", Timestamp(10, 20)},
  660. {"bool array", AnyVec{true, false}},
  661. {"int array", AnyVec{INT64_C(5), INT64_C(6)}},
  662. {"float array", AnyVec{1.1f, 2.2f}},
  663. {"double array", AnyVec{3.3, 4.4}},
  664. {"string array", AnyVec{"a"s, "b"s, "c"s}},
  665. {"data array", AnyVec{"d"s, "e"s, "f"s}},
  666. {"date array", AnyVec{}},
  667. {"object array", AnyVec{AnyDict{{"value", INT64_C(20)}}}},
  668. };
  669. r->begin_transaction();
  670. auto obj = Object::create(d, r, *r->schema().find("all optional types"), util::Any(initial_values));
  671. // Missing fields in dictionary do not update anything
  672. Object::create(d, r, *r->schema().find("all optional types"),
  673. util::Any(AnyDict{{"pk", INT64_C(1)}}), policy);
  674. REQUIRE(any_cast<bool>(obj.get_property_value<util::Any>(d, "bool")) == true);
  675. REQUIRE(any_cast<int64_t>(obj.get_property_value<util::Any>(d, "int")) == 5);
  676. REQUIRE(any_cast<float>(obj.get_property_value<util::Any>(d, "float")) == 2.2f);
  677. REQUIRE(any_cast<double>(obj.get_property_value<util::Any>(d, "double")) == 3.3);
  678. REQUIRE(any_cast<std::string>(obj.get_property_value<util::Any>(d, "string")) == "hello");
  679. REQUIRE(any_cast<Timestamp>(obj.get_property_value<util::Any>(d, "date")) == Timestamp(10, 20));
  680. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "bool array")).get<util::Optional<bool>>(0) == true);
  681. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "int array")).get<util::Optional<int64_t>>(0) == 5);
  682. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "float array")).get<util::Optional<float>>(0) == 1.1f);
  683. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "double array")).get<util::Optional<double>>(0) == 3.3);
  684. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "string array")).get<StringData>(0) == "a");
  685. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "date array")).size() == 0);
  686. // Set all properties to null
  687. AnyDict null_values{
  688. {"pk", INT64_C(1)},
  689. {"bool", util::Any()},
  690. {"int", util::Any()},
  691. {"float", util::Any()},
  692. {"double", util::Any()},
  693. {"string", util::Any()},
  694. {"data", util::Any()},
  695. {"date", util::Any()},
  696. {"bool array", AnyVec{util::Any()}},
  697. {"int array", AnyVec{util::Any()}},
  698. {"float array", AnyVec{util::Any()}},
  699. {"double array", AnyVec{util::Any()}},
  700. {"string array", AnyVec{util::Any()}},
  701. {"data array", AnyVec{util::Any()}},
  702. {"date array", AnyVec{Timestamp()}},
  703. };
  704. Object::create(d, r, *r->schema().find("all optional types"), util::Any(null_values), policy);
  705. REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "bool").has_value());
  706. REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "int").has_value());
  707. REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "float").has_value());
  708. REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "double").has_value());
  709. REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "string").has_value());
  710. REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "data").has_value());
  711. REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "date").has_value());
  712. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "bool array")).get<util::Optional<bool>>(0) == util::none);
  713. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "int array")).get<util::Optional<int64_t>>(0) == util::none);
  714. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "float array")).get<util::Optional<float>>(0) == util::none);
  715. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "double array")).get<util::Optional<double>>(0) == util::none);
  716. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "string array")).get<StringData>(0) == StringData());
  717. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "data array")).get<BinaryData>(0) == BinaryData());
  718. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "date array")).get<Timestamp>(0) == Timestamp());
  719. // Set all properties back to non-null
  720. Object::create(d, r, *r->schema().find("all optional types"), util::Any(initial_values), policy);
  721. REQUIRE(any_cast<bool>(obj.get_property_value<util::Any>(d, "bool")) == true);
  722. REQUIRE(any_cast<int64_t>(obj.get_property_value<util::Any>(d, "int")) == 5);
  723. REQUIRE(any_cast<float>(obj.get_property_value<util::Any>(d, "float")) == 2.2f);
  724. REQUIRE(any_cast<double>(obj.get_property_value<util::Any>(d, "double")) == 3.3);
  725. REQUIRE(any_cast<std::string>(obj.get_property_value<util::Any>(d, "string")) == "hello");
  726. REQUIRE(any_cast<Timestamp>(obj.get_property_value<util::Any>(d, "date")) == Timestamp(10, 20));
  727. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "bool array")).get<util::Optional<bool>>(0) == true);
  728. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "int array")).get<util::Optional<int64_t>>(0) == 5);
  729. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "float array")).get<util::Optional<float>>(0) == 1.1f);
  730. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "double array")).get<util::Optional<double>>(0) == 3.3);
  731. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "string array")).get<StringData>(0) == "a");
  732. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "date array")).size() == 0);
  733. }
  734. }
  735. SECTION("create throws for duplicate pk if update is not specified") {
  736. create(AnyDict{
  737. {"pk", INT64_C(1)},
  738. {"bool", true},
  739. {"int", INT64_C(5)},
  740. {"float", 2.2f},
  741. {"double", 3.3},
  742. {"string", "hello"s},
  743. {"data", "olleh"s},
  744. {"date", Timestamp(10, 20)},
  745. {"object", AnyDict{{"value", INT64_C(10)}}},
  746. {"array", AnyVector{AnyDict{{"value", INT64_C(20)}}}},
  747. });
  748. REQUIRE_THROWS(create(AnyDict{
  749. {"pk", INT64_C(1)},
  750. {"bool", true},
  751. {"int", INT64_C(5)},
  752. {"float", 2.2f},
  753. {"double", 3.3},
  754. {"string", "hello"s},
  755. {"data", "olleh"s},
  756. {"date", Timestamp(10, 20)},
  757. {"object", AnyDict{{"value", INT64_C(10)}}},
  758. {"array", AnyVector{AnyDict{{"value", INT64_C(20)}}}},
  759. }));
  760. }
  761. SECTION("create with explicit null pk does not fall back to default") {
  762. d.defaults["nullable int pk"] = {
  763. {"pk", INT64_C(10)},
  764. };
  765. d.defaults["nullable string pk"] = {
  766. {"pk", "value"s},
  767. };
  768. auto create = [&](util::Any&& value, StringData type) {
  769. r->begin_transaction();
  770. auto obj = Object::create(d, r, *r->schema().find(type), value);
  771. r->commit_transaction();
  772. return obj;
  773. };
  774. auto obj = create(AnyDict{{"pk", d.null_value()}}, "nullable int pk");
  775. auto col_pk_int = r->read_group().get_table("class_nullable int pk")->get_column_key("pk");
  776. auto col_pk_str = r->read_group().get_table("class_nullable string pk")->get_column_key("pk");
  777. REQUIRE(obj.obj().is_null(col_pk_int));
  778. obj = create(AnyDict{{"pk", d.null_value()}}, "nullable string pk");
  779. REQUIRE(obj.obj().is_null(col_pk_str));
  780. obj = create(AnyDict{{}}, "nullable int pk");
  781. REQUIRE(obj.obj().get<util::Optional<Int>>(col_pk_int) == 10);
  782. obj = create(AnyDict{{}}, "nullable string pk");
  783. REQUIRE(obj.obj().get<String>(col_pk_str) == "value");
  784. }
  785. SECTION("getters and setters") {
  786. r->begin_transaction();
  787. auto table = r->read_group().get_table("class_all types");
  788. table->create_object();
  789. Object obj(r, *r->schema().find("all types"), *table->begin());
  790. auto link_table = r->read_group().get_table("class_link target");
  791. link_table->create_object();
  792. Object linkobj(r, *r->schema().find("link target"), *link_table->begin());
  793. obj.set_property_value(d, "bool", util::Any(true));
  794. REQUIRE(any_cast<bool>(obj.get_property_value<util::Any>(d, "bool")) == true);
  795. obj.set_property_value(d, "int", util::Any(INT64_C(5)));
  796. REQUIRE(any_cast<int64_t>(obj.get_property_value<util::Any>(d, "int")) == 5);
  797. obj.set_property_value(d, "float", util::Any(1.23f));
  798. REQUIRE(any_cast<float>(obj.get_property_value<util::Any>(d, "float")) == 1.23f);
  799. obj.set_property_value(d, "double", util::Any(1.23));
  800. REQUIRE(any_cast<double>(obj.get_property_value<util::Any>(d, "double")) == 1.23);
  801. obj.set_property_value(d, "string", util::Any("abc"s));
  802. REQUIRE(any_cast<std::string>(obj.get_property_value<util::Any>(d, "string")) == "abc");
  803. obj.set_property_value(d, "data", util::Any("abc"s));
  804. REQUIRE(any_cast<std::string>(obj.get_property_value<util::Any>(d, "data")) == "abc");
  805. obj.set_property_value(d, "date", util::Any(Timestamp(1, 2)));
  806. REQUIRE(any_cast<Timestamp>(obj.get_property_value<util::Any>(d, "date")) == Timestamp(1, 2));
  807. REQUIRE_FALSE(obj.get_property_value<util::Any>(d, "object").has_value());
  808. obj.set_property_value(d, "object", util::Any(linkobj));
  809. REQUIRE(any_cast<Object>(obj.get_property_value<util::Any>(d, "object")).obj().get_key() == linkobj.obj().get_key());
  810. auto linking = any_cast<Results>(linkobj.get_property_value<util::Any>(d, "origin"));
  811. REQUIRE(linking.size() == 1);
  812. REQUIRE_THROWS(obj.set_property_value(d, "pk", util::Any(INT64_C(5))));
  813. REQUIRE_THROWS(obj.set_property_value(d, "not a property", util::Any(INT64_C(5))));
  814. r->commit_transaction();
  815. REQUIRE_THROWS(obj.get_property_value<util::Any>(d, "not a property"));
  816. REQUIRE_THROWS(obj.set_property_value(d, "int", util::Any(INT64_C(5))));
  817. }
  818. SECTION("list property self-assign is a no-op") {
  819. auto obj = create(AnyDict{
  820. {"pk", INT64_C(1)},
  821. {"bool", true},
  822. {"int", INT64_C(5)},
  823. {"float", 2.2f},
  824. {"double", 3.3},
  825. {"string", "hello"s},
  826. {"data", "olleh"s},
  827. {"date", Timestamp(10, 20)},
  828. {"bool array", AnyVec{true, false}},
  829. {"object array", AnyVec{AnyDict{{"value", INT64_C(20)}}}},
  830. });
  831. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "bool array")).size() == 2);
  832. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "object array")).size() == 1);
  833. r->begin_transaction();
  834. obj.set_property_value(d, "bool array", obj.get_property_value<util::Any>(d, "bool array"));
  835. obj.set_property_value(d, "object array", obj.get_property_value<util::Any>(d, "object array"));
  836. r->commit_transaction();
  837. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "bool array")).size() == 2);
  838. REQUIRE(any_cast<List&&>(obj.get_property_value<util::Any>(d, "object array")).size() == 1);
  839. }
  840. #if REALM_ENABLE_SYNC
  841. if (!util::EventLoop::has_implementation())
  842. return;
  843. SyncServer server(false);
  844. SyncTestFile config1(server, "shared");
  845. config1.schema = config.schema;
  846. SyncTestFile config2(server, "shared");
  847. config2.schema = config.schema;
  848. SECTION("defaults do not override values explicitly passed to create()") {
  849. AnyDict v1{
  850. {"pk", INT64_C(7)},
  851. {"array 1", AnyVector{AnyDict{{"value", INT64_C(1)}}}},
  852. {"array 2", AnyVector{AnyDict{{"value", INT64_C(2)}}}},
  853. };
  854. auto v2 = v1;
  855. v1["int 1"] = INT64_C(1);
  856. v2["int 2"] = INT64_C(2);
  857. auto r1 = Realm::get_shared_realm(config1);
  858. auto r2 = Realm::get_shared_realm(config2);
  859. TestContext c1(r1);
  860. TestContext c2(r2);
  861. c1.defaults["pk after list"] = {
  862. {"int 1", INT64_C(10)},
  863. {"int 2", INT64_C(10)},
  864. };
  865. c2.defaults = c1.defaults;
  866. r1->begin_transaction();
  867. r2->begin_transaction();
  868. auto object1 = Object::create(c1, r1, *r1->schema().find("pk after list"), util::Any(v1));
  869. auto object2 = Object::create(c2, r2, *r2->schema().find("pk after list"), util::Any(v2));
  870. r2->commit_transaction();
  871. r1->commit_transaction();
  872. server.start();
  873. util::EventLoop::main().run_until([&] {
  874. return r1->read_group().get_table("class_array target")->size() == 4;
  875. });
  876. Obj obj = object1.obj();
  877. REQUIRE(obj.get<Int>("pk") == 7); // pk
  878. REQUIRE(obj.get_linklist("array 1").size() == 2);
  879. REQUIRE(obj.get<Int>("int 1") == 1); // non-default from r1
  880. REQUIRE(obj.get<Int>("int 2") == 2); // non-default from r2
  881. REQUIRE(obj.get_linklist("array 2").size() == 2);
  882. }
  883. #endif
  884. }