mirror of https://github.com/stella-emu/stella.git
Add transaction, rework error handling.
This commit is contained in:
parent
a6e7dcf399
commit
408dd6a30c
|
@ -27,13 +27,50 @@
|
||||||
#define SEPARATOR "/"
|
#define SEPARATOR "/"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct SqliteError {
|
||||||
|
SqliteError(const string _message) : message(_message) {}
|
||||||
|
|
||||||
|
const string message;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Statement {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Statement(sqlite3* handle, string sql) : myStmt(nullptr)
|
||||||
|
{
|
||||||
|
if (sqlite3_prepare_v2(handle, sql.c_str(), -1, &myStmt, nullptr) != SQLITE_OK)
|
||||||
|
throw SqliteError(sqlite3_errmsg(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
~Statement()
|
||||||
|
{
|
||||||
|
if (myStmt) sqlite3_finalize(myStmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
operator sqlite3_stmt*() const { return myStmt; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
sqlite3_stmt* myStmt;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Statement() = delete;
|
||||||
|
Statement(const Statement&) = delete;
|
||||||
|
Statement(Statement&&) = delete;
|
||||||
|
Statement& operator=(const Statement&) = delete;
|
||||||
|
Statement& operator=(Statement&&) = delete;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
KeyValueRepositorySqlite::KeyValueRepositorySqlite(
|
KeyValueRepositorySqlite::KeyValueRepositorySqlite(
|
||||||
const string& databaseDirectory,
|
const string& databaseDirectory,
|
||||||
const string& databaseName
|
const string& databaseName
|
||||||
) : myDatabaseFile(databaseDirectory + SEPARATOR + databaseName + ".sqlite3"),
|
) : myDatabaseFile(databaseDirectory + SEPARATOR + databaseName + ".sqlite3"),
|
||||||
myDbHandle(nullptr),
|
myIsFailed(false),
|
||||||
myDbInitialized(false)
|
myDbHandle(nullptr)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -46,26 +83,19 @@ KeyValueRepositorySqlite::~KeyValueRepositorySqlite()
|
||||||
std::map<string, Variant> KeyValueRepositorySqlite::load()
|
std::map<string, Variant> KeyValueRepositorySqlite::load()
|
||||||
{
|
{
|
||||||
std::map<string, Variant> values;
|
std::map<string, Variant> values;
|
||||||
|
if (myIsFailed) return values;
|
||||||
|
|
||||||
|
try {
|
||||||
initializeDb();
|
initializeDb();
|
||||||
|
Statement stmt(myDbHandle, "SELECT `key`, `VALUE` FROM `values`");
|
||||||
if (!myDbInitialized) {
|
|
||||||
cout << "Unable to load from sqlite DB " << myDatabaseFile << endl;
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlite3_stmt* stmt;
|
|
||||||
if (sqlite3_prepare_v2(
|
|
||||||
myDbHandle,
|
|
||||||
"SELECT `key`, `VALUE` FROM `values`",
|
|
||||||
-1, &stmt, nullptr
|
|
||||||
) != SQLITE_OK) return values;
|
|
||||||
|
|
||||||
while (sqlite3_step(stmt) == SQLITE_ROW)
|
while (sqlite3_step(stmt) == SQLITE_ROW)
|
||||||
values[reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0))] =
|
values[reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0))] =
|
||||||
reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
|
reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
|
||||||
|
}
|
||||||
sqlite3_finalize(stmt);
|
catch (SqliteError err) {
|
||||||
|
cout << "failed to load from sqlite DB " << myDatabaseFile << ": " << err.message << endl;
|
||||||
|
}
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
@ -73,52 +103,66 @@ std::map<string, Variant> KeyValueRepositorySqlite::load()
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void KeyValueRepositorySqlite::save(const std::map<string, Variant>& values)
|
void KeyValueRepositorySqlite::save(const std::map<string, Variant>& values)
|
||||||
{
|
{
|
||||||
|
if (myIsFailed) return;
|
||||||
|
|
||||||
|
try {
|
||||||
initializeDb();
|
initializeDb();
|
||||||
|
Statement stmt(myDbHandle, "INSERT OR REPLACE INTO `values` VALUES (?, ?)");
|
||||||
|
|
||||||
if (!myDbInitialized) {
|
if (sqlite3_exec(myDbHandle, "BEGIN TRANSACTION", nullptr, nullptr, nullptr) != SQLITE_OK)
|
||||||
cout << "Unable to save to sqlite DB " << myDatabaseFile << endl;
|
throw SqliteError(sqlite3_errmsg(myDbHandle));
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlite3_stmt* stmt;
|
|
||||||
if (sqlite3_prepare_v2(
|
|
||||||
myDbHandle,
|
|
||||||
"INSERT OR REPLACE INTO `values` VALUES (?, ?)",
|
|
||||||
-1, &stmt, nullptr
|
|
||||||
) != SQLITE_OK) return;
|
|
||||||
|
|
||||||
for (const auto& pair: values) {
|
for (const auto& pair: values) {
|
||||||
sqlite3_bind_text(stmt, 1, pair.first.c_str(), -1, SQLITE_STATIC);
|
sqlite3_bind_text(stmt, 1, pair.first.c_str(), -1, SQLITE_STATIC);
|
||||||
sqlite3_bind_text(stmt, 2, pair.second.toCString(), -1, SQLITE_STATIC);
|
sqlite3_bind_text(stmt, 2, pair.second.toCString(), -1, SQLITE_STATIC);
|
||||||
sqlite3_step(stmt);
|
sqlite3_step(stmt);
|
||||||
|
|
||||||
if (sqlite3_reset(stmt) != SQLITE_OK) break;
|
if (sqlite3_reset(stmt) != SQLITE_OK) throw SqliteError(sqlite3_errmsg(myDbHandle));
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
if (sqlite3_exec(myDbHandle, "COMMIT", nullptr, nullptr, nullptr) != SQLITE_OK)
|
||||||
|
throw SqliteError(sqlite3_errmsg(myDbHandle));
|
||||||
|
}
|
||||||
|
catch (SqliteError err) {
|
||||||
|
cout << "failed to write to sqlite DB " << myDatabaseFile << ": " << err.message << endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void KeyValueRepositorySqlite::initializeDb()
|
void KeyValueRepositorySqlite::initializeDb()
|
||||||
{
|
{
|
||||||
if (myDbHandle) return;
|
if (myIsFailed || myDbHandle) return;
|
||||||
|
|
||||||
for (int tries = 1; tries < 3 && !myDbInitialized; tries++) {
|
bool dbInitialized = false;
|
||||||
myDbInitialized = (sqlite3_open(myDatabaseFile.c_str(), &myDbHandle) == SQLITE_OK);
|
|
||||||
|
|
||||||
myDbInitialized = myDbInitialized && (sqlite3_exec(
|
for (int tries = 1; tries < 3 && !dbInitialized; tries++) {
|
||||||
|
dbInitialized = (sqlite3_open(myDatabaseFile.c_str(), &myDbHandle) == SQLITE_OK);
|
||||||
|
|
||||||
|
dbInitialized = dbInitialized && (sqlite3_exec(
|
||||||
myDbHandle,
|
myDbHandle,
|
||||||
"CREATE TABLE IF NOT EXISTS `values` (`key` TEXT PRIMARY KEY, `value` TEXT) WITHOUT ROWID",
|
"CREATE TABLE IF NOT EXISTS `values` (`key` TEXT PRIMARY KEY, `value` TEXT) WITHOUT ROWID",
|
||||||
nullptr, nullptr, nullptr
|
nullptr, nullptr, nullptr
|
||||||
) == SQLITE_OK);
|
) == SQLITE_OK);
|
||||||
|
|
||||||
if (!myDbInitialized && tries == 1) {
|
if (!dbInitialized && tries == 1) {
|
||||||
cout << "sqlite DB " << myDatabaseFile << " seems to be corrupt, removing and retrying..." << endl;
|
cout << "sqlite DB " << myDatabaseFile << " seems to be corrupt, removing and retrying..." << endl;
|
||||||
|
|
||||||
remove(myDatabaseFile.c_str());
|
remove(myDatabaseFile.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!myDbInitialized)
|
myIsFailed = !dbInitialized;
|
||||||
cout << "unable to initialize sqlite DB " << myDatabaseFile << endl;
|
|
||||||
|
if (myIsFailed) {
|
||||||
|
if (myDbHandle) {
|
||||||
|
string emsg = sqlite3_errmsg(myDbHandle);
|
||||||
|
|
||||||
|
sqlite3_close(myDbHandle);
|
||||||
|
myDbHandle = nullptr;
|
||||||
|
|
||||||
|
throw SqliteError(emsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw SqliteError("unable to initialize sqlite DB " + myDatabaseFile);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,8 +42,9 @@ class KeyValueRepositorySqlite : public KeyValueRepository
|
||||||
|
|
||||||
string myDatabaseFile;
|
string myDatabaseFile;
|
||||||
|
|
||||||
|
bool myIsFailed;
|
||||||
|
|
||||||
sqlite3* myDbHandle;
|
sqlite3* myDbHandle;
|
||||||
bool myDbInitialized;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue