Avoid exceptions for flow control during database creation

Unfortuntely, it's difficult to avoid all exceptions, because sqlite3_open_v2 does not reliably return SQLITE_NOTADB if the file is not a database. However, this should avoid cases where developers misinterpret the SQLITE_CANTOPEN exception as a crash, which is the common case.
This commit is contained in:
John Firebaugh
2018-05-08 10:40:35 -07:00
parent d1dff1d6e7
commit a1195f6a9d
7 changed files with 137 additions and 102 deletions

View File

@@ -14,27 +14,20 @@ namespace sqlite {
class DatabaseImpl {
public:
DatabaseImpl(const char* filename, int flags)
DatabaseImpl(sqlite3* db_)
: db(db_)
{
const int error = sqlite3_open_v2(filename, &db, flags, nullptr);
if (error != SQLITE_OK) {
const auto message = sqlite3_errmsg(db);
db = nullptr;
throw Exception { error, message };
}
}
~DatabaseImpl()
{
if (!db) return;
const int error = sqlite3_close(db);
if (error != SQLITE_OK) {
mbgl::Log::Error(mbgl::Event::Database, "%s (Code %i)", sqlite3_errmsg(db), error);
}
}
sqlite3* db = nullptr;
sqlite3* db;
};
class StatementImpl {
@@ -164,11 +157,29 @@ const static bool sqliteVersionCheck __attribute__((unused)) = []() {
return true;
}();
Database::Database(const std::string &filename, int flags)
: impl(std::make_unique<DatabaseImpl>(filename.c_str(), 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, nullptr);
if (error != SQLITE_OK) {
const auto message = sqlite3_errmsg(db);
return Exception { error, message };
}
return Database(std::make_unique<DatabaseImpl>(db));
}
Database Database::open(const std::string &filename, int flags) {
auto result = tryOpen(filename, flags);
if (result.is<Exception>()) {
throw result.get<Exception>();
} else {
return std::move(result.get<Database>());
}
}
Database::Database(std::unique_ptr<DatabaseImpl> impl_)
: impl(std::move(impl_))
{}
Database::Database(Database &&other)
: impl(std::move(other.impl)) {}