diff options
Diffstat (limited to 'platform/default/sqlite3.cpp')
-rw-r--r-- | platform/default/sqlite3.cpp | 320 |
1 files changed, 199 insertions, 121 deletions
diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp index 2e08354fdf..8a567d602e 100644 --- a/platform/default/sqlite3.cpp +++ b/platform/default/sqlite3.cpp @@ -69,14 +69,84 @@ public: template <typename T> using optional = std::experimental::optional<T>; +static const char* codeToString(const int err) { + switch (err) { + case SQLITE_OK: return "SQLITE_OK"; + case SQLITE_ERROR: return "SQLITE_ERROR"; + case SQLITE_INTERNAL: return "SQLITE_INTERNAL"; + case SQLITE_PERM: return "SQLITE_PERM"; + case SQLITE_ABORT: return "SQLITE_ABORT"; + case SQLITE_BUSY: return "SQLITE_BUSY"; + case SQLITE_LOCKED: return "SQLITE_LOCKED"; + case SQLITE_NOMEM: return "SQLITE_NOMEM"; + case SQLITE_READONLY: return "SQLITE_READONLY"; + case SQLITE_INTERRUPT: return "SQLITE_INTERRUPT"; + case SQLITE_IOERR: return "SQLITE_IOERR"; + case SQLITE_CORRUPT: return "SQLITE_CORRUPT"; + case SQLITE_NOTFOUND: return "SQLITE_NOTFOUND"; + case SQLITE_FULL: return "SQLITE_FULL"; + case SQLITE_CANTOPEN: return "SQLITE_CANTOPEN"; + case SQLITE_PROTOCOL: return "SQLITE_PROTOCOL"; + case SQLITE_EMPTY: return "SQLITE_EMPTY"; + case SQLITE_SCHEMA: return "SQLITE_SCHEMA"; + case SQLITE_TOOBIG: return "SQLITE_TOOBIG"; + case SQLITE_CONSTRAINT: return "SQLITE_CONSTRAINT"; + case SQLITE_MISMATCH: return "SQLITE_MISMATCH"; + case SQLITE_MISUSE: return "SQLITE_MISUSE"; + case SQLITE_NOLFS: return "SQLITE_NOLFS"; + case SQLITE_AUTH: return "SQLITE_AUTH"; + case SQLITE_FORMAT: return "SQLITE_FORMAT"; + case SQLITE_RANGE: return "SQLITE_RANGE"; + case SQLITE_NOTADB: return "SQLITE_NOTADB"; + case SQLITE_NOTICE: return "SQLITE_NOTICE"; + case SQLITE_WARNING: return "SQLITE_WARNING"; + case SQLITE_ROW: return "SQLITE_ROW"; + case SQLITE_DONE: return "SQLITE_DONE"; + default: return "<unknown>"; + } +} + static void errorLogCallback(void *, const int err, const char *msg) { - if (err == SQLITE_ERROR) { - mbgl::Log::Error(mbgl::Event::Database, "%s (Code %i)", msg, err); - } else if (err == SQLITE_WARNING) { - mbgl::Log::Warning(mbgl::Event::Database, "%s (Code %i)", msg, err); - } else { - mbgl::Log::Info(mbgl::Event::Database, "%s (Code %i)", msg, err); + auto severity = mbgl::EventSeverity::Info; + + switch (err) { + case SQLITE_ERROR: // Generic error + case SQLITE_INTERNAL: // Internal logic error in SQLite + case SQLITE_PERM: // Access permission denied + case SQLITE_ABORT: // Callback routine requested an abort + case SQLITE_BUSY: // The database file is locked + case SQLITE_LOCKED: // A table in the database is locked + case SQLITE_NOMEM: // A malloc() failed + case SQLITE_READONLY: // Attempt to write a readonly database + case SQLITE_INTERRUPT: // Operation terminated by sqlite3_interrupt( + case SQLITE_IOERR: // Some kind of disk I/O error occurred + case SQLITE_CORRUPT: // The database disk image is malformed + case SQLITE_NOTFOUND: // Unknown opcode in sqlite3_file_control() + case SQLITE_FULL: // Insertion failed because database is full + case SQLITE_CANTOPEN: // Unable to open the database file + case SQLITE_PROTOCOL: // Database lock protocol error + case SQLITE_EMPTY: // Internal use only + case SQLITE_SCHEMA: // The database schema changed + case SQLITE_TOOBIG: // String or BLOB exceeds size limit + case SQLITE_CONSTRAINT: // Abort due to constraint violation + case SQLITE_MISMATCH: // Data type mismatch + case SQLITE_MISUSE: // Library used incorrectly + case SQLITE_NOLFS: // Uses OS features not supported on host + case SQLITE_AUTH: // Authorization denied + case SQLITE_FORMAT: // Not used + case SQLITE_RANGE: // 2nd parameter to sqlite3_bind out of range + case SQLITE_NOTADB: // File opened that is not a database file + severity = mbgl::EventSeverity::Error; + break; + case SQLITE_WARNING: // Warnings from sqlite3_log() + severity = mbgl::EventSeverity::Warning; + break; + case SQLITE_NOTICE: // Notifications from sqlite3_log() + default: + break; } + + mbgl::Log::Record(severity, mbgl::Event::Database, "%s (%s)", msg, codeToString(err)); } const static bool sqliteVersionCheck __attribute__((unused)) = []() { @@ -131,85 +201,93 @@ void Database::exec(const std::string &sql) { } } -Statement Database::prepare(const char *query) { - assert(impl); - return Statement(this, query); +Statement::Statement(Database& db, const char* sql) + : impl(std::make_unique<StatementImpl>(db.impl->db, sql)) { } -Statement::Statement(Database *db, const char *sql) - : impl(std::make_unique<StatementImpl>(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 } -Statement::Statement(Statement &&other) { - *this = std::move(other); -} +Query::Query(Statement& stmt_) : stmt(stmt_) { + assert(stmt.impl); -Statement &Statement::operator=(Statement &&other) { - std::swap(impl, other.impl); - return *this; +#ifndef NDEBUG + assert(!stmt.used); + stmt.used = true; +#endif } -Statement::~Statement() = default; +Query::~Query() { + reset(); + clearBindings(); -template <> void Statement::bind(int offset, std::nullptr_t) { - assert(impl); - impl->check(sqlite3_bind_null(impl->stmt, offset)); +#ifndef NDEBUG + stmt.used = false; +#endif } -template <> void Statement::bind(int offset, int8_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, std::nullptr_t) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_null(stmt.impl->stmt, offset)); } -template <> void Statement::bind(int offset, int16_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, 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 Statement::bind(int offset, int32_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(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 Statement::bind(int offset, int64_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(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 Statement::bind(int offset, uint8_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(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 Statement::bind(int offset, uint16_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(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 Statement::bind(int offset, uint32_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(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 Statement::bind(int offset, float value) { - assert(impl); - impl->check(sqlite3_bind_double(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 Statement::bind(int offset, double value) { - assert(impl); - impl->check(sqlite3_bind_double(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 Statement::bind(int offset, bool value) { - assert(impl); - impl->check(sqlite3_bind_int(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 Statement::bind(int offset, const char *value) { - assert(impl); - impl->check(sqlite3_bind_text(impl->stmt, offset, value, -1, SQLITE_STATIC)); +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 @@ -219,40 +297,40 @@ template <> void Statement::bind(int offset, const char *value) { // According to http://stackoverflow.com/questions/14288128/what-version-of-sqlite-does-ios-provide, // the first iOS version with 3.8.7+ was 9.0, with 3.8.8. -void Statement::bind(int offset, const char * value, std::size_t length, bool retain) { - assert(impl); +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"); } - impl->check(sqlite3_bind_text(impl->stmt, offset, value, int(length), + stmt.impl->check(sqlite3_bind_text(stmt.impl->stmt, offset, value, int(length), retain ? SQLITE_TRANSIENT : SQLITE_STATIC)); } -void Statement::bind(int offset, const std::string& value, bool retain) { +void Query::bind(int offset, const std::string& value, bool retain) { bind(offset, value.data(), value.size(), retain); } -void Statement::bindBlob(int offset, const void * value, std::size_t length, bool retain) { - assert(impl); +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"); } - impl->check(sqlite3_bind_blob(impl->stmt, offset, value, int(length), + stmt.impl->check(sqlite3_bind_blob(stmt.impl->stmt, offset, value, int(length), retain ? SQLITE_TRANSIENT : SQLITE_STATIC)); } -void Statement::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) { +void Query::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) { bindBlob(offset, value.data(), value.size(), retain); } template <> -void Statement::bind( +void Query::bind( int offset, std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, std::chrono::system_clock::to_time_t(value))); + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, std::chrono::system_clock::to_time_t(value))); } -template <> void Statement::bind(int offset, optional<std::string> value) { +template <> void Query::bind(int offset, optional<std::string> value) { if (!value) { bind(offset, nullptr); } else { @@ -261,7 +339,7 @@ template <> void Statement::bind(int offset, optional<std::string> value) { } template <> -void Statement::bind( +void Query::bind( int offset, optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> value) { if (!value) { @@ -271,86 +349,86 @@ void Statement::bind( } } -bool Statement::run() { - assert(impl); - const int err = sqlite3_step(impl->stmt); - impl->lastInsertRowId = sqlite3_last_insert_rowid(sqlite3_db_handle(impl->stmt)); - impl->changes = sqlite3_changes(sqlite3_db_handle(impl->stmt)); +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(impl->stmt)) }; + throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt.impl->stmt)) }; } else { return false; } } -template <> bool Statement::get(int offset) { - assert(impl); - return sqlite3_column_int(impl->stmt, offset); +template <> bool Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_int(stmt.impl->stmt, offset); } -template <> int Statement::get(int offset) { - assert(impl); - return sqlite3_column_int(impl->stmt, offset); +template <> int Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_int(stmt.impl->stmt, offset); } -template <> int64_t Statement::get(int offset) { - assert(impl); - return sqlite3_column_int64(impl->stmt, offset); +template <> int64_t Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_int64(stmt.impl->stmt, offset); } -template <> double Statement::get(int offset) { - assert(impl); - return sqlite3_column_double(impl->stmt, offset); +template <> double Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_double(stmt.impl->stmt, offset); } -template <> std::string Statement::get(int offset) { - assert(impl); +template <> std::string Query::get(int offset) { + assert(stmt.impl); return { - reinterpret_cast<const char *>(sqlite3_column_blob(impl->stmt, offset)), - size_t(sqlite3_column_bytes(impl->stmt, offset)) + 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> Statement::get(int offset) { - assert(impl); - const auto* begin = reinterpret_cast<const uint8_t*>(sqlite3_column_blob(impl->stmt, offset)); - const uint8_t* end = begin + sqlite3_column_bytes(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 }; } template <> std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> -Statement::get(int offset) { - assert(impl); +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(impl->stmt, offset))); + std::chrono::system_clock::from_time_t(sqlite3_column_int64(stmt.impl->stmt, offset))); } -template <> optional<int64_t> Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +template <> optional<int64_t> Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return optional<int64_t>(); } else { return get<int64_t>(offset); } } -template <> optional<double> Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +template <> optional<double> Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return optional<double>(); } else { return get<double>(offset); } } -template <> optional<std::string> Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +template <> optional<std::string> Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return optional<std::string>(); } else { return get<std::string>(offset); @@ -359,9 +437,9 @@ template <> optional<std::string> Statement::get(int offset) { template <> optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> -Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return {}; } else { return get<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>>( @@ -369,24 +447,24 @@ Statement::get(int offset) { } } -void Statement::reset() { - assert(impl); - sqlite3_reset(impl->stmt); +void Query::reset() { + assert(stmt.impl); + sqlite3_reset(stmt.impl->stmt); } -void Statement::clearBindings() { - assert(impl); - sqlite3_clear_bindings(impl->stmt); +void Query::clearBindings() { + assert(stmt.impl); + sqlite3_clear_bindings(stmt.impl->stmt); } -int64_t Statement::lastInsertRowId() const { - assert(impl); - return impl->lastInsertRowId; +int64_t Query::lastInsertRowId() const { + assert(stmt.impl); + return stmt.impl->lastInsertRowId; } -uint64_t Statement::changes() const { - assert(impl); - auto changes_ = impl->changes; +uint64_t Query::changes() const { + assert(stmt.impl); + auto changes_ = stmt.impl->changes; return (changes_ < 0 ? 0 : changes_); } |