From 1d8235f5b899a2cd8414522b2d72b96fab91577b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Thu, 13 Dec 2018 18:45:29 +0100 Subject: [build] rework platform/default directory and add -files.txt for vendored libs --- platform/default/src/mbgl/storage/sqlite3.cpp | 494 ++++++++++++++++++++++++++ 1 file changed, 494 insertions(+) create mode 100644 platform/default/src/mbgl/storage/sqlite3.cpp (limited to 'platform/default/src/mbgl/storage/sqlite3.cpp') diff --git a/platform/default/src/mbgl/storage/sqlite3.cpp b/platform/default/src/mbgl/storage/sqlite3.cpp new file mode 100644 index 0000000000..0017dc45db --- /dev/null +++ b/platform/default/src/mbgl/storage/sqlite3.cpp @@ -0,0 +1,494 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace mapbox { +namespace sqlite { + +static_assert(mbgl::underlying_type(ResultCode::OK) == SQLITE_OK, "error"); +static_assert(mbgl::underlying_type(ResultCode::Error) == SQLITE_ERROR, "error"); +static_assert(mbgl::underlying_type(ResultCode::Internal) == SQLITE_INTERNAL, "error"); +static_assert(mbgl::underlying_type(ResultCode::Perm) == SQLITE_PERM, "error"); +static_assert(mbgl::underlying_type(ResultCode::Abort) == SQLITE_ABORT, "error"); +static_assert(mbgl::underlying_type(ResultCode::Busy) == SQLITE_BUSY, "error"); +static_assert(mbgl::underlying_type(ResultCode::Locked) == SQLITE_LOCKED, "error"); +static_assert(mbgl::underlying_type(ResultCode::NoMem) == SQLITE_NOMEM, "error"); +static_assert(mbgl::underlying_type(ResultCode::ReadOnly) == SQLITE_READONLY, "error"); +static_assert(mbgl::underlying_type(ResultCode::Interrupt) == SQLITE_INTERRUPT, "error"); +static_assert(mbgl::underlying_type(ResultCode::IOErr) == SQLITE_IOERR, "error"); +static_assert(mbgl::underlying_type(ResultCode::Corrupt) == SQLITE_CORRUPT, "error"); +static_assert(mbgl::underlying_type(ResultCode::NotFound) == SQLITE_NOTFOUND, "error"); +static_assert(mbgl::underlying_type(ResultCode::Full) == SQLITE_FULL, "error"); +static_assert(mbgl::underlying_type(ResultCode::CantOpen) == SQLITE_CANTOPEN, "error"); +static_assert(mbgl::underlying_type(ResultCode::Protocol) == SQLITE_PROTOCOL, "error"); +static_assert(mbgl::underlying_type(ResultCode::Schema) == SQLITE_SCHEMA, "error"); +static_assert(mbgl::underlying_type(ResultCode::TooBig) == SQLITE_TOOBIG, "error"); +static_assert(mbgl::underlying_type(ResultCode::Constraint) == SQLITE_CONSTRAINT, "error"); +static_assert(mbgl::underlying_type(ResultCode::Mismatch) == SQLITE_MISMATCH, "error"); +static_assert(mbgl::underlying_type(ResultCode::Misuse) == SQLITE_MISUSE, "error"); +static_assert(mbgl::underlying_type(ResultCode::NoLFS) == SQLITE_NOLFS, "error"); +static_assert(mbgl::underlying_type(ResultCode::Auth) == SQLITE_AUTH, "error"); +static_assert(mbgl::underlying_type(ResultCode::Range) == SQLITE_RANGE, "error"); +static_assert(mbgl::underlying_type(ResultCode::NotADB) == SQLITE_NOTADB, "error"); + +void setTempPath(const std::string& path) { + sqlite3_temp_directory = sqlite3_mprintf("%s", path.c_str()); +} + +class DatabaseImpl { +public: + 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, "Failed to enable extended result codes: %s", sqlite3_errmsg(db)); + } + } + + ~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)); + } + } + + void setBusyTimeout(std::chrono::milliseconds timeout); + void exec(const std::string& sql); + + sqlite3* db; +}; + +class StatementImpl { +public: + 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) }; + } + } + + ~StatementImpl() + { + if (!stmt) return; + + sqlite3_finalize(stmt); + } + + void check(int err) { + if (err != SQLITE_OK) { + throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt)) }; + } + } + + sqlite3_stmt* stmt = nullptr; + int64_t lastInsertRowId = 0; + int64_t changes = 0; +}; + +template +using optional = std::experimental::optional; + + +#ifndef NDEBUG +void logSqlMessage(void *, const int err, const char *msg) { + mbgl::Log::Record(mbgl::EventSeverity::Debug, mbgl::Event::Database, err, "%s", msg); +} +#endif + +__attribute__((constructor)) +static void initalize() { + 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()); + throw std::runtime_error(message); + } + +#ifndef NDEBUG + // Enable SQLite logging before initializing the database. + sqlite3_config(SQLITE_CONFIG_LOG, &logSqlMessage, nullptr); +#endif +} + +mapbox::util::variant 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); + return Exception { error, message }; + } + return Database(std::make_unique(db)); +} + +Database Database::open(const std::string &filename, int flags) { + auto result = tryOpen(filename, flags); + if (result.is()) { + throw result.get(); + } else { + return std::move(result.get()); + } +} + +Database::Database(std::unique_ptr impl_) + : impl(std::move(impl_)) +{} + +Database::Database(Database &&other) + : impl(std::move(other.impl)) {} + +Database &Database::operator=(Database &&other) { + std::swap(impl, other.impl); + return *this; +} + +Database::~Database() = default; + +void Database::setBusyTimeout(std::chrono::milliseconds timeout) { + assert(impl); + impl->setBusyTimeout(timeout); +} + +void DatabaseImpl::setBusyTimeout(std::chrono::milliseconds timeout) { + const int err = sqlite3_busy_timeout(db, + int(std::min(timeout.count(), std::numeric_limits::max()))); + if (err != SQLITE_OK) { + throw Exception { err, sqlite3_errmsg(db) }; + } +} + +void Database::exec(const std::string &sql) { + assert(impl); + impl->exec(sql); +} + +void DatabaseImpl::exec(const std::string& sql) { + 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 }; + } else if (err != SQLITE_OK) { + throw Exception { err, sqlite3_errmsg(db) }; + } +} + +Statement::Statement(Database& db, const char* sql) + : impl(std::make_unique(db.impl->db, sql)) { +} + +Statement::~Statement() { +#ifndef NDEBUG + // Crash if we're destructing this object while we know a Query object references this. + assert(!used); +#endif +} + +Query::Query(Statement& stmt_) : stmt(stmt_) { + assert(stmt.impl); + +#ifndef NDEBUG + assert(!stmt.used); + stmt.used = true; +#endif +} + +Query::~Query() { + reset(); + clearBindings(); + +#ifndef NDEBUG + stmt.used = false; +#endif +} + +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) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, 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) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, 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) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, 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) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, 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) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_double(stmt.impl->stmt, offset, 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) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_text(stmt.impl->stmt, offset, value, -1, SQLITE_STATIC)); +} + +// We currently cannot use sqlite3_bind_blob64 / sqlite3_bind_text64 because they +// were introduced in SQLite 3.8.7, and we need to support earlier versions: +// Android 11: 3.7 +// Android 21: 3.8 +// Android 24: 3.9 +// Per https://developer.android.com/reference/android/database/sqlite/package-summary. +// The first iOS version with 3.8.7+ was 9.0, with 3.8.8. + +void Query::bind(int offset, const char * value, std::size_t length, bool retain) { + assert(stmt.impl); + if (length > std::numeric_limits::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)); +} + +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) { + assert(stmt.impl); + if (length > std::numeric_limits::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)); +} + +void Query::bindBlob(int offset, const std::vector& value, bool retain) { + bindBlob(offset, value.data(), value.size(), retain); +} + +template <> +void Query::bind( + int offset, std::chrono::time_point 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, optional value) { + if (!value) { + bind(offset, nullptr); + } else { + bind(offset, *value); + } +} + +template <> +void Query::bind( + int offset, + optional> value) { + if (!value) { + bind(offset, nullptr); + } else { + bind(offset, *value); + } +} + +bool Query::run() { + assert(stmt.impl); + const int err = sqlite3_step(stmt.impl->stmt); + stmt.impl->lastInsertRowId = sqlite3_last_insert_rowid(sqlite3_db_handle(stmt.impl->stmt)); + stmt.impl->changes = sqlite3_changes(sqlite3_db_handle(stmt.impl->stmt)); + if (err == SQLITE_DONE) { + return false; + } else if (err == SQLITE_ROW) { + return true; + } else if (err != SQLITE_OK) { + throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt.impl->stmt)) }; + } else { + return false; + } +} + +template <> bool Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_int(stmt.impl->stmt, 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) { + assert(stmt.impl); + return sqlite3_column_int64(stmt.impl->stmt, 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) { + assert(stmt.impl); + return { + reinterpret_cast(sqlite3_column_blob(stmt.impl->stmt, offset)), + size_t(sqlite3_column_bytes(stmt.impl->stmt, offset)) + }; +} + +template <> std::vector Query::get(int offset) { + assert(stmt.impl); + const auto* begin = reinterpret_cast(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 +Query::get(int offset) { + assert(stmt.impl); + return std::chrono::time_point_cast( + std::chrono::system_clock::from_time_t(sqlite3_column_int64(stmt.impl->stmt, offset))); +} + +template <> optional Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { + return optional(); + } else { + return get(offset); + } +} + +template <> optional Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { + return optional(); + } else { + return get(offset); + } +} + +template <> optional Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { + return optional(); + } else { + return get(offset); + } +} + +template <> +optional> +Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { + return {}; + } else { + return get>( + offset); + } +} + +void Query::reset() { + assert(stmt.impl); + sqlite3_reset(stmt.impl->stmt); +} + +void Query::clearBindings() { + assert(stmt.impl); + sqlite3_clear_bindings(stmt.impl->stmt); +} + +int64_t Query::lastInsertRowId() const { + assert(stmt.impl); + return stmt.impl->lastInsertRowId; +} + +uint64_t Query::changes() const { + assert(stmt.impl); + auto changes_ = stmt.impl->changes; + return (changes_ < 0 ? 0 : changes_); +} + +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; + } +} + +Transaction::~Transaction() { + if (needRollback) { + try { + rollback(); + } catch (...) { + // Ignore failed rollbacks in destructor. + } + } +} + +void Transaction::commit() { + needRollback = false; + dbImpl.exec("COMMIT TRANSACTION"); +} + +void Transaction::rollback() { + needRollback = false; + dbImpl.exec("ROLLBACK TRANSACTION"); +} + +} // namespace sqlite +} // namespace mapbox -- cgit v1.2.1