Skip to content

Commit 9491502

Browse files
committed
Consolidate database capability checks
There were several places (pgsql.hpp, pgsql-capabilities.hpp, db-check.hpp) that had code to check various capabilities, version numbers etc. of the database we are connected to. This is now consolidated in pgsql-capabilities.hpp. It needs to be initialized once at program start by calling init_database_capabilities() with a database connection object. Checking version numbers or capabilities is then much easier, because no active database connection is needed any more. This also adds code to get the list of supported index methods from the database which we will need shortly. (Only works with PostgreSQL >9.5.)
1 parent a0ae47c commit 9491502

16 files changed

+237
-212
lines changed

src/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
add_library(osm2pgsql_lib STATIC)
33

44
target_sources(osm2pgsql_lib PRIVATE
5-
db-check.cpp
65
db-copy.cpp
76
dependency-manager.cpp
87
expire-tiles.cpp

src/db-check.cpp

Lines changed: 0 additions & 79 deletions
This file was deleted.

src/db-check.hpp

Lines changed: 0 additions & 21 deletions
This file was deleted.

src/flex-table.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ void table_connection_t::start(bool append)
258258
{
259259
assert(m_db_connection);
260260

261-
if (!has_schema(*m_db_connection, table().schema())) {
261+
if (!has_schema(table().schema())) {
262262
throw std::runtime_error{
263263
"Schema '{0}' not available. "
264264
"Use 'CREATE SCHEMA \"{0}\";' to create it."_format(
@@ -267,7 +267,7 @@ void table_connection_t::start(bool append)
267267

268268
for (auto const &ts :
269269
{table().data_tablespace(), table().index_tablespace()}) {
270-
if (!has_tablespace(*m_db_connection, ts)) {
270+
if (!has_tablespace(ts)) {
271271
throw std::runtime_error{
272272
"Tablespace '{0}' not available. "
273273
"Use 'CREATE TABLESPACE \"{0}\" ...;' to create it."_format(
@@ -276,7 +276,7 @@ void table_connection_t::start(bool append)
276276
}
277277

278278
if (table().has_hstore_column()) {
279-
if (!has_extension(*m_db_connection, "hstore")) {
279+
if (!has_extension("hstore")) {
280280
throw std::runtime_error{"Extension 'hstore' not available. Use "
281281
"'CREATE EXTENSION hstore;' to load it."};
282282
}
@@ -336,7 +336,7 @@ void table_connection_t::stop(bool updateable, bool append)
336336
std::string sql = "INSERT INTO {} ({}) SELECT {} FROM {}"_format(
337337
table().full_tmp_name(), columns, columns, table().full_name());
338338

339-
auto const postgis_version = get_postgis_version(*m_db_connection);
339+
auto const postgis_version = get_postgis_version();
340340

341341
sql += " ORDER BY ";
342342
if (postgis_version.major == 2 && postgis_version.minor < 4) {

src/osm2pgsql.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@
77
* For a full list of authors see the git log.
88
*/
99

10-
#include "db-check.hpp"
1110
#include "dependency-manager.hpp"
1211
#include "input.hpp"
1312
#include "logging.hpp"
1413
#include "middle.hpp"
1514
#include "options.hpp"
1615
#include "osmdata.hpp"
1716
#include "output.hpp"
17+
#include "pgsql.hpp"
18+
#include "pgsql-capabilities.hpp"
19+
#include "pgsql-helper.hpp"
1820
#include "util.hpp"
1921
#include "version.hpp"
2022

@@ -76,6 +78,26 @@ static void run(options_t const &options)
7678
osmdata.stop();
7779
}
7880

81+
void check_db(options_t const &options)
82+
{
83+
pg_conn_t db_connection{options.conninfo};
84+
85+
init_database_capabilities(db_connection);
86+
87+
// If we are in append mode and the middle nodes table isn't there,
88+
// it probably means we used a flat node store when we created this
89+
// database. Check for that and stop if it looks like we are missing
90+
// the node location store option.
91+
if (options.append && options.flat_node_file.empty()) {
92+
if (!has_table(db_connection, options.middle_dbschema,
93+
options.prefix + "_nodes")) {
94+
throw std::runtime_error{
95+
"You seem to not have a nodes table. Did "
96+
"you forget the --flat-nodes option?"};
97+
}
98+
}
99+
}
100+
79101
int main(int argc, char *argv[])
80102
{
81103
try {

src/pgsql-capabilities.cpp

Lines changed: 134 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,59 +8,169 @@
88
*/
99

1010
#include "format.hpp"
11+
#include "logging.hpp"
12+
#include "pgsql-capabilities.hpp"
1113
#include "pgsql.hpp"
12-
#include "pgsql-helper.hpp"
14+
#include "version.hpp"
1315

16+
#include <map>
1417
#include <set>
18+
#include <stdexcept>
1519
#include <string>
1620

17-
static std::set<std::string> init_set_from_table(pg_conn_t const &db_connection,
18-
char const *table,
19-
char const *column,
20-
char const *condition)
21+
struct database_capabilities_t
2122
{
22-
std::set<std::string> values;
23+
std::map<std::string, std::string> settings;
2324

25+
std::set<std::string> extensions;
26+
std::set<std::string> schemas;
27+
std::set<std::string> tablespaces;
28+
std::set<std::string> index_methods;
29+
30+
std::string database_name;
31+
32+
uint32_t database_version = 0;
33+
postgis_version postgis{};
34+
};
35+
36+
static database_capabilities_t &capabilities() noexcept
37+
{
38+
static database_capabilities_t c;
39+
return c;
40+
}
41+
42+
static void init_set_from_query(std::set<std::string> *set,
43+
pg_conn_t const &db_connection,
44+
char const *table, char const *column,
45+
char const *condition = "true")
46+
{
2447
auto const res = db_connection.query(
2548
PGRES_TUPLES_OK,
2649
"SELECT {} FROM {} WHERE {}"_format(column, table, condition));
2750
for (int i = 0; i < res.num_tuples(); ++i) {
28-
values.insert(res.get_value_as_string(i, 0));
51+
set->insert(res.get_value_as_string(i, 0));
2952
}
53+
}
54+
55+
/// Get all config settings from the database.
56+
static void init_settings(pg_conn_t const &db_connection)
57+
{
58+
auto const res = db_connection.query(
59+
PGRES_TUPLES_OK, "SELECT name, setting FROM pg_settings");
3060

31-
return values;
61+
for (int i = 0; i < res.num_tuples(); ++i) {
62+
capabilities().settings.emplace(res.get_value_as_string(i, 0),
63+
res.get_value_as_string(i, 1));
64+
}
3265
}
3366

34-
bool has_extension(pg_conn_t const &db_connection, std::string const &value)
67+
static void init_database_name(pg_conn_t const &db_connection)
3568
{
36-
static const std::set<std::string> values = init_set_from_table(
37-
db_connection, "pg_catalog.pg_extension", "extname", "true");
69+
auto const res =
70+
db_connection.query(PGRES_TUPLES_OK, "SELECT current_catalog");
71+
72+
if (res.num_tuples() != 1) {
73+
throw std::runtime_error{
74+
"Database error: Can not access database name."};
75+
}
3876

39-
return values.count(value);
77+
capabilities().database_name = res.get_value_as_string(0, 0);
4078
}
4179

42-
bool has_schema(pg_conn_t const &db_connection, std::string const &value)
80+
static void init_postgis_version(pg_conn_t const &db_connection)
4381
{
44-
static const std::set<std::string> values = init_set_from_table(
45-
db_connection, "pg_catalog.pg_namespace", "nspname",
46-
"nspname !~ '^pg_' AND nspname <> 'information_schema'");
82+
auto const res = db_connection.query(
83+
PGRES_TUPLES_OK, "SELECT regexp_split_to_table(extversion, '\\.') FROM"
84+
" pg_extension WHERE extname='postgis'");
4785

48-
if (value.empty()) {
49-
return true;
86+
if (res.num_tuples() == 0) {
87+
throw std::runtime_error{
88+
"The postgis extension is not enabled on the database '{}'."
89+
" Are you using the correct database?"
90+
" Enable with 'CREATE EXTENSION postgis;'"_format(
91+
capabilities().database_name)};
5092
}
5193

52-
return values.count(value);
94+
capabilities().postgis = {std::stoi(res.get_value_as_string(0, 0)),
95+
std::stoi(res.get_value_as_string(1, 0))};
5396
}
5497

55-
bool has_tablespace(pg_conn_t const &db_connection, std::string const &value)
98+
void init_database_capabilities(pg_conn_t const &db_connection)
5699
{
57-
static const std::set<std::string> values =
58-
init_set_from_table(db_connection, "pg_catalog.pg_tablespace",
59-
"spcname", "spcname != 'pg_global'");
100+
init_settings(db_connection);
101+
init_database_name(db_connection);
102+
init_postgis_version(db_connection);
103+
104+
try {
105+
log_info("Database version: {}",
106+
capabilities().settings.at("server_version"));
107+
log_info("PostGIS version: {}.{}", capabilities().postgis.major,
108+
capabilities().postgis.minor);
109+
110+
auto const version_str =
111+
capabilities().settings.at("server_version_num");
112+
capabilities().database_version =
113+
std::strtoul(version_str.c_str(), nullptr, 10);
114+
if (capabilities().database_version <
115+
get_minimum_postgresql_server_version_num()) {
116+
throw std::runtime_error{
117+
"Your database version is too old (need at least {})."_format(
118+
get_minimum_postgresql_server_version())};
119+
}
60120

121+
if (capabilities().settings.at("server_encoding") != "UTF8") {
122+
throw std::runtime_error{"Database is not using UTF8 encoding."};
123+
}
124+
125+
} catch (std::out_of_range const &) {
126+
// Thrown by the settings.at() if the named setting isn't found
127+
throw std::runtime_error{"Can't access database setting."};
128+
}
129+
130+
init_set_from_query(&capabilities().extensions, db_connection,
131+
"pg_catalog.pg_extension", "extname");
132+
init_set_from_query(
133+
&capabilities().schemas, db_connection, "pg_catalog.pg_namespace",
134+
"nspname", "nspname !~ '^pg_' AND nspname <> 'information_schema'");
135+
init_set_from_query(&capabilities().tablespaces, db_connection,
136+
"pg_catalog.pg_tablespace", "spcname",
137+
"spcname != 'pg_global'");
138+
init_set_from_query(&capabilities().index_methods, db_connection,
139+
"pg_catalog.pg_am", "amname", "amtype = 'i'");
140+
}
141+
142+
bool has_extension(std::string const &value)
143+
{
144+
return capabilities().extensions.count(value);
145+
}
146+
147+
bool has_schema(std::string const &value)
148+
{
61149
if (value.empty()) {
62150
return true;
63151
}
152+
return capabilities().schemas.count(value);
153+
}
64154

65-
return values.count(value);
155+
bool has_tablespace(std::string const &value)
156+
{
157+
if (value.empty()) {
158+
return true;
159+
}
160+
return capabilities().tablespaces.count(value);
161+
}
162+
163+
bool has_index_method(std::string const &value)
164+
{
165+
return capabilities().index_methods.count(value);
166+
}
167+
168+
uint32_t get_database_version() noexcept
169+
{
170+
return capabilities().database_version;
171+
}
172+
173+
postgis_version get_postgis_version() noexcept
174+
{
175+
return capabilities().postgis;
66176
}

0 commit comments

Comments
 (0)