1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Record changes

This commit is contained in:
Laurent Cozic 2017-01-02 15:04:27 +01:00
parent 0a08837ef0
commit 629fe42c59
10 changed files with 127 additions and 77 deletions

View File

@ -6,10 +6,9 @@ Database::Database() {}
void Database::initialize(const QString &path) { void Database::initialize(const QString &path) {
version_ = -1; version_ = -1;
transactionCount_ = 0;
// QFile::remove(path); //QFile::remove(path);
//qDebug() << Select << Text;
db_ = QSqlDatabase::addDatabase("QSQLITE"); db_ = QSqlDatabase::addDatabase("QSQLITE");
db_.setDatabaseName(path); db_.setDatabaseName(path);
@ -123,6 +122,28 @@ bool Database::errorCheck(const QSqlQuery& query) {
return true; return true;
} }
bool Database::transaction() {
transactionCount_++;
if (transactionCount_ > 1) return true;
return db_.transaction();
}
bool Database::commit() {
transactionCount_--;
if (transactionCount_ < 0) {
transactionCount_ = 0;
qCritical() << "Attempting commit on a database that is not in transaction mode";
return false;
}
if (transactionCount_ <= 0) {
return db_.commit();
}
return true;
}
void Database::log(const QString &sql, const QSqlQuery &query) const { void Database::log(const QString &sql, const QSqlQuery &query) const {
qDebug() <<"SQL:"<<sql; qDebug() <<"SQL:"<<sql;

View File

@ -20,6 +20,8 @@ public:
QSqlQuery buildSqlQuery(Database::QueryType type, const QString& tableName, const QStringList& fields, const VariantVector& values, const QString& whereCondition = ""); QSqlQuery buildSqlQuery(Database::QueryType type, const QString& tableName, const QStringList& fields, const VariantVector& values, const QString& whereCondition = "");
QSqlQuery buildSqlQuery(Database::QueryType type, const QString& tableName, const QMap<QString, QVariant>& values, const QString& whereCondition = ""); QSqlQuery buildSqlQuery(Database::QueryType type, const QString& tableName, const QMap<QString, QVariant>& values, const QString& whereCondition = "");
bool errorCheck(const QSqlQuery& query); bool errorCheck(const QSqlQuery& query);
bool transaction();
bool commit();
private: private:
@ -30,6 +32,7 @@ private:
int version() const; int version() const;
mutable int version_; mutable int version_;
QStringList sqlStringToLines(const QString& sql); QStringList sqlStringToLines(const QString& sql);
int transactionCount_;
}; };

View File

@ -5,9 +5,7 @@
namespace jop { namespace jop {
enum ColType { UndefinedType, TextColType, IntColType }; enum Table { UndefinedTable, FoldersTable, NotesTable, ChangesTable };
enum Table { UndefinedTable, FoldersTable, NotesTable };
} }

View File

@ -1,5 +1,6 @@
#include "basemodel.h" #include "basemodel.h"
#include "models/change.h"
#include "database.h" #include "database.h"
#include "uuid.h" #include "uuid.h"
@ -8,9 +9,7 @@ using namespace jop;
QMap<int, QVector<BaseModel::Field>> BaseModel::tableFields_; QMap<int, QVector<BaseModel::Field>> BaseModel::tableFields_;
QHash<QString, QVariant> BaseModel::cache_; QHash<QString, QVariant> BaseModel::cache_;
BaseModel::BaseModel() { BaseModel::BaseModel() {}
}
QStringList BaseModel::changedFields() const { QStringList BaseModel::changedFields() const {
QStringList output; QStringList output;
@ -26,7 +25,7 @@ int BaseModel::count(Table table) {
QVariant r = BaseModel::cacheGet(k); QVariant r = BaseModel::cacheGet(k);
if (r.isValid()) return r.toInt(); if (r.isValid()) return r.toInt();
QSqlQuery q = jop::db().query("SELECT count(*) as row_count FROM " + t); QSqlQuery q = jop::db().query("SELECT count(*) AS row_count FROM " + t);
q.exec(); q.exec();
q.next(); q.next();
int output = q.value(0).toInt(); int output = q.value(0).toInt();
@ -47,10 +46,28 @@ bool BaseModel::save() {
values[field] = value(field).toQVariant(); values[field] = value(field).toQVariant();
} }
// If it's a new entry and the ID is a UUID, we need to create this
// ID now. If the ID is an INT, it will be automatically set by
// SQLite.
if (isNew && primaryKeyIsUuid()) { if (isNew && primaryKeyIsUuid()) {
values[primaryKey()] = uuid::createUuid(); values[primaryKey()] = uuid::createUuid();
} }
// Update created_time and updated_time if needed. If updated_time
// has already been updated (maybe manually by the user), don't
// automatically update it.
if (isNew) {
if (BaseModel::hasField(table(), "created_time")) {
values["created_time"] = (int)(QDateTime::currentMSecsSinceEpoch() / 1000);
}
} else {
if (!values.contains("updated_time")) {
if (BaseModel::hasField(table(), "updated_time")) {
values["updated_time"] = (int)(QDateTime::currentMSecsSinceEpoch() / 1000);
}
}
}
changedFields_.clear(); changedFields_.clear();
const QString& tableName = BaseModel::tableName(table()); const QString& tableName = BaseModel::tableName(table());
@ -59,17 +76,43 @@ bool BaseModel::save() {
cacheDelete(QString("%1:count").arg(tableName)); cacheDelete(QString("%1:count").arg(tableName));
} }
bool output = false;
jop::db().transaction();
if (isNew) { if (isNew) {
QSqlQuery q = jop::db().buildSqlQuery(Database::Insert, tableName, values); QSqlQuery q = jop::db().buildSqlQuery(Database::Insert, tableName, values);
q.exec(); q.exec();
return jop::db().errorCheck(q); output = jop::db().errorCheck(q);
if (output) setValue("id", values["id"]);
} else { } else {
QSqlQuery q = jop::db().buildSqlQuery(Database::Update, tableName, values, QString("%1 = '%2'").arg(primaryKey()).arg(value("id").toString())); QSqlQuery q = jop::db().buildSqlQuery(Database::Update, tableName, values, QString("%1 = '%2'").arg(primaryKey()).arg(value("id").toString()));
q.exec(); q.exec();
return jop::db().errorCheck(q); output = jop::db().errorCheck(q);
} }
return false; // Unreachable if (output && trackChanges()) {
if (isNew) {
Change change;
change.setValue("item_id", id());
change.setValue("item_type", table());
change.setValue("type", Change::Create);
change.save();
} else {
for (QMap<QString, QVariant>::const_iterator it = values.begin(); it != values.end(); ++it) {
Change change;
change.setValue("item_id", id());
change.setValue("item_type", table());
change.setValue("type", Change::Update);
change.setValue("item_field", it.key());
change.save();
}
}
}
jop::db().commit();
return output;
} }
bool BaseModel::dispose() { bool BaseModel::dispose() {
@ -78,8 +121,20 @@ bool BaseModel::dispose() {
q.prepare("DELETE FROM " + tableName + " WHERE " + primaryKey() + " = :id"); q.prepare("DELETE FROM " + tableName + " WHERE " + primaryKey() + " = :id");
q.bindValue(":id", id().toString()); q.bindValue(":id", id().toString());
q.exec(); q.exec();
cacheDelete(QString("%1:count").arg(tableName));
return jop::db().errorCheck(q); bool output = jop::db().errorCheck(q);
if (output) cacheDelete(QString("%1:count").arg(tableName));
if (output && trackChanges()) {
Change change;
change.setValue("item_id", id());
change.setValue("item_type", table());
change.setValue("type", Change::Delete);
change.save();
}
return output;
} }
Table BaseModel::table() const { Table BaseModel::table() const {
@ -95,6 +150,10 @@ bool BaseModel::primaryKeyIsUuid() const {
return false; return false;
} }
bool BaseModel::trackChanges() const {
return false;
}
bool BaseModel::isNew() const { bool BaseModel::isNew() const {
return !valueIsSet(primaryKey()); return !valueIsSet(primaryKey());
} }
@ -121,6 +180,14 @@ QVector<BaseModel::Field> BaseModel::tableFields(jop::Table table) {
return output; return output;
} }
bool BaseModel::hasField(jop::Table table, const QString &name) {
QVector<BaseModel::Field> fields = tableFields(table);
foreach (Field field, fields) {
if (field.name == name) return true;
}
return false;
}
QStringList BaseModel::tableFieldNames(Table table) { QStringList BaseModel::tableFieldNames(Table table) {
QVector<BaseModel::Field> fields = BaseModel::tableFields(table); QVector<BaseModel::Field> fields = BaseModel::tableFields(table);
QStringList output; QStringList output;
@ -202,6 +269,7 @@ BaseModel::Value BaseModel::id() const {
QString BaseModel::tableName(Table t) { QString BaseModel::tableName(Table t) {
if (t == jop::FoldersTable) return "folders"; if (t == jop::FoldersTable) return "folders";
if (t == jop::NotesTable) return "notes"; if (t == jop::NotesTable) return "notes";
if (t == jop::ChangesTable) return "changes";
return "UNDEFINED"; return "UNDEFINED";
} }

View File

@ -3,7 +3,6 @@
#include <stable.h> #include <stable.h>
#include "database.h"
#include "enum.h" #include "enum.h"
namespace jop { namespace jop {
@ -49,10 +48,12 @@ public:
virtual Table table() const; virtual Table table() const;
virtual QString primaryKey() const; virtual QString primaryKey() const;
virtual bool primaryKeyIsUuid() const; virtual bool primaryKeyIsUuid() const;
virtual bool trackChanges() const;
bool isNew() const; bool isNew() const;
static QVector<BaseModel::Field> tableFields(Table table); static QVector<BaseModel::Field> tableFields(Table table);
static bool hasField(jop::Table table, const QString& name);
static QStringList tableFieldNames(Table table); static QStringList tableFieldNames(Table table);
static bool isValidFieldName(Table table, const QString& name); static bool isValidFieldName(Table table, const QString& name);

View File

@ -2,6 +2,8 @@
using namespace jop; using namespace jop;
Change::Change(Database &database) : database_(database) { Change::Change() {}
Table Change::table() const {
return jop::ChangesTable;
} }

View File

@ -3,19 +3,18 @@
#include <stable.h> #include <stable.h>
#include "database.h" #include "models/basemodel.h"
namespace jop { namespace jop {
class Change { class Change : public BaseModel {
public: public:
Change(Database& database); enum Type { Undefined, Create, Update, Delete };
private: Change();
Table table() const;
Database& database_;
}; };

View File

@ -5,53 +5,25 @@
using namespace jop; using namespace jop;
Folder::Folder() : Item() { Folder::Folder() : Item() {}
Table Folder::table() const {
return jop::FoldersTable;
} }
//bool Folder::isNew() const { bool Folder::primaryKeyIsUuid() const {
// return id().isEmpty(); return true;
//} }
//bool Folder::save() { bool Folder::trackChanges() const {
// bool isNew = this->isNew(); return true;
}
// QStringList fields;
// VariantVector values;
// if (isNew) {
// setId(uuid::createUuid());
// fields << "id";
// values << id();
// }
// fields << "title" << "synced";
// values << title() << QVariant(0);
// if (isNew) {
// QSqlQuery q = jop::db().buildSqlQuery(Database::Insert, "folders", fields, values);
// q.exec();
// return jop::db().errorCheck(q);
// } else {
// QSqlQuery q = jop::db().buildSqlQuery(Database::Update, "folders", fields, values, "id = \"" + id() + "\"");
// q.exec();
// return jop::db().errorCheck(q);
// }
//}
//bool Folder::dispose() {
// return false;
//// QSqlQuery q(jop::db().database());
//// q.prepare("DELETE FROM folders WHERE id = :id");
//// q.bindValue(":id", id());
//// q.exec();
//// return jop::db().errorCheck(q);
//}
int Folder::count() { int Folder::count() {
return BaseModel::count(jop::FoldersTable); return BaseModel::count(jop::FoldersTable);
} }
QVector<Folder> Folder::all(const QString &orderBy) { QVector<Folder> Folder::all(const QString &orderBy) {
//QSqlQuery q = jop::db().query("SELECT " + Folder::dbFields().join(",") + " FROM folders ORDER BY " + orderBy);
QSqlQuery q = jop::db().query("SELECT " + BaseModel::tableFieldNames(jop::FoldersTable).join(",") + " FROM folders ORDER BY " + orderBy); QSqlQuery q = jop::db().query("SELECT " + BaseModel::tableFieldNames(jop::FoldersTable).join(",") + " FROM folders ORDER BY " + orderBy);
q.exec(); q.exec();
@ -61,23 +33,7 @@ QVector<Folder> Folder::all(const QString &orderBy) {
Folder folder; Folder folder;
folder.loadSqlQuery(q); folder.loadSqlQuery(q);
output.push_back(folder); output.push_back(folder);
// Folder folder;
// folder.fromSqlQuery(q);
// output.push_back(folder);
// Folder f2;
// f2.loadSqlQuery(q);
// qDebug() << "xxx" << f2.value("title").toString();
} }
return output; return output;
} }
Table Folder::table() const {
return jop::FoldersTable;
}
bool Folder::primaryKeyIsUuid() const {
return true;
}

View File

@ -17,6 +17,7 @@ public:
Table table() const; Table table() const;
bool primaryKeyIsUuid() const; bool primaryKeyIsUuid() const;
bool trackChanges() const;
private: private:

View File

@ -66,9 +66,10 @@ CREATE TABLE version (
CREATE TABLE changes ( CREATE TABLE changes (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
`type` INT,
item_id TEXT, item_id TEXT,
item_type INT, item_type INT,
item_property TEXT item_field TEXT
); );
--CREATE TABLE mimetypes ( --CREATE TABLE mimetypes (