database_connection.cpp
Go to the documentation of this file.
1 // SPDX-License-Identifier: BSD-3-Clause
2 
3 /*
4  * Copyright (c) 2020, Bjarne von Horn
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  * * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  * * Neither the name of the copyright holder nor the names of its contributors
15  * may be used to endorse or promote products derived from this software
16  * without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL BJARNE VON HORN BE LIABLE FOR ANY DIRECT,
22  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
30 
34 
35 #include <boost/make_shared.hpp>
36 #include <boost/format.hpp>
38 #include <sqlite3.h>
39 #include <ros/console.h>
40 #include <sstream>
41 
42 #include <thread>
43 #include <chrono>
44 
45 namespace
46 {
47 int busy_handler(void* /* user_ptr */, int times_called_before)
48 {
49  constexpr auto wait_interval =
52  return 0;
53 
54  std::this_thread::sleep_for((times_called_before + 1) * wait_interval);
55  return 1;
56 }
57 } // namespace
58 
59 // instance to avoid linking errors
62 
66 {
67  if (!db_)
68  {
69  sqlite3* s = nullptr;
70  if (sqlite3_open(uri_.c_str(), &s) != SQLITE_OK)
71  {
72  return false;
73  }
75  }
76  if (sqlite3_busy_handler(db_.get(), busy_handler, nullptr) != SQLITE_OK)
77  {
78  throw InternalError("setting busy handler failed", db_.get());
79  }
80  initDb();
81  return true;
82 }
83 
86 {
87  return static_cast<bool>(db_);
88 }
89 
90 std::vector<std::string> warehouse_ros_sqlite::DatabaseConnection::getTablesOfDatabase(const std::string& db_name)
91 {
92  std::ostringstream query_builder;
93  query_builder << "SELECT " << schema::M_D5_TABLE_INDEX_COLUMN << " FROM " << schema::M_D5_TABLE_NAME << " WHERE "
95  const auto select_query = query_builder.str();
96  sqlite3_stmt* raw_stmt = nullptr;
97  if (sqlite3_prepare_v2(db_.get(), select_query.c_str(), select_query.size() + 1, &raw_stmt, nullptr) != SQLITE_OK)
98  {
99  throw InternalError("Prepare statement for getTablesOfDatabase() failed", db_.get());
100  }
101  sqlite3_stmt_ptr stmt(std::exchange(raw_stmt, nullptr));
102  if (sqlite3_bind_text(stmt.get(), 1, db_name.c_str(), db_name.size(), SQLITE_STATIC) != SQLITE_OK)
103  {
104  throw InternalError("Bind parameter for getTablesOfDatabase() failed", db_.get());
105  }
106  std::vector<std::string> tables;
107  for (int res = sqlite3_step(stmt.get()); res != SQLITE_DONE; res = sqlite3_step(stmt.get()))
108  {
109  if (res == SQLITE_ROW)
110  {
111  tables.emplace_back(reinterpret_cast<const char*>(sqlite3_column_text(stmt.get(), 0)),
112  sqlite3_column_bytes(stmt.get(), 0));
113  }
114  else
115  {
116  throw InternalError("Get results for getTablesOfDatabase() failed", db_.get());
117  }
118  }
119  return tables;
120 }
121 
125 {
126  const auto tables_to_be_dropped = getTablesOfDatabase(db_name);
127  std::ostringstream query_builder;
128  for (const auto& table : tables_to_be_dropped)
129  {
130  const auto escaped_table_string = schema::escape_string_literal_without_quotes(table);
131  const auto escaped_table_identifier = schema::escape_identifier(table);
132  query_builder << "DELETE FROM " << schema::M_D5_TABLE_NAME << " WHERE " << schema::M_D5_TABLE_INDEX_COLUMN
133  << " == '" << escaped_table_string << "'; ";
134  query_builder << "DROP TABLE " << escaped_table_identifier << ";";
135  }
136  query_builder << "COMMIT;";
137  const auto query = query_builder.str();
138  if (sqlite3_exec(db_.get(), "BEGIN TRANSACTION;", nullptr, nullptr, nullptr) == SQLITE_OK)
139  {
140  if (sqlite3_exec(db_.get(), query.c_str(), nullptr, nullptr, nullptr) == SQLITE_OK)
141  {
142  return;
143  }
144  sqlite3_exec(db_.get(), "ROLLBACK;", nullptr, nullptr, nullptr);
145  }
146  throw InternalError("Drop tables failed", db_.get());
147 }
148 
150 std::string warehouse_ros_sqlite::DatabaseConnection::messageType(const std::string& db_name,
151  const std::string& collection_name)
152 {
153  using namespace warehouse_ros_sqlite::schema;
154 
155  std::ostringstream query_builder;
156  query_builder << "SELECT " << M_D5_TABLE_DATATYPE_COLUMN << " FROM " << M_D5_TABLE_NAME << " WHERE "
157  << M_D5_TABLE_INDEX_COLUMN << " = ?;";
158  const auto query = query_builder.str();
159  sqlite3_stmt* stmt = nullptr;
160  if (sqlite3_prepare_v2(db_.get(), query.c_str(), query.size() + 1, &stmt, nullptr) != SQLITE_OK)
161  {
162  throw InternalError("Prepare statement for messageType() failed", db_.get());
163  }
164  const sqlite3_stmt_ptr guard(stmt);
165  const auto mangled_name = schema::mangle_database_and_collection_name(db_name, collection_name);
166  if (sqlite3_bind_text(stmt, 1, mangled_name.c_str(), mangled_name.size(), SQLITE_STATIC) != SQLITE_OK)
167  throw InternalError("Bind parameter for getTablesOfDatabase() failed", db_.get());
168  switch (sqlite3_step(stmt))
169  {
170  case SQLITE_ROW:
171  break;
172  case SQLITE_DONE:
173  default:
174  throw InternalError("Get result for getTablesOfDatabase() failed", db_.get());
175  }
176  return std::string(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0)), sqlite3_column_bytes(stmt, 0));
177 }
178 
180 {
181  if (schemaVersionSet())
182  return;
183  std::ostringstream query_builder;
184  query_builder << "PRAGMA user_version = " << schema::VERSION << ";"
185  << "CREATE TABLE " << schema::M_D5_TABLE_NAME << " ( " << schema::M_D5_TABLE_INDEX_COLUMN
186  << " TEXT PRIMARY KEY, " << schema::M_D5_TABLE_M_D5_COLUMN << " BLOB NOT NULL, "
188  << " TEXT NOT NULL, " << schema::M_D5_TABLE_DATATYPE_COLUMN << " TEXT NOT NULL);";
189  const auto query = query_builder.str();
190  ROS_DEBUG_NAMED("warehouse_ros_sqlite", "MD5 table init: %s", query.c_str());
191  if (sqlite3_exec(db_.get(), query.c_str(), nullptr, nullptr, nullptr) != SQLITE_OK)
192  throw InternalError("Could not initialize Database", db_.get());
193 }
194 
196 {
197  sqlite3_stmt* stmt = nullptr;
198  if (sqlite3_prepare_v2(db_.get(), "PRAGMA user_version;", -1, &stmt, nullptr) != SQLITE_OK)
199  throw InternalError("Could not get schema version", db_.get());
200  sqlite3_stmt_ptr stmt_guard(std::exchange(stmt, nullptr));
201  if (sqlite3_step(stmt_guard.get()) != SQLITE_ROW)
202  throw InternalError("Could not get schema version", db_.get());
203  const int current_schema_version = sqlite3_column_int(stmt_guard.get(), 0);
204  if (current_schema_version == 0)
205  {
206  return false;
207  }
208  else
209  {
210  if (current_schema_version == schema::VERSION)
211  return true;
212  else
213  throw SchemaVersionMismatch(current_schema_version, schema::VERSION);
214  }
215 }
216 
219  const std::string& collection_name)
220 {
221  return boost::make_shared<warehouse_ros_sqlite::MessageCollectionHelper>(db_, db_name, collection_name);
222 }
223 
225 {
226  sqlite3_finalize(stmt);
227 }
229 {
230  if (sqlite3_close(db) != SQLITE_OK)
231  {
232  ROS_ERROR("sqlite connection closed when still in use");
233  }
234 }
235 
237  : warehouse_ros::WarehouseRosException(boost::format("%1% %2%") % msg % sqlite3_errmsg(db))
238 {
239 }
240 warehouse_ros_sqlite::InternalError::InternalError(const char* msg, sqlite3_stmt* stmt)
241  : InternalError(msg, sqlite3_db_handle(stmt))
242 {
243 }
244 
warehouse_ros_sqlite::schema
Definition: utils.h:53
exceptions.h
warehouse_ros_sqlite::DatabaseConnection::isConnected
bool isConnected() override
Returns whether the database is connected.
Definition: database_connection.cpp:85
warehouse_ros_sqlite::Sqlite3StmtDeleter::operator()
void operator()(sqlite3_stmt *stmt) const
Definition: database_connection.cpp:224
warehouse_ros_sqlite::schema::M_D5_TABLE_DATABASE_COLUMN
constexpr const char * M_D5_TABLE_DATABASE_COLUMN
Definition: utils.h:92
boost::shared_ptr< MessageCollectionHelper >
warehouse_ros_sqlite::schema::M_D5_TABLE_NAME
constexpr const char * M_D5_TABLE_NAME
Definition: utils.h:87
warehouse_ros_sqlite::DatabaseConnection::initDb
void initDb()
Definition: database_connection.cpp:179
s
XmlRpcServer s
warehouse_ros_sqlite::DatabaseConnection::BUSY_WAIT_MILLISECS
static const int BUSY_WAIT_MILLISECS
Definition: database_connection.h:75
warehouse_ros_sqlite::schema::mangle_database_and_collection_name
std::string mangle_database_and_collection_name(const std::string &db_name, const std::string &collection_name)
Definition: utils.h:110
warehouse_ros_sqlite::DatabaseConnection::connect
bool connect() override
Definition: database_connection.cpp:65
boost
warehouse_ros_sqlite::InternalError::InternalError
InternalError(const char *msg, sqlite3 *db)
Definition: database_connection.cpp:236
warehouse_ros_sqlite::DatabaseConnection::uri_
std::string uri_
Definition: database_connection.h:41
warehouse_ros_sqlite::schema::M_D5_TABLE_DATATYPE_COLUMN
constexpr const char * M_D5_TABLE_DATATYPE_COLUMN
Definition: utils.h:90
warehouse_ros_sqlite::DatabaseConnection::messageType
std::string messageType(const std::string &db_name, const std::string &collection_name) override
Return the ROS Message type of a given collection.
Definition: database_connection.cpp:150
warehouse_ros_sqlite::DatabaseConnection::dropDatabase
void dropDatabase(const std::string &db_name) override
Drop a db and all its collections. A DbClientConnection exception will be thrown if the database is n...
Definition: database_connection.cpp:124
console.h
utils.h
warehouse_ros_sqlite::DatabaseConnection::openCollectionHelper
warehouse_ros::MessageCollectionHelper::Ptr openCollectionHelper(const std::string &db_name, const std::string &collection_name) override
Definition: database_connection.cpp:218
PLUGINLIB_EXPORT_CLASS
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
ROS_DEBUG_NAMED
#define ROS_DEBUG_NAMED(name,...)
warehouse_ros_sqlite::sqlite3_delete
WAREHOUSE_ROS_SQLITE_EXPORT void sqlite3_delete(sqlite3 *db)
Definition: database_connection.cpp:228
warehouse_ros_sqlite::sqlite3_stmt_ptr
std::unique_ptr< sqlite3_stmt, Sqlite3StmtDeleter > sqlite3_stmt_ptr
Definition: utils.h:50
warehouse_ros_sqlite::schema::M_D5_TABLE_INDEX_COLUMN
constexpr const char * M_D5_TABLE_INDEX_COLUMN
Definition: utils.h:88
warehouse_ros_sqlite::DatabaseConnection
Definition: database_connection.h:38
warehouse_ros_sqlite::InternalError
Definition: exceptions.h:42
warehouse_ros::DatabaseConnection
warehouse_ros_sqlite::schema::escape_identifier
std::string escape_identifier(const std::string &s)
Definition: utils.h:98
database_connection.h
warehouse_ros_sqlite::DatabaseConnection::BUSY_MAX_RETRIES
static const int BUSY_MAX_RETRIES
Definition: database_connection.h:76
warehouse_ros_sqlite::DatabaseConnection::schemaVersionSet
bool schemaVersionSet()
Definition: database_connection.cpp:195
ROS_ERROR
#define ROS_ERROR(...)
class_list_macros.hpp
warehouse_ros_sqlite::DatabaseConnection::db_
sqlite3_ptr db_
Definition: database_connection.h:40
warehouse_ros
warehouse_ros_sqlite::DatabaseConnection::getTablesOfDatabase
std::vector< std::string > getTablesOfDatabase(const std::string &db_name)
Definition: database_connection.cpp:90
warehouse_ros_sqlite::schema::M_D5_TABLE_M_D5_COLUMN
constexpr const char * M_D5_TABLE_M_D5_COLUMN
Definition: utils.h:89
warehouse_ros_sqlite::schema::escape_string_literal_without_quotes
std::string escape_string_literal_without_quotes(const std::string &c)
Definition: utils.h:106
warehouse_ros_sqlite::schema::M_D5_TABLE_TABLE_COLUMN
constexpr const char * M_D5_TABLE_TABLE_COLUMN
Definition: utils.h:91
message_collection_helper.h
warehouse_ros_sqlite::SchemaVersionMismatch
Definition: exceptions.h:54
warehouse_ros_sqlite::schema::VERSION
const int VERSION
Definition: utils.h:94


warehouse_ros_sqlite
Author(s): Bjarne von Horn
autogenerated on Mon Oct 14 2024 02:16:58