Skip to content

Commit 4bfe6ec

Browse files
authored
Merge pull request #2460 from joto/id-cache
Introduce new "id cache" for node tables
2 parents 5464539 + 0577e45 commit 4bfe6ec

11 files changed

Lines changed: 648 additions & 0 deletions

flex-config/turning-circles.lua

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
-- This config example file is released into the Public Domain.
2+
3+
-- Create a table with turning circles that can be styled in sync with the
4+
-- highway they are on.
5+
6+
local turning_circles = osm2pgsql.define_table({
7+
name = 'turning_circles',
8+
ids = { type = 'node', id_column = 'node_id', cache = true },
9+
columns = {
10+
{ column = 'geom', type = 'point', not_null = true },
11+
}
12+
})
13+
14+
local highways = osm2pgsql.define_table({
15+
name = 'highways',
16+
ids = { type = 'way', id_column = 'way_id' },
17+
columns = {
18+
{ column = 'htype', type = 'text', not_null = true },
19+
{ column = 'geom', type = 'linestring', not_null = true },
20+
}
21+
})
22+
23+
-- This table will contain entries for all node/way combinations where the way
24+
-- is tagged as "highway" and the node is tagged as "highway=turning_circle".
25+
-- The "htype" column contains the highway type, the "geom" the geometry of
26+
-- the node. This can be used, for instance, to draw the point in a style that
27+
-- fits with the style of the highway.
28+
--
29+
-- Note that you might have multiple entries for the same node in this table
30+
-- if it is in several ways. In that case you might have to decide at rendering
31+
-- time which of them to render.
32+
local highway_ends = osm2pgsql.define_table({
33+
name = 'highway_ends',
34+
ids = { type = 'way', id_column = 'way_id' },
35+
columns = {
36+
{ column = 'htype', type = 'text', not_null = true },
37+
{ column = 'node_id', type = 'int8', not_null = true },
38+
{ column = 'geom', type = 'point', not_null = true },
39+
}
40+
})
41+
42+
function osm2pgsql.process_node(object)
43+
if object.tags.highway == 'turning_circle' then
44+
-- This insert will add the entry to the id cache later read with
45+
-- in_id_cache().
46+
turning_circles:insert({
47+
geom = object:as_point(),
48+
})
49+
end
50+
end
51+
52+
function osm2pgsql.process_way(object)
53+
local t = object.tags.highway
54+
if t then
55+
highways:insert({
56+
htype = t,
57+
geom = object:as_linestring(),
58+
})
59+
local c = turning_circles:in_id_cache(object.nodes)
60+
for _, n in ipairs(c) do
61+
highway_ends:insert({
62+
htype = t,
63+
node_id = object.nodes[n],
64+
geom = object:as_point(n),
65+
})
66+
end
67+
end
68+
end
69+

src/debug-output.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ void write_table_list_to_debug_log(std::vector<flex_table_t> const &tables)
6161
log_debug(" - data_tablespace={}", table.data_tablespace());
6262
log_debug(" - index_tablespace={}", table.index_tablespace());
6363
log_debug(" - cluster={}", table.cluster_by_geom());
64+
log_debug(" - id_cache={}", table.with_id_cache());
6465
for (auto const &index : table.indexes()) {
6566
log_debug(" - INDEX USING {}", index.method());
6667
if (index.name().empty()) {

src/flex-lua-table.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,17 @@ void setup_flex_table_id_columns(lua_State *lua_state, flex_table_t *table)
174174
throw fmt_error("Unknown ids type: {}.", type);
175175
}
176176

177+
bool const cache =
178+
luaX_get_table_bool(lua_state, "cache", -1, "The ids", false);
179+
lua_pop(lua_state, 1); // "cache"
180+
if (cache) {
181+
if (type == "node") {
182+
table->enable_id_cache();
183+
} else {
184+
throw std::runtime_error{"ID cache only available for node ids."};
185+
}
186+
}
187+
177188
std::string const name =
178189
luaX_get_table_string(lua_state, "id_column", -1, "The ids field");
179190
lua_pop(lua_state, 1); // "id_column"
@@ -459,6 +470,7 @@ void lua_wrapper_table_t::init(lua_State *lua_state)
459470
luaX_set_up_metatable(lua_state, "Table", OSM2PGSQL_TABLE_CLASS,
460471
{{"__tostring", lua_trampoline_table_tostring},
461472
{"insert", lua_trampoline_table_insert},
473+
{"in_id_cache", lua_trampoline_table_in_id_cache},
462474
{"name", lua_trampoline_table_name},
463475
{"schema", lua_trampoline_table_schema},
464476
{"cluster", lua_trampoline_table_cluster},

src/flex-table.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,10 @@ void flex_table_t::analyze(pg_conn_t const &db_connection) const
263263
analyze_table(db_connection, schema(), name());
264264
}
265265

266+
void flex_table_t::enable_id_cache() noexcept { m_with_id_cache = true; }
267+
268+
bool flex_table_t::with_id_cache() const noexcept { return m_with_id_cache; }
269+
266270
namespace {
267271

268272
void enable_check_trigger(pg_conn_t const &db_connection,

src/flex-table.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ class flex_table_t
215215

216216
void analyze(pg_conn_t const &db_connection) const;
217217

218+
void enable_id_cache() noexcept;
219+
220+
bool with_id_cache() const noexcept;
221+
218222
private:
219223
/// The schema this table is in
220224
std::string m_schema;
@@ -271,6 +275,9 @@ class flex_table_t
271275
/// Index should be a primary key.
272276
bool m_primary_key_index = false;
273277

278+
/// Do we want an ID cache for this table?
279+
bool m_with_id_cache = false;
280+
274281
}; // class flex_table_t
275282

276283
class table_connection_t

src/idlist.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ osmid_t idlist_t::pop_id()
2222
return id;
2323
}
2424

25+
bool idlist_t::contains(osmid_t id) const
26+
{
27+
return std::binary_search(m_list.begin(), m_list.end(), id);
28+
}
29+
2530
void idlist_t::sort_unique()
2631
{
2732
std::sort(m_list.begin(), m_list.end());

src/idlist.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ class idlist_t
6262

6363
void reserve(std::size_t size) { m_list.reserve(size); }
6464

65+
/**
66+
* Is the specified id in the list?
67+
*
68+
* You must have called sort_unique() before calling this.
69+
*/
70+
bool contains(osmid_t id) const;
71+
6572
/**
6673
* Remove id at the end of the list and return it.
6774
*

src/output-flex.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ TRAMPOLINE(app_as_geometrycollection, as_geometrycollection)
8989
} // anonymous namespace
9090

9191
TRAMPOLINE(table_insert, insert)
92+
TRAMPOLINE(table_in_id_cache, in_id_cache)
9293

9394
prepared_lua_function_t::prepared_lua_function_t(lua_State *lua_state,
9495
calling_context context,
@@ -270,6 +271,9 @@ void flush_tables(std::vector<table_connection_t> &table_connections)
270271
for (auto &table : table_connections) {
271272
table.flush();
272273
}
274+
for (auto &table : table_connections) {
275+
table.sync();
276+
}
273277
}
274278

275279
void create_expire_tables(std::vector<expire_output_t> const &expire_outputs,
@@ -789,6 +793,10 @@ int output_flex_t::table_insert()
789793
auto const &object = check_and_get_context_object(table);
790794
osmid_t const id = table.map_id(object.type(), object.id());
791795

796+
if (table.with_id_cache()) {
797+
get_id_cache(table).push_back(id);
798+
}
799+
792800
table_connection.new_line();
793801
auto *copy_mgr = table_connection.copy_mgr();
794802

@@ -823,6 +831,46 @@ int output_flex_t::table_insert()
823831
return 1;
824832
}
825833

834+
int output_flex_t::table_in_id_cache()
835+
{
836+
if (m_calling_context == calling_context::process_node) {
837+
throw std::runtime_error{
838+
"Id cache not available while processing nodes."};
839+
}
840+
841+
// The first parameter is the table object.
842+
auto const &table = get_table_from_param();
843+
844+
if (!table.with_id_cache()) {
845+
throw fmt_error("No ID cache on table '{}'.", table.name());
846+
}
847+
848+
// The second parameter is an array of ids
849+
if (!lua_istable(lua_state(), 1) || !luaX_is_array(lua_state())) {
850+
throw std::runtime_error{"Second parameter must be an array of ids."};
851+
}
852+
853+
std::vector<osmid_t> ids;
854+
luaX_for_each(lua_state(),
855+
[&]() { ids.push_back(lua_tointeger(lua_state(), -1)); });
856+
857+
auto const &cache = get_id_cache(table);
858+
lua_createtable(lua_state(), 0, 0);
859+
860+
lua_Integer n = 0;
861+
lua_Integer idx = 1;
862+
for (auto const id : ids) {
863+
if (cache.contains(id)) {
864+
lua_pushinteger(lua_state(), ++n);
865+
lua_pushinteger(lua_state(), idx);
866+
lua_rawset(lua_state(), -3);
867+
}
868+
++idx;
869+
}
870+
871+
return 1;
872+
}
873+
826874
void output_flex_t::call_lua_function(prepared_lua_function_t func)
827875
{
828876
lua_pushvalue(lua_state(), func.index());
@@ -980,6 +1028,28 @@ void output_flex_t::after_nodes()
9801028
}
9811029

9821030
flush_tables(m_table_connections);
1031+
1032+
for (auto &table : *m_tables) {
1033+
if (table.with_id_cache()) {
1034+
auto &cache = get_id_cache(table);
1035+
if (get_options()->append) {
1036+
log_debug("Initializing cache for table '{}' from database...",
1037+
table.name());
1038+
auto const result = m_db_connection.exec(
1039+
"SELECT \"{}\" FROM {}", table.id_column_names(),
1040+
table.full_name());
1041+
1042+
cache.reserve(result.num_tuples());
1043+
for (int i = 0; i < result.num_tuples(); ++i) {
1044+
cache.push_back(
1045+
osmium::string_to_object_id(result.get_value(i, 0)));
1046+
}
1047+
}
1048+
cache.sort_unique();
1049+
log_debug("Cache for table '{}' initialized with {} entries.",
1050+
table.name(), cache.size());
1051+
}
1052+
}
9831053
}
9841054

9851055
void output_flex_t::after_ways()
@@ -1199,6 +1269,10 @@ void output_flex_t::relation_modify(osmium::Relation const &rel)
11991269
void output_flex_t::start()
12001270
{
12011271
for (auto &table : m_table_connections) {
1272+
if (table.table().with_id_cache()) {
1273+
log_debug("Enable cache for table '{}'.", table.table().name());
1274+
create_id_cache(table.table());
1275+
}
12021276
table.start(m_db_connection, get_options()->append);
12031277
}
12041278

@@ -1212,6 +1286,7 @@ output_flex_t::output_flex_t(output_flex_t const *other,
12121286
std::shared_ptr<db_copy_thread_t> copy_thread)
12131287
: output_t(other, std::move(mid)), m_locators(other->m_locators),
12141288
m_tables(other->m_tables), m_expire_outputs(other->m_expire_outputs),
1289+
m_id_caches(other->m_id_caches),
12151290
m_db_connection(get_options()->connection_params, "out.flex.thread"),
12161291
m_stage2_way_ids(other->m_stage2_way_ids),
12171292
m_copy_thread(std::move(copy_thread)), m_lua_state(other->m_lua_state),

src/output-flex.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ class output_flex_t : public output_t
165165
int app_get_bbox();
166166

167167
int table_insert();
168+
int table_in_id_cache();
168169

169170
// Get the flex table that is as first parameter on the Lua stack.
170171
flex_table_t &get_table_from_param();
@@ -216,6 +217,21 @@ class output_flex_t : public output_t
216217

217218
lua_State *lua_state() noexcept { return m_lua_state.get(); }
218219

220+
void create_id_cache(flex_table_t const &table)
221+
{
222+
if (table.num() >= m_id_caches.size()) {
223+
m_id_caches.resize(table.num() + 1);
224+
}
225+
m_id_caches[table.num()] = std::make_shared<idlist_t>();
226+
}
227+
228+
idlist_t &get_id_cache(flex_table_t const &table)
229+
{
230+
auto& c = m_id_caches[table.num()];
231+
assert(c);
232+
return *c;
233+
}
234+
219235
class way_cache_t
220236
{
221237
public:
@@ -273,6 +289,8 @@ class output_flex_t : public output_t
273289
std::shared_ptr<std::vector<expire_output_t>> m_expire_outputs =
274290
std::make_shared<std::vector<expire_output_t>>();
275291

292+
std::vector<std::shared_ptr<idlist_t>> m_id_caches;
293+
276294
std::vector<table_connection_t> m_table_connections;
277295

278296
/// The connection to the database server.
@@ -325,5 +343,6 @@ class output_flex_t : public output_t
325343
};
326344

327345
int lua_trampoline_table_insert(lua_State *lua_state);
346+
int lua_trampoline_table_in_id_cache(lua_State *lua_state);
328347

329348
#endif // OSM2PGSQL_OUTPUT_FLEX_HPP

0 commit comments

Comments
 (0)