db backend: mostly feature-complete
This commit is contained in:
parent
872bb95acf
commit
0afe9ca6bf
12 changed files with 497 additions and 160 deletions
|
|
@ -4,15 +4,85 @@
|
|||
|
||||
#include "sqlitebackend.h"
|
||||
|
||||
SQLiteSaveFile::SQLiteSaveFile(QObject *parent, QString filename) :
|
||||
SQLiteSaveFile::SQLiteSaveFile(QObject *parent) :
|
||||
QObject(parent)
|
||||
, lastError(NoError)
|
||||
, lastErrorString(QString())
|
||||
, db(QSqlDatabase::addDatabase("QSQLITE", QUuid::createUuid().toString()))
|
||||
, filename(filename)
|
||||
, m_isOpen(false)
|
||||
, m_lastError(NoError)
|
||||
, m_lastErrorString(QString())
|
||||
, m_open(false)
|
||||
, m_dirty(false)
|
||||
{
|
||||
connect();
|
||||
clearNew();
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::open(const QString &filename)
|
||||
{
|
||||
qDebug() << "open";
|
||||
{
|
||||
QMutexLocker l(&m_dbMut);
|
||||
QFile f(filename);
|
||||
if (!f.exists()) {
|
||||
setError(FileNotFoundError, QString("File \"%1\"does not exist.").arg(filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
QSqlDatabase new_db(QSqlDatabase::addDatabase("QSQLITE", QUuid::createUuid().toString()));
|
||||
new_db.setDatabaseName(f.fileName());
|
||||
if (!new_db.open()) {
|
||||
setDatabaseError(new_db);
|
||||
new_db.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_db.close();
|
||||
m_db = new_db;
|
||||
|
||||
/* Try to load image, ignore if image is unset */
|
||||
QSqlQuery q("SELECT data FROM blobs WHERE name = 'image'", m_db);
|
||||
if (!q.exec())
|
||||
return setDatabaseError(q);
|
||||
if (!q.next())
|
||||
return setDatabaseError(q);
|
||||
|
||||
m_memory = false;
|
||||
m_dirty = false;
|
||||
m_open = true;
|
||||
m_image = q.value(0).toByteArray();
|
||||
}
|
||||
fileReload(); /* Call after unlocking mutex to allow accesses by receivers */
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::clearNew()
|
||||
{
|
||||
qDebug() << "clearNew";
|
||||
{
|
||||
QMutexLocker l(&m_dbMut);
|
||||
|
||||
QSqlDatabase new_db(QSqlDatabase::addDatabase("QSQLITE", QUuid::createUuid().toString()));
|
||||
new_db.setDatabaseName(":memory:");
|
||||
if (!new_db.open()) {
|
||||
setDatabaseError(new_db);
|
||||
new_db.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
QSqlDatabase old_db = m_db;
|
||||
m_db = new_db;
|
||||
|
||||
if (!initDb(true)) {
|
||||
m_db.close();
|
||||
m_db = old_db;
|
||||
return false;
|
||||
}
|
||||
|
||||
old_db.close();
|
||||
m_memory = true;
|
||||
m_dirty = false;
|
||||
m_open = true;
|
||||
m_image = QByteArray();
|
||||
}
|
||||
fileReload(); /* Call after unlocking mutex to allow accesses by receivers */
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::initDb(bool setCreationDate)
|
||||
|
|
@ -33,38 +103,6 @@ bool SQLiteSaveFile::initDb(bool setCreationDate)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::connect()
|
||||
{
|
||||
QMutexLocker l(&dbMut);
|
||||
resetError();
|
||||
m_isOpen = false;
|
||||
imageData = QByteArray();
|
||||
|
||||
bool newlyCreated = QFile(filename).exists();
|
||||
|
||||
db.setDatabaseName(filename);
|
||||
if (!db.open()) {
|
||||
setDatabaseError(db);
|
||||
db.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initDb(newlyCreated)) {
|
||||
db.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_isOpen = true;
|
||||
|
||||
/* Try to load image, ignore if image is unset */
|
||||
QSqlQuery q("SELECT data FROM blobs WHERE name = 'image'", db);
|
||||
if (!q.next())
|
||||
return setDatabaseError(q);
|
||||
|
||||
imageData = q.value(0).toByteArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
static sqlite3 *getSqliteHandle(QSqlDatabase &db) {
|
||||
QVariant v = db.driver()->handle();
|
||||
assert (v.isValid());
|
||||
|
|
@ -74,66 +112,50 @@ static sqlite3 *getSqliteHandle(QSqlDatabase &db) {
|
|||
|
||||
bool SQLiteSaveFile::saveAs(const QString &filename)
|
||||
{
|
||||
QMutexLocker l(&dbMut);
|
||||
qDebug() << "saveAs" << filename;
|
||||
QMutexLocker l(&m_dbMut);
|
||||
QFile f(filename);
|
||||
QSqlDatabase old_db = db;
|
||||
QSqlDatabase new_db(QSqlDatabase::addDatabase("QSQLITE", QUuid::createUuid().toString()));
|
||||
new_db.setDatabaseName(f.fileName());
|
||||
if (!new_db.open()) {
|
||||
setDatabaseError(new_db);
|
||||
new_db.close();
|
||||
return false;
|
||||
{
|
||||
new_db.setDatabaseName(f.fileName());
|
||||
if (!new_db.open())
|
||||
goto err_cleanup;
|
||||
|
||||
sqlite3 *old_handle = getSqliteHandle(m_db);
|
||||
sqlite3 *new_handle = getSqliteHandle(new_db);
|
||||
sqlite3_backup *bck = sqlite3_backup_init(new_handle, "main", old_handle, "main");
|
||||
if (!bck)
|
||||
goto err_cleanup;
|
||||
|
||||
if (sqlite3_backup_step(bck, -1) != SQLITE_DONE)
|
||||
goto err_cleanup;
|
||||
|
||||
if (sqlite3_backup_finish(bck) != SQLITE_DONE)
|
||||
goto err_cleanup;
|
||||
|
||||
m_db.close();
|
||||
m_db = new_db;
|
||||
m_memory = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
sqlite3 *old_handle = getSqliteHandle(db);
|
||||
sqlite3 *new_handle = getSqliteHandle(new_db);
|
||||
sqlite3_backup *bck = sqlite3_backup_init(new_handle, "main", old_handle, "main");
|
||||
if (!bck)
|
||||
goto err_cleanup;
|
||||
|
||||
if (sqlite3_backup_step(bck, -1) != SQLITE_DONE)
|
||||
goto err_cleanup;
|
||||
|
||||
if (sqlite3_backup_finish(bck) != SQLITE_DONE)
|
||||
goto err_cleanup;
|
||||
|
||||
db = new_db;
|
||||
old_db.close();
|
||||
return true;
|
||||
|
||||
err_cleanup:
|
||||
setDatabaseError(new_db);
|
||||
new_db.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::clearNew()
|
||||
{
|
||||
QMutexLocker l(&dbMut);
|
||||
|
||||
QSqlDatabase new_db(QSqlDatabase::addDatabase("QSQLITE", QUuid::createUuid().toString()));
|
||||
new_db.setDatabaseName(":memory:");
|
||||
if (!new_db.open()) {
|
||||
setDatabaseError(new_db);
|
||||
new_db.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initDb()) {
|
||||
db.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
imageData = QByteArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<Tag> SQLiteSaveFile::getAllTags()
|
||||
{
|
||||
QMutexLocker l(&dbMut);
|
||||
qDebug() << "getAllTags";
|
||||
QMutexLocker l(&m_dbMut);
|
||||
resetError();
|
||||
QList<Tag> rv;
|
||||
QSqlQuery q("SELECT (id, name, anchor_x, anchor_y, meta) FROM tags", db);
|
||||
|
||||
QSqlQuery q("SELECT id, name, anchor_x, anchor_y, meta FROM tags", m_db);
|
||||
if (!q.exec()) {
|
||||
setDatabaseError(q);
|
||||
return QList<Tag>();
|
||||
}
|
||||
|
||||
while (q.next()) {
|
||||
rv << Tag {
|
||||
|
|
@ -153,39 +175,44 @@ QList<Tag> SQLiteSaveFile::getAllTags()
|
|||
|
||||
bool SQLiteSaveFile::updateTag(Tag tag)
|
||||
{
|
||||
QMutexLocker l(&dbMut);
|
||||
QMutexLocker l(&m_dbMut);
|
||||
if (!runSql("UPDATE tags SET name=?, anchor_x=?, anchor_y=?, meta=? WHERE id=?", {
|
||||
tag.name, tag.anchor.x(), tag.anchor.y(), QJsonDocument::fromVariant(tag.metadata).toJson(), tag.id
|
||||
}))
|
||||
return false;
|
||||
|
||||
m_dirty = true;
|
||||
tagChange(TagChange::CHANGED, tag);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::deleteTag(Tag tag)
|
||||
{
|
||||
QMutexLocker l(&dbMut);
|
||||
QMutexLocker l(&m_dbMut);
|
||||
if (!runSql("DELETE FROM tags WHERE id=?", {tag.id}))
|
||||
return false;
|
||||
|
||||
m_dirty = true;
|
||||
tagChange(TagChange::DELETED, tag);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::createTag(Tag tag)
|
||||
{
|
||||
QMutexLocker l(&dbMut);
|
||||
qDebug() << "createTag";
|
||||
QMutexLocker l(&m_dbMut);
|
||||
resetError();
|
||||
QSqlQuery q("INSERT INTO tags(name, anchor_x, anchor_y, meta) VALUES (?, ?, ?, ?)", db);
|
||||
QSqlQuery q("INSERT INTO tags(name, anchor_x, anchor_y, meta) VALUES (?, ?, ?, ?)", m_db);
|
||||
q.addBindValue(tag.name);
|
||||
q.addBindValue(tag.anchor.x());
|
||||
q.addBindValue(tag.anchor.y());
|
||||
q.addBindValue(QJsonDocument::fromVariant(tag.metadata).toJson());
|
||||
|
||||
if (!q.exec())
|
||||
return setDatabaseError(q);
|
||||
|
||||
Tag created_tag(q.lastInsertId().toLongLong(), tag);
|
||||
m_dirty = true;
|
||||
tagChange(TagChange::CREATED, created_tag);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -205,25 +232,34 @@ bool SQLiteSaveFile::setMetaLocked(std::initializer_list<QPair<QString, QVariant
|
|||
}
|
||||
|
||||
bool SQLiteSaveFile::setMeta(const QString &key, const QVariant &value) {
|
||||
QMutexLocker l(&dbMut);
|
||||
QMutexLocker l(&m_dbMut);
|
||||
m_dirty = true;
|
||||
return setMetaLocked(key, value);
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::setMeta(std::initializer_list<QPair<QString, QVariant>> metas) {
|
||||
QMutexLocker l(&dbMut);
|
||||
QMutexLocker l(&m_dbMut);
|
||||
m_dirty = true;
|
||||
return setMetaLocked(metas);
|
||||
}
|
||||
|
||||
QVariant SQLiteSaveFile::getMeta(const QString &key) {
|
||||
QMutexLocker l(&dbMut);
|
||||
const QVariant SQLiteSaveFile::getMeta(const QString &key) const {
|
||||
QMutexLocker l(&m_dbMut);
|
||||
return getMetaLocked(key);
|
||||
}
|
||||
|
||||
QVariant SQLiteSaveFile::getMetaLocked(const QString &key)
|
||||
const QVariant SQLiteSaveFile::getMetaLocked(const QString &key) const
|
||||
{
|
||||
qDebug() << "getMeta " << key;
|
||||
resetError();
|
||||
QSqlQuery q("SELECT value FROM metadata WHERE key=?", db);
|
||||
QSqlQuery q("SELECT value FROM metadata WHERE key=?", m_db);
|
||||
q.addBindValue(key);
|
||||
|
||||
if (!q.exec()) {
|
||||
setDatabaseError(q);
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (!q.next()) {
|
||||
setDatabaseError(q);
|
||||
return QVariant();
|
||||
|
|
@ -234,18 +270,22 @@ QVariant SQLiteSaveFile::getMetaLocked(const QString &key)
|
|||
|
||||
bool SQLiteSaveFile::runSql(QString query, std::initializer_list<QVariant> bindings)
|
||||
{
|
||||
qDebug() << "runSql " << query;
|
||||
resetError();
|
||||
QSqlQuery q(query, db);
|
||||
for (const QVariant &v : bindings)
|
||||
QSqlQuery q(query, m_db);
|
||||
for (const QVariant &v : bindings) {
|
||||
q.addBindValue(v);
|
||||
}
|
||||
|
||||
q.exec();
|
||||
return setDatabaseError(q);
|
||||
if (!q.exec()) {
|
||||
return setDatabaseError(q);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::loadImageFromDisk(const QString &filename)
|
||||
{
|
||||
QMutexLocker l(&dbMut);
|
||||
QMutexLocker l(&m_dbMut);
|
||||
QFile f(filename);
|
||||
resetError();
|
||||
|
||||
|
|
@ -254,7 +294,7 @@ bool SQLiteSaveFile::loadImageFromDisk(const QString &filename)
|
|||
return false;
|
||||
}
|
||||
|
||||
imageData = f.readAll();
|
||||
m_image = f.readAll();
|
||||
if (f.error() != QFileDevice::NoError) {
|
||||
setError(ImageReadError, QString("Failed to read image: %1").arg(f.errorString()));
|
||||
return false;
|
||||
|
|
@ -266,7 +306,8 @@ bool SQLiteSaveFile::loadImageFromDisk(const QString &filename)
|
|||
{"imageLoadedTime", QDateTime::currentDateTimeUtc().toMSecsSinceEpoch()}}))
|
||||
return false;
|
||||
|
||||
return runSql("INSERT OR REPLACE INTO blobs(name, data) VALUES ('image', ?)", {imageData});
|
||||
m_dirty = true;
|
||||
return runSql("INSERT OR REPLACE INTO blobs(name, data) VALUES ('image', ?)", {m_image});
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::reloadImageFromDisk()
|
||||
|
|
@ -287,11 +328,7 @@ Tag::Tag(long long id, QString name, qreal anchor_x, qreal anchor_y, QByteArray
|
|||
, name(name)
|
||||
, anchor(QPointF(anchor_x, anchor_y))
|
||||
, metadata(QJsonDocument::fromJson(metadata).object().toVariantMap())
|
||||
{
|
||||
}
|
||||
|
||||
Tag::Tag()
|
||||
: id(-1)
|
||||
, valid(true)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -300,23 +337,26 @@ Tag::Tag(long long id, const Tag &other)
|
|||
, name(other.name)
|
||||
, anchor(other.anchor)
|
||||
, metadata(other.metadata)
|
||||
, valid(true)
|
||||
{
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::setDatabaseError(const QSqlQuery &q)
|
||||
bool SQLiteSaveFile::setDatabaseError(const QSqlQuery &q) const
|
||||
{
|
||||
if (!q.lastError().isValid())
|
||||
return true;
|
||||
|
||||
setError(SQLiteError, QString("Project file database error: %1").arg(q.lastError().text()));
|
||||
qDebug() << "Query error: " << q.lastError().text();
|
||||
setError(SQLiteError, QString("Project file database error executing %1: %2").arg(q.executedQuery()).arg(q.lastError().text()));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::setDatabaseError(const QSqlDatabase &db)
|
||||
bool SQLiteSaveFile::setDatabaseError(const QSqlDatabase &db) const
|
||||
{
|
||||
if (!db.lastError().isValid())
|
||||
return true;
|
||||
|
||||
qDebug() << "Database error: " << db.lastError().text();
|
||||
setError(SQLiteError, QString("Project file database error: %1").arg(db.lastError().text()));
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue