1 /**
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 *
4 * This file is part of osm2pgsql (https://osm2pgsql.org/).
5 *
6 * Copyright (C) 2006-2021 by the osm2pgsql developer community.
7 * For a full list of authors see the git log.
8 */
9
10 #include <catch.hpp>
11
12 #include <algorithm>
13
14 #include <osmium/osm/crc.hpp>
15 #include <osmium/osm/crc_zlib.hpp>
16
17 #include "dependency-manager.hpp"
18 #include "middle-pgsql.hpp"
19 #include "middle-ram.hpp"
20
21 #include "common-buffer.hpp"
22 #include "common-cleanup.hpp"
23 #include "common-options.hpp"
24 #include "common-pg.hpp"
25
26 static testing::pg::tempdb_t db;
27
28 namespace {
29
expect_location(osmium::Location loc,osmium::Node const & expected)30 void expect_location(osmium::Location loc, osmium::Node const &expected)
31 {
32 CHECK(loc.lat() == Approx(expected.location().lat()));
33 CHECK(loc.lon() == Approx(expected.location().lon()));
34 }
35
36 } // namespace
37
38 struct options_slim_default
39 {
optionsoptions_slim_default40 static options_t options(testing::pg::tempdb_t const &tmpdb)
41 {
42 return testing::opt_t().slim(tmpdb);
43 }
44 };
45
46 struct options_slim_with_lc_prefix
47 {
optionsoptions_slim_with_lc_prefix48 static options_t options(testing::pg::tempdb_t const &tmpdb)
49 {
50 options_t o = testing::opt_t().slim(tmpdb);
51 o.prefix = "pre";
52 return o;
53 }
54 };
55
56 struct options_slim_with_uc_prefix
57 {
optionsoptions_slim_with_uc_prefix58 static options_t options(testing::pg::tempdb_t const &tmpdb)
59 {
60 options_t o = testing::opt_t().slim(tmpdb);
61 o.prefix = "PRE";
62 return o;
63 }
64 };
65
66 struct options_slim_with_schema
67 {
optionsoptions_slim_with_schema68 static options_t options(testing::pg::tempdb_t const &tmpdb)
69 {
70 options_t o = testing::opt_t().slim(tmpdb);
71 o.middle_dbschema = "osm";
72 return o;
73 }
74 };
75
76 struct options_flat_node_cache
77 {
optionsoptions_flat_node_cache78 static options_t options(testing::pg::tempdb_t const &tmpdb)
79 {
80 return testing::opt_t().slim(tmpdb).flatnodes();
81 }
82 };
83
84 struct options_ram_optimized
85 {
optionsoptions_ram_optimized86 static options_t options(testing::pg::tempdb_t const &)
87 {
88 return testing::opt_t();
89 }
90 };
91
92 TEMPLATE_TEST_CASE("middle import", "", options_slim_default,
93 options_slim_with_lc_prefix, options_slim_with_uc_prefix,
94 options_slim_with_schema, options_ram_optimized)
95 {
96 options_t const options = TestType::options(db);
97 testing::cleanup::file_t flatnode_cleaner{options.flat_node_file};
98
99 auto conn = db.connect();
100 auto const num_tables =
101 conn.get_count("pg_tables", "schemaname = 'public'");
102 auto const num_indexes =
103 conn.get_count("pg_indexes", "schemaname = 'public'");
104 auto const num_procs =
105 conn.get_count("pg_proc", "pronamespace = (SELECT oid FROM "
106 "pg_namespace WHERE nspname = 'public')");
107
108 if (!options.middle_dbschema.empty()) {
109 conn.exec("CREATE SCHEMA IF NOT EXISTS osm;");
110 }
111
112 auto thread_pool = std::make_shared<thread_pool_t>(1U);
113 auto mid = create_middle(thread_pool, options);
114 mid->start();
115
116 output_requirements requirements;
117 requirements.full_ways = true;
118 requirements.full_relations = true;
119 mid->set_requirements(requirements);
120
121 auto const mid_q = mid->get_query_instance();
122
123 test_buffer_t buffer;
124
125 SECTION("Set and retrieve a single node")
126 {
127 auto const &node = buffer.add_node("n1234 x98.7654321 y12.3456789");
128
129 // set the node
130 mid->node(node);
131 mid->after_nodes();
132
133 // getting it back works only via a waylist
134 auto &nodes = buffer.add_way("w3 Nn1234").nodes();
135
136 // get it back
137 REQUIRE(mid_q->nodes_get_list(&nodes) == nodes.size());
138 expect_location(nodes[0].location(), node);
139
140 // other nodes are not retrievable
141 auto &n2 = buffer.add_way("w3 Nn1,n2,n1235").nodes();
142 REQUIRE(mid_q->nodes_get_list(&n2) == 0);
143 }
144
145 SECTION("Set and retrieve a single way")
146 {
147 osmid_t const way_id = 1;
148 double const lon = 98.7654321;
149 double const lat = 12.3456789;
150 idlist_t nds;
151
152 // set nodes
153 for (osmid_t i = 1; i <= 10; ++i) {
154 nds.push_back(i);
155 auto const &node = buffer.add_node(
156 "n{} x{} y{}"_format(i, lon - i * 0.003, lat + i * 0.001));
157 mid->node(node);
158 }
159 mid->after_nodes();
160
161 // set the way
162 mid->way(buffer.add_way(way_id, nds));
163 mid->after_ways();
164
165 // get it back
166 osmium::memory::Buffer outbuf{4096,
167 osmium::memory::Buffer::auto_grow::yes};
168
169 REQUIRE(mid_q->way_get(way_id, &outbuf));
170
171 auto &way = outbuf.get<osmium::Way>(0);
172
173 CHECK(way.id() == way_id);
174 REQUIRE(way.nodes().size() == nds.size());
175
176 REQUIRE(mid_q->nodes_get_list(&(way.nodes())) == nds.size());
177 for (osmid_t i = 1; i <= 10; ++i) {
178 CHECK(way.nodes()[(size_t)i - 1].ref() == i);
179 }
180
181 // other ways are not retrievable
182 REQUIRE_FALSE(mid_q->way_get(way_id + 1, &outbuf));
183 }
184
185 SECTION("Set and retrieve a single relation with supporting ways")
186 {
187 idlist_t const nds[] = {{4, 5, 13, 14, 342}, {45, 90}, {30, 3, 45}};
188
189 // set the node
190 mid->node(buffer.add_node("n1 x4.1 y12.8"));
191 mid->after_nodes();
192
193 // set the ways
194 osmid_t wid = 10;
195 for (auto const &n : nds) {
196 mid->way(buffer.add_way(wid, n));
197 ++wid;
198 }
199 mid->after_ways();
200
201 // set the relation
202 auto const &relation =
203 buffer.add_relation("r123 Mw11@,w10@outer,n1@,w12@inner");
204 osmium::CRC<osmium::CRC_zlib> orig_crc;
205 orig_crc.update(relation);
206
207 mid->relation(relation);
208 mid->after_relations();
209
210 // retrieve the relation
211 osmium::memory::Buffer outbuf{4096,
212 osmium::memory::Buffer::auto_grow::yes};
213 REQUIRE(mid_q->relation_get(123, &outbuf));
214 auto const &rel = outbuf.get<osmium::Relation>(0);
215
216 CHECK(rel.id() == 123);
217 CHECK(rel.members().size() == 4);
218
219 osmium::CRC<osmium::CRC_zlib> crc;
220 crc.update(rel);
221 CHECK(orig_crc().checksum() == crc().checksum());
222
223 // retrieve the supporting ways
224 REQUIRE(mid_q->rel_members_get(rel, &outbuf,
225 osmium::osm_entity_bits::way) == 3);
226
227 for (auto &w : outbuf.select<osmium::Way>()) {
228 REQUIRE(w.id() >= 10);
229 REQUIRE(w.id() <= 12);
230 auto const &expected = nds[w.id() - 10];
231 REQUIRE(w.nodes().size() == expected.size());
232 for (size_t i = 0; i < expected.size(); ++i) {
233 REQUIRE(w.nodes()[i].ref() == expected[i]);
234 }
235 }
236
237 // other relations are not retrievable
238 REQUIRE_FALSE(mid_q->relation_get(999, &outbuf));
239 }
240
241 if (!options.middle_dbschema.empty()) {
242 REQUIRE(num_tables ==
243 conn.get_count("pg_tables", "schemaname = 'public'"));
244 REQUIRE(num_indexes ==
245 conn.get_count("pg_indexes", "schemaname = 'public'"));
246 REQUIRE(num_procs ==
247 conn.get_count("pg_proc",
248 "pronamespace = (SELECT oid FROM "
249 "pg_namespace WHERE nspname = 'public')"));
250 }
251 }
252
253 /**
254 * Check that the node is in the mid with the right id and location.
255 */
check_node(std::shared_ptr<middle_pgsql_t> const & mid,osmium::Node const & node)256 static void check_node(std::shared_ptr<middle_pgsql_t> const &mid,
257 osmium::Node const &node)
258 {
259 test_buffer_t buffer;
260 auto &nodes = buffer.add_way(999, {node.id()}).nodes();
261 auto const mid_q = mid->get_query_instance();
262 REQUIRE(mid_q->nodes_get_list(&nodes) == 1);
263 REQUIRE(nodes[0].ref() == node.id());
264 REQUIRE(nodes[0].location() == node.location());
265 }
266
267 /// Return true if the node with the specified id is not in the mid.
no_node(std::shared_ptr<middle_pgsql_t> const & mid,osmid_t id)268 static bool no_node(std::shared_ptr<middle_pgsql_t> const &mid, osmid_t id)
269 {
270 test_buffer_t buffer;
271 auto &nodes = buffer.add_way(999, {id}).nodes();
272 auto const mid_q = mid->get_query_instance();
273 return mid_q->nodes_get_list(&nodes) == 0;
274 }
275
276 TEMPLATE_TEST_CASE("middle: add, delete and update node", "",
277 options_slim_default, options_flat_node_cache)
278 {
279 auto thread_pool = std::make_shared<thread_pool_t>(1U);
280
281 options_t options = TestType::options(db);
282
283 testing::cleanup::file_t flatnode_cleaner{options.flat_node_file};
284
285 // Prepare a buffer with some nodes which we will add and change.
286 test_buffer_t buffer;
287 auto const &node10 = buffer.add_node("n10 x1.0 y0.0");
288 auto const &node11 = buffer.add_node("n11 x1.1 y0.0");
289 auto const &node12 = buffer.add_node("n12 x1.2 y0.0");
290
291 auto const &node10a = buffer.add_node("n10 x1.0 y1.0");
292
293 auto const &node5d = buffer.add_node("n5 dD");
294 auto const &node10d = buffer.add_node("n10 dD");
295 auto const &node12d = buffer.add_node("n12 dD");
296 auto const &node42d = buffer.add_node("n42 dD");
297
298 // Set up middle in "create" mode to get a cleanly initialized database
299 // and add some nodes. Does this in its own scope so that the mid is
300 // closed properly.
301 {
302 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
303
304 mid->start();
305
306 mid->node(node10);
307 mid->node(node11);
308 mid->after_nodes();
309 mid->after_relations();
310
311 check_node(mid, node10);
312 check_node(mid, node11);
313 }
314
315 // From now on use append mode to not destroy the data we just added.
316 options.append = true;
317
318 SECTION("Added nodes are there and no others")
319 {
320 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
321 mid->start();
322
323 check_node(mid, node10);
324 check_node(mid, node11);
325 REQUIRE(no_node(mid, 5));
326 REQUIRE(no_node(mid, 42));
327 }
328
329 SECTION("Delete existing and non-existing node")
330 {
331 {
332 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
333 mid->start();
334
335 mid->node(node5d);
336 mid->node(node10d);
337 mid->node(node42d);
338 mid->after_nodes();
339 mid->after_relations();
340
341 REQUIRE(no_node(mid, 5));
342 REQUIRE(no_node(mid, 10));
343 check_node(mid, node11);
344 REQUIRE(no_node(mid, 42));
345 }
346 {
347 // Check with a new mid.
348 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
349 mid->start();
350
351 REQUIRE(no_node(mid, 5));
352 REQUIRE(no_node(mid, 10));
353 check_node(mid, node11);
354 REQUIRE(no_node(mid, 42));
355 }
356 }
357
358 SECTION("Change (delete and set) existing and non-existing node")
359 {
360 {
361 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
362 mid->start();
363
364 mid->node(node10d);
365 mid->node(node10a);
366 mid->node(node12d);
367 mid->node(node12);
368 mid->after_nodes();
369 mid->after_relations();
370
371 check_node(mid, node10a);
372 check_node(mid, node11);
373 check_node(mid, node12);
374 }
375 {
376 // Check with a new mid.
377 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
378 mid->start();
379
380 check_node(mid, node10a);
381 check_node(mid, node11);
382 check_node(mid, node12);
383 }
384 }
385
386 SECTION("Add new node")
387 {
388 {
389 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
390 mid->start();
391
392 mid->node(node12);
393 mid->after_nodes();
394 mid->after_relations();
395
396 REQUIRE(no_node(mid, 5));
397 check_node(mid, node10);
398 check_node(mid, node11);
399 check_node(mid, node12);
400 REQUIRE(no_node(mid, 42));
401 }
402 {
403 // Check with a new mid.
404 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
405 mid->start();
406
407 REQUIRE(no_node(mid, 5));
408 check_node(mid, node10);
409 check_node(mid, node11);
410 check_node(mid, node12);
411 REQUIRE(no_node(mid, 42));
412 }
413 }
414 }
415
416 /**
417 * Check that the way is in the mid with the right attributes and tags.
418 * Does not check node locations.
419 */
check_way(std::shared_ptr<middle_pgsql_t> const & mid,osmium::Way const & orig_way)420 static void check_way(std::shared_ptr<middle_pgsql_t> const &mid,
421 osmium::Way const &orig_way)
422 {
423 auto const mid_q = mid->get_query_instance();
424
425 osmium::memory::Buffer outbuf{4096, osmium::memory::Buffer::auto_grow::yes};
426 REQUIRE(mid_q->way_get(orig_way.id(), &outbuf));
427 auto const &way = outbuf.get<osmium::Way>(0);
428
429 osmium::CRC<osmium::CRC_zlib> orig_crc;
430 orig_crc.update(orig_way);
431
432 osmium::CRC<osmium::CRC_zlib> test_crc;
433 test_crc.update(way);
434
435 REQUIRE(orig_crc().checksum() == test_crc().checksum());
436 }
437
438 /**
439 * Check that the nodes (ids and locations) of the way with the way_id in the
440 * mid are identical to the nodes in the nodes vector.
441 */
check_way_nodes(std::shared_ptr<middle_pgsql_t> const & mid,osmid_t way_id,std::vector<osmium::Node const * > const & nodes)442 static void check_way_nodes(std::shared_ptr<middle_pgsql_t> const &mid,
443 osmid_t way_id,
444 std::vector<osmium::Node const *> const &nodes)
445 {
446 auto const mid_q = mid->get_query_instance();
447
448 osmium::memory::Buffer outbuf{4096, osmium::memory::Buffer::auto_grow::yes};
449 REQUIRE(mid_q->way_get(way_id, &outbuf));
450 auto &way = outbuf.get<osmium::Way>(0);
451
452 REQUIRE(mid_q->nodes_get_list(&way.nodes()) == way.nodes().size());
453 REQUIRE(way.nodes().size() == nodes.size());
454
455 REQUIRE(std::equal(way.nodes().cbegin(), way.nodes().cend(), nodes.cbegin(),
456 [](osmium::NodeRef const &nr, osmium::Node const *node) {
457 return nr.ref() == node->id() &&
458 nr.location() == node->location();
459 }));
460 }
461
462 /// Return true if the way with the specified id is not in the mid.
no_way(std::shared_ptr<middle_pgsql_t> const & mid,osmid_t id)463 static bool no_way(std::shared_ptr<middle_pgsql_t> const &mid, osmid_t id)
464 {
465 auto const mid_q = mid->get_query_instance();
466 osmium::memory::Buffer outbuf{4096, osmium::memory::Buffer::auto_grow::yes};
467 return !mid_q->way_get(id, &outbuf);
468 }
469
470 TEMPLATE_TEST_CASE("middle: add, delete and update way", "",
471 options_slim_default, options_flat_node_cache)
472 {
473 auto thread_pool = std::make_shared<thread_pool_t>(1U);
474
475 options_t options = TestType::options(db);
476
477 testing::cleanup::file_t flatnode_cleaner{options.flat_node_file};
478
479 // Create some ways we'll use for the tests.
480 test_buffer_t buffer;
481 auto const &way20 =
482 buffer.add_way("w20 Nn10,n11 Thighway=residential,name=High_Street");
483
484 auto const &way21 = buffer.add_way("w21 Nn11,n12");
485
486 auto const &way22 = buffer.add_way("w22 Nn12,n10 Tpower=line");
487
488 auto const &way20a =
489 buffer.add_way("w20 Nn10,n12 Thighway=primary,name=High_Street");
490
491 auto const &way5d = buffer.add_way("w5 dD");
492 auto const &way20d = buffer.add_way("w20 dD");
493 auto const &way22d = buffer.add_way("w22 dD");
494 auto const &way42d = buffer.add_way("w42 dD");
495
496 // Set up middle in "create" mode to get a cleanly initialized database and
497 // add some ways. Does this in its own scope so that the mid is closed
498 // properly.
499 {
500 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
501 mid->start();
502
503 mid->way(way20);
504 mid->way(way21);
505 mid->after_ways();
506 mid->after_relations();
507
508 check_way(mid, way20);
509 check_way(mid, way21);
510 }
511
512 // From now on use append mode to not destroy the data we just added.
513 options.append = true;
514
515 SECTION("Added ways are there and no others")
516 {
517 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
518 mid->start();
519
520 REQUIRE(no_way(mid, 5));
521 check_way(mid, way20);
522 check_way(mid, way21);
523 REQUIRE(no_way(mid, 22));
524 }
525
526 SECTION("Delete existing and non-existing way")
527 {
528 {
529 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
530 mid->start();
531
532 mid->way(way5d);
533 mid->way(way20d);
534 mid->way(way42d);
535 mid->after_ways();
536 mid->after_relations();
537
538 REQUIRE(no_way(mid, 5));
539 REQUIRE(no_way(mid, 20));
540 check_way(mid, way21);
541 REQUIRE(no_way(mid, 42));
542 }
543 {
544 // Check with a new mid.
545 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
546 mid->start();
547
548 REQUIRE(no_way(mid, 5));
549 REQUIRE(no_way(mid, 20));
550 check_way(mid, way21);
551 REQUIRE(no_way(mid, 42));
552 }
553 }
554
555 SECTION("Change (delete and set) existing and non-existing way")
556 {
557 {
558 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
559 mid->start();
560
561 mid->way(way20d);
562 mid->way(way20a);
563 mid->way(way22d);
564 mid->way(way22);
565 mid->after_ways();
566 mid->after_relations();
567
568 REQUIRE(no_way(mid, 5));
569 check_way(mid, way20a);
570 check_way(mid, way21);
571 check_way(mid, way22);
572 REQUIRE(no_way(mid, 42));
573 }
574 {
575 // Check with a new mid.
576 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
577 mid->start();
578
579 REQUIRE(no_way(mid, 5));
580 check_way(mid, way20a);
581 check_way(mid, way21);
582 check_way(mid, way22);
583 REQUIRE(no_way(mid, 42));
584 }
585 }
586
587 SECTION("Add new way")
588 {
589 {
590 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
591 mid->start();
592
593 mid->way(way22);
594 mid->after_ways();
595 mid->after_relations();
596
597 REQUIRE(no_way(mid, 5));
598 check_way(mid, way20);
599 check_way(mid, way21);
600 check_way(mid, way22);
601 REQUIRE(no_way(mid, 42));
602 }
603 {
604 // Check with a new mid.
605 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
606 mid->start();
607
608 REQUIRE(no_way(mid, 5));
609 check_way(mid, way20);
610 check_way(mid, way21);
611 check_way(mid, way22);
612 REQUIRE(no_way(mid, 42));
613 }
614 }
615 }
616
617 TEMPLATE_TEST_CASE("middle: add way with attributes", "", options_slim_default,
618 options_flat_node_cache)
619 {
620 auto thread_pool = std::make_shared<thread_pool_t>(1U);
621
622 options_t options = TestType::options(db);
623
624 SECTION("With attributes") { options.extra_attributes = true; }
625 SECTION("No attributes") { options.extra_attributes = false; }
626
627 testing::cleanup::file_t flatnode_cleaner{options.flat_node_file};
628
629 // Create some ways we'll use for the tests.
630 test_buffer_t buffer;
631 auto &way20 =
632 buffer.add_way("w20 Nn10,n11 Thighway=residential,name=High_Street");
633 way20.set_version(123);
634 way20.set_timestamp(1234567890);
635 way20.set_changeset(456);
636 way20.set_uid(789);
637
638 // The same way but with default attributes.
639 auto &way20_no_attr =
640 buffer.add_way("w20 Nn10,n11 Thighway=residential,name=High_Street");
641
642 // The same way but with attributes in tags.
643 // The order of the tags is important here!
644 auto &way20_attr_tags = buffer.add_way(
645 "w20 Nn10,n11 "
646 "Thighway=residential,name=High_Street,osm_user=,osm_uid=789,"
647 "osm_version=123,osm_timestamp=2009-02-13T23:31:30Z,osm_changeset=456");
648
649 {
650 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
651 mid->start();
652
653 mid->way(way20);
654 mid->after_ways();
655 mid->after_relations();
656
657 check_way(mid,
658 options.extra_attributes ? way20_attr_tags : way20_no_attr);
659 }
660
661 // From now on use append mode to not destroy the data we just added.
662 options.append = true;
663
664 {
665 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
666 mid->start();
667
668 check_way(mid,
669 options.extra_attributes ? way20_attr_tags : way20_no_attr);
670 }
671 }
672
673 /**
674 * Check that the relation is in the mid with the right attributes, members
675 * and tags. Only checks the relation, does not recurse into members.
676 */
check_relation(std::shared_ptr<middle_pgsql_t> const & mid,osmium::Relation const & orig_relation)677 static void check_relation(std::shared_ptr<middle_pgsql_t> const &mid,
678 osmium::Relation const &orig_relation)
679 {
680 auto const mid_q = mid->get_query_instance();
681
682 osmium::memory::Buffer outbuf{4096, osmium::memory::Buffer::auto_grow::yes};
683 REQUIRE(mid_q->relation_get(orig_relation.id(), &outbuf));
684 auto const &relation = outbuf.get<osmium::Relation>(0);
685
686 osmium::CRC<osmium::CRC_zlib> orig_crc;
687 orig_crc.update(orig_relation);
688
689 osmium::CRC<osmium::CRC_zlib> test_crc;
690 test_crc.update(relation);
691
692 REQUIRE(orig_crc().checksum() == test_crc().checksum());
693 }
694
695 /// Return true if the relation with the specified id is not in the mid.
no_relation(std::shared_ptr<middle_pgsql_t> const & mid,osmid_t id)696 static bool no_relation(std::shared_ptr<middle_pgsql_t> const &mid, osmid_t id)
697 {
698 auto const mid_q = mid->get_query_instance();
699 osmium::memory::Buffer outbuf{4096, osmium::memory::Buffer::auto_grow::yes};
700 return !mid_q->relation_get(id, &outbuf);
701 }
702
703 TEMPLATE_TEST_CASE("middle: add, delete and update relation", "",
704 options_slim_default, options_flat_node_cache)
705 {
706 auto thread_pool = std::make_shared<thread_pool_t>(1U);
707
708 options_t options = TestType::options(db);
709
710 testing::cleanup::file_t flatnode_cleaner{options.flat_node_file};
711
712 // Create some relations we'll use for the tests.
713 test_buffer_t buffer;
714 auto const &relation30 = buffer.add_relation(
715 "r30 Mw10@outer,w11@innder Ttype=multipolygon,name=Penguin_Park");
716
717 auto const &relation31 = buffer.add_relation("r31 Mn10@");
718
719 auto const &relation32 = buffer.add_relation("r32 Mr39@ Ttype=site");
720
721 auto const &relation30a = buffer.add_relation(
722 "r30 Mw10@outer,w11@outer Ttype=multipolygon,name=Pigeon_Park");
723
724 auto const &relation5d = buffer.add_relation("r5 dD");
725 auto const &relation30d = buffer.add_relation("r30 dD");
726 auto const &relation32d = buffer.add_relation("r32 dD");
727 auto const &relation42d = buffer.add_relation("r42 dD");
728
729 // Set up middle in "create" mode to get a cleanly initialized database and
730 // add some relations. Does this in its own scope so that the mid is closed
731 // properly.
732 {
733 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
734 mid->start();
735
736 mid->relation(relation30);
737 mid->relation(relation31);
738 mid->after_relations();
739
740 check_relation(mid, relation30);
741 check_relation(mid, relation31);
742 }
743
744 // From now on use append mode to not destroy the data we just added.
745 options.append = true;
746
747 SECTION("Added relations are there and no others")
748 {
749 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
750 mid->start();
751
752 REQUIRE(no_relation(mid, 5));
753 check_relation(mid, relation30);
754 check_relation(mid, relation31);
755 REQUIRE(no_relation(mid, 32));
756 }
757
758 SECTION("Delete existing and non-existing relation")
759 {
760 {
761 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
762 mid->start();
763
764 mid->relation(relation5d);
765 mid->relation(relation30d);
766 mid->relation(relation42d);
767 mid->after_relations();
768
769 REQUIRE(no_relation(mid, 5));
770 REQUIRE(no_relation(mid, 30));
771 check_relation(mid, relation31);
772 REQUIRE(no_relation(mid, 42));
773 }
774 {
775 // Check with a new mid.
776 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
777 mid->start();
778
779 REQUIRE(no_relation(mid, 5));
780 REQUIRE(no_relation(mid, 30));
781 check_relation(mid, relation31);
782 REQUIRE(no_relation(mid, 42));
783 }
784 }
785
786 SECTION("Change (delete and set) existing and non-existing relation")
787 {
788 {
789 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
790 mid->start();
791
792 mid->relation(relation30d);
793 mid->relation(relation30a);
794 mid->relation(relation32d);
795 mid->relation(relation32);
796 mid->after_relations();
797
798 REQUIRE(no_relation(mid, 5));
799 check_relation(mid, relation30a);
800 check_relation(mid, relation31);
801 check_relation(mid, relation32);
802 REQUIRE(no_relation(mid, 42));
803 }
804 {
805 // Check with a new mid.
806 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
807 mid->start();
808
809 REQUIRE(no_relation(mid, 5));
810 check_relation(mid, relation30a);
811 check_relation(mid, relation31);
812 check_relation(mid, relation32);
813 REQUIRE(no_relation(mid, 42));
814 }
815 }
816
817 SECTION("Add new relation")
818 {
819 {
820 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
821 mid->start();
822
823 mid->relation(relation32);
824 mid->after_relations();
825
826 REQUIRE(no_relation(mid, 5));
827 check_relation(mid, relation30);
828 check_relation(mid, relation31);
829 check_relation(mid, relation32);
830 REQUIRE(no_relation(mid, 42));
831 }
832 {
833 // Check with a new mid.
834 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
835 mid->start();
836
837 REQUIRE(no_relation(mid, 5));
838 check_relation(mid, relation30);
839 check_relation(mid, relation31);
840 check_relation(mid, relation32);
841 REQUIRE(no_relation(mid, 42));
842 }
843 }
844 }
845
846 TEMPLATE_TEST_CASE("middle: add relation with attributes", "",
847 options_slim_default, options_flat_node_cache)
848 {
849 auto thread_pool = std::make_shared<thread_pool_t>(1U);
850
851 options_t options = TestType::options(db);
852
853 SECTION("With attributes") { options.extra_attributes = true; }
854 SECTION("No attributes") { options.extra_attributes = false; }
855
856 testing::cleanup::file_t flatnode_cleaner{options.flat_node_file};
857
858 // Create some relations we'll use for the tests.
859 test_buffer_t buffer;
860 auto &relation30 = buffer.add_relation(
861 "r30 v123 c456 i789 t2009-02-13T23:31:30Z Mw10@outer,w11@inner "
862 "Ttype=multipolygon,name=Penguin_Park");
863
864 // The same relation but with default attributes.
865 auto &relation30_no_attr = buffer.add_relation(
866 "r30 Mw10@outer,w11@inner Ttype=multipolygon,name=Penguin_Park");
867
868 // The same relation but with attributes in tags.
869 // The order of the tags is important here!
870 auto &relation30_attr_tags = buffer.add_relation(
871 "r30 Mw10@outer,w11@inner "
872 "Ttype=multipolygon,name=Penguin_Park,osm_user=,osm_uid=789,"
873 "osm_version=123,osm_timestamp=2009-02-13T23:31:30Z,osm_changeset=456");
874
875 {
876 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
877 mid->start();
878
879 mid->relation(relation30);
880 mid->after_relations();
881
882 check_relation(mid, options.extra_attributes ? relation30_attr_tags
883 : relation30_no_attr);
884 }
885
886 // From now on use append mode to not destroy the data we just added.
887 options.append = true;
888
889 {
890 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
891 mid->start();
892
893 check_relation(mid, options.extra_attributes ? relation30_attr_tags
894 : relation30_no_attr);
895 }
896 }
897
898 TEMPLATE_TEST_CASE("middle: change nodes in way", "", options_slim_default,
899 options_flat_node_cache)
900 {
901 auto thread_pool = std::make_shared<thread_pool_t>(1U);
902
903 options_t options = TestType::options(db);
904
905 testing::cleanup::file_t flatnode_cleaner{options.flat_node_file};
906
907 // create some nodes and ways we'll use for the tests
908 test_buffer_t buffer;
909 auto const &node10 = buffer.add_node("n10 x1.0 y0.0");
910 auto const &node11 = buffer.add_node("n11 x1.1 y0.0");
911 auto const &node12 = buffer.add_node("n12 x1.2 y0.0");
912 auto const &node10a = buffer.add_node("n10 x2.0 y0.0");
913
914 auto const &node10d = buffer.add_node("n10 dD");
915
916 auto const &way20 = buffer.add_way("w20 Nn10,n11");
917 auto const &way21 = buffer.add_way("w21 Nn11,n12");
918 auto const &way22 = buffer.add_way("w22 Nn12,n10");
919 auto const &way20a = buffer.add_way("w20 Nn11,n12");
920
921 auto const &way20d = buffer.add_way("w20 dD");
922
923 // Set up middle in "create" mode to get a cleanly initialized database and
924 // add some nodes and ways. Does this in its own scope so that the mid is
925 // closed properly.
926 {
927 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
928 full_dependency_manager_t dependency_manager{mid};
929 mid->start();
930
931 mid->node(node10);
932 mid->node(node11);
933 mid->node(node12);
934 mid->after_nodes();
935 mid->way(way20);
936 mid->way(way21);
937 mid->after_ways();
938 mid->after_relations();
939
940 check_node(mid, node10);
941 check_node(mid, node11);
942 check_node(mid, node12);
943 check_way(mid, way20);
944 check_way_nodes(mid, way20.id(), {&node10, &node11});
945 check_way(mid, way21);
946 check_way_nodes(mid, way21.id(), {&node11, &node12});
947
948 REQUIRE_FALSE(dependency_manager.has_pending());
949 }
950
951 // From now on use append mode to not destroy the data we just added.
952 options.append = true;
953
954 SECTION("Single way affected")
955 {
956 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
957 full_dependency_manager_t dependency_manager{mid};
958 mid->start();
959
960 mid->node(node10d);
961 mid->node(node10a);
962 dependency_manager.node_changed(10);
963 mid->after_nodes();
964
965 REQUIRE(dependency_manager.has_pending());
966 idlist_t const way_ids = dependency_manager.get_pending_way_ids();
967 REQUIRE_THAT(way_ids, Catch::Equals<osmid_t>({20}));
968
969 check_way(mid, way20);
970 check_way_nodes(mid, way20.id(), {&node10a, &node11});
971 }
972
973 SECTION("Two ways affected")
974 {
975 {
976 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
977 mid->start();
978
979 mid->way(way22);
980 mid->after_ways();
981 mid->after_relations();
982 check_way(mid, way22);
983 }
984 {
985 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
986 full_dependency_manager_t dependency_manager{mid};
987 mid->start();
988
989 mid->node(node10d);
990 mid->node(node10a);
991 dependency_manager.node_changed(10);
992 mid->after_nodes();
993
994 REQUIRE(dependency_manager.has_pending());
995 idlist_t const way_ids = dependency_manager.get_pending_way_ids();
996 REQUIRE_THAT(way_ids, Catch::Equals<osmid_t>({20, 22}));
997
998 check_way(mid, way20);
999 check_way_nodes(mid, way20.id(), {&node10a, &node11});
1000 check_way(mid, way22);
1001 check_way_nodes(mid, way22.id(), {&node12, &node10a});
1002 }
1003 }
1004
1005 SECTION("Change way so the changing node isn't in it any more")
1006 {
1007 {
1008 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
1009 mid->start();
1010
1011 mid->way(way20d);
1012 mid->way(way20a);
1013 mid->after_ways();
1014 mid->after_relations();
1015
1016 check_way(mid, way20a);
1017 check_way_nodes(mid, way20.id(), {&node11, &node12});
1018 }
1019
1020 {
1021 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
1022 full_dependency_manager_t dependency_manager{mid};
1023 mid->start();
1024
1025 mid->node(node10d);
1026 mid->node(node10a);
1027 dependency_manager.node_changed(10);
1028 mid->after_nodes();
1029
1030 REQUIRE_FALSE(dependency_manager.has_pending());
1031 }
1032 }
1033 }
1034
1035 TEMPLATE_TEST_CASE("middle: change nodes in relation", "", options_slim_default,
1036 options_flat_node_cache)
1037 {
1038 auto thread_pool = std::make_shared<thread_pool_t>(1U);
1039
1040 options_t options = TestType::options(db);
1041
1042 testing::cleanup::file_t flatnode_cleaner{options.flat_node_file};
1043
1044 // create some nodes, ways, and relations we'll use for the tests
1045 test_buffer_t buffer;
1046 auto const &node10 = buffer.add_node("n10 x1.0 y0.0");
1047 auto const &node11 = buffer.add_node("n11 x1.1 y0.0");
1048 auto const &node12 = buffer.add_node("n12 x1.2 y0.0");
1049 auto const &node10a = buffer.add_node("n10 x1.0 y1.0");
1050 auto const &node11a = buffer.add_node("n11 x1.1 y1.0");
1051
1052 auto const &node10d = buffer.add_node("n10 dD");
1053 auto const &node11d = buffer.add_node("n11 dD");
1054
1055 auto const &way20 = buffer.add_way("w20 Nn11,n12");
1056
1057 auto const &rel30 = buffer.add_relation("r30 Mn10@");
1058 auto const &rel31 = buffer.add_relation("r31 Mw20@");
1059
1060 // Set up middle in "create" mode to get a cleanly initialized database and
1061 // add some nodes and ways. Does this in its own scope so that the mid is
1062 // closed properly.
1063 {
1064 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
1065 mid->start();
1066
1067 mid->node(node10);
1068 mid->node(node11);
1069 mid->node(node12);
1070 mid->after_nodes();
1071 mid->way(way20);
1072 mid->after_ways();
1073 mid->relation(rel30);
1074 mid->relation(rel31);
1075 mid->after_relations();
1076 }
1077
1078 // From now on use append mode to not destroy the data we just added.
1079 options.append = true;
1080
1081 SECTION("Single relation directly affected")
1082 {
1083 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
1084 full_dependency_manager_t dependency_manager{mid};
1085 mid->start();
1086
1087 mid->node(node10d);
1088 mid->node(node10a);
1089 dependency_manager.node_changed(10);
1090 mid->after_nodes();
1091 mid->after_relations();
1092
1093 REQUIRE(dependency_manager.has_pending());
1094 idlist_t const rel_ids = dependency_manager.get_pending_relation_ids();
1095
1096 REQUIRE_THAT(rel_ids, Catch::Equals<osmid_t>({30}));
1097 check_relation(mid, rel30);
1098 }
1099
1100 SECTION("Single relation indirectly affected (through way)")
1101 {
1102 auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
1103 full_dependency_manager_t dependency_manager{mid};
1104 mid->start();
1105
1106 mid->node(node11d);
1107 mid->node(node11a);
1108 dependency_manager.node_changed(11);
1109 mid->after_nodes();
1110 mid->after_relations();
1111
1112 REQUIRE(dependency_manager.has_pending());
1113 idlist_t const way_ids = dependency_manager.get_pending_way_ids();
1114 REQUIRE_THAT(way_ids, Catch::Equals<osmid_t>({20}));
1115 idlist_t const rel_ids = dependency_manager.get_pending_relation_ids();
1116 REQUIRE_THAT(rel_ids, Catch::Equals<osmid_t>({31}));
1117 check_relation(mid, rel31);
1118 }
1119 }
1120