From e369e9b5d82a81bd5ff1e9957ff742789c9d2958 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Thu, 12 May 2016 10:38:58 +0430 Subject: [PATCH] First commit --- .directory | 7 + include/Database | 1 + include/Nut | 5 + include/Query | 1 + include/Table | 1 + include/TableSet | 1 + include/database.h | 1 + include/header_copier | 22 +++ include/nut.h | 5 + include/query.h | 1 + include/table.h | 1 + include/tableset.h | 1 + nut.pri | 36 ++++ src/changelogtable.cpp | 30 +++ src/changelogtable.h | 48 +++++ src/database.cpp | 372 ++++++++++++++++++++++++++++++++++++ src/database.h | 84 ++++++++ src/database_p.h | 66 +++++++ src/databasemodel.cpp | 118 ++++++++++++ src/databasemodel.h | 47 +++++ src/defines.h | 114 +++++++++++ src/defines_p.h | 45 +++++ src/mysqlgenerator.cpp | 56 ++++++ src/mysqlgenerator.h | 20 ++ src/postgresqlgenerator.cpp | 71 +++++++ src/postgresqlgenerator.h | 23 +++ src/query.cpp | 23 +++ src/query.h | 289 ++++++++++++++++++++++++++++ src/querybase.cpp | 6 + src/querybase_p.h | 18 ++ src/sqlgeneratorbase.cpp | 182 ++++++++++++++++++ src/sqlgeneratorbase_p.h | 37 ++++ src/sqlitegenerator.cpp | 51 +++++ src/sqlitegenerator.h | 15 ++ src/table.cpp | 166 ++++++++++++++++ src/table.h | 90 +++++++++ src/tablescheema.cpp | 350 +++++++++++++++++++++++++++++++++ src/tablescheema.h | 114 +++++++++++ src/tableset.cpp | 21 ++ src/tableset.h | 127 ++++++++++++ src/tablesetbase.cpp | 58 ++++++ src/tablesetbase_p.h | 51 +++++ test/.maintest.cpp.kate-swp | Bin 0 -> 341 bytes test/comment.cpp | 6 + test/comment.h | 24 +++ test/maintest.cpp | 131 +++++++++++++ test/maintest.h | 30 +++ test/post.cpp | 9 + test/post.h | 34 ++++ test/tst_nut.pro | 21 ++ test/weblogdatabase.cpp | 9 + test/weblogdatabase.h | 23 +++ 52 files changed, 3062 insertions(+) create mode 100644 .directory create mode 100644 include/Database create mode 100644 include/Nut create mode 100644 include/Query create mode 100644 include/Table create mode 100644 include/TableSet create mode 100644 include/database.h create mode 100755 include/header_copier create mode 100644 include/nut.h create mode 100644 include/query.h create mode 100644 include/table.h create mode 100644 include/tableset.h create mode 100644 nut.pri create mode 100644 src/changelogtable.cpp create mode 100644 src/changelogtable.h create mode 100644 src/database.cpp create mode 100644 src/database.h create mode 100644 src/database_p.h create mode 100644 src/databasemodel.cpp create mode 100644 src/databasemodel.h create mode 100644 src/defines.h create mode 100644 src/defines_p.h create mode 100644 src/mysqlgenerator.cpp create mode 100644 src/mysqlgenerator.h create mode 100644 src/postgresqlgenerator.cpp create mode 100644 src/postgresqlgenerator.h create mode 100644 src/query.cpp create mode 100644 src/query.h create mode 100644 src/querybase.cpp create mode 100644 src/querybase_p.h create mode 100644 src/sqlgeneratorbase.cpp create mode 100644 src/sqlgeneratorbase_p.h create mode 100644 src/sqlitegenerator.cpp create mode 100644 src/sqlitegenerator.h create mode 100644 src/table.cpp create mode 100644 src/table.h create mode 100644 src/tablescheema.cpp create mode 100644 src/tablescheema.h create mode 100644 src/tableset.cpp create mode 100644 src/tableset.h create mode 100644 src/tablesetbase.cpp create mode 100644 src/tablesetbase_p.h create mode 100644 test/.maintest.cpp.kate-swp create mode 100644 test/comment.cpp create mode 100644 test/comment.h create mode 100644 test/maintest.cpp create mode 100644 test/maintest.h create mode 100644 test/post.cpp create mode 100644 test/post.h create mode 100644 test/tst_nut.pro create mode 100644 test/weblogdatabase.cpp create mode 100644 test/weblogdatabase.h diff --git a/.directory b/.directory new file mode 100644 index 0000000..fab6437 --- /dev/null +++ b/.directory @@ -0,0 +1,7 @@ +[Dolphin] +Timestamp=2016,5,12,10,37,46 +Version=3 +ViewMode=1 + +[Settings] +HiddenFilesShown=true diff --git a/include/Database b/include/Database new file mode 100644 index 0000000..6dca7b4 --- /dev/null +++ b/include/Database @@ -0,0 +1 @@ +#include "../src/database.h" diff --git a/include/Nut b/include/Nut new file mode 100644 index 0000000..5b89c95 --- /dev/null +++ b/include/Nut @@ -0,0 +1,5 @@ + +#include "../src/table.h" +#include "../src/database.h" +#include "../src/tableset.h" +#include "../src/query.h" diff --git a/include/Query b/include/Query new file mode 100644 index 0000000..e6f4849 --- /dev/null +++ b/include/Query @@ -0,0 +1 @@ +#include "../src/query.h" diff --git a/include/Table b/include/Table new file mode 100644 index 0000000..cd24517 --- /dev/null +++ b/include/Table @@ -0,0 +1 @@ +#include "../src/table.h" diff --git a/include/TableSet b/include/TableSet new file mode 100644 index 0000000..dc9d87c --- /dev/null +++ b/include/TableSet @@ -0,0 +1 @@ +#include "../src/tableset.h" diff --git a/include/database.h b/include/database.h new file mode 100644 index 0000000..6dca7b4 --- /dev/null +++ b/include/database.h @@ -0,0 +1 @@ +#include "../src/database.h" diff --git a/include/header_copier b/include/header_copier new file mode 100755 index 0000000..b799f01 --- /dev/null +++ b/include/header_copier @@ -0,0 +1,22 @@ +#!/bin/bash + +exec 3< <(egrep -o "class\sNUT_EXPORT\s(\S+)" ../src -R 2>&1) + +pattern="\.\.\/src\/([a-z]+)\.h\:class\sNUT_EXPORT\s(\w+)" + +echo "" > "Nut" +echo "" > "nut.h" + +while read line; do + if [[ $line =~ $pattern ]]; then + header=${BASH_REMATCH[1]} + class=${BASH_REMATCH[2]} + + echo "#include \"../src/$header.h\"" > $class + echo "#include \"../src/$header.h\"" > "$header.h" + + echo "#include \"../src/$header.h\"" >> "Nut" + echo "#include \"../src/$header.h\"" >> "nut.h" + fi +done <&3 +exec 3<&- diff --git a/include/nut.h b/include/nut.h new file mode 100644 index 0000000..5b89c95 --- /dev/null +++ b/include/nut.h @@ -0,0 +1,5 @@ + +#include "../src/table.h" +#include "../src/database.h" +#include "../src/tableset.h" +#include "../src/query.h" diff --git a/include/query.h b/include/query.h new file mode 100644 index 0000000..e6f4849 --- /dev/null +++ b/include/query.h @@ -0,0 +1 @@ +#include "../src/query.h" diff --git a/include/table.h b/include/table.h new file mode 100644 index 0000000..cd24517 --- /dev/null +++ b/include/table.h @@ -0,0 +1 @@ +#include "../src/table.h" diff --git a/include/tableset.h b/include/tableset.h new file mode 100644 index 0000000..dc9d87c --- /dev/null +++ b/include/tableset.h @@ -0,0 +1 @@ +#include "../src/tableset.h" diff --git a/nut.pri b/nut.pri new file mode 100644 index 0000000..d7bddb3 --- /dev/null +++ b/nut.pri @@ -0,0 +1,36 @@ +QT += core widgets sql + +INCLUDEPATH += $$PWD/include + +HEADERS += \ + $$PWD/src/database.h \ + $$PWD/src/table.h \ + $$PWD/src/tableset.h \ + $$PWD/src/database_p.h \ + $$PWD/src/defines_p.h \ + $$PWD/src/defines.h \ + $$PWD/src/query.h \ + $$PWD/src/tablescheema.h \ + $$PWD/src/databasemodel.h \ + $$PWD/src/sqlgeneratorbase_p.h \ + $$PWD/src/postgresqlgenerator.h \ + $$PWD/src/changelogtable.h \ + $$PWD/src/tablesetbase_p.h \ + $$PWD/src/querybase_p.h \ + $$PWD/src/mysqlgenerator.h \ + $$PWD/src/sqlitegenerator.h + +SOURCES += \ + $$PWD/src/database.cpp \ + $$PWD/src/table.cpp \ + $$PWD/src/tableset.cpp \ + $$PWD/src/query.cpp \ + $$PWD/src/tablescheema.cpp \ + $$PWD/src/databasemodel.cpp \ + $$PWD/src/tablesetbase.cpp \ + $$PWD/src/sqlgeneratorbase.cpp \ + $$PWD/src/postgresqlgenerator.cpp \ + $$PWD/src/changelogtable.cpp \ + $$PWD/src/querybase.cpp \ + $$PWD/src/mysqlgenerator.cpp \ + $$PWD/src/sqlitegenerator.cpp diff --git a/src/changelogtable.cpp b/src/changelogtable.cpp new file mode 100644 index 0000000..fe34e0c --- /dev/null +++ b/src/changelogtable.cpp @@ -0,0 +1,30 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#include "changelogtable.h" + +QT_BEGIN_NAMESPACE + +ChangeLogTable::ChangeLogTable() +{ + +} + +QT_END_NAMESPACE diff --git a/src/changelogtable.h b/src/changelogtable.h new file mode 100644 index 0000000..76bddaf --- /dev/null +++ b/src/changelogtable.h @@ -0,0 +1,48 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#ifndef CHANGELOGTABLE_H +#define CHANGELOGTABLE_H + +#include +#include "table.h" + +QT_BEGIN_NAMESPACE + +class ChangeLogTable : public Table +{ + Q_OBJECT + + NUT_PRIMARY_AUTO_INCREMENT(id) + NUT_DECLARE_FIELD(int, id, id, setId) + + NUT_DECLARE_FIELD(QByteArray, data, data, setData) + + NUT_DECLARE_FIELD(int, versionMajor, versionMajor, setVersionMajor) + + NUT_DECLARE_FIELD(int, versionMinor, versionMinor, setVersionMinor) + +public: + ChangeLogTable(); +}; + +QT_END_NAMESPACE + +#endif // CHANGELOGTABLE_H diff --git a/src/database.cpp b/src/database.cpp new file mode 100644 index 0000000..8131b82 --- /dev/null +++ b/src/database.cpp @@ -0,0 +1,372 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "database.h" +#include "table.h" +#include "tableset.h" +#include "database_p.h" +#include "defines_p.h" +#include "tablescheema.h" +#include "postgresqlgenerator.h" +#include "mysqlgenerator.h" +#include "sqlitegenerator.h" +#include "query.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +DatabasePrivate::DatabasePrivate(Database *parent) : q_ptr(parent) +{ +} + +bool DatabasePrivate::open() +{ + getCurrectScheema(); + + db = QSqlDatabase::addDatabase(driver, connectionName); + db.setHostName(hostName); + db.setDatabaseName(databaseName); + db.setUserName(userName); + db.setPassword(password); + bool ok = db.open(); + + if(!ok){ + qWarning("Could not connect to database"); + qWarning(db.lastError().text().toLocal8Bit().data()); + + if(db.lastError().text().contains("database \"" + databaseName + "\" does not exist")){ + db.setDatabaseName(""); + ok = db.open(); + qInfo("Creating database"); + if(ok){ + db.exec("CREATE DATABASE " + databaseName); + db.close(); + return open(); + }else{ + qWarning(db.lastError().text().toLatin1().data()); + } + } + return false; + } + + return updateDatabase(); +} + +bool DatabasePrivate::updateDatabase() +{ + DatabaseModel last = getLastScheema(); + + if(last == currentModel){ + qInfo("Databse is up-to-date"); + return true; + } + + if(!last.size()){ + qInfo("Databse is new"); + createChangeLogs(); + }else{ + qInfo("Databse is changed"); + } + + QStringList sql = sqlGenertor->getDiff(last, currentModel); + db.transaction(); + foreach (QString s, sql){ + qDebug() << "going to exec " << s; + db.exec(s); + + if(db.lastError().type() != QSqlError::NoError) + qWarning(db.lastError().text().toLatin1().data()); + } + bool ok = db.commit(); + + if(ok){ + storeScheemaInDB(); + }else{ + qWarning("Unable update database"); + qWarning(db.lastError().text().toLatin1().data()); + } + + return ok; +} + +QVariantMap DatabasePrivate::getCurrectScheema() +{ + Q_Q(Database); + tables.clear(); + + int changeLogTypeId = qRegisterMetaType(); + currentModel.append(new TableScheema(changeLogTypeId, "__change_logs")); + tables.insert("ChangeLogTable", "__change_logs"); + + for(int i = 0; i < q->metaObject()->classInfoCount(); i++){ + QMetaClassInfo ci = q->metaObject()->classInfo(i); + if(QString(ci.name()).startsWith(__nut_TABLE)) + tables.insert(QString(ci.name()).replace(__nut_NAME_PERFIX, "").split(" ").at(1), ci.value()); + } + + QVariantMap databaseVariant; + for(int i = 1; i < q->metaObject()->propertyCount(); i++){ + QMetaProperty tableProperty = q->metaObject()->property(i); + int typeId = QMetaType::type(tableProperty.typeName()); + + if(tables.values().contains(tableProperty.name()) && typeId >= QVariant::UserType){ + TableScheema *sch = new TableScheema(typeId, tableProperty.name()); + currentModel.append(sch); + } + } + + foreach (TableScheema *sch, currentModel) + foreach (Relation *fk, sch->foregionKeys()) + fk->table = currentModel.scheemaByClass(fk->className); + + return databaseVariant; +} + +DatabaseModel DatabasePrivate::getLastScheema() +{ +// ChangeLogTable *changeLog = FROM(_changeLogs) +// ORDERBY_DESC(id) +// FIRST(); + + auto u = changeLogs->createQuery()->orderBy("id", "desc")->first(); + +// QSqlQuery q = db.exec("select data from __change_logs order by id desc limit 1"); + DatabaseModel ret; + + if(u){ + QJsonObject json = QJsonDocument::fromJson(u->data()).object(); + + foreach (QString key, json.keys()) { + TableScheema *sch = new TableScheema(json.value(key).toObject(), key); + ret.append(sch); + } + } + return ret; +} + +bool DatabasePrivate::storeScheemaInDB() +{ + /*int changeLogTypeId = qRegisterMetaType(); + TableScheema *changeLogModel = new TableScheema(changeLogTypeId, "__change_logs"); + + sqlGenertor->getDiff(0, changeLogModel);*/ + + /*Q_Q(Database); + ChangeLogTable *changeLog = new ChangeLogTable(); + changeLog->setData(QJsonDocument(currentModel.toJson()).toJson()); + q->saveChanges();*/ + + QSqlQuery q(db); + q.prepare("insert into __change_logs (data) values (:data)"); + q.bindValue(":data", QString(QJsonDocument(currentModel.toJson()).toJson())); + bool ret = q.exec(); + if(q.lastError().type() != QSqlError::NoError) + qWarning(q.lastError().text().toLatin1().data()); + return ret; +} + +void DatabasePrivate::createChangeLogs() +{ + Q_Q(Database); + + QString diff = sqlGenertor->getDiff(0, currentModel.scheema("__change_logs")); + + db.exec(diff); +} + +Database::Database(QObject *parent) : QObject(parent), d_ptr(new DatabasePrivate(this)) +{ + Q_D(Database); + d->changeLogs = new TableSet(this); +} + +QString Database::databaseName() const +{ + Q_D(const Database); + return d->databaseName; +} + +QString Database::hostName() const +{ + Q_D(const Database); + return d->hostName; +} + +int Database::port() const +{ + Q_D(const Database); + return d->port; +} + +QString Database::userName() const +{ + Q_D(const Database); + return d->userName; +} + +QString Database::password() const +{ + Q_D(const Database); + return d->password; +} + +QString Database::connectionName() const +{ + Q_D(const Database); + return d->connectionName; +} + +QString Database::driver() const +{ + Q_D(const Database); + return d->driver; +} + +DatabaseModel Database::model() const +{ + Q_D(const Database); + return d->currentModel; +} + +QString Database::tableName(QString className) +{ + Q_D(Database); + return d->tables[className]; +} + +void Database::setDatabaseName(QString databaseName) +{ + Q_D(Database); + d->databaseName = databaseName; +} + +void Database::setHostName(QString hostName) +{ + Q_D(Database); + d->hostName = hostName; +} + +void Database::setPort(int port) +{ + Q_D(Database); + d->port = port; +} + +void Database::setUserName(QString username) +{ + Q_D(Database); + d->userName = username; +} + +void Database::setPassword(QString password) +{ + Q_D(Database); + d->password = password; +} + +void Database::setConnectionName(QString connectionName) +{ + Q_D(Database); + d->connectionName = connectionName; +} + +void Database::setDriver(QString driver) +{ + Q_D(Database); + d->driver = driver; +} + +SqlGeneratorBase *Database::sqlGenertor() const +{ + Q_D(const Database); + return d->sqlGenertor; +} + +bool Database::open() +{ + Q_D(Database); + + if(driver() == "QPSQL") + d->sqlGenertor = new PostgreSqlGenerator; + else if (driver() == "QMYSQL") + d->sqlGenertor = new MySqlGenerator; + else if (driver() == "QSQLITE") + d->sqlGenertor = new SqliteGenerator; + + if(!d->sqlGenertor){ + qWarning(QString("Sql generator for driver " + driver() + " not found").toLatin1().data()); + return false; + }else{ + return d->open(); + } +} + +QString Database::decodeQuery(QString sql) +{ + Q_D(Database); + + sql = sql + .replace("::", ".") + .replace("()", "") + .replace("==", "=") + .replace("!=", "<>"); + + foreach (QString tableName, d->tables.keys()) + sql = sql.replace(tableName + ".", d->tables[tableName] + "."); + + + return sql; +} + +QSqlQuery Database::exec(QString sql) +{ + Q_D(Database); + QSqlQuery q = d->db.exec(sql); + if(d->db.lastError().type() != QSqlError::NoError) + qWarning(d->db.lastError().text().toLatin1().data()); + return q; +} + +void Database::add(TableSetBase *t) +{ + tableSets.insert(t); +} + +void Database::saveChanges() +{ + Q_D(Database); + + foreach(TableSetBase *ts, tableSets) + ts->save(this); +} + +QT_END_NAMESPACE diff --git a/src/database.h b/src/database.h new file mode 100644 index 0000000..94e2251 --- /dev/null +++ b/src/database.h @@ -0,0 +1,84 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#ifndef DATABASE_H +#define DATABASE_H + +#include +#include +#include + +#include "defines.h" +#include "tableset.h" + +QT_BEGIN_NAMESPACE + +class DatabaseModel; +class DatabasePrivate; +class TableSetBase; +class SqlGeneratorBase; +class NUT_EXPORT Database : public QObject +{ + Q_OBJECT + + DatabasePrivate *d_ptr; + Q_DECLARE_PRIVATE(Database) + +public: + Database(QObject *parent = 0); + + bool open(); + + QString decodeQuery(QString sql); + + QSqlQuery exec(QString sql); + + void add(TableSetBase *); + void saveChanges(); + + QString databaseName() const; + QString hostName() const; + int port() const; + QString userName() const; + QString password() const; + QString connectionName() const; + QString driver() const; + + DatabaseModel model() const; + QString tableName(QString className); + + SqlGeneratorBase *sqlGenertor() const; + +public slots: + void setDatabaseName(QString databaseName); + void setHostName(QString hostName); + void setPort(int port); + void setUserName(QString userName); + void setPassword(QString password); + void setConnectionName(QString connectionName); + void setDriver(QString driver); + +private: + QSet tableSets; +}; + +QT_END_NAMESPACE + +#endif // DATABASE_H diff --git a/src/database_p.h b/src/database_p.h new file mode 100644 index 0000000..2382105 --- /dev/null +++ b/src/database_p.h @@ -0,0 +1,66 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#ifndef DATABASE_P_H +#define DATABASE_P_H + +#include "database.h" +#include "databasemodel.h" +#include "changelogtable.h" + +#include + + +class DatabasePrivate +{ + Database *q_ptr; + Q_DECLARE_PUBLIC(Database) + +public: + DatabasePrivate(Database *parent); + + + bool open(); + + bool updateDatabase(); + void createChangeLogs(); + bool storeScheemaInDB(); + DatabaseModel getLastScheema(); + QVariantMap getCurrectScheema(); + + QSqlDatabase db; + + QString hostName; + QString databaseName; + int port; + QString userName; + QString password; + QString connectionName; + QString driver; + + QHash tables; + + SqlGeneratorBase *sqlGenertor; + DatabaseModel currentModel; + + TableSet *changeLogs; +}; + +#endif // DATABASE_P_H diff --git a/src/databasemodel.cpp b/src/databasemodel.cpp new file mode 100644 index 0000000..83dea77 --- /dev/null +++ b/src/databasemodel.cpp @@ -0,0 +1,118 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#include "databasemodel.h" +#include "tablescheema.h" + +#include + +DatabaseModel::DatabaseModel() : QList() +{ + +} + +TableScheema *DatabaseModel::scheema(QString tableName) const +{ + for(int i = 0; i < size(); i++){ + TableScheema *s = at(i); + if(s->name() == tableName) + return s; + } + return 0; +} + +TableScheema *DatabaseModel::scheemaByClass(QString className) const +{ + for(int i = 0; i < size(); i++){ + TableScheema *s = at(i); + if(s->className() == className) + return s; + } + return 0; +} + +bool DatabaseModel::operator ==(const DatabaseModel &other) const +{ + if(size() != other.size()) + return false; + + for(int i = 0; i < size(); i++){ + TableScheema *mine = at(i); + TableScheema *others = other.scheema(mine->name()); + + if(!others) + return false; + + if(*mine != *others) + return false; + } + + return true; +} + +QJsonObject DatabaseModel::toJson() const +{ + QJsonObject obj; + + for(int i = 0; i < size(); i++){ + TableScheema *s = at(i); + obj.insert(s->name(), s->toJson()); + } + + return obj; +} + +Relation *DatabaseModel::relationByClassNames(QString masterClassName, QString childClassName) +{ + TableScheema *childTable = scheemaByClass(childClassName); + + if(!childTable) + return 0; + + foreach (Relation *rel, childTable->foregionKeys()) + if(rel->className == masterClassName) + return rel; + + return 0; +} + +Relation *DatabaseModel::relationByTableNames(QString masterTableName, QString childTableName) +{ + TableScheema *childTable = scheema(childTableName); + + if(!childTable) + return 0; + + foreach (Relation *rel, childTable->foregionKeys()) + if(rel->table->name() == masterTableName) + return rel; + + return 0; +} + +DatabaseModel DatabaseModel::fromJson(QJsonObject &json) +{ + DatabaseModel model; + foreach (QString key, json.keys()) { + TableScheema *sch = new TableScheema(json.value(key).toObject(), key); + model.append(sch); + } + return model; +} diff --git a/src/databasemodel.h b/src/databasemodel.h new file mode 100644 index 0000000..0bc4f8c --- /dev/null +++ b/src/databasemodel.h @@ -0,0 +1,47 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#ifndef DATABASEMODEL_H +#define DATABASEMODEL_H + +#include + +class TableScheema; +struct Relation; +class QJsonObject; +class DatabaseModel : public QList +{ +public: + DatabaseModel(); + + TableScheema *scheema(QString tableName) const; + TableScheema *scheemaByClass(QString className) const; + + Relation *relationByClassNames(QString masterClassName, QString childClassName); + Relation *relationByTableNames(QString masterTableName, QString childTableName); + + bool operator ==(const DatabaseModel &other) const; + + static DatabaseModel fromJson(QJsonObject &json); + QJsonObject toJson() const; + +}; + +#endif // DATABASEMODEL_H diff --git a/src/defines.h b/src/defines.h new file mode 100644 index 0000000..07e1903 --- /dev/null +++ b/src/defines.h @@ -0,0 +1,114 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#ifndef SYNTAX_DEFINES_H +#define SYNTAX_DEFINES_H + + +#include "defines_p.h" + +#define QT_NAMESPACE Nut + +#ifdef NUT_COMPILE_STATIC +# define NUT_EXPORT +#else +# define NUT_EXPORT Q_DECL_EXPORT +#endif + +// Database +#define NUT_DB_VERSION(major, minor) Q_CLASSINFO(__nut_NAME_PERFIX " " __nut_DB_VERSION, #major "." #minor) + +#define NUT_DECLARE_TABLE(type, name) \ + Q_CLASSINFO(__nut_TABLE " " #type, #name) \ + Q_PROPERTY(type* name READ name) \ + Q_PROPERTY(TableSet name##s READ name##s) \ + type* m_##name; \ + TableSet *m_##name##s; \ +public: \ + static const type _##name; \ + type* name() const{ return m_##name; } \ + TableSet *name##s() const { return m_##name##s; } + +//Table +#define NUT_DECLARE_FIELD(type, name, read, write) \ + Q_PROPERTY(type name READ read WRITE write) \ + Q_CLASSINFO(__nut_NAME_PERFIX #name " " __nut_FIELD, #name) \ + type m_##name; \ +public: \ + static type type_##name; \ + type read() const{ \ + return m_##name; \ + } \ + void write(type name){ \ + m_##name = name; \ + propertyChanged(#name); \ + } + +#define NUT_FOREGION_KEY(type, keytype, name, read, write) \ + Q_PROPERTY(type* name READ read WRITE write) \ + NUT_DECLARE_FIELD(keytype, name##Id, read##Id, write##Id) \ + Q_CLASSINFO(__nut_NAME_PERFIX #name "Id " __nut_FOREGION_KEY, #type) \ + type *m_##name; \ +public: \ + type *read() const { return m_##name ; } \ + void write(type *name){ \ + m_##name = name; \ + } + +#define NUT_DECLARE_CHILD_TABLE(type, n) \ + private: \ + TableSet *m_##n; \ + public: \ + TableSet *n(){ \ + return m_##n; \ + } + + +#define NUT_INDEX(name, field, order) +#define NUT_PRIMARY_KEY(x) Q_CLASSINFO(__nut_NAME_PERFIX #x " " __nut_PRIMARY_KEY, #x) +#define NUT_AUTO_INCREMENT(x) Q_CLASSINFO(__nut_NAME_PERFIX #x " " __nut_AUTO_INCREMENT, #x) +#define NUT_PRIMARY_AUTO_INCREMENT(x) NUT_PRIMARY_KEY(x) \ + NUT_AUTO_INCREMENT(x) +#define NUT_LEN(x, n) Q_CLASSINFO(__nut_NAME_PERFIX #x " " __nut_LEN, #n) +#define NUT_DEFAULT_VALUE(x, n) Q_CLASSINFO(__nut_NAME_PERFIX #x " " __nut_DEFAULT_VALUE, #n) +#define NUT_NOT_NULL(x) Q_CLASSINFO(__nut_NAME_PERFIX #x " " __nut_NOT_NULL, "1") + +#ifndef NUT_NO_KEYWORDS +//Query +# define LIKE +# define BETWEEN(min,max) BETWEEN min AND max +# define IS +# ifndef NULL +# define NULL +# endif + +# define FROM(x) /*QScopedPointer*/(x->createQuery()) +# define WHERE(x) ->setWhere(#x) +# define BIND(...) ->bindValues(__VA_ARGS__) +# define JOIN(x) ->join(#x) +# define SELECT() ->toList() +# define COUNT() ->count() +# define DELETE() ->remove() +# define FIRST() ->first() +# define ORDERBY(x) ->orderBy(#x, "ASC"); +# define ORDERBY_DESC(x) ->orderBy(#x, "DESC"); +#endif // NUT_NO_KEYWORDS + +#endif // SYNTAX_DEFINES_H diff --git a/src/defines_p.h b/src/defines_p.h new file mode 100644 index 0000000..2560bb2 --- /dev/null +++ b/src/defines_p.h @@ -0,0 +1,45 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#ifndef DEFINES_P_H +#define DEFINES_P_H + +#define __NAME "name" +#define __TYPE "type" +#define __FIELDS "fields" +#define __nut_FIELD "field" + +#define __nut_DB_VERSION "database_version" +#define __nut_NAME_PERFIX "nut_db_key:" +#define __nut_PRIMARY_KEY "primary_key" +#define __nut_AUTO_INCREMENT "auto_increment" +#define __nut_TABLE "table" +#define __nut_TABLE_NAME "table_name" + +#define __nut_LEN "len" +#define __nut_DEFAULT_VALUE "def" +#define __nut_NOT_NULL "notnull" + +#define __nut_FOREGION_KEY "foregion_key" +#define __nut_NEW "new" +#define __nut_REMOVE "remove" +#define __nut_CHANGE "change" + +#endif // DEFINES_P_H diff --git a/src/mysqlgenerator.cpp b/src/mysqlgenerator.cpp new file mode 100644 index 0000000..0e7e21a --- /dev/null +++ b/src/mysqlgenerator.cpp @@ -0,0 +1,56 @@ +#include "mysqlgenerator.h" +#include "tablescheema.h" + +QT_BEGIN_NAMESPACE + +MySqlGenerator::MySqlGenerator() : SqlGeneratorBase() +{ + +} + +QString MySqlGenerator::getColumnDef(Field *field) +{ + QString ret = field->name + " "; + QString dbType; + + switch (field->type) { + case QVariant::Bool: + dbType = "boolean"; + break; + case QVariant::ByteArray: + dbType = "blob"; + break; + case QVariant::Double: + dbType = "real"; + break; + case QVariant::Int: + dbType = "int(4)"; + if(field->isAutoIncrement) + dbType += " auto_increment"; + + break; + case QVariant::String: + if(field->length) + dbType = QString("varchar(%1)").arg(field->length); + else + dbType = "text"; + break; + case QVariant::DateTime: + dbType = "datetime"; + break; + + case QVariant::Date: + dbType = "date"; + break; + + case QVariant::Time: + dbType = "time"; + break; + default: + dbType = ""; + } + ret.append(dbType); + return ret; +} + +QT_END_NAMESPACE diff --git a/src/mysqlgenerator.h b/src/mysqlgenerator.h new file mode 100644 index 0000000..736db1e --- /dev/null +++ b/src/mysqlgenerator.h @@ -0,0 +1,20 @@ +#ifndef MYSQLGENERATOR_H +#define MYSQLGENERATOR_H + +#include +#include "sqlgeneratorbase_p.h" + +QT_BEGIN_NAMESPACE + +class MySqlGenerator : public SqlGeneratorBase +{ +public: + MySqlGenerator(); + + QString getColumnDef(Field *field); + +}; + +QT_END_NAMESPACE + +#endif // MYSQLGENERATOR_H diff --git a/src/postgresqlgenerator.cpp b/src/postgresqlgenerator.cpp new file mode 100644 index 0000000..ac8302f --- /dev/null +++ b/src/postgresqlgenerator.cpp @@ -0,0 +1,71 @@ +#include "postgresqlgenerator.h" +#include "table.h" +#include "tablescheema.h" + +QT_BEGIN_NAMESPACE + +PostgreSqlGenerator::PostgreSqlGenerator() : SqlGeneratorBase () +{ + +} + +QString PostgreSqlGenerator::getColumnDef(Field *field) +{ + QString ret = field->name + " "; + QString dbType; + + switch (field->type) { + case QVariant::Bool: + dbType = "boolean"; + break; + case QVariant::ByteArray: + dbType = "bytea"; + break; + case QVariant::Date: + dbType = "date"; + break; + case QVariant::DateTime: + dbType = "timestamp"; + break; + case QVariant::Double: + dbType = "real"; + break; + case QVariant::Int: + if(field->isAutoIncrement) + dbType = "serial"; + else + dbType = "integer"; + break; + case QVariant::String: + if(field->length) + dbType = QString("varchar(%1)").arg(field->length); + else + dbType = "text"; + break; + case QVariant::Time: + dbType = "time"; + break; + default: + dbType = ""; + } + ret.append(dbType); + return ret; +} + +//QString PostgreSqlGenerator::saveSql(Table *t, QString tableName) +//{ +// switch(t->status()){ +// case Table::Added: +// return insertCommand(t, tableName) + " RETURNING " + t->primaryKey(); + +// default: +// return SqlGeneratorBase::saveSql(t, tableName); +// } +//} + +QString PostgreSqlGenerator::deleteTableRows(QString tableName, QString where) +{ + return SqlGeneratorBase::deleteTableRows(tableName, where) + " RETURNING *"; +} + +QT_END_NAMESPACE diff --git a/src/postgresqlgenerator.h b/src/postgresqlgenerator.h new file mode 100644 index 0000000..976716a --- /dev/null +++ b/src/postgresqlgenerator.h @@ -0,0 +1,23 @@ +#ifndef POSTGRESQLGENERATOR_H +#define POSTGRESQLGENERATOR_H + +#include +#include "sqlgeneratorbase_p.h" + +QT_BEGIN_NAMESPACE + +class Field; +class PostgreSqlGenerator : public SqlGeneratorBase +{ +public: + PostgreSqlGenerator(); + + QString getColumnDef(Field *field); +// QString saveSql(Table *t, QString tableName); + + QString deleteTableRows(QString tableName, QString where); +}; + +QT_END_NAMESPACE + +#endif // POSTGRESQLGENERATOR_H diff --git a/src/query.cpp b/src/query.cpp new file mode 100644 index 0000000..de01254 --- /dev/null +++ b/src/query.cpp @@ -0,0 +1,23 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#include "query.h" + + diff --git a/src/query.h b/src/query.h new file mode 100644 index 0000000..e8ab6b0 --- /dev/null +++ b/src/query.h @@ -0,0 +1,289 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#ifndef QUERY_H +#define QUERY_H + +#include +#include +#include +#include + +#include "database.h" +#include "databasemodel.h" +#include "tablesetbase_p.h" +#include "sqlgeneratorbase_p.h" +#include "querybase_p.h" + +QT_BEGIN_NAMESPACE + +template +class NUT_EXPORT Query : public QueryBase +{ + QString _tableName; + QString _select; + QString _where; + QString _joinClassName; + Database *_database; + TableSetBase *_tableSet; +public: + Query(Database *database, TableSetBase *tableSet); + + QList toList(int count = -1); + T *first(); + int count(); + int remove(); + + void bind(QVariant v); + void bind(QString name, QVariant v); + + Query *join(const QString &tableName); + Query *setWhere(const QString &where); + Query *bindValues(QVariant v1 = QVariant(), QVariant v2 = QVariant(), QVariant v3 = QVariant(), + QVariant v4 = QVariant(), QVariant v5 = QVariant(), QVariant v6 = QVariant(), + QVariant v7 = QVariant(), QVariant v8 = QVariant(), QVariant v9 = QVariant()); + + Query *orderBy(QString fieldName, QString type); + +private: + static QHash _compiledCommands; + QString compileCommand(QString command); + QString queryText(); +}; + +template +QHash Query::_compiledCommands; + +template +Q_OUTOFLINE_TEMPLATE Query::Query(Database *database, TableSetBase *tableSet) : QueryBase(database), _database(database), _tableSet(tableSet), + _joinClassName(QString::null) +{ + _tableName = _database->tableName(T::staticMetaObject.className()); +} + +template +Q_OUTOFLINE_TEMPLATE QList Query::toList(int count) +{ + QList result; + _select = "*"; + qDebug()<exec(queryText()); + + QString pk =_database->model().scheema(_tableName)->primaryKey(); + QVariant lastPkValue = QVariant(); + int childTypeId = 0; + T *lastRow = 0; + TableSetBase *childTableSet; + QStringList masterFields = _database->model().scheema(_tableName)->fieldsNames(); + QStringList childFields; + if(!_joinClassName.isNull()){ + childFields = _database->model().scheemaByClass(_joinClassName)->fieldsNames(); + QString joinTableName = _database->tableName(_joinClassName); + childTypeId = _database->model().scheema(joinTableName)->typeId(); + } + + while (q.next()) { + if(lastPkValue != q.value(pk)){ + T *t = new T(); + + foreach (QString field, masterFields) + t->setProperty(field.toLatin1().data(), q.value(field)); + + t->setTableSet(_tableSet); + t->setStatus(Table::FeatchedFromDB); + t->setParent(this); + + result.append(t); + lastRow = t; + + if(childTypeId){ + QSet tableSets = t->tableSets; + foreach (TableSetBase *ts, tableSets) + if(ts->childClassName() == _joinClassName) + childTableSet = ts; + } + } + + if(childTypeId){ + const QMetaObject *childMetaObject = QMetaType::metaObjectForType(childTypeId); + Table *childTable = qobject_cast(childMetaObject->newInstance()); + + foreach (QString field, childFields) + childTable->setProperty(field.toLatin1().data(), q.value(field)); + + childTable->setParent(this); + childTable->setParentTable(lastRow); + childTable->setStatus(Table::FeatchedFromDB); + childTable->setTableSet(childTableSet); + childTableSet->add(childTable); + } + lastPkValue = q.value(pk); + + if(!--count) + break; + } + + deleteLater(); + return result; +} + +template +Q_OUTOFLINE_TEMPLATE T *Query::first() +{ + QList list = toList(1); + + if(list.count()) + return list.first(); + else + return 0; +} + +template +Q_OUTOFLINE_TEMPLATE int Query::count() +{ + _select = "COUNT(*)"; + QSqlQuery q = _database->exec(queryText()); + if(q.next()) + return q.value(0).toInt(); + return 0; +} + +template +Q_OUTOFLINE_TEMPLATE int Query::remove() +{ + QString sql = _database->sqlGenertor()->deleteTableRows(_tableName, _where); + sql = compileCommand(sql); + QSqlQuery q = _database->exec(sql); + return q.numRowsAffected(); +} + +template +Q_OUTOFLINE_TEMPLATE Query *Query::join(const QString &tableName) +{ + _joinClassName = tableName; + return this; +} + +template +Q_OUTOFLINE_TEMPLATE Query *Query::setWhere(const QString &where) +{ + _where = where; + return this; +} + +template +Q_OUTOFLINE_TEMPLATE void Query::bind(QVariant v) +{ + _where = _where.arg(v.toString()); +} + +template +Q_OUTOFLINE_TEMPLATE void Query::bind(QString name, QVariant v) +{ + if(!name.startsWith(":")) + name.prepend(":"); + _where = _where.replace(name, v.toString()); +} + +template +Q_OUTOFLINE_TEMPLATE Query *Query::bindValues(QVariant v1, QVariant v2, QVariant v3, QVariant v4, QVariant v5, QVariant v6, QVariant v7, QVariant v8, QVariant v9) +{ + if(v1 != QVariant()) _where = _where.arg(v1.toString()); + if(v2 != QVariant()) _where = _where.arg(v2.toString()); + if(v3 != QVariant()) _where = _where.arg(v3.toString()); + if(v4 != QVariant()) _where = _where.arg(v4.toString()); + if(v5 != QVariant()) _where = _where.arg(v5.toString()); + if(v6 != QVariant()) _where = _where.arg(v6.toString()); + if(v7 != QVariant()) _where = _where.arg(v7.toString()); + if(v8 != QVariant()) _where = _where.arg(v8.toString()); + if(v9 != QVariant()) _where = _where.arg(v9.toString()); + + return this; +} + +template +Q_OUTOFLINE_TEMPLATE Query *Query::orderBy(QString fieldName, QString type) +{ + return this; +} + +template +Q_OUTOFLINE_TEMPLATE QString Query::compileCommand(QString command) +{ + if(!_compiledCommands.contains(command)){ + QString q = command + .replace("::", ".") + .replace("()", "") + .replace("==", "=") + .replace("!=", "<>"); + + QRegularExpression r("(\\w+)\\.(\\w+)"); + QRegularExpressionMatchIterator i = r.globalMatch(command); + while (i.hasNext()) { + QRegularExpressionMatch match = i.next(); + QString tableName = match.captured(1); + QString fieldName = match.captured(2); + tableName = _database->tableName(tableName); + q = command.replace(match.captured(), tableName + "." + fieldName); + } + _compiledCommands.insert(command, q); + } + + return _compiledCommands[command]; +} + +template +Q_OUTOFLINE_TEMPLATE QString Query::queryText() +{ + QString orderby = ""; + QString q = compileCommand(_where); + + QString t = _tableName; + if(!_joinClassName.isNull()){ + QString joinTableName = _database->tableName(_joinClassName); + Relation *rel = _database->model().relationByTableNames(_tableName, joinTableName); + if(rel){ + QString pk = _database->model().scheema(_tableName)->primaryKey(); + t = QString("%1 INNER JOIN %2 ON (%1.%3 = %2.%4)") + .arg(_tableName) + .arg(joinTableName) + .arg(pk) + .arg(rel->localColumn); + orderby = " ORDER BY " + _tableName + "." + pk; + }else{ + qWarning(QString("Relation between table %1 and class %2 (%3) not exists!") + .arg(_tableName) + .arg(_joinClassName) + .arg(joinTableName.isNull() ? "NULL" : joinTableName) + .toLatin1().data()); + _joinClassName = QString::null; + } + } + + return QString("SELECT %1 FROM %2 %3%4") + .arg(_select) + .arg(t) + .arg(q.isEmpty() ? "" : "WHERE " + q) + .arg(orderby); +} + +QT_END_NAMESPACE + +#endif // QUERY_H diff --git a/src/querybase.cpp b/src/querybase.cpp new file mode 100644 index 0000000..66f0a8e --- /dev/null +++ b/src/querybase.cpp @@ -0,0 +1,6 @@ +#include "querybase_p.h" + +QueryBase::QueryBase(QObject *parent) : QObject(parent) +{ + +} diff --git a/src/querybase_p.h b/src/querybase_p.h new file mode 100644 index 0000000..cd0c59b --- /dev/null +++ b/src/querybase_p.h @@ -0,0 +1,18 @@ +#ifndef QUERYBASE_H +#define QUERYBASE_H + +#include +#include + +class QueryBase : public QObject +{ + Q_OBJECT +public: + explicit QueryBase(QObject *parent = 0); + +signals: + +public slots: +}; + +#endif // QUERYBASE_H \ No newline at end of file diff --git a/src/sqlgeneratorbase.cpp b/src/sqlgeneratorbase.cpp new file mode 100644 index 0000000..2d2d895 --- /dev/null +++ b/src/sqlgeneratorbase.cpp @@ -0,0 +1,182 @@ +#include "databasemodel.h" +#include "sqlgeneratorbase_p.h" +#include "table.h" +#include "tablescheema.h" + +QT_BEGIN_NAMESPACE + +SqlGeneratorBase::SqlGeneratorBase() +{ + +} + +SqlGeneratorBase::~SqlGeneratorBase() +{ + +} + +QString SqlGeneratorBase::saveSql(Table *t, QString tableName) +{ + switch(t->status()){ + case Table::Added: + return insertCommand(t, tableName); + + case Table::Deleted: + return deleteCommand(t, tableName); + + case Table::Modified: + return updateCommand(t, tableName); + + case Table::NewCreated: + case Table::FeatchedFromDB: + // disable compiler warning + return "***"; + } +} + +QStringList SqlGeneratorBase::getDiff(DatabaseModel lastModel, DatabaseModel newModel) +{ + QStringList ret; + + QSet tableNames; + foreach (TableScheema *table, lastModel) + tableNames.insert(table->name()); + + foreach (TableScheema *table, newModel) + tableNames.insert(table->name()); + + + foreach (QString tableName, tableNames) { + TableScheema *oldTable = lastModel.scheema(tableName); + TableScheema *newTable = newModel.scheema(tableName); + ret << getDiff(oldTable, newTable); + } + + return ret; +} + +QString SqlGeneratorBase::getDiff(Field *oldField, Field *newField) +{ + QString sql = ""; + if(oldField && newField) + if(*oldField == *newField) + return QString::null; + + if(!newField){ + sql = "DROP COLUMN " + oldField->name; + }else{ + if(oldField) + sql = "ALTER COLUMN "; + else + sql = "ADD COLUMN "; + sql.append(getColumnDef(newField)); + } + return sql; +} + +QString SqlGeneratorBase::getDiff(TableScheema *oldTable, TableScheema *newTable) +{ + if(oldTable && newTable) + if(*oldTable == *newTable) + return ""; + + if(!newTable) + return "DROP TABLE " + oldTable->name(); + + QSet fieldNames; + + if(oldTable) + foreach (Field *f, oldTable->fields()) + fieldNames.insert(f->name); + + foreach (Field *f, newTable->fields()) + fieldNames.insert(f->name); + + QStringList columnSql; + foreach (QString fieldName, fieldNames) { + Field *newField = newTable->field(fieldName); + if(oldTable){ + Field *oldField = oldTable->field(fieldName); + + QString buffer = getDiff(oldField, newField); + if(!buffer.isNull()) + columnSql << buffer; + }else{ + columnSql << getColumnDef(newField); + } + } + QString sql; + if(oldTable){ + sql = QString("ALTER TABLE %1 \n%2") + .arg(newTable->name()) + .arg(columnSql.join(",\n")); + }else{ + if(!newTable->primaryKey().isNull()) + columnSql << QString("CONSTRAINT pk_%1 PRIMARY KEY (%2)") + .arg(newTable->name()) + .arg(newTable->primaryKey()); + + sql = QString("CREATE TABLE %1 \n(%2)") + .arg(newTable->name()) + .arg(columnSql.join(",\n")); + + } + return sql; +} + +QString SqlGeneratorBase::insertCommand(Table *t, QString tableName) +{ + QString sql = ""; + QString key = t->primaryKey(); + QStringList values; + + foreach (QString f, t->changedProperties()) + if(f != key) + values.append("'" + t->property(f.toLatin1().data()).toString() + "'"); + + sql = QString("INSERT INTO %1 (%2) VALUES (%3)") + .arg(tableName) + .arg(t->changedProperties().toList().join(", ")) + .arg(values.join(", ")); + + return sql; +} + +QString SqlGeneratorBase::updateCommand(Table *t, QString tableName) +{ + QString sql = ""; + QString key = t->primaryKey(); + QStringList values; + + foreach (QString f, t->changedProperties()) + if(f != key) + values.append(f + "='" + t->property(f.toLatin1().data()).toString() + "'"); + sql = QString("UPDATE %1 SET %2 WHERE %3=%4") + .arg(tableName) + .arg(values.join(", ")) + .arg(key) + .arg(t->primaryValue().toString()); + + return sql; +} + +QString SqlGeneratorBase::deleteCommand(Table *t, QString tableName) +{ + return QString("DELETE FROM %1 WHERE %2='%3'") + .arg(tableName) + .arg(t->primaryKey()) + .arg(t->primaryValue().toString()); +} + +QString SqlGeneratorBase::deleteTableRows(QString tableName, QString where) +{ + QString sql = ""; + if(where.isEmpty() || where.isNull()) + sql = "DELETE FROM " + tableName; + else + sql = "DELETE FROM " + tableName + " WHERE " + where; + return sql; +} + + +QT_END_NAMESPACE diff --git a/src/sqlgeneratorbase_p.h b/src/sqlgeneratorbase_p.h new file mode 100644 index 0000000..4a25dfe --- /dev/null +++ b/src/sqlgeneratorbase_p.h @@ -0,0 +1,37 @@ +#ifndef SQLGENERATORBASE_H +#define SQLGENERATORBASE_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +class Table; +class Field; +class DatabaseModel; +class TableScheema; +class SqlGeneratorBase +{ +public: + SqlGeneratorBase(); + virtual ~SqlGeneratorBase(); + + virtual QString saveSql(Table *t, QString tableName); + virtual QString getColumnDef(Field *field) = 0; + virtual QStringList getDiff(DatabaseModel lastModel, DatabaseModel newModel); + virtual QString getDiff(Field *oldField, Field *newField); + virtual QString getDiff(TableScheema *oldTable, TableScheema *newTable); + + + virtual QString insertCommand(Table *t, QString tableName); + virtual QString updateCommand(Table *t, QString tableName); + virtual QString deleteCommand(Table *t, QString tableName); + + virtual QString deleteTableRows(QString tableName, QString where); + +}; + +QT_END_NAMESPACE + +#endif // SQLGENERATORBASE_H diff --git a/src/sqlitegenerator.cpp b/src/sqlitegenerator.cpp new file mode 100644 index 0000000..c04032d --- /dev/null +++ b/src/sqlitegenerator.cpp @@ -0,0 +1,51 @@ +#include "sqlitegenerator.h" +#include "table.h" +#include "tablescheema.h" + +SqliteGenerator::SqliteGenerator() : SqlGeneratorBase() +{ + +} + +QString SqliteGenerator::getColumnDef(Field *field) +{ + QString ret = field->name + " "; + QString dbType; + + switch (field->type) { + case QVariant::Bool: + dbType = "int"; + break; + case QVariant::ByteArray: + dbType = "blob"; + break; + case QVariant::Date: + dbType = "date"; + break; + case QVariant::DateTime: + dbType = "datetime"; + break; + case QVariant::Double: + dbType = "real"; + break; + case QVariant::Int: +// if(field->isAutoIncrement) +// dbType = "INTEGER PRIMARY KEY"; +// else + dbType = "integer"; + break; + case QVariant::String: + if(field->length) + dbType = QString("varchar(%1)").arg(field->length); + else + dbType = "text"; + break; + case QVariant::Time: + dbType = "time"; + break; + default: + dbType = ""; + } + ret.append(dbType); + return ret; +} diff --git a/src/sqlitegenerator.h b/src/sqlitegenerator.h new file mode 100644 index 0000000..7f7842d --- /dev/null +++ b/src/sqlitegenerator.h @@ -0,0 +1,15 @@ +#ifndef SQLITEGENERATOR_H +#define SQLITEGENERATOR_H + +#include +#include "sqlgeneratorbase_p.h" + +class SqliteGenerator : public SqlGeneratorBase +{ +public: + SqliteGenerator(); + + QString getColumnDef(Field *field); +}; + +#endif // SQLITEGENERATOR_H diff --git a/src/table.cpp b/src/table.cpp new file mode 100644 index 0000000..0325c0f --- /dev/null +++ b/src/table.cpp @@ -0,0 +1,166 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#include +#include +#include "table.h" +#include "database.h" +#include "sqlgeneratorbase_p.h" + +QT_BEGIN_NAMESPACE + +Table::Table(QObject *parent) : QObject(parent) +{ + setStatus(NewCreated); +} + +void Table::add(TableSetBase *t) +{ + this->tableSets.insert(t); +} + + +QString Table::primaryKey() const +{ + static QString ret = QString::null; + + if(ret == QString::null){ + for(int i = 0; i < metaObject()->classInfoCount(); i++){ + QMetaClassInfo ci = metaObject()->classInfo(i); + QString ciName = ci.name(); + + if(ciName.startsWith(__nut_NAME_PERFIX)) + ciName.remove(__nut_NAME_PERFIX); + + if(ciName.contains(" ")){ + QStringList parts = ciName.split(" "); + QString propName = parts.at(1); + if(propName == __nut_PRIMARY_KEY) + ret = parts.at(0); + } + } + + if(ret == QString::null) + ret = ""; + } + + return ret; +} + +QString Table::autoIncrementField() const +{ + static QString ret = QString::null; + + if(ret == QString::null){ + for(int i = 0; i < metaObject()->classInfoCount(); i++){ + QMetaClassInfo ci = metaObject()->classInfo(i); + QString ciName = ci.name(); + + if(ciName.startsWith(__nut_NAME_PERFIX)) + ciName.remove(__nut_NAME_PERFIX); + + if(ciName.contains(" ")){ + QStringList parts = ciName.split(" "); + QString propName = parts.at(1); + if(propName == __nut_AUTO_INCREMENT) + ret = parts.at(0); + } + } + + if(ret == QString::null) + ret = ""; + } + + return ret; +} + +QVariant Table::primaryValue() const +{ + return property(primaryKey().toLatin1().data()); +} + +void Table::propertyChanged(QString propName) +{ + if(propName == primaryKey()) + return; + +// qDebug() << "Table::propertyChanged" << metaObject()->className() << propName; + _changedProperties.insert(propName); + if(_status == FeatchedFromDB) + _status = Modified; + + if(_status == NewCreated) + _status = Added; +} + +QSet Table::changedProperties() const +{ + return _changedProperties; +} + +bool Table::setParentTable(Table *master) +{ + QString masterClassName = master->metaObject()->className(); + TableScheema *myModel = TableScheema::findByClassName(metaObject()->className()); + + foreach (Relation *r, myModel->foregionKeys()) + if(r->className == masterClassName) + { + setProperty(QString(r->localColumn).toLatin1().data(), master->primaryValue()); + _changedProperties.insert(r->localColumn); + } +} + +TableSetBase *Table::tableSet() const +{ + return _tableSet; +} + +void Table::setTableSet(TableSetBase *parent) +{ + _tableSet = parent; + _tableSet->add(this); +} + +void Table::save(Database *db) +{ + QSqlQuery q = db->exec(db->sqlGenertor()->saveSql(this, db->tableName(metaObject()->className()))); + //if(q.next()) + // setProperty(primaryKey().toLatin1().data(), q.value(0)); + + if(status() == Added) + setProperty(primaryKey().toLatin1().data(), q.lastInsertId()); + + foreach(TableSetBase *ts, tableSets) + ts->save(db); + setStatus(FeatchedFromDB); +} + +Table::Status Table::status() const +{ + return _status; +} + +void Table::setStatus(const Status &status) +{ + _status = status; +} + +QT_END_NAMESPACE diff --git a/src/table.h b/src/table.h new file mode 100644 index 0000000..348d30c --- /dev/null +++ b/src/table.h @@ -0,0 +1,90 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#ifndef TABLE_H +#define TABLE_H + +#include +#include +#include + +#include "tablescheema.h" +#include "defines.h" +//#include "tableset.h" + +QT_BEGIN_NAMESPACE + +class Database; +class TableSetBase; +class NUT_EXPORT Table : public QObject +{ + Q_OBJECT + +public: + explicit Table(QObject *tableSet = 0); + + enum Status{ + NewCreated, + FeatchedFromDB, + Added, + Modified, + Deleted + }; + + void add(TableSetBase *); + void save(Database *db); + + QString primaryKey() const; + QString autoIncrementField() const; + QVariant primaryValue() const; + Status status() const; + void setStatus(const Status &status); + + TableSetBase *tableSet() const; + void setTableSet(TableSetBase *tableSet); + + QSet changedProperties() const; + + bool setParentTable(Table *master); +signals: + +public slots: + +protected: + void propertyChanged(QString propName); + +private: + Status _status; + QSet _changedProperties; + TableSetBase *_tableSet; + + QSet tableSets; + +// template +// friend class TableSet; + template + friend class Query; +// friend class Database; +}; + +QT_END_NAMESPACE + +#include "tableset.cpp" +#endif // TABLE_H diff --git a/src/tablescheema.cpp b/src/tablescheema.cpp new file mode 100644 index 0000000..bd0439b --- /dev/null +++ b/src/tablescheema.cpp @@ -0,0 +1,350 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#include +#include +#include + +#include +#include + +#include "tablescheema.h" +#include "defines_p.h" + + +QSet TableScheema::_allModels; +//QMap TableScheema::scheemas; + +QString TableScheema::name() const +{ + return _name; +} + +void TableScheema::setName(const QString &name) +{ + _name = name; +} + +QString TableScheema::className() const +{ + return _className; +} + +void TableScheema::setClassName(const QString &className) +{ + _className = className; +} + +int TableScheema::typeId() const +{ + return _typeId; +} + +void TableScheema::setTypeId(const int &typeId) +{ + _typeId = typeId; +} + +Field *TableScheema::field(QString name) const +{ + foreach (Field *f, _fields) + if(f->name == name) + return f; + + return 0; +} + +QList TableScheema::fields() const +{ + return _fields; +} + +QList TableScheema::foregionKeys() const +{ + return _foregionKeys; +} + +QStringList TableScheema::fieldsNames() const +{ + QStringList ret; + foreach (Field *f, _fields) + ret.append(f->name); + return ret; +} + +TableScheema *TableScheema::findByTypeId(int typeId) +{ + foreach (TableScheema *model, _allModels) + if(model->typeId() == typeId) + return model; + return 0; +} + +TableScheema *TableScheema::findByName(QString name) +{ + foreach (TableScheema *model, _allModels) + if(model->name() == name) + return model; + return 0; +} + +TableScheema *TableScheema::findByClassName(QString className) +{ + foreach (TableScheema *model, _allModels) + if(model->className() == className) + return model; + return 0; +} + +bool TableScheema::operator ==(const TableScheema &t) const{ + if(_name != t.name()) + return false; + + foreach (Field *f, _fields) { + Field *tf = t.field(f->name); + if(!tf) + return false; + + if(*f != *tf) + return false; + } + + return true; +} + +bool TableScheema::operator !=(const TableScheema &t) const +{ + return !(*this == t); +} + +TableScheema::TableScheema(int typeId, QString tableName) +{ + const QMetaObject *tableMetaObject = QMetaType::metaObjectForType(typeId); + + _typeId = typeId; + _name = tableName; + _className = tableMetaObject->className(); + + // get fields names + for(int j = 0; j < tableMetaObject->classInfoCount(); j++){ + QString name = tableMetaObject->classInfo(j).name(); + + name = name.remove(__nut_NAME_PERFIX); + + if(name.contains(" ")){ + QStringList parts = name.split(" "); + QString propName = parts.at(1); + + if(propName == __nut_FIELD){ + Field *f = new Field; + f->name = parts.at(0); + _fields.append(f); + } + } + } + // Browse all fields + for(int j = 1; j < tableMetaObject->propertyCount(); j++){ + QMetaProperty fieldProperty = tableMetaObject->property(j); + + Field *f = field(fieldProperty.name()); + if(!f) + continue; + + f->type = fieldProperty.type(); + + _fields.append(f); + } + + // Browse class infos + for(int j = 0; j < tableMetaObject->classInfoCount(); j++){ + QString name = tableMetaObject->classInfo(j).name(); + QString value = tableMetaObject->classInfo(j).value(); + + name = name.remove(__nut_NAME_PERFIX); + + + if(name.contains(" ")){ + QStringList parts = name.split(" "); + QString propName = parts.at(1); + + if(propName == __nut_FOREGION_KEY){ + Relation *fk = new Relation; + fk->localColumn = parts.at(0); + fk->foregionColumn = value; + fk->className = value; + _foregionKeys.append(fk); + } + + if(propName == __nut_FIELD){ + + } + + + Field *f = field(parts.at(0)); + if(!f) + continue; + + if(propName == __nut_LEN) + f->length = value.toInt(); + else if(propName == __nut_NOT_NULL) + f->notNull = false; + else if(propName == __nut_DEFAULT_VALUE) + f->defaultValue = value; + else if(propName == __nut_PRIMARY_KEY) + f->isPrimaryKey = true; + else if(propName == __nut_AUTO_INCREMENT) + f->isAutoIncrement = true; + + } + } + + _allModels.insert(this); +} + +/* + "comment": { + "auto_increment": "id", + "fields": { + "id": { + "name": "id", + "type": "int" + }, + "userId": { + "name": "userId", + "type": "int" + } + }, + "primary_key": "id" + }, +*/ +TableScheema::TableScheema(QJsonObject json, QString tableName) +{ + _name = tableName; + + QJsonObject fields = json.value(__FIELDS).toObject(); + foreach (QString key, fields.keys()) { + QJsonObject fieldObject = fields.value(key).toObject(); + Field *f = new Field; + f->name = fieldObject.value(__NAME).toString(); + f->type = QVariant::nameToType(fieldObject.value(__TYPE).toString().toLatin1().data()); + + if(fieldObject.contains(__nut_NOT_NULL)) + f->notNull = fieldObject.value(__nut_NOT_NULL).toBool(); + + if(fieldObject.contains(__nut_LEN)) + f->length = fieldObject.value(__nut_LEN).toInt(); + + if(fieldObject.contains(__nut_DEFAULT_VALUE)) + f->defaultValue = fieldObject.value(__nut_DEFAULT_VALUE).toString(); + _fields.append(f); + } + + if(json.keys().contains(__nut_AUTO_INCREMENT)) + field(json.value(__nut_AUTO_INCREMENT).toString())->isAutoIncrement = true; + + if(json.keys().contains(__nut_PRIMARY_KEY)) + field(json.value(__nut_PRIMARY_KEY).toString())->isAutoIncrement = true; + +} + +QJsonObject TableScheema::toJson() const +{ + QJsonObject obj; + QJsonObject fieldsObj; + + foreach (Field *f, _fields) { + QJsonObject fieldObj; + fieldObj.insert(__NAME, f->name); + fieldObj.insert(__TYPE, QVariant::typeToName(f->type)); + + if(f->length) + fieldObj.insert(__nut_LEN, f->length); + + if(f->notNull) + fieldObj.insert(__nut_NOT_NULL, f->notNull); + + if(!f->defaultValue.isNull()) + fieldObj.insert(__nut_DEFAULT_VALUE, f->defaultValue); + + fieldsObj.insert(f->name, fieldObj); + + if(f->isAutoIncrement) + obj.insert(__nut_PRIMARY_KEY, f->name); + + if(f->isPrimaryKey) + obj.insert(__nut_AUTO_INCREMENT, f->name); + } + obj.insert(__FIELDS, fieldsObj); + + return obj; +} + +//TableScheema *TableScheema::registerTable(int typeId, QString tableName) +//{ +// TableScheema *scheema = new TableScheema(typeId, tableName); +// scheemas.insert(typeId, scheema); +// return scheema; +//} + +//void TableScheema::createForegionKeys() +//{ +// foreach (TableScheema *sch, scheemas) { +// foreach (ForegionKey *fk, sch->_foregionKeys) { +// fk->table = scheema(fk->tableName); +// } +// } +//} + +//TableScheema *TableScheema::scheema(QString className) +//{ +// foreach (TableScheema *s, scheemas) +// if(s->_className == className) +// return s; +// return 0; +//} + +Relation *TableScheema::foregionKey(QString otherTable) const +{ + foreach (Relation *fk, _foregionKeys) + if(fk->className == otherTable) + return fk; + + return 0; +} + +QString TableScheema::toString() const +{ + QStringList sl; + foreach (Field *f, _fields) + sl.append(f->name + " " + QVariant::typeToName(f->type)); + + QString ret = QString("%1 (%2)") + .arg(_name) + .arg(sl.join(", ")); + return ret; +} + +QString TableScheema::primaryKey() const +{ + foreach (Field *f, _fields) + if(f->isPrimaryKey) + return f->name; + return QString::null; +} diff --git a/src/tablescheema.h b/src/tablescheema.h new file mode 100644 index 0000000..f1f646b --- /dev/null +++ b/src/tablescheema.h @@ -0,0 +1,114 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#ifndef TABLESCHEEMA_H +#define TABLESCHEEMA_H + +#include +#include +class QJsonObject; +class TableScheema; + +struct Field{ + Field() : name(QString::null), length(0), defaultValue(QString::null), + notNull(false), isPrimaryKey(false), isAutoIncrement(false) + { + + } + + QString name; + QVariant::Type type; + int length; + QString defaultValue; + bool notNull; + bool isPrimaryKey; + bool isAutoIncrement; + + bool operator ==(const Field &f) const{ + + bool b = name == f.name + && type == f.type + && length == f.length + && defaultValue == f.defaultValue + && notNull == f.notNull; + + return b; + } + + bool operator !=(const Field &f) const{ + return !(*this == f); + } +}; + +struct Relation{ + QString className; + QString localColumn; + TableScheema *table; + QString foregionColumn; +}; +class TableScheema +{ +public: + + TableScheema(int typeId, QString tableName); + TableScheema(QJsonObject json, QString tableName); + + QJsonObject toJson() const; + +// static TableScheema *registerTable(int typeId, QString tableName); +// static void createForegionKeys(); +// static TableScheema* scheema(QString className); + + Field *field(QString name) const; + Relation *foregionKey(QString otherTable) const; + + QString toString() const; + + QString primaryKey() const; + + QString name() const; + void setName(const QString &name); + + QString className() const; + void setClassName(const QString &className); + + int typeId() const; + void setTypeId(const int &typeId); + QList fields() const; + QList foregionKeys() const; + QStringList fieldsNames() const; + + static TableScheema *findByTypeId(int typeId); + static TableScheema *findByName(QString name); + static TableScheema *findByClassName(QString className); + + bool operator ==(const TableScheema &t) const; + bool operator !=(const TableScheema &t) const; + +private: + QString _name; + QString _className; + int _typeId; + QList _fields; + QList _foregionKeys; + static QSet_allModels; +}; + +#endif // TABLESCHEEMA_H diff --git a/src/tableset.cpp b/src/tableset.cpp new file mode 100644 index 0000000..b80f27a --- /dev/null +++ b/src/tableset.cpp @@ -0,0 +1,21 @@ +///************************************************************************** +//** +//** This file is part of Nut project. +//** https://github.com/HamedMasafi/Nut +//** +//** Nut is free software: you can redistribute it and/or modify +//** it under the terms of the GNU Lesser General Public License as published by +//** the Free Software Foundation, either version 3 of the License, or +//** (at your option) any later version. +//** +//** Nut is distributed in the hope that it will be useful, +//** but WITHOUT ANY WARRANTY; without even the implied warranty of +//** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//** GNU Lesser General Public License for more details. +//** +//** You should have received a copy of the GNU Lesser General Public License +//** along with Nut. If not, see . +//** +//**************************************************************************/ + +#include "tableset.h" diff --git a/src/tableset.h b/src/tableset.h new file mode 100644 index 0000000..1e67384 --- /dev/null +++ b/src/tableset.h @@ -0,0 +1,127 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#ifndef TABLESET_H +#define TABLESET_H + +#include +#include +#include +#include + +#include "tablesetbase_p.h" +#include "database.h" +#include "table.h" + +QT_BEGIN_NAMESPACE + +template +class Query; + +template +class NUT_EXPORT TableSet : public TableSetBase +{ +public: + TableSet(Database *parent); + TableSet(Table *parent); + + void append(T *t); + void append(QList t); + void remove(T *t); + void remove(QList t); + + int length() const; + T *at(int i) const; + const T &operator[](int i) const; + Query *createQuery(); +}; + +template +Q_OUTOFLINE_TEMPLATE TableSet::TableSet(Database *parent) : TableSetBase(parent) +{ + _childClassName = T::staticMetaObject.className(); +} + +template +Q_OUTOFLINE_TEMPLATE TableSet::TableSet(Table *parent) : TableSetBase(parent) +{ + _childClassName = T::staticMetaObject.className(); +} + +template +Q_OUTOFLINE_TEMPLATE Query *TableSet::createQuery() +{ + Query *q = new Query(_database, this); + + return q; +} + +template +Q_OUTOFLINE_TEMPLATE int TableSet::length() const +{ + return _tables.count(); +} + +template +Q_OUTOFLINE_TEMPLATE T *TableSet::at(int i) const +{ + return (T*)_tables.values().at(i); +} + +template +Q_OUTOFLINE_TEMPLATE const T &TableSet::operator[](int i) const +{ + return _tables.values()[i]; +} + +template +Q_OUTOFLINE_TEMPLATE void TableSet::append(T *t) +{ + _tables.insert(t); +// rows.append(t); + t->setTableSet(this); + if(t->status() != Table::FeatchedFromDB) + t->setStatus(Table::Added); +} + +template +Q_OUTOFLINE_TEMPLATE void TableSet::append(QList t) +{ + foreach (T* i, t) + append(i); +} + +template +Q_OUTOFLINE_TEMPLATE void TableSet::remove(T *t) +{ + _tables.remove(t); + t->setStatus(Table::Deleted); +} + +template +Q_OUTOFLINE_TEMPLATE void TableSet::remove(QList t) +{ + foreach (T* i, t) + remove(i); +} + +QT_END_NAMESPACE + +#endif // TABLESET_H diff --git a/src/tablesetbase.cpp b/src/tablesetbase.cpp new file mode 100644 index 0000000..09849e1 --- /dev/null +++ b/src/tablesetbase.cpp @@ -0,0 +1,58 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#include "table.h" +#include "database.h" +#include "tablesetbase_p.h" +#include "databasemodel.h" + +TableSetBase::TableSetBase(Database *parent) : QObject(parent), _database(parent), _table(0) +{ + parent->add(this); +} + +TableSetBase::TableSetBase(Table *parent) : QObject(parent), _database(0), _table(parent) +{ + parent->add(this); +} + +void TableSetBase::save(Database *db) +{ + foreach (Table *t, _tables) { + if(_table) + t->setParentTable(_table); + + if(t->status() == Table::Added + || t->status() == Table::Modified + || t->status() == Table::Deleted){ + t->save(db); + } + } +} + +void TableSetBase::add(Table *t) +{ + _tables.insert(t); +} + +QString TableSetBase::childClassName() const +{ + return _childClassName; +} diff --git a/src/tablesetbase_p.h b/src/tablesetbase_p.h new file mode 100644 index 0000000..9d47381 --- /dev/null +++ b/src/tablesetbase_p.h @@ -0,0 +1,51 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#ifndef TABLESETBASE_H +#define TABLESETBASE_H + +#include +#include +#include + +#include "defines.h" + +class Table; +class Database; +class TableSetBase : public QObject +{ + +public: + TableSetBase(Database *parent); + TableSetBase(Table *parent); + + virtual void save(Database *db); + void add(Table* t); + QString childClassName() const; + +protected: + QSet _tables; + QString _tableName; + Database *_database; + Table *_table; + QString _childClassName; +}; + +#endif // TABLESETBASE_H diff --git a/test/.maintest.cpp.kate-swp b/test/.maintest.cpp.kate-swp new file mode 100644 index 0000000000000000000000000000000000000000..1f932fa125bf7a35c4b4e147d7449e39ad694c86 GIT binary patch literal 341 zcmXYr%MOAt5Jkt2a0l8bAP@2J4N^eeNp#PG1&L9kCjP#hGL!V=o=iJa5%JFLw=>PJ zeVMYqcP2Nt?)~YX53}Fwnv00cheIupnTsO+bqEH`t-F#=q4=q};S>%ra$9sp6Je6`xL_;@%uo+_n>w NkDvw>D1ilq{R31b9=HGi literal 0 HcmV?d00001 diff --git a/test/comment.cpp b/test/comment.cpp new file mode 100644 index 0000000..87ec017 --- /dev/null +++ b/test/comment.cpp @@ -0,0 +1,6 @@ +#include "comment.h" + +Comment::Comment(QObject *parent) : Table(parent) +{ + +} diff --git a/test/comment.h b/test/comment.h new file mode 100644 index 0000000..7a81b13 --- /dev/null +++ b/test/comment.h @@ -0,0 +1,24 @@ +#ifndef COMMENT_H +#define COMMENT_H + +#include +#include +#include "table.h" + +class Post; +class Comment : public Table +{ + Q_OBJECT + + NUT_PRIMARY_AUTO_INCREMENT(id) + NUT_DECLARE_FIELD(int, id, id, setId) + NUT_DECLARE_FIELD(QString, message, message, setMessage) + NUT_DECLARE_FIELD(QDateTime, saveDate, saveDate, setSaveDate) + + NUT_FOREGION_KEY(Post, int, post, post, setPost) + +public: + Q_INVOKABLE explicit Comment(QObject *tableSet = 0); +}; + +#endif // COMMENT_H diff --git a/test/maintest.cpp b/test/maintest.cpp new file mode 100644 index 0000000..cbd2be7 --- /dev/null +++ b/test/maintest.cpp @@ -0,0 +1,131 @@ +#include +#include +#include + +#include "maintest.h" +#include "query.h" +#include "tableset.h" +#include "tablescheema.h" +#include "databasemodel.h" + +#include "post.h" +#include "comment.h" + +MainTest::MainTest(QObject *parent) : QObject(parent) +{ + +} + +void MainTest::initTestCase() +{ + qDebug() << "Table type id:" << qRegisterMetaType(); + qDebug() << "User type id:" << qRegisterMetaType(); + qDebug() << "Comment type id:" << qRegisterMetaType(); + qDebug() << "DB type id:" << qRegisterMetaType(); + + db.setDriver("QSQLITE"); + db.setHostName("127.0.0.1"); + db.setDatabaseName("nutdb"); +// db.setUserName("postgres"); +// db.setPassword("856856"); +// db.setUserName("root"); +// db.setPassword("onlyonlyi"); + bool ok = db.open(); + + QTEST_ASSERT(ok); +} + +void MainTest::dataScheema() +{ + auto json = db.model().toJson(); + auto model = DatabaseModel::fromJson(json); + + // qDebug() << model.toJson(); + // qDebug() << db.model().toJson(); + QTEST_ASSERT(model == db.model()); +} + +void MainTest::createPost() +{ + Post *newPost = new Post; + newPost->setTitle("post title"); + + db.posts()->append(newPost); + + for(int i = 0 ; i < 3; i++){ + Comment *comment = new Comment; + comment->setMessage("comment #" + QString::number(i)); + + newPost->comments()->append(comment); + } + db.saveChanges(); + + postId = newPost->id(); + + QTEST_ASSERT(newPost->id() != 0); + qDebug() << "New post inserted with id:" << newPost->id(); +} + +void MainTest::selectPosts() +{ + auto q = FROM(db.posts()) + JOIN(Comment) + WHERE(Post::id() == %1) + BIND(postId); + auto posts = q->toList(); + + QTEST_ASSERT(posts.length() == 1); + QTEST_ASSERT(posts.at(0)->comments()->length() == 3); + QTEST_ASSERT(posts.at(0)->title() == "post title"); + + qDebug()<comments()->at(0)->message(); + QTEST_ASSERT(posts.at(0)->comments()->at(0)->message() == "comment #0"); + QTEST_ASSERT(posts.at(0)->comments()->at(1)->message() == "comment #1"); + QTEST_ASSERT(posts.at(0)->comments()->at(2)->message() == "comment #2"); +} + +void MainTest::selectWithInvalidRelation() +{ + auto q = FROM(db.posts()) + JOIN(Invalid_Class_Name) + SELECT(); +} + +void MainTest::modifyPost() +{ + auto q = FROM(db.posts()) + WHERE(Post::id() == %1) + BIND(postId); + + Post *post = q->first(); + + QTEST_ASSERT(post != 0); + + post->setTitle("new name"); + db.saveChanges(); + + q = FROM(db.posts()) + WHERE(Post::id() == %1) + BIND(postId); + post = q->first(); + QTEST_ASSERT(post->title() == "new name"); +} + +void MainTest::deletePost() +{ + auto count = FROM(db.posts()) + WHERE(Post::id() == %1) + BIND(postId) + DELETE(); + + QTEST_ASSERT(count == 1); + + count = FROM(db.posts()) + WHERE(Post::id() == %1) + BIND(postId) + COUNT(); + + QTEST_ASSERT(count == 0); +} + +QTEST_MAIN(MainTest) diff --git a/test/maintest.h b/test/maintest.h new file mode 100644 index 0000000..f870330 --- /dev/null +++ b/test/maintest.h @@ -0,0 +1,30 @@ +#ifndef MAINTEST_H +#define MAINTEST_H + +#include +#include + +#include "weblogdatabase.h" +class MainTest : public QObject +{ + Q_OBJECT + WeblogDatabase db; + int postId; + +public: + explicit MainTest(QObject *parent = 0); + +signals: + +private slots: + void initTestCase(); + + void dataScheema(); + void createPost(); + void selectPosts(); + void selectWithInvalidRelation(); + void modifyPost(); + void deletePost(); +}; + +#endif // MAINTEST_H diff --git a/test/post.cpp b/test/post.cpp new file mode 100644 index 0000000..e4f6c06 --- /dev/null +++ b/test/post.cpp @@ -0,0 +1,9 @@ +#include "comment.h" +#include "tableset.h" +#include "post.h" + +Post::Post(QObject *parent) : Table(parent), + m_comments(new TableSet(this)), m_id(0), m_title("") +{ + +} diff --git a/test/post.h b/test/post.h new file mode 100644 index 0000000..a295abd --- /dev/null +++ b/test/post.h @@ -0,0 +1,34 @@ +#ifndef USER_H +#define USER_H + +#include +#include "table.h" +#include "database.h" +#include "comment.h" +#include "databasemodel.h" + +class Post : public Table +{ + Q_OBJECT + + NUT_PRIMARY_AUTO_INCREMENT(id) + NUT_DECLARE_FIELD(int, id, id, setId) + + NUT_NOT_NULL(title) + NUT_LEN(title, 50) + NUT_DECLARE_FIELD(QString, title, title, setTitle) + + NUT_DECLARE_FIELD(QString, body, body, setBody) + + NUT_DECLARE_CHILD_TABLE(Comment, comments) + +public: + explicit Post(QObject *tableSet = 0); + +signals: + +public slots: +}; + +//Q_DECLARE_METATYPE(User*) +#endif // USER_H diff --git a/test/tst_nut.pro b/test/tst_nut.pro new file mode 100644 index 0000000..84bc38a --- /dev/null +++ b/test/tst_nut.pro @@ -0,0 +1,21 @@ +QT += qml quick testlib sql + +QT -= gui + +TARGET = tst_nut +CONFIG += warn_on qmltestcase c++11 +INCLUDEPATH += $$PWD/../src +include(../nut.pri) +TEMPLATE = app +IMPORTPATH += $$OUT_PWD/../src/imports +SOURCES += \ + maintest.cpp \ + comment.cpp \ + post.cpp \ + weblogdatabase.cpp + +HEADERS += \ + maintest.h \ + comment.h \ + post.h \ + weblogdatabase.h diff --git a/test/weblogdatabase.cpp b/test/weblogdatabase.cpp new file mode 100644 index 0000000..ef77234 --- /dev/null +++ b/test/weblogdatabase.cpp @@ -0,0 +1,9 @@ +#include + +#include "post.h" +#include "comment.h" +#include "weblogdatabase.h" + +WeblogDatabase::WeblogDatabase() : Database(), m_posts(new TableSet(this)), m_comments(new TableSet(this)) +{ +} diff --git a/test/weblogdatabase.h b/test/weblogdatabase.h new file mode 100644 index 0000000..e059f7d --- /dev/null +++ b/test/weblogdatabase.h @@ -0,0 +1,23 @@ +#ifndef TDATABASE_H +#define TDATABASE_H + +#include "database.h" + +class Post; +class Comment; +class WeblogDatabase : public Database +{ + Q_OBJECT + + NUT_DB_VERSION(1, 0) + + NUT_DECLARE_TABLE(Post, post) + + NUT_DECLARE_TABLE(Comment, comment) + +public: + WeblogDatabase(); +}; + + +#endif // TDATABASE_H