summaryrefslogtreecommitdiff
path: root/platform/default/sqlite3.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'platform/default/sqlite3.cpp')
-rw-r--r--platform/default/sqlite3.cpp320
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_);
}