mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Record changes
This commit is contained in:
parent
0a08837ef0
commit
629fe42c59
@ -6,11 +6,10 @@ Database::Database() {}
|
||||
|
||||
void Database::initialize(const QString &path) {
|
||||
version_ = -1;
|
||||
transactionCount_ = 0;
|
||||
|
||||
//QFile::remove(path);
|
||||
|
||||
//qDebug() << Select << Text;
|
||||
|
||||
db_ = QSqlDatabase::addDatabase("QSQLITE");
|
||||
db_.setDatabaseName(path);
|
||||
|
||||
@ -123,6 +122,28 @@ bool Database::errorCheck(const QSqlQuery& query) {
|
||||
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 {
|
||||
qDebug() <<"SQL:"<<sql;
|
||||
|
||||
|
@ -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 QMap<QString, QVariant>& values, const QString& whereCondition = "");
|
||||
bool errorCheck(const QSqlQuery& query);
|
||||
bool transaction();
|
||||
bool commit();
|
||||
|
||||
private:
|
||||
|
||||
@ -30,6 +32,7 @@ private:
|
||||
int version() const;
|
||||
mutable int version_;
|
||||
QStringList sqlStringToLines(const QString& sql);
|
||||
int transactionCount_;
|
||||
|
||||
};
|
||||
|
||||
|
@ -5,9 +5,7 @@
|
||||
|
||||
namespace jop {
|
||||
|
||||
enum ColType { UndefinedType, TextColType, IntColType };
|
||||
|
||||
enum Table { UndefinedTable, FoldersTable, NotesTable };
|
||||
enum Table { UndefinedTable, FoldersTable, NotesTable, ChangesTable };
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "basemodel.h"
|
||||
|
||||
#include "models/change.h"
|
||||
#include "database.h"
|
||||
#include "uuid.h"
|
||||
|
||||
@ -8,9 +9,7 @@ using namespace jop;
|
||||
QMap<int, QVector<BaseModel::Field>> BaseModel::tableFields_;
|
||||
QHash<QString, QVariant> BaseModel::cache_;
|
||||
|
||||
BaseModel::BaseModel() {
|
||||
|
||||
}
|
||||
BaseModel::BaseModel() {}
|
||||
|
||||
QStringList BaseModel::changedFields() const {
|
||||
QStringList output;
|
||||
@ -26,7 +25,7 @@ int BaseModel::count(Table table) {
|
||||
QVariant r = BaseModel::cacheGet(k);
|
||||
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.next();
|
||||
int output = q.value(0).toInt();
|
||||
@ -47,10 +46,28 @@ bool BaseModel::save() {
|
||||
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()) {
|
||||
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();
|
||||
|
||||
const QString& tableName = BaseModel::tableName(table());
|
||||
@ -59,17 +76,43 @@ bool BaseModel::save() {
|
||||
cacheDelete(QString("%1:count").arg(tableName));
|
||||
}
|
||||
|
||||
bool output = false;
|
||||
|
||||
jop::db().transaction();
|
||||
|
||||
if (isNew) {
|
||||
QSqlQuery q = jop::db().buildSqlQuery(Database::Insert, tableName, values);
|
||||
q.exec();
|
||||
return jop::db().errorCheck(q);
|
||||
output = jop::db().errorCheck(q);
|
||||
if (output) setValue("id", values["id"]);
|
||||
} else {
|
||||
QSqlQuery q = jop::db().buildSqlQuery(Database::Update, tableName, values, QString("%1 = '%2'").arg(primaryKey()).arg(value("id").toString()));
|
||||
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() {
|
||||
@ -78,8 +121,20 @@ bool BaseModel::dispose() {
|
||||
q.prepare("DELETE FROM " + tableName + " WHERE " + primaryKey() + " = :id");
|
||||
q.bindValue(":id", id().toString());
|
||||
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 {
|
||||
@ -95,6 +150,10 @@ bool BaseModel::primaryKeyIsUuid() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BaseModel::trackChanges() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BaseModel::isNew() const {
|
||||
return !valueIsSet(primaryKey());
|
||||
}
|
||||
@ -121,6 +180,14 @@ QVector<BaseModel::Field> BaseModel::tableFields(jop::Table table) {
|
||||
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) {
|
||||
QVector<BaseModel::Field> fields = BaseModel::tableFields(table);
|
||||
QStringList output;
|
||||
@ -202,6 +269,7 @@ BaseModel::Value BaseModel::id() const {
|
||||
QString BaseModel::tableName(Table t) {
|
||||
if (t == jop::FoldersTable) return "folders";
|
||||
if (t == jop::NotesTable) return "notes";
|
||||
if (t == jop::ChangesTable) return "changes";
|
||||
return "UNDEFINED";
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
#include <stable.h>
|
||||
|
||||
#include "database.h"
|
||||
#include "enum.h"
|
||||
|
||||
namespace jop {
|
||||
@ -49,10 +48,12 @@ public:
|
||||
virtual Table table() const;
|
||||
virtual QString primaryKey() const;
|
||||
virtual bool primaryKeyIsUuid() const;
|
||||
virtual bool trackChanges() const;
|
||||
|
||||
bool isNew() const;
|
||||
|
||||
static QVector<BaseModel::Field> tableFields(Table table);
|
||||
static bool hasField(jop::Table table, const QString& name);
|
||||
static QStringList tableFieldNames(Table table);
|
||||
static bool isValidFieldName(Table table, const QString& name);
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
using namespace jop;
|
||||
|
||||
Change::Change(Database &database) : database_(database) {
|
||||
Change::Change() {}
|
||||
|
||||
Table Change::table() const {
|
||||
return jop::ChangesTable;
|
||||
}
|
||||
|
@ -3,19 +3,18 @@
|
||||
|
||||
#include <stable.h>
|
||||
|
||||
#include "database.h"
|
||||
#include "models/basemodel.h"
|
||||
|
||||
namespace jop {
|
||||
|
||||
class Change {
|
||||
class Change : public BaseModel {
|
||||
|
||||
public:
|
||||
|
||||
Change(Database& database);
|
||||
enum Type { Undefined, Create, Update, Delete };
|
||||
|
||||
private:
|
||||
|
||||
Database& database_;
|
||||
Change();
|
||||
Table table() const;
|
||||
|
||||
};
|
||||
|
||||
|
@ -5,53 +5,25 @@
|
||||
|
||||
using namespace jop;
|
||||
|
||||
Folder::Folder() : Item() {
|
||||
Folder::Folder() : Item() {}
|
||||
|
||||
Table Folder::table() const {
|
||||
return jop::FoldersTable;
|
||||
}
|
||||
|
||||
//bool Folder::isNew() const {
|
||||
// return id().isEmpty();
|
||||
//}
|
||||
bool Folder::primaryKeyIsUuid() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
//bool Folder::save() {
|
||||
// bool isNew = this->isNew();
|
||||
|
||||
// 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);
|
||||
//}
|
||||
bool Folder::trackChanges() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
int Folder::count() {
|
||||
return BaseModel::count(jop::FoldersTable);
|
||||
}
|
||||
|
||||
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);
|
||||
q.exec();
|
||||
|
||||
@ -61,23 +33,7 @@ QVector<Folder> Folder::all(const QString &orderBy) {
|
||||
Folder folder;
|
||||
folder.loadSqlQuery(q);
|
||||
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;
|
||||
}
|
||||
|
||||
Table Folder::table() const {
|
||||
return jop::FoldersTable;
|
||||
}
|
||||
|
||||
bool Folder::primaryKeyIsUuid() const {
|
||||
return true;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ public:
|
||||
|
||||
Table table() const;
|
||||
bool primaryKeyIsUuid() const;
|
||||
bool trackChanges() const;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -66,9 +66,10 @@ CREATE TABLE version (
|
||||
|
||||
CREATE TABLE changes (
|
||||
id INTEGER PRIMARY KEY,
|
||||
`type` INT,
|
||||
item_id TEXT,
|
||||
item_type INT,
|
||||
item_property TEXT
|
||||
item_field TEXT
|
||||
);
|
||||
|
||||
--CREATE TABLE mimetypes (
|
||||
|
Loading…
Reference in New Issue
Block a user