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 0000000..1f932fa
Binary files /dev/null and b/test/.maintest.cpp.kate-swp differ
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