1 #ifndef OSM2PGSQL_TESTS_COMMON_PG_HPP 2 #define OSM2PGSQL_TESTS_COMMON_PG_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-2021 by the osm2pgsql developer community. 10 * For a full list of authors see the git log. 11 */ 12 13 #include <cstdio> 14 #include <cstdlib> 15 #include <stdexcept> 16 #include <string> 17 18 #include "format.hpp" 19 #include "options.hpp" 20 #include "pgsql.hpp" 21 #include <catch.hpp> 22 23 #ifdef _MSC_VER 24 #include <process.h> 25 #include <windows.h> 26 #define getpid _getpid 27 #else 28 #include <sys/types.h> 29 #include <unistd.h> 30 #endif 31 32 namespace testing { 33 /// Helper classes for postgres connections 34 namespace pg { 35 36 class conn_t : public pg_conn_t 37 { 38 public: conn_t(std::string const & conninfo)39 conn_t(std::string const &conninfo) : pg_conn_t(conninfo) {} 40 result_as_string(std::string const & cmd) const41 std::string result_as_string(std::string const &cmd) const 42 { 43 pg_result_t const res = query(PGRES_TUPLES_OK, cmd); 44 REQUIRE(res.num_tuples() == 1); 45 return res.get_value_as_string(0, 0); 46 } 47 result_as_int(std::string const & cmd) const48 int result_as_int(std::string const &cmd) const 49 { 50 return std::stoi(result_as_string(cmd)); 51 } 52 result_as_ulong(std::string const & cmd) const53 unsigned long result_as_ulong(std::string const &cmd) const 54 { 55 return std::stoul(result_as_string(cmd)); 56 } 57 result_as_double(std::string const & cmd) const58 double result_as_double(std::string const &cmd) const 59 { 60 return std::stod(result_as_string(cmd)); 61 } 62 assert_double(double expected,std::string const & cmd) const63 void assert_double(double expected, std::string const &cmd) const 64 { 65 REQUIRE(Approx(expected).epsilon(0.01) == result_as_double(cmd)); 66 } 67 assert_null(std::string const & cmd) const68 void assert_null(std::string const &cmd) const 69 { 70 pg_result_t const res = query(PGRES_TUPLES_OK, cmd); 71 REQUIRE(res.num_tuples() == 1); 72 REQUIRE(res.is_null(0, 0)); 73 } 74 require_row(std::string const & cmd) const75 pg_result_t require_row(std::string const &cmd) const 76 { 77 pg_result_t res = query(PGRES_TUPLES_OK, cmd); 78 REQUIRE(res.num_tuples() == 1); 79 80 return res; 81 } 82 get_count(char const * table_name,std::string const & where="") const83 unsigned long get_count(char const *table_name, 84 std::string const &where = "") const 85 { 86 auto const query = "SELECT count(*) FROM {} {} {}"_format( 87 table_name, (where.empty() ? "" : "WHERE"), where); 88 89 return result_as_ulong(query); 90 } 91 require_has_table(char const * table_name) const92 void require_has_table(char const *table_name) const 93 { 94 auto const where = "oid = '{}'::regclass"_format(table_name); 95 96 REQUIRE(get_count("pg_catalog.pg_class", where) == 1); 97 } 98 }; 99 100 class tempdb_t 101 { 102 public: tempdb_t()103 tempdb_t() noexcept 104 { 105 try { 106 conn_t conn{"dbname=postgres"}; 107 108 m_db_name = "osm2pgsql-test-{}-{}"_format(getpid(), time(nullptr)); 109 conn.exec("DROP DATABASE IF EXISTS \"{}\""_format(m_db_name)); 110 conn.exec("CREATE DATABASE \"{}\" WITH ENCODING 'UTF8'"_format( 111 m_db_name)); 112 113 conn_t local = connect(); 114 local.exec("CREATE EXTENSION postgis"); 115 local.exec("CREATE EXTENSION hstore"); 116 } catch (std::runtime_error const &e) { 117 fmt::print(stderr, 118 "Test database cannot be created: {}\n" 119 "Did you mean to run 'pg_virtualenv ctest'?\n", 120 e.what()); 121 std::exit(1); 122 } 123 } 124 125 tempdb_t(tempdb_t const &) = delete; 126 tempdb_t &operator=(tempdb_t const &) = delete; 127 128 tempdb_t(tempdb_t &&) = delete; 129 tempdb_t &operator=(tempdb_t const &&) = delete; 130 ~tempdb_t()131 ~tempdb_t() noexcept 132 { 133 if (!m_db_name.empty()) { 134 // Disable removal of the test database by setting the environment 135 // variable OSM2PGSQL_KEEP_TEST_DB to anything. This can be useful 136 // when debugging tests. 137 char const *const keep_db = std::getenv("OSM2PGSQL_KEEP_TEST_DB"); 138 if (keep_db != nullptr) { 139 return; 140 } 141 try { 142 conn_t conn{"dbname=postgres"}; 143 conn.exec("DROP DATABASE IF EXISTS \"{}\""_format(m_db_name)); 144 } catch (...) { 145 fprintf(stderr, "DROP DATABASE failed. Ignored.\n"); 146 } 147 } 148 } 149 connect() const150 conn_t connect() const { return conn_t{conninfo()}; } 151 conninfo() const152 std::string conninfo() const { return "dbname=" + m_db_name; } 153 db_options() const154 database_options_t db_options() const 155 { 156 database_options_t opt; 157 opt.db = m_db_name; 158 159 return opt; 160 } 161 162 private: 163 std::string m_db_name; 164 }; 165 166 } // namespace pg 167 } // namespace testing 168 169 #endif // OSM2PGSQL_TESTS_COMMON_PG_HPP 170