Skip to content

Commit d170a72

Browse files
authored
Merge pull request #1788 from joto/database-check-helper
Better error messages when extension/schema/tablespace is missing
2 parents a51da52 + 05d8bf8 commit d170a72

7 files changed

Lines changed: 161 additions & 0 deletions

File tree

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ target_sources(osm2pgsql_lib PRIVATE
2626
output-pgsql.cpp
2727
output.cpp
2828
pgsql.cpp
29+
pgsql-capabilities.cpp
2930
pgsql-helper.cpp
3031
progress-display.cpp
3132
reprojection.cpp

src/flex-table.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "flex-table.hpp"
1111
#include "format.hpp"
1212
#include "logging.hpp"
13+
#include "pgsql-capabilities.hpp"
1314
#include "pgsql-helper.hpp"
1415
#include "util.hpp"
1516

@@ -189,6 +190,30 @@ void table_connection_t::start(bool append)
189190
{
190191
assert(m_db_connection);
191192

193+
if (!has_schema(*m_db_connection, table().schema())) {
194+
throw std::runtime_error{
195+
"Schema '{0}' not available. "
196+
"Use 'CREATE SCHEMA \"{0}\";' to create it."_format(
197+
table().schema())};
198+
}
199+
200+
for (auto const &ts :
201+
{table().data_tablespace(), table().index_tablespace()}) {
202+
if (!has_tablespace(*m_db_connection, ts)) {
203+
throw std::runtime_error{
204+
"Tablespace '{0}' not available. "
205+
"Use 'CREATE TABLESPACE \"{0}\" ...;' to create it."_format(
206+
ts)};
207+
}
208+
}
209+
210+
if (table().has_hstore_column()) {
211+
if (!has_extension(*m_db_connection, "hstore")) {
212+
throw std::runtime_error{"Extension 'hstore' not available. Use "
213+
"'CREATE EXTENSION hstore;' to load it."};
214+
}
215+
}
216+
192217
m_db_connection->exec("SET client_min_messages = WARNING");
193218

194219
if (!append) {

src/flex-table.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,14 @@ class flex_table_t
110110
return m_geom_column != std::numeric_limits<std::size_t>::max();
111111
}
112112

113+
bool has_hstore_column() const noexcept
114+
{
115+
auto const it = std::find_if(begin(), end(), [&](auto const &column) {
116+
return column.type() == table_column_type::hstore;
117+
});
118+
return it != end();
119+
}
120+
113121
/// Get the (first, if there are multiple) geometry column.
114122
flex_table_column_t const &geom_column() const noexcept
115123
{

src/pgsql-capabilities.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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-2022 by the osm2pgsql developer community.
7+
* For a full list of authors see the git log.
8+
*/
9+
10+
#include "format.hpp"
11+
#include "pgsql.hpp"
12+
#include "pgsql-helper.hpp"
13+
14+
#include <set>
15+
#include <string>
16+
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+
{
22+
std::set<std::string> values;
23+
24+
auto const res = db_connection.query(
25+
PGRES_TUPLES_OK,
26+
"SELECT {} FROM {} WHERE {}"_format(column, table, condition));
27+
for (int i = 0; i < res.num_tuples(); ++i) {
28+
values.insert(res.get_value_as_string(i, 0));
29+
}
30+
31+
return values;
32+
}
33+
34+
bool has_extension(pg_conn_t const &db_connection, std::string const &value)
35+
{
36+
static const std::set<std::string> values = init_set_from_table(
37+
db_connection, "pg_catalog.pg_extension", "extname", "true");
38+
39+
return values.count(value);
40+
}
41+
42+
bool has_schema(pg_conn_t const &db_connection, std::string const &value)
43+
{
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'");
47+
48+
if (value.empty()) {
49+
return true;
50+
}
51+
52+
return values.count(value);
53+
}
54+
55+
bool has_tablespace(pg_conn_t const &db_connection, std::string const &value)
56+
{
57+
static const std::set<std::string> values =
58+
init_set_from_table(db_connection, "pg_catalog.pg_tablespace",
59+
"spcname", "spcname != 'pg_global'");
60+
61+
if (value.empty()) {
62+
return true;
63+
}
64+
65+
return values.count(value);
66+
}

src/pgsql-capabilities.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#ifndef OSM2PGSQL_PGSQL_CAPABILITIES_HPP
2+
#define OSM2PGSQL_PGSQL_CAPABILITIES_HPP
3+
4+
/**
5+
* SPDX-License-Identifier: GPL-2.0-or-later
6+
*
7+
* This file is part of osm2pgsql (https://osm2pgsql.org/).
8+
*
9+
* Copyright (C) 2006-2022 by the osm2pgsql developer community.
10+
* For a full list of authors see the git log.
11+
*/
12+
13+
#include "pgsql.hpp"
14+
15+
#include <string>
16+
17+
bool has_extension(pg_conn_t const &db_connection, std::string const &value);
18+
bool has_schema(pg_conn_t const &db_connection, std::string const &value);
19+
bool has_tablespace(pg_conn_t const &db_connection, std::string const &value);
20+
21+
#endif // OSM2PGSQL_PGSQL_CAPABILITIES_HPP

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ set_test(test-output-pgsql-z_order)
7272
set_test(test-parse-osmium LABELS NoDB)
7373
set_test(test-persistent-cache LABELS NoDB)
7474
set_test(test-pgsql)
75+
set_test(test-pgsql-capabilities)
7576
set_test(test-reprojection LABELS NoDB)
7677
set_test(test-taginfo LABELS NoDB)
7778
set_test(test-tile LABELS NoDB)

tests/test-pgsql-capabilities.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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-2022 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 "common-import.hpp"
13+
14+
#include "pgsql-capabilities.hpp"
15+
16+
static testing::db::import_t const db;
17+
18+
TEST_CASE("has_extension() should work")
19+
{
20+
auto const conn = db.db().connect();
21+
REQUIRE(has_extension(conn, "postgis"));
22+
REQUIRE_FALSE(has_schema(conn, "xzxzxzxz"));
23+
}
24+
25+
TEST_CASE("has_schema() should work")
26+
{
27+
auto const conn = db.db().connect();
28+
REQUIRE(has_schema(conn, "public"));
29+
REQUIRE_FALSE(has_schema(conn, "xzxzxzxz"));
30+
REQUIRE_FALSE(has_schema(conn, "pg_toast"));
31+
}
32+
33+
TEST_CASE("has_tablespace() should work")
34+
{
35+
auto const conn = db.db().connect();
36+
REQUIRE(has_tablespace(conn, "pg_default"));
37+
REQUIRE_FALSE(has_tablespace(conn, "xzxzxzxz"));
38+
REQUIRE_FALSE(has_tablespace(conn, "pg_global"));
39+
}

0 commit comments

Comments
 (0)