osm2pgsql/tests/test-middle.cpp

1284 lines
40 KiB
C++

/**
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This file is part of osm2pgsql (https://osm2pgsql.org/).
*
* Copyright (C) 2006-2024 by the osm2pgsql developer community.
* For a full list of authors see the git log.
*/
#include <catch.hpp>
#include <algorithm>
#include <array>
#include <osmium/osm/crc.hpp>
#include <osmium/osm/crc_zlib.hpp>
#include "middle-pgsql.hpp"
#include "middle-ram.hpp"
#include "osmdata.hpp"
#include "output-null.hpp"
#include "output-requirements.hpp"
#include "common-buffer.hpp"
#include "common-cleanup.hpp"
#include "common-options.hpp"
#include "common-pg.hpp"
namespace {
testing::pg::tempdb_t db;
void check_locations_are_equal(osmium::Location a, osmium::Location b)
{
CHECK(a.lat() == Approx(b.lat()));
CHECK(a.lon() == Approx(b.lon()));
}
} // anonymous namespace
struct options_slim_default
{
static options_t options(testing::pg::tempdb_t const &tmpdb)
{
return testing::opt_t().slim(tmpdb);
}
};
struct options_slim_with_lc_prefix
{
static options_t options(testing::pg::tempdb_t const &tmpdb)
{
options_t o = testing::opt_t().slim(tmpdb);
o.prefix = "pre";
return o;
}
};
struct options_slim_with_uc_prefix
{
static options_t options(testing::pg::tempdb_t const &tmpdb)
{
options_t o = testing::opt_t().slim(tmpdb);
o.prefix = "PRE";
return o;
}
};
struct options_slim_with_schema
{
static options_t options(testing::pg::tempdb_t const &tmpdb)
{
options_t o = testing::opt_t().slim(tmpdb);
o.middle_dbschema = "osm";
return o;
}
};
struct options_flat_node_cache
{
static options_t options(testing::pg::tempdb_t const &tmpdb)
{
return testing::opt_t().slim(tmpdb).flatnodes();
}
};
struct options_ram_optimized
{
static options_t options(testing::pg::tempdb_t const &)
{
return testing::opt_t();
}
};
TEMPLATE_TEST_CASE("middle import", "", options_slim_default,
options_slim_with_lc_prefix, options_slim_with_uc_prefix,
options_slim_with_schema, options_ram_optimized)
{
options_t const options = TestType::options(db);
testing::cleanup::file_t const flatnode_cleaner{options.flat_node_file};
auto conn = db.connect();
auto const num_tables =
conn.get_count("pg_catalog.pg_tables", "schemaname = 'public'");
auto const num_indexes =
conn.get_count("pg_catalog.pg_indexes", "schemaname = 'public'");
auto const num_procs =
conn.get_count("pg_catalog.pg_proc",
"pronamespace = (SELECT oid FROM "
"pg_catalog.pg_namespace WHERE nspname = 'public')");
if (options.middle_dbschema == "osm") {
conn.exec("CREATE SCHEMA IF NOT EXISTS osm;");
}
auto thread_pool = std::make_shared<thread_pool_t>(1U);
auto mid = create_middle(thread_pool, options);
mid->start();
output_requirements requirements;
requirements.full_ways = true;
requirements.full_relations = true;
mid->set_requirements(requirements);
auto const mid_q = mid->get_query_instance();
test_buffer_t buffer;
SECTION("Set and retrieve a single node")
{
auto const &node = buffer.add_node("n1234 x98.7654321 y12.3456789");
// set the node
mid->node(node);
mid->after_nodes();
mid->after_ways();
mid->after_relations();
// getting it back works only via a waylist
auto &nodes = buffer.add_way("w3 Nn1234").nodes();
// get it back
REQUIRE(mid_q->nodes_get_list(&nodes) == nodes.size());
check_locations_are_equal(nodes[0].location(), node.location());
// other nodes are not retrievable
auto &n2 = buffer.add_way("w3 Nn1,n2,n1235").nodes();
REQUIRE(mid_q->nodes_get_list(&n2) == 0);
// check the same thing again using get_node_location() function
check_locations_are_equal(mid_q->get_node_location(1234),
node.location());
CHECK_FALSE(mid_q->get_node_location(1).valid());
CHECK_FALSE(mid_q->get_node_location(2).valid());
CHECK_FALSE(mid_q->get_node_location(1235).valid());
}
SECTION("Set and retrieve a single way")
{
osmid_t const way_id = 1;
double const lon = 98.7654321;
double const lat = 12.3456789;
idlist_t nds;
// set nodes
for (osmid_t i = 1; i <= 10; ++i) {
nds.push_back(i);
auto const &node = buffer.add_node(fmt::format(
"n{} x{:.7f} y{:.7f}", i, lon - static_cast<double>(i) * 0.003,
lat + static_cast<double>(i) * 0.001));
mid->node(node);
}
mid->after_nodes();
// set the way
mid->way(buffer.add_way(way_id, nds));
mid->after_ways();
mid->after_relations();
// get it back
osmium::memory::Buffer outbuf{4096,
osmium::memory::Buffer::auto_grow::yes};
REQUIRE(mid_q->way_get(way_id, &outbuf));
auto &way = outbuf.get<osmium::Way>(0);
CHECK(way.id() == way_id);
REQUIRE(way.nodes().size() == nds.size());
REQUIRE(mid_q->nodes_get_list(&(way.nodes())) == nds.size());
for (osmid_t i = 1; i <= 10; ++i) {
auto const &nr = way.nodes()[static_cast<size_t>(i) - 1];
CHECK(nr.ref() == i);
CHECK(nr.location().lon() == Approx(lon - i * 0.003));
CHECK(nr.location().lat() == Approx(lat + i * 0.001));
}
// other ways are not retrievable
REQUIRE_FALSE(mid_q->way_get(way_id + 1, &outbuf));
}
SECTION("Set and retrieve a single relation with supporting ways")
{
std::array<idlist_t, 3> const nds = {
{{4, 5, 13, 14, 342}, {45, 90}, {30, 3, 45}}};
// set the node
mid->node(buffer.add_node("n1 x4.1 y12.8"));
mid->after_nodes();
// set the ways
osmid_t wid = 10;
for (auto const &n : nds) {
mid->way(buffer.add_way(wid, n));
++wid;
}
mid->after_ways();
// set the relation
auto const &relation =
buffer.add_relation("r123 Mw11@,w10@outer,n1@,w12@inner");
std::vector<std::pair<osmium::item_type, osmid_t>> const expected = {
{osmium::item_type::way, 11},
{osmium::item_type::way, 10},
{osmium::item_type::node, 1},
{osmium::item_type::way, 12},
};
osmium::CRC<osmium::CRC_zlib> orig_crc;
orig_crc.update(relation);
mid->relation(relation);
mid->after_relations();
// retrieve the relation
osmium::memory::Buffer outbuf{4096,
osmium::memory::Buffer::auto_grow::yes};
REQUIRE(mid_q->relation_get(123, &outbuf));
auto const &rel = outbuf.get<osmium::Relation>(0);
CHECK(rel.id() == 123);
CHECK(rel.members().size() == 4);
osmium::CRC<osmium::CRC_zlib> crc;
crc.update(rel);
CHECK(orig_crc().checksum() == crc().checksum());
// retrieve node members only
osmium::memory::Buffer memberbuf{
4096, osmium::memory::Buffer::auto_grow::yes};
REQUIRE(mid_q->rel_members_get(rel, &memberbuf,
osmium::osm_entity_bits::node) == 1);
{
auto const objects = memberbuf.select<osmium::OSMObject>();
auto it = objects.cbegin();
auto const end = objects.cend();
for (auto const &p : expected) {
if (p.first == osmium::item_type::node) {
REQUIRE(it != end);
REQUIRE(it->type() == p.first);
REQUIRE(it->id() == p.second);
++it;
}
}
}
memberbuf.clear();
// retrieve way members only
REQUIRE(mid_q->rel_members_get(rel, &memberbuf,
osmium::osm_entity_bits::way) == 3);
{
auto const objects = memberbuf.select<osmium::OSMObject>();
auto it = objects.cbegin();
auto const end = objects.cend();
for (auto const &p : expected) {
if (p.first == osmium::item_type::way) {
REQUIRE(it != end);
REQUIRE(it->type() == p.first);
REQUIRE(it->id() == p.second);
++it;
}
}
}
memberbuf.clear();
// retrieve all members
REQUIRE(mid_q->rel_members_get(rel, &memberbuf,
osmium::osm_entity_bits::node |
osmium::osm_entity_bits::way) == 4);
{
auto const objects = memberbuf.select<osmium::OSMObject>();
auto it = objects.cbegin();
auto const end = objects.cend();
for (auto const &p : expected) {
REQUIRE(it != end);
REQUIRE(it->type() == p.first);
REQUIRE(it->id() == p.second);
++it;
}
}
// other relations are not retrievable
REQUIRE_FALSE(mid_q->relation_get(999, &outbuf));
}
if (options.middle_dbschema != "public") {
REQUIRE(num_tables == conn.get_count("pg_catalog.pg_tables",
"schemaname = 'public'"));
REQUIRE(num_indexes == conn.get_count("pg_catalog.pg_indexes",
"schemaname = 'public'"));
REQUIRE(num_procs ==
conn.get_count(
"pg_catalog.pg_proc",
"pronamespace = (SELECT oid FROM "
"pg_catalog.pg_namespace WHERE nspname = 'public')"));
}
}
namespace {
/**
* Check that the node is in the mid with the right id and location.
*/
void check_node(std::shared_ptr<middle_pgsql_t> const &mid,
osmium::Node const &node)
{
test_buffer_t buffer;
auto &nodes = buffer.add_way(999, {node.id()}).nodes();
auto const mid_q = mid->get_query_instance();
REQUIRE(mid_q->nodes_get_list(&nodes) == 1);
REQUIRE(nodes[0].ref() == node.id());
REQUIRE(nodes[0].location() == node.location());
}
/// Return true if the node with the specified id is not in the mid.
bool no_node(std::shared_ptr<middle_pgsql_t> const &mid, osmid_t id)
{
test_buffer_t buffer;
auto &nodes = buffer.add_way(999, {id}).nodes();
auto const mid_q = mid->get_query_instance();
return mid_q->nodes_get_list(&nodes) == 0;
}
} // anonymous namespace
TEMPLATE_TEST_CASE("middle: add, delete and update node", "",
options_slim_default, options_flat_node_cache)
{
auto thread_pool = std::make_shared<thread_pool_t>(1U);
options_t options = TestType::options(db);
testing::cleanup::file_t const flatnode_cleaner{options.flat_node_file};
// Prepare a buffer with some nodes which we will add and change.
test_buffer_t buffer;
auto const &node10 = buffer.add_node("n10 x1.0 y0.0");
auto const &node11 = buffer.add_node("n11 x1.1 y0.0");
auto const &node12 = buffer.add_node("n12 x1.2 y0.0");
auto const &node10a = buffer.add_node("n10 x1.0 y1.0");
auto const &node5d = buffer.add_node("n5 dD");
auto const &node10d = buffer.add_node("n10 dD");
auto const &node12d = buffer.add_node("n12 dD");
auto const &node42d = buffer.add_node("n42 dD");
// Set up middle in "create" mode to get a cleanly initialized database
// and add some nodes. Does this in its own scope so that the mid is
// closed properly.
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
mid->node(node10);
mid->node(node11);
mid->after_nodes();
mid->after_ways();
mid->after_relations();
check_node(mid, node10);
check_node(mid, node11);
}
// From now on use append mode to not destroy the data we just added.
options.append = true;
SECTION("Added nodes are there and no others")
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
check_node(mid, node10);
check_node(mid, node11);
REQUIRE(no_node(mid, 5));
REQUIRE(no_node(mid, 42));
}
SECTION("Delete existing and non-existing node")
{
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
mid->node(node5d);
mid->node(node10d);
mid->node(node42d);
mid->after_nodes();
mid->after_ways();
mid->after_relations();
REQUIRE(no_node(mid, 5));
REQUIRE(no_node(mid, 10));
check_node(mid, node11);
REQUIRE(no_node(mid, 42));
}
{
// Check with a new mid.
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
REQUIRE(no_node(mid, 5));
REQUIRE(no_node(mid, 10));
check_node(mid, node11);
REQUIRE(no_node(mid, 42));
}
}
SECTION("Change (delete and set) existing and non-existing node")
{
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
mid->node(node10d);
mid->node(node10a);
mid->node(node12d);
mid->node(node12);
mid->after_nodes();
mid->after_ways();
mid->after_relations();
check_node(mid, node10a);
check_node(mid, node11);
check_node(mid, node12);
}
{
// Check with a new mid.
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
check_node(mid, node10a);
check_node(mid, node11);
check_node(mid, node12);
}
}
SECTION("Add new node")
{
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
mid->node(node12);
mid->after_nodes();
mid->after_ways();
mid->after_relations();
REQUIRE(no_node(mid, 5));
check_node(mid, node10);
check_node(mid, node11);
check_node(mid, node12);
REQUIRE(no_node(mid, 42));
}
{
// Check with a new mid.
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
REQUIRE(no_node(mid, 5));
check_node(mid, node10);
check_node(mid, node11);
check_node(mid, node12);
REQUIRE(no_node(mid, 42));
}
}
}
namespace {
/**
* Check that the way is in the mid with the right attributes and tags.
* Does not check node locations.
*/
void check_way(std::shared_ptr<middle_pgsql_t> const &mid,
osmium::Way const &orig_way)
{
auto const mid_q = mid->get_query_instance();
osmium::memory::Buffer outbuf{4096, osmium::memory::Buffer::auto_grow::yes};
REQUIRE(mid_q->way_get(orig_way.id(), &outbuf));
auto const &way = outbuf.get<osmium::Way>(0);
CHECK(orig_way.id() == way.id());
CHECK(orig_way.timestamp() == way.timestamp());
CHECK(orig_way.version() == way.version());
CHECK(orig_way.changeset() == way.changeset());
CHECK(orig_way.uid() == way.uid());
CHECK(std::strcmp(orig_way.user(), way.user()) == 0);
REQUIRE(orig_way.tags().size() == way.tags().size());
for (auto it1 = orig_way.tags().begin(), it2 = way.tags().begin();
it1 != orig_way.tags().end(); ++it1, ++it2) {
CHECK(*it1 == *it2);
}
REQUIRE(orig_way.nodes().size() == way.nodes().size());
for (auto it1 = orig_way.nodes().begin(), it2 = way.nodes().begin();
it1 != orig_way.nodes().end(); ++it1, ++it2) {
CHECK(*it1 == *it2);
}
osmium::CRC<osmium::CRC_zlib> orig_crc;
orig_crc.update(orig_way);
osmium::CRC<osmium::CRC_zlib> test_crc;
test_crc.update(way);
REQUIRE(orig_crc().checksum() == test_crc().checksum());
}
/**
* Check that the nodes (ids and locations) of the way with the way_id in the
* mid are identical to the nodes in the nodes vector.
*/
void check_way_nodes(std::shared_ptr<middle_pgsql_t> const &mid, osmid_t way_id,
std::vector<osmium::Node const *> const &nodes)
{
auto const mid_q = mid->get_query_instance();
osmium::memory::Buffer outbuf{4096, osmium::memory::Buffer::auto_grow::yes};
REQUIRE(mid_q->way_get(way_id, &outbuf));
auto &way = outbuf.get<osmium::Way>(0);
REQUIRE(mid_q->nodes_get_list(&way.nodes()) == way.nodes().size());
REQUIRE(way.nodes().size() == nodes.size());
REQUIRE(std::equal(way.nodes().cbegin(), way.nodes().cend(), nodes.cbegin(),
[](osmium::NodeRef const &nr, osmium::Node const *node) {
return nr.ref() == node->id() &&
nr.location() == node->location();
}));
}
/// Return true if the way with the specified id is not in the mid.
bool no_way(std::shared_ptr<middle_pgsql_t> const &mid, osmid_t id)
{
auto const mid_q = mid->get_query_instance();
osmium::memory::Buffer outbuf{4096, osmium::memory::Buffer::auto_grow::yes};
return !mid_q->way_get(id, &outbuf);
}
} // anonymous namespace
TEMPLATE_TEST_CASE("middle: add, delete and update way", "",
options_slim_default, options_flat_node_cache)
{
auto thread_pool = std::make_shared<thread_pool_t>(1U);
options_t options = TestType::options(db);
testing::cleanup::file_t const flatnode_cleaner{options.flat_node_file};
// Create some ways we'll use for the tests.
test_buffer_t buffer;
auto const &way20 =
buffer.add_way("w20 Nn10,n11 Thighway=residential,name=High_Street");
auto const &way21 = buffer.add_way("w21 Nn11,n12");
auto const &way22 = buffer.add_way("w22 Nn12,n10 Tpower=line");
auto &way20a =
buffer.add_way("w20 Nn10,n12 Thighway=primary,name=High_Street");
auto const &way5d = buffer.add_way("w5 dD");
auto &way20d = buffer.add_way("w20 dD");
auto &way22d = buffer.add_way("w22 dD");
auto &way42d = buffer.add_way("w42 dD");
// Set up middle in "create" mode to get a cleanly initialized database and
// add some ways. Does this in its own scope so that the mid is closed
// properly.
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
mid->after_nodes();
mid->way(way20);
mid->way(way21);
mid->after_ways();
mid->after_relations();
check_way(mid, way20);
check_way(mid, way21);
}
// From now on use append mode to not destroy the data we just added.
options.append = true;
SECTION("Added ways are there and no others")
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
REQUIRE(no_way(mid, 5));
check_way(mid, way20);
check_way(mid, way21);
REQUIRE(no_way(mid, 22));
}
SECTION("Delete existing and non-existing way")
{
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
mid->after_nodes();
mid->way(way5d);
mid->way(way20d);
mid->way(way42d);
mid->after_ways();
mid->after_relations();
REQUIRE(no_way(mid, 5));
REQUIRE(no_way(mid, 20));
check_way(mid, way21);
REQUIRE(no_way(mid, 42));
}
{
// Check with a new mid.
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
REQUIRE(no_way(mid, 5));
REQUIRE(no_way(mid, 20));
check_way(mid, way21);
REQUIRE(no_way(mid, 42));
}
}
SECTION("Change (delete and set) existing and non-existing way")
{
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
mid->after_nodes();
mid->way(way20d);
mid->way(way20a);
mid->way(way22d);
mid->way(way22);
mid->after_ways();
mid->after_relations();
REQUIRE(no_way(mid, 5));
check_way(mid, way20a);
check_way(mid, way21);
check_way(mid, way22);
REQUIRE(no_way(mid, 42));
}
{
// Check with a new mid.
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
REQUIRE(no_way(mid, 5));
check_way(mid, way20a);
check_way(mid, way21);
check_way(mid, way22);
REQUIRE(no_way(mid, 42));
}
}
SECTION("Add new way")
{
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
mid->after_nodes();
mid->way(way22);
mid->after_ways();
mid->after_relations();
REQUIRE(no_way(mid, 5));
check_way(mid, way20);
check_way(mid, way21);
check_way(mid, way22);
REQUIRE(no_way(mid, 42));
}
{
// Check with a new mid.
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
REQUIRE(no_way(mid, 5));
check_way(mid, way20);
check_way(mid, way21);
check_way(mid, way22);
REQUIRE(no_way(mid, 42));
}
}
}
TEMPLATE_TEST_CASE("middle: add way with attributes", "", options_slim_default,
options_flat_node_cache)
{
auto thread_pool = std::make_shared<thread_pool_t>(1U);
options_t options = TestType::options(db);
SECTION("With attributes") { options.extra_attributes = true; }
SECTION("No attributes") { options.extra_attributes = false; }
testing::cleanup::file_t const flatnode_cleaner{options.flat_node_file};
// Create some ways we'll use for the tests.
test_buffer_t buffer;
auto &way20 =
buffer.add_way("w20 Nn10,n11 Thighway=residential,name=High_Street");
way20.set_version(123);
way20.set_timestamp(1234567890);
way20.set_changeset(456);
way20.set_uid(789);
// The same way but with default attributes.
auto &way20_no_attr =
buffer.add_way("w20 Nn10,n11 Thighway=residential,name=High_Street");
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
mid->after_nodes();
mid->way(way20);
mid->after_ways();
mid->after_relations();
check_way(mid, options.extra_attributes ? way20 : way20_no_attr);
}
// From now on use append mode to not destroy the data we just added.
options.append = true;
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
check_way(mid, options.extra_attributes ? way20 : way20_no_attr);
}
}
namespace {
/**
* Check that the relation is in the mid with the right attributes, members
* and tags. Only checks the relation, does not recurse into members.
*/
void check_relation(std::shared_ptr<middle_pgsql_t> const &mid,
osmium::Relation const &orig_relation)
{
auto const mid_q = mid->get_query_instance();
osmium::memory::Buffer outbuf{4096, osmium::memory::Buffer::auto_grow::yes};
REQUIRE(mid_q->relation_get(orig_relation.id(), &outbuf));
auto const &relation = outbuf.get<osmium::Relation>(0);
CHECK(orig_relation.id() == relation.id());
CHECK(orig_relation.timestamp() == relation.timestamp());
CHECK(orig_relation.version() == relation.version());
CHECK(orig_relation.changeset() == relation.changeset());
CHECK(orig_relation.uid() == relation.uid());
CHECK(std::strcmp(orig_relation.user(), relation.user()) == 0);
REQUIRE(orig_relation.tags().size() == relation.tags().size());
for (auto it1 = orig_relation.tags().begin(), it2 = relation.tags().begin();
it1 != orig_relation.tags().end(); ++it1, ++it2) {
CHECK(*it1 == *it2);
}
REQUIRE(orig_relation.members().size() == relation.members().size());
for (auto it1 = orig_relation.members().begin(),
it2 = relation.members().begin();
it1 != orig_relation.members().end(); ++it1, ++it2) {
CHECK(it1->type() == it2->type());
CHECK(it1->ref() == it2->ref());
CHECK(std::strcmp(it1->role(), it2->role()) == 0);
}
osmium::CRC<osmium::CRC_zlib> orig_crc;
orig_crc.update(orig_relation);
osmium::CRC<osmium::CRC_zlib> test_crc;
test_crc.update(relation);
REQUIRE(orig_crc().checksum() == test_crc().checksum());
}
/// Return true if the relation with the specified id is not in the mid.
bool no_relation(std::shared_ptr<middle_pgsql_t> const &mid, osmid_t id)
{
auto const mid_q = mid->get_query_instance();
osmium::memory::Buffer outbuf{4096, osmium::memory::Buffer::auto_grow::yes};
return !mid_q->relation_get(id, &outbuf);
}
} // anonymous namespace
TEMPLATE_TEST_CASE("middle: add, delete and update relation", "",
options_slim_default, options_flat_node_cache)
{
auto thread_pool = std::make_shared<thread_pool_t>(1U);
options_t options = TestType::options(db);
testing::cleanup::file_t const flatnode_cleaner{options.flat_node_file};
// Create some relations we'll use for the tests.
test_buffer_t buffer;
auto const &relation30 = buffer.add_relation(
"r30 Mw10@outer,w11@innder Tname=Penguin_Park,type=multipolygon");
auto const &relation31 = buffer.add_relation("r31 Mn10@");
auto const &relation32 = buffer.add_relation("r32 Mr39@ Ttype=site");
auto const &relation30a = buffer.add_relation(
"r30 Mw10@outer,w11@outer Tname=Pigeon_Park,type=multipolygon");
auto const &relation5d = buffer.add_relation("r5 dD");
auto const &relation30d = buffer.add_relation("r30 dD");
auto const &relation32d = buffer.add_relation("r32 dD");
auto const &relation42d = buffer.add_relation("r42 dD");
// Set up middle in "create" mode to get a cleanly initialized database and
// add some relations. Does this in its own scope so that the mid is closed
// properly.
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
mid->after_nodes();
mid->after_ways();
mid->relation(relation30);
mid->relation(relation31);
mid->after_relations();
check_relation(mid, relation30);
check_relation(mid, relation31);
}
// From now on use append mode to not destroy the data we just added.
options.append = true;
SECTION("Added relations are there and no others")
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
REQUIRE(no_relation(mid, 5));
check_relation(mid, relation30);
check_relation(mid, relation31);
REQUIRE(no_relation(mid, 32));
}
SECTION("Delete existing and non-existing relation")
{
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
mid->after_nodes();
mid->after_ways();
mid->relation(relation5d);
mid->relation(relation30d);
mid->relation(relation42d);
mid->after_relations();
REQUIRE(no_relation(mid, 5));
REQUIRE(no_relation(mid, 30));
check_relation(mid, relation31);
REQUIRE(no_relation(mid, 42));
}
{
// Check with a new mid.
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
REQUIRE(no_relation(mid, 5));
REQUIRE(no_relation(mid, 30));
check_relation(mid, relation31);
REQUIRE(no_relation(mid, 42));
}
}
SECTION("Change (delete and set) existing and non-existing relation")
{
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
mid->after_nodes();
mid->after_ways();
mid->relation(relation30d);
mid->relation(relation30a);
mid->relation(relation32d);
mid->relation(relation32);
mid->after_relations();
REQUIRE(no_relation(mid, 5));
check_relation(mid, relation30a);
check_relation(mid, relation31);
check_relation(mid, relation32);
REQUIRE(no_relation(mid, 42));
}
{
// Check with a new mid.
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
REQUIRE(no_relation(mid, 5));
check_relation(mid, relation30a);
check_relation(mid, relation31);
check_relation(mid, relation32);
REQUIRE(no_relation(mid, 42));
}
}
SECTION("Add new relation")
{
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
mid->after_nodes();
mid->after_ways();
mid->relation(relation32);
mid->after_relations();
REQUIRE(no_relation(mid, 5));
check_relation(mid, relation30);
check_relation(mid, relation31);
check_relation(mid, relation32);
REQUIRE(no_relation(mid, 42));
}
{
// Check with a new mid.
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
REQUIRE(no_relation(mid, 5));
check_relation(mid, relation30);
check_relation(mid, relation31);
check_relation(mid, relation32);
REQUIRE(no_relation(mid, 42));
}
}
}
TEMPLATE_TEST_CASE("middle: add relation with attributes", "",
options_slim_default, options_flat_node_cache)
{
auto thread_pool = std::make_shared<thread_pool_t>(1U);
options_t options = TestType::options(db);
SECTION("With attributes") { options.extra_attributes = true; }
SECTION("No attributes") { options.extra_attributes = false; }
testing::cleanup::file_t const flatnode_cleaner{options.flat_node_file};
// Create some relations we'll use for the tests.
test_buffer_t buffer;
auto const &relation30 = buffer.add_relation(
"r30 v123 c456 i789 t2009-02-13T23:31:30Z Mw10@outer,w11@inner "
"Tname=Penguin_Park,type=multipolygon");
// The same relation but with default attributes.
auto const &relation30_no_attr = buffer.add_relation(
"r30 Mw10@outer,w11@inner Tname=Penguin_Park,type=multipolygon");
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
mid->after_nodes();
mid->after_ways();
mid->relation(relation30);
mid->after_relations();
check_relation(mid, options.extra_attributes ? relation30
: relation30_no_attr);
}
// From now on use append mode to not destroy the data we just added.
options.append = true;
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
check_relation(mid, options.extra_attributes ? relation30
: relation30_no_attr);
}
}
TEMPLATE_TEST_CASE("middle: change nodes in way", "", options_slim_default,
options_flat_node_cache)
{
auto thread_pool = std::make_shared<thread_pool_t>(1U);
options_t options = TestType::options(db);
testing::cleanup::file_t const flatnode_cleaner{options.flat_node_file};
// create some nodes and ways we'll use for the tests
test_buffer_t buffer;
auto const &node10 = buffer.add_node("n10 x1.0 y0.0");
auto const &node11 = buffer.add_node("n11 x1.1 y0.0");
auto const &node12 = buffer.add_node("n12 x1.2 y0.0");
auto const &node10a = buffer.add_node("n10 x2.0 y0.0");
auto const &node10d = buffer.add_node("n10 dD");
auto &way20 = buffer.add_way("w20 Nn10,n11");
auto &way21 = buffer.add_way("w21 Nn11,n12");
auto &way22 = buffer.add_way("w22 Nn12,n10");
auto &way20a = buffer.add_way("w20 Nn11,n12");
auto &way20d = buffer.add_way("w20 dD");
// Set up middle in "create" mode to get a cleanly initialized database and
// add some nodes and ways. Does this in its own scope so that the mid is
// closed properly.
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
auto output = std::make_shared<output_null_t>(mid->get_query_instance(),
thread_pool, options);
osmdata_t osmdata{mid, output, options};
osmdata.node(node10);
osmdata.node(node11);
osmdata.node(node12);
osmdata.after_nodes();
osmdata.way(way20);
osmdata.way(way21);
osmdata.after_ways();
osmdata.after_relations();
check_node(mid, node10);
check_node(mid, node11);
check_node(mid, node12);
check_way(mid, way20);
check_way_nodes(mid, way20.id(), {&node10, &node11});
check_way(mid, way21);
check_way_nodes(mid, way21.id(), {&node11, &node12});
REQUIRE(osmdata.get_pending_way_ids().empty());
REQUIRE(osmdata.get_pending_relation_ids().empty());
osmdata.stop();
}
// From now on use append mode to not destroy the data we just added.
options.append = true;
SECTION("Single way affected")
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
auto output = std::make_shared<output_null_t>(mid->get_query_instance(),
thread_pool, options);
osmdata_t osmdata{mid, output, options};
osmdata.node(node10d);
osmdata.node(node10a);
osmdata.after_nodes();
osmdata.after_ways();
osmdata.after_relations();
idlist_t const &way_ids = osmdata.get_pending_way_ids();
REQUIRE(way_ids == idlist_t{20});
REQUIRE(osmdata.get_pending_relation_ids().empty());
check_way(mid, way20);
check_way_nodes(mid, way20.id(), {&node10a, &node11});
}
SECTION("Two ways affected")
{
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
mid->after_nodes();
mid->way(way22);
mid->after_ways();
mid->after_relations();
check_way(mid, way22);
}
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
auto output = std::make_shared<output_null_t>(
mid->get_query_instance(), thread_pool, options);
osmdata_t osmdata{mid, output, options};
osmdata.node(node10d);
osmdata.node(node10a);
osmdata.after_nodes();
osmdata.after_ways();
osmdata.after_relations();
idlist_t const &way_ids = osmdata.get_pending_way_ids();
REQUIRE(way_ids == idlist_t{20, 22});
REQUIRE(osmdata.get_pending_relation_ids().empty());
check_way(mid, way20);
check_way_nodes(mid, way20.id(), {&node10a, &node11});
check_way(mid, way22);
check_way_nodes(mid, way22.id(), {&node12, &node10a});
}
}
SECTION("Change way so the changing node isn't in it any more")
{
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
auto output = std::make_shared<output_null_t>(
mid->get_query_instance(), thread_pool, options);
osmdata_t osmdata{mid, output, options};
osmdata.after_nodes();
osmdata.way(way20d);
osmdata.way(way20a);
osmdata.after_ways();
osmdata.after_relations();
check_way(mid, way20a);
check_way_nodes(mid, way20.id(), {&node11, &node12});
}
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
auto output = std::make_shared<output_null_t>(
mid->get_query_instance(), thread_pool, options);
osmdata_t osmdata{mid, output, options};
osmdata.node(node10d);
osmdata.node(node10a);
osmdata.after_nodes();
osmdata.after_ways();
osmdata.after_relations();
REQUIRE(osmdata.get_pending_way_ids().empty());
REQUIRE(osmdata.get_pending_relation_ids().empty());
}
}
}
TEMPLATE_TEST_CASE("middle: change nodes in relation", "", options_slim_default,
options_flat_node_cache)
{
auto thread_pool = std::make_shared<thread_pool_t>(1U);
options_t options = TestType::options(db);
testing::cleanup::file_t const flatnode_cleaner{options.flat_node_file};
// create some nodes, ways, and relations we'll use for the tests
test_buffer_t buffer;
auto const &node10 = buffer.add_node("n10 x1.0 y0.0");
auto const &node11 = buffer.add_node("n11 x1.1 y0.0");
auto const &node12 = buffer.add_node("n12 x1.2 y0.0");
auto const &node10a = buffer.add_node("n10 x1.0 y1.0");
auto const &node11a = buffer.add_node("n11 x1.1 y1.0");
auto const &node10d = buffer.add_node("n10 dD");
auto const &node11d = buffer.add_node("n11 dD");
auto const &way20 = buffer.add_way("w20 Nn11,n12");
auto const &rel30 = buffer.add_relation("r30 Mn10@");
auto const &rel31 = buffer.add_relation("r31 Mw20@");
// Set up middle in "create" mode to get a cleanly initialized database and
// add some nodes and ways. Does this in its own scope so that the mid is
// closed properly.
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
mid->node(node10);
mid->node(node11);
mid->node(node12);
mid->after_nodes();
mid->way(way20);
mid->after_ways();
mid->relation(rel30);
mid->relation(rel31);
mid->after_relations();
mid->stop();
mid->wait();
}
// From now on use append mode to not destroy the data we just added.
options.append = true;
SECTION("Single relation directly affected")
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
auto output = std::make_shared<output_null_t>(mid->get_query_instance(),
thread_pool, options);
osmdata_t osmdata{mid, output, options};
osmdata.node(node10d);
osmdata.node(node10a);
osmdata.after_nodes();
osmdata.after_ways();
osmdata.after_relations();
REQUIRE(osmdata.get_pending_way_ids().empty());
idlist_t const &rel_ids = osmdata.get_pending_relation_ids();
REQUIRE(rel_ids == idlist_t{30});
check_relation(mid, rel30);
}
SECTION("Single relation indirectly affected (through way)")
{
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
mid->start();
auto output = std::make_shared<output_null_t>(mid->get_query_instance(),
thread_pool, options);
osmdata_t osmdata{mid, output, options};
osmdata.node(node11d);
osmdata.node(node11a);
osmdata.after_nodes();
osmdata.after_ways();
osmdata.after_relations();
idlist_t const &way_ids = osmdata.get_pending_way_ids();
REQUIRE(way_ids == idlist_t{20});
idlist_t const &rel_ids = osmdata.get_pending_relation_ids();
REQUIRE(rel_ids == idlist_t{31});
check_relation(mid, rel31);
}
}