@@ -11,9 +11,12 @@
|
||||
#include <mbgl/util/traits.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
|
||||
#define MBGL_CONSTRUCTOR(f) \
|
||||
static void f(void); \
|
||||
struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \
|
||||
#define MBGL_CONSTRUCTOR(f) \
|
||||
static void f(void); \
|
||||
struct f##_t_ { \
|
||||
f##_t_(void) { f(); } \
|
||||
}; \
|
||||
static f##_t_ f##_; \
|
||||
static void f(void)
|
||||
|
||||
namespace mapbox {
|
||||
@@ -51,15 +54,17 @@ void setTempPath(const std::string& path) {
|
||||
|
||||
class DatabaseImpl {
|
||||
public:
|
||||
explicit DatabaseImpl(sqlite3* db_) : db(db_) {
|
||||
explicit DatabaseImpl(sqlite3* db_)
|
||||
: db(db_) {
|
||||
const int error = sqlite3_extended_result_codes(db, true);
|
||||
if (error != SQLITE_OK) {
|
||||
mbgl::Log::Warning(mbgl::Event::Database, error, std::string("Failed to enable extended result codes: ") + sqlite3_errmsg(db));
|
||||
mbgl::Log::Warning(mbgl::Event::Database,
|
||||
error,
|
||||
std::string("Failed to enable extended result codes: ") + sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
|
||||
~DatabaseImpl()
|
||||
{
|
||||
~DatabaseImpl() {
|
||||
const int error = sqlite3_close(db);
|
||||
if (error != SQLITE_OK) {
|
||||
mbgl::Log::Error(mbgl::Event::Database, error, "Failed to close database: %s", sqlite3_errmsg(db));
|
||||
@@ -74,17 +79,15 @@ public:
|
||||
|
||||
class StatementImpl {
|
||||
public:
|
||||
StatementImpl(sqlite3* db, const char* sql)
|
||||
{
|
||||
StatementImpl(sqlite3* db, const char* sql) {
|
||||
const int error = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
|
||||
if (error != SQLITE_OK) {
|
||||
stmt = nullptr;
|
||||
throw Exception { error, sqlite3_errmsg(db) };
|
||||
throw Exception{error, sqlite3_errmsg(db)};
|
||||
}
|
||||
}
|
||||
|
||||
~StatementImpl()
|
||||
{
|
||||
~StatementImpl() {
|
||||
if (!stmt) return;
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
@@ -92,7 +95,7 @@ public:
|
||||
|
||||
void check(int err) {
|
||||
if (err != SQLITE_OK) {
|
||||
throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt)) };
|
||||
throw Exception{err, sqlite3_errmsg(sqlite3_db_handle(stmt))};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +105,7 @@ public:
|
||||
};
|
||||
|
||||
#ifndef NDEBUG
|
||||
void logSqlMessage(void *, const int err, const char *msg) {
|
||||
void logSqlMessage(void*, const int err, const char* msg) {
|
||||
mbgl::Log::Record(mbgl::EventSeverity::Debug, mbgl::Event::Database, err, "%s", msg);
|
||||
}
|
||||
#endif
|
||||
@@ -110,9 +113,12 @@ void logSqlMessage(void *, const int err, const char *msg) {
|
||||
MBGL_CONSTRUCTOR(initialize) {
|
||||
if (sqlite3_libversion_number() / 1000000 != SQLITE_VERSION_NUMBER / 1000000) {
|
||||
char message[96];
|
||||
snprintf(message, 96,
|
||||
"sqlite3 libversion mismatch: headers report %d, but library reports %d",
|
||||
SQLITE_VERSION_NUMBER, sqlite3_libversion_number());
|
||||
snprintf(message,
|
||||
96,
|
||||
"sqlite3 libversion mismatch: headers report %d, but library "
|
||||
"reports %d",
|
||||
SQLITE_VERSION_NUMBER,
|
||||
sqlite3_libversion_number());
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
|
||||
@@ -122,18 +128,18 @@ MBGL_CONSTRUCTOR(initialize) {
|
||||
#endif
|
||||
}
|
||||
|
||||
mapbox::util::variant<Database, Exception> Database::tryOpen(const std::string &filename, int flags) {
|
||||
mapbox::util::variant<Database, Exception> Database::tryOpen(const std::string& filename, int flags) {
|
||||
sqlite3* db = nullptr;
|
||||
const int error = sqlite3_open_v2(filename.c_str(), &db, flags | SQLITE_OPEN_URI, nullptr);
|
||||
if (error != SQLITE_OK) {
|
||||
const auto message = sqlite3_errmsg(db);
|
||||
sqlite3_close(db);
|
||||
return Exception { error, message };
|
||||
return Exception{error, message};
|
||||
}
|
||||
return Database(std::make_unique<DatabaseImpl>(db));
|
||||
}
|
||||
|
||||
Database Database::open(const std::string &filename, int flags) {
|
||||
Database Database::open(const std::string& filename, int flags) {
|
||||
auto result = tryOpen(filename, flags);
|
||||
if (result.is<Exception>()) {
|
||||
throw std::move(result.get<Exception>());
|
||||
@@ -143,10 +149,10 @@ Database Database::open(const std::string &filename, int flags) {
|
||||
}
|
||||
|
||||
Database::Database(std::unique_ptr<DatabaseImpl> impl_)
|
||||
: impl(std::move(impl_))
|
||||
{}
|
||||
: impl(std::move(impl_)) {}
|
||||
|
||||
Database::Database(Database&& other) noexcept : impl(std::move(other.impl)) {}
|
||||
Database::Database(Database&& other) noexcept
|
||||
: impl(std::move(other.impl)) {}
|
||||
|
||||
Database& Database::operator=(Database&& other) noexcept {
|
||||
std::swap(impl, other.impl);
|
||||
@@ -161,33 +167,32 @@ void Database::setBusyTimeout(std::chrono::milliseconds timeout) {
|
||||
}
|
||||
|
||||
void DatabaseImpl::setBusyTimeout(std::chrono::milliseconds timeout) {
|
||||
const int err = sqlite3_busy_timeout(db,
|
||||
int(std::min<std::chrono::milliseconds::rep>(timeout.count(), std::numeric_limits<int>::max())));
|
||||
const int err = sqlite3_busy_timeout(
|
||||
db, int(std::min<std::chrono::milliseconds::rep>(timeout.count(), std::numeric_limits<int>::max())));
|
||||
if (err != SQLITE_OK) {
|
||||
throw Exception { err, sqlite3_errmsg(db) };
|
||||
throw Exception{err, sqlite3_errmsg(db)};
|
||||
}
|
||||
}
|
||||
|
||||
void Database::exec(const std::string &sql) {
|
||||
void Database::exec(const std::string& sql) {
|
||||
assert(impl);
|
||||
impl->exec(sql);
|
||||
}
|
||||
|
||||
void DatabaseImpl::exec(const std::string& sql) {
|
||||
char *msg = nullptr;
|
||||
char* msg = nullptr;
|
||||
const int err = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &msg);
|
||||
if (msg) {
|
||||
const std::string message = msg;
|
||||
sqlite3_free(msg);
|
||||
throw Exception { err, message };
|
||||
throw Exception{err, message};
|
||||
} else if (err != SQLITE_OK) {
|
||||
throw Exception { err, sqlite3_errmsg(db) };
|
||||
throw Exception{err, sqlite3_errmsg(db)};
|
||||
}
|
||||
}
|
||||
|
||||
Statement::Statement(Database& db, const char* sql)
|
||||
: impl(std::make_unique<StatementImpl>(db.impl->db, sql)) {
|
||||
}
|
||||
: impl(std::make_unique<StatementImpl>(db.impl->db, sql)) {}
|
||||
|
||||
// NOLINTNEXTLINE(modernize-use-equals-default)
|
||||
Statement::~Statement() {
|
||||
@@ -197,7 +202,8 @@ Statement::~Statement() {
|
||||
#endif
|
||||
}
|
||||
|
||||
Query::Query(Statement& stmt_) : stmt(stmt_) {
|
||||
Query::Query(Statement& stmt_)
|
||||
: stmt(stmt_) {
|
||||
assert(stmt.impl);
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -215,86 +221,98 @@ Query::~Query() {
|
||||
#endif
|
||||
}
|
||||
|
||||
template <> void Query::bind(int offset, std::nullptr_t) {
|
||||
template <>
|
||||
void Query::bind(int offset, std::nullptr_t) {
|
||||
assert(stmt.impl);
|
||||
stmt.impl->check(sqlite3_bind_null(stmt.impl->stmt, offset));
|
||||
}
|
||||
|
||||
template <> void Query::bind(int offset, int8_t value) {
|
||||
template <>
|
||||
void Query::bind(int offset, int8_t value) {
|
||||
assert(stmt.impl);
|
||||
stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value));
|
||||
}
|
||||
|
||||
template <> void Query::bind(int offset, int16_t value) {
|
||||
template <>
|
||||
void Query::bind(int offset, int16_t value) {
|
||||
assert(stmt.impl);
|
||||
stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value));
|
||||
}
|
||||
|
||||
template <> void Query::bind(int offset, int32_t value) {
|
||||
template <>
|
||||
void Query::bind(int offset, int32_t value) {
|
||||
assert(stmt.impl);
|
||||
stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value));
|
||||
}
|
||||
|
||||
template <> void Query::bind(int offset, int64_t value) {
|
||||
template <>
|
||||
void Query::bind(int offset, int64_t value) {
|
||||
assert(stmt.impl);
|
||||
stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value));
|
||||
}
|
||||
|
||||
template <> void Query::bind(int offset, uint8_t value) {
|
||||
template <>
|
||||
void Query::bind(int offset, uint8_t value) {
|
||||
assert(stmt.impl);
|
||||
stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value));
|
||||
}
|
||||
|
||||
template <> void Query::bind(int offset, uint16_t value) {
|
||||
template <>
|
||||
void Query::bind(int offset, uint16_t value) {
|
||||
assert(stmt.impl);
|
||||
stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value));
|
||||
}
|
||||
|
||||
template <> void Query::bind(int offset, uint32_t value) {
|
||||
template <>
|
||||
void Query::bind(int offset, uint32_t value) {
|
||||
assert(stmt.impl);
|
||||
stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value));
|
||||
}
|
||||
|
||||
template <> void Query::bind(int offset, float value) {
|
||||
template <>
|
||||
void Query::bind(int offset, float value) {
|
||||
assert(stmt.impl);
|
||||
stmt.impl->check(sqlite3_bind_double(stmt.impl->stmt, offset, value));
|
||||
}
|
||||
|
||||
template <> void Query::bind(int offset, double value) {
|
||||
template <>
|
||||
void Query::bind(int offset, double value) {
|
||||
assert(stmt.impl);
|
||||
stmt.impl->check(sqlite3_bind_double(stmt.impl->stmt, offset, value));
|
||||
}
|
||||
|
||||
template <> void Query::bind(int offset, bool value) {
|
||||
template <>
|
||||
void Query::bind(int offset, bool value) {
|
||||
assert(stmt.impl);
|
||||
stmt.impl->check(sqlite3_bind_int(stmt.impl->stmt, offset, value));
|
||||
}
|
||||
|
||||
template <> void Query::bind(int offset, const char *value) {
|
||||
template <>
|
||||
void Query::bind(int offset, const char* value) {
|
||||
assert(stmt.impl);
|
||||
stmt.impl->check(sqlite3_bind_text(stmt.impl->stmt, offset, value, -1, SQLITE_STATIC));
|
||||
}
|
||||
|
||||
void Query::bind(int offset, const char * value, std::size_t length, bool retain) {
|
||||
void Query::bind(int offset, const char* value, std::size_t length, bool retain) {
|
||||
assert(stmt.impl);
|
||||
if (length > std::numeric_limits<int>::max()) {
|
||||
throw std::range_error("value too long for sqlite3_bind_text");
|
||||
}
|
||||
stmt.impl->check(sqlite3_bind_text(stmt.impl->stmt, offset, value, int(length),
|
||||
retain ? SQLITE_TRANSIENT : SQLITE_STATIC));
|
||||
stmt.impl->check(
|
||||
sqlite3_bind_text(stmt.impl->stmt, offset, value, int(length), retain ? SQLITE_TRANSIENT : SQLITE_STATIC));
|
||||
}
|
||||
|
||||
void Query::bind(int offset, const std::string& value, bool retain) {
|
||||
bind(offset, value.data(), value.size(), retain);
|
||||
}
|
||||
|
||||
void Query::bindBlob(int offset, const void * value, std::size_t length, bool retain) {
|
||||
void Query::bindBlob(int offset, const void* value, std::size_t length, bool retain) {
|
||||
assert(stmt.impl);
|
||||
if (length > std::numeric_limits<int>::max()) {
|
||||
throw std::range_error("value too long for sqlite3_bind_text");
|
||||
}
|
||||
stmt.impl->check(sqlite3_bind_blob(stmt.impl->stmt, offset, value, int(length),
|
||||
retain ? SQLITE_TRANSIENT : SQLITE_STATIC));
|
||||
stmt.impl->check(
|
||||
sqlite3_bind_blob(stmt.impl->stmt, offset, value, int(length), retain ? SQLITE_TRANSIENT : SQLITE_STATIC));
|
||||
}
|
||||
|
||||
void Query::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) {
|
||||
@@ -302,13 +320,13 @@ void Query::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain)
|
||||
}
|
||||
|
||||
template <>
|
||||
void Query::bind(
|
||||
int offset, std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> value) {
|
||||
void Query::bind(int offset, std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> value) {
|
||||
assert(stmt.impl);
|
||||
stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, std::chrono::system_clock::to_time_t(value)));
|
||||
}
|
||||
|
||||
template <> void Query::bind(int offset, std::optional<std::string> value) {
|
||||
template <>
|
||||
void Query::bind(int offset, std::optional<std::string> value) {
|
||||
if (!value) {
|
||||
bind(offset, nullptr);
|
||||
} else {
|
||||
@@ -317,9 +335,8 @@ template <> void Query::bind(int offset, std::optional<std::string> value) {
|
||||
}
|
||||
|
||||
template <>
|
||||
void Query::bind(
|
||||
int offset,
|
||||
std::optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> value) {
|
||||
void Query::bind(int offset,
|
||||
std::optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> value) {
|
||||
if (!value) {
|
||||
bind(offset, nullptr);
|
||||
} else {
|
||||
@@ -337,56 +354,60 @@ bool Query::run() {
|
||||
} else if (err == SQLITE_ROW) {
|
||||
return true;
|
||||
} else if (err != SQLITE_OK) {
|
||||
throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt.impl->stmt)) };
|
||||
throw Exception{err, sqlite3_errmsg(sqlite3_db_handle(stmt.impl->stmt))};
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <> bool Query::get(int offset) {
|
||||
template <>
|
||||
bool Query::get(int offset) {
|
||||
assert(stmt.impl);
|
||||
return sqlite3_column_int(stmt.impl->stmt, offset);
|
||||
}
|
||||
|
||||
template <> int Query::get(int offset) {
|
||||
template <>
|
||||
int Query::get(int offset) {
|
||||
assert(stmt.impl);
|
||||
return sqlite3_column_int(stmt.impl->stmt, offset);
|
||||
}
|
||||
|
||||
template <> int64_t Query::get(int offset) {
|
||||
template <>
|
||||
int64_t Query::get(int offset) {
|
||||
assert(stmt.impl);
|
||||
return sqlite3_column_int64(stmt.impl->stmt, offset);
|
||||
}
|
||||
|
||||
template <> double Query::get(int offset) {
|
||||
template <>
|
||||
double Query::get(int offset) {
|
||||
assert(stmt.impl);
|
||||
return sqlite3_column_double(stmt.impl->stmt, offset);
|
||||
}
|
||||
|
||||
template <> std::string Query::get(int offset) {
|
||||
template <>
|
||||
std::string Query::get(int offset) {
|
||||
assert(stmt.impl);
|
||||
return {
|
||||
reinterpret_cast<const char *>(sqlite3_column_blob(stmt.impl->stmt, offset)),
|
||||
size_t(sqlite3_column_bytes(stmt.impl->stmt, offset))
|
||||
};
|
||||
}
|
||||
|
||||
template <> std::vector<uint8_t> Query::get(int offset) {
|
||||
assert(stmt.impl);
|
||||
const auto* begin = reinterpret_cast<const uint8_t*>(sqlite3_column_blob(stmt.impl->stmt, offset));
|
||||
const uint8_t* end = begin + sqlite3_column_bytes(stmt.impl->stmt, offset);
|
||||
return { begin, end };
|
||||
return {reinterpret_cast<const char*>(sqlite3_column_blob(stmt.impl->stmt, offset)),
|
||||
size_t(sqlite3_column_bytes(stmt.impl->stmt, offset))};
|
||||
}
|
||||
|
||||
template <>
|
||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>
|
||||
Query::get(int offset) {
|
||||
std::vector<uint8_t> Query::get(int offset) {
|
||||
assert(stmt.impl);
|
||||
const auto* begin = reinterpret_cast<const uint8_t*>(sqlite3_column_blob(stmt.impl->stmt, offset));
|
||||
const uint8_t* end = begin + sqlite3_column_bytes(stmt.impl->stmt, offset);
|
||||
return {begin, end};
|
||||
}
|
||||
|
||||
template <>
|
||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> Query::get(int offset) {
|
||||
assert(stmt.impl);
|
||||
return std::chrono::time_point_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::from_time_t(sqlite3_column_int64(stmt.impl->stmt, offset)));
|
||||
}
|
||||
|
||||
template <> std::optional<int64_t> Query::get(int offset) {
|
||||
template <>
|
||||
std::optional<int64_t> Query::get(int offset) {
|
||||
assert(stmt.impl);
|
||||
if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) {
|
||||
return std::nullopt;
|
||||
@@ -395,7 +416,8 @@ template <> std::optional<int64_t> Query::get(int offset) {
|
||||
}
|
||||
}
|
||||
|
||||
template <> std::optional<double> Query::get(int offset) {
|
||||
template <>
|
||||
std::optional<double> Query::get(int offset) {
|
||||
assert(stmt.impl);
|
||||
if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) {
|
||||
return std::nullopt;
|
||||
@@ -404,7 +426,8 @@ template <> std::optional<double> Query::get(int offset) {
|
||||
}
|
||||
}
|
||||
|
||||
template <> std::optional<std::string> Query::get(int offset) {
|
||||
template <>
|
||||
std::optional<std::string> Query::get(int offset) {
|
||||
assert(stmt.impl);
|
||||
if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) {
|
||||
return std::nullopt;
|
||||
@@ -414,14 +437,12 @@ template <> std::optional<std::string> Query::get(int offset) {
|
||||
}
|
||||
|
||||
template <>
|
||||
std::optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>>
|
||||
Query::get(int offset) {
|
||||
std::optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> Query::get(int offset) {
|
||||
assert(stmt.impl);
|
||||
if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) {
|
||||
return std::nullopt;
|
||||
} else {
|
||||
return get<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>>(
|
||||
offset);
|
||||
return get<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>>(offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,15 +470,15 @@ uint64_t Query::changes() const {
|
||||
Transaction::Transaction(Database& db_, Mode mode)
|
||||
: dbImpl(*db_.impl) {
|
||||
switch (mode) {
|
||||
case Deferred:
|
||||
dbImpl.exec("BEGIN DEFERRED TRANSACTION");
|
||||
break;
|
||||
case Immediate:
|
||||
dbImpl.exec("BEGIN IMMEDIATE TRANSACTION");
|
||||
break;
|
||||
case Exclusive:
|
||||
dbImpl.exec("BEGIN EXCLUSIVE TRANSACTION");
|
||||
break;
|
||||
case Deferred:
|
||||
dbImpl.exec("BEGIN DEFERRED TRANSACTION");
|
||||
break;
|
||||
case Immediate:
|
||||
dbImpl.exec("BEGIN IMMEDIATE TRANSACTION");
|
||||
break;
|
||||
case Exclusive:
|
||||
dbImpl.exec("BEGIN EXCLUSIVE TRANSACTION");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user