object.cpp 39 KB

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