First commit

This commit is contained in:
Hamed Masafi 2016-05-12 10:38:58 +04:30
parent 1be874513c
commit e369e9b5d8
52 changed files with 3062 additions and 0 deletions

7
.directory Normal file
View File

@ -0,0 +1,7 @@
[Dolphin]
Timestamp=2016,5,12,10,37,46
Version=3
ViewMode=1
[Settings]
HiddenFilesShown=true

1
include/Database Normal file
View File

@ -0,0 +1 @@
#include "../src/database.h"

5
include/Nut Normal file
View File

@ -0,0 +1,5 @@
#include "../src/table.h"
#include "../src/database.h"
#include "../src/tableset.h"
#include "../src/query.h"

1
include/Query Normal file
View File

@ -0,0 +1 @@
#include "../src/query.h"

1
include/Table Normal file
View File

@ -0,0 +1 @@
#include "../src/table.h"

1
include/TableSet Normal file
View File

@ -0,0 +1 @@
#include "../src/tableset.h"

1
include/database.h Normal file
View File

@ -0,0 +1 @@
#include "../src/database.h"

22
include/header_copier Executable file
View File

@ -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<&-

5
include/nut.h Normal file
View File

@ -0,0 +1,5 @@
#include "../src/table.h"
#include "../src/database.h"
#include "../src/tableset.h"
#include "../src/query.h"

1
include/query.h Normal file
View File

@ -0,0 +1 @@
#include "../src/query.h"

1
include/table.h Normal file
View File

@ -0,0 +1 @@
#include "../src/table.h"

1
include/tableset.h Normal file
View File

@ -0,0 +1 @@
#include "../src/tableset.h"

36
nut.pri Normal file
View File

@ -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

30
src/changelogtable.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#include "changelogtable.h"
QT_BEGIN_NAMESPACE
ChangeLogTable::ChangeLogTable()
{
}
QT_END_NAMESPACE

48
src/changelogtable.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#ifndef CHANGELOGTABLE_H
#define CHANGELOGTABLE_H
#include <QtCore/qglobal.h>
#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

372
src/database.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#include <QtCore/QMetaProperty>
#include <QtCore/QDebug>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlError>
#include <QtSql/QSqlQuery>
#include <QtSql/QSqlResult>
#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 <iostream>
#include <cstdarg>
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<ChangeLogTable*>();
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<ChangeLogTable>();
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<ChangeLogTable>(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

84
src/database.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#ifndef DATABASE_H
#define DATABASE_H
#include <QtCore/qglobal.h>
#include <QtCore/QList>
#include <QtSql/QSqlDatabase>
#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<TableSetBase*> tableSets;
};
QT_END_NAMESPACE
#endif // DATABASE_H

66
src/database_p.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#ifndef DATABASE_P_H
#define DATABASE_P_H
#include "database.h"
#include "databasemodel.h"
#include "changelogtable.h"
#include <QDebug>
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<QString, QString> tables;
SqlGeneratorBase *sqlGenertor;
DatabaseModel currentModel;
TableSet<ChangeLogTable> *changeLogs;
};
#endif // DATABASE_P_H

118
src/databasemodel.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#include "databasemodel.h"
#include "tablescheema.h"
#include <QJsonObject>
DatabaseModel::DatabaseModel() : QList<TableScheema*>()
{
}
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;
}

47
src/databasemodel.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#ifndef DATABASEMODEL_H
#define DATABASEMODEL_H
#include <QtCore/QList>
class TableScheema;
struct Relation;
class QJsonObject;
class DatabaseModel : public QList<TableScheema*>
{
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

114
src/defines.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#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<type> name##s READ name##s) \
type* m_##name; \
TableSet<type> *m_##name##s; \
public: \
static const type _##name; \
type* name() const{ return m_##name; } \
TableSet<type> *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<type> *m_##n; \
public: \
TableSet<type> *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<QueryBase*>*/(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

45
src/defines_p.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#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

56
src/mysqlgenerator.cpp Normal file
View File

@ -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

20
src/mysqlgenerator.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef MYSQLGENERATOR_H
#define MYSQLGENERATOR_H
#include <QtCore/qglobal.h>
#include "sqlgeneratorbase_p.h"
QT_BEGIN_NAMESPACE
class MySqlGenerator : public SqlGeneratorBase
{
public:
MySqlGenerator();
QString getColumnDef(Field *field);
};
QT_END_NAMESPACE
#endif // MYSQLGENERATOR_H

View File

@ -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

23
src/postgresqlgenerator.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef POSTGRESQLGENERATOR_H
#define POSTGRESQLGENERATOR_H
#include <QtCore/qglobal.h>
#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

23
src/query.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#include "query.h"

289
src/query.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#ifndef QUERY_H
#define QUERY_H
#include <QtCore/QVariant>
#include <QtCore/QDebug>
#include <QtCore/QScopedPointer>
#include <QtCore/QRegularExpression>
#include "database.h"
#include "databasemodel.h"
#include "tablesetbase_p.h"
#include "sqlgeneratorbase_p.h"
#include "querybase_p.h"
QT_BEGIN_NAMESPACE
template<class T>
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<T *> toList(int count = -1);
T *first();
int count();
int remove();
void bind(QVariant v);
void bind(QString name, QVariant v);
Query<T> *join(const QString &tableName);
Query<T> *setWhere(const QString &where);
Query<T> *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<T> *orderBy(QString fieldName, QString type);
private:
static QHash<QString, QString> _compiledCommands;
QString compileCommand(QString command);
QString queryText();
};
template<class T>
QHash<QString, QString> Query<T>::_compiledCommands;
template<class T>
Q_OUTOFLINE_TEMPLATE Query<T>::Query(Database *database, TableSetBase *tableSet) : QueryBase(database), _database(database), _tableSet(tableSet),
_joinClassName(QString::null)
{
_tableName = _database->tableName(T::staticMetaObject.className());
}
template<class T>
Q_OUTOFLINE_TEMPLATE QList<T *> Query<T>::toList(int count)
{
QList<T*> result;
_select = "*";
qDebug()<<queryText();
QSqlQuery q = _database->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<TableSetBase*> 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<Table*>(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<class T>
Q_OUTOFLINE_TEMPLATE T *Query<T>::first()
{
QList<T *> list = toList(1);
if(list.count())
return list.first();
else
return 0;
}
template<class T>
Q_OUTOFLINE_TEMPLATE int Query<T>::count()
{
_select = "COUNT(*)";
QSqlQuery q = _database->exec(queryText());
if(q.next())
return q.value(0).toInt();
return 0;
}
template<class T>
Q_OUTOFLINE_TEMPLATE int Query<T>::remove()
{
QString sql = _database->sqlGenertor()->deleteTableRows(_tableName, _where);
sql = compileCommand(sql);
QSqlQuery q = _database->exec(sql);
return q.numRowsAffected();
}
template<class T>
Q_OUTOFLINE_TEMPLATE Query<T> *Query<T>::join(const QString &tableName)
{
_joinClassName = tableName;
return this;
}
template<class T>
Q_OUTOFLINE_TEMPLATE Query<T> *Query<T>::setWhere(const QString &where)
{
_where = where;
return this;
}
template<class T>
Q_OUTOFLINE_TEMPLATE void Query<T>::bind(QVariant v)
{
_where = _where.arg(v.toString());
}
template<class T>
Q_OUTOFLINE_TEMPLATE void Query<T>::bind(QString name, QVariant v)
{
if(!name.startsWith(":"))
name.prepend(":");
_where = _where.replace(name, v.toString());
}
template<class T>
Q_OUTOFLINE_TEMPLATE Query<T> *Query<T>::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<class T>
Q_OUTOFLINE_TEMPLATE Query<T> *Query<T>::orderBy(QString fieldName, QString type)
{
return this;
}
template<class T>
Q_OUTOFLINE_TEMPLATE QString Query<T>::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<class T>
Q_OUTOFLINE_TEMPLATE QString Query<T>::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

6
src/querybase.cpp Normal file
View File

@ -0,0 +1,6 @@
#include "querybase_p.h"
QueryBase::QueryBase(QObject *parent) : QObject(parent)
{
}

18
src/querybase_p.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef QUERYBASE_H
#define QUERYBASE_H
#include <QtCore/QObject>
#include <QtCore/qglobal.h>
class QueryBase : public QObject
{
Q_OBJECT
public:
explicit QueryBase(QObject *parent = 0);
signals:
public slots:
};
#endif // QUERYBASE_H

182
src/sqlgeneratorbase.cpp Normal file
View File

@ -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<QString> 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<QString> 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

37
src/sqlgeneratorbase_p.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef SQLGENERATORBASE_H
#define SQLGENERATORBASE_H
#include <QtCore/qglobal.h>
#include <QStringList>
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

51
src/sqlitegenerator.cpp Normal file
View File

@ -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;
}

15
src/sqlitegenerator.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef SQLITEGENERATOR_H
#define SQLITEGENERATOR_H
#include <QtCore/qglobal.h>
#include "sqlgeneratorbase_p.h"
class SqliteGenerator : public SqlGeneratorBase
{
public:
SqliteGenerator();
QString getColumnDef(Field *field);
};
#endif // SQLITEGENERATOR_H

166
src/table.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#include <QMetaMethod>
#include <QVariant>
#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<QString> 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

90
src/table.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#ifndef TABLE_H
#define TABLE_H
#include <QtCore/QObject>
#include <QtCore/qglobal.h>
#include <QtCore/QSet>
#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<QString> changedProperties() const;
bool setParentTable(Table *master);
signals:
public slots:
protected:
void propertyChanged(QString propName);
private:
Status _status;
QSet<QString> _changedProperties;
TableSetBase *_tableSet;
QSet<TableSetBase*> tableSets;
// template<class T>
// friend class TableSet;
template<class T>
friend class Query;
// friend class Database;
};
QT_END_NAMESPACE
#include "tableset.cpp"
#endif // TABLE_H

350
src/tablescheema.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#include <QtCore/QMetaObject>
#include <QtCore/QMetaProperty>
#include <QtCore/QDebug>
#include <QJsonArray>
#include <QJsonObject>
#include "tablescheema.h"
#include "defines_p.h"
QSet<TableScheema*> TableScheema::_allModels;
//QMap<int, TableScheema*> 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<Field *> TableScheema::fields() const
{
return _fields;
}
QList<Relation *> 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;
}

114
src/tablescheema.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#ifndef TABLESCHEEMA_H
#define TABLESCHEEMA_H
#include <QtCore/QVariant>
#include <QDebug>
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<Field *> fields() const;
QList<Relation *> 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<Field*> _fields;
QList<Relation*> _foregionKeys;
static QSet<TableScheema*>_allModels;
};
#endif // TABLESCHEEMA_H

21
src/tableset.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
//**
//**************************************************************************/
#include "tableset.h"

127
src/tableset.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#ifndef TABLESET_H
#define TABLESET_H
#include <QtCore/qglobal.h>
#include <QtCore/QVariant>
#include <QtCore/QMetaMethod>
#include <QtSql/QSqlQuery>
#include "tablesetbase_p.h"
#include "database.h"
#include "table.h"
QT_BEGIN_NAMESPACE
template<class T>
class Query;
template<class T>
class NUT_EXPORT TableSet : public TableSetBase
{
public:
TableSet(Database *parent);
TableSet(Table *parent);
void append(T *t);
void append(QList<T*> t);
void remove(T *t);
void remove(QList<T*> t);
int length() const;
T *at(int i) const;
const T &operator[](int i) const;
Query<T> *createQuery();
};
template<class T>
Q_OUTOFLINE_TEMPLATE TableSet<T>::TableSet(Database *parent) : TableSetBase(parent)
{
_childClassName = T::staticMetaObject.className();
}
template<class T>
Q_OUTOFLINE_TEMPLATE TableSet<T>::TableSet(Table *parent) : TableSetBase(parent)
{
_childClassName = T::staticMetaObject.className();
}
template<class T>
Q_OUTOFLINE_TEMPLATE Query<T> *TableSet<T>::createQuery()
{
Query<T> *q = new Query<T>(_database, this);
return q;
}
template<class T>
Q_OUTOFLINE_TEMPLATE int TableSet<T>::length() const
{
return _tables.count();
}
template<class T>
Q_OUTOFLINE_TEMPLATE T *TableSet<T>::at(int i) const
{
return (T*)_tables.values().at(i);
}
template<class T>
Q_OUTOFLINE_TEMPLATE const T &TableSet<T>::operator[](int i) const
{
return _tables.values()[i];
}
template<class T>
Q_OUTOFLINE_TEMPLATE void TableSet<T>::append(T *t)
{
_tables.insert(t);
// rows.append(t);
t->setTableSet(this);
if(t->status() != Table::FeatchedFromDB)
t->setStatus(Table::Added);
}
template<class T>
Q_OUTOFLINE_TEMPLATE void TableSet<T>::append(QList<T *> t)
{
foreach (T* i, t)
append(i);
}
template<class T>
Q_OUTOFLINE_TEMPLATE void TableSet<T>::remove(T *t)
{
_tables.remove(t);
t->setStatus(Table::Deleted);
}
template<class T>
Q_OUTOFLINE_TEMPLATE void TableSet<T>::remove(QList<T *> t)
{
foreach (T* i, t)
remove(i);
}
QT_END_NAMESPACE
#endif // TABLESET_H

58
src/tablesetbase.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#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;
}

51
src/tablesetbase_p.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
**
**************************************************************************/
#ifndef TABLESETBASE_H
#define TABLESETBASE_H
#include <QtCore/QObject>
#include <QtCore/qglobal.h>
#include <QtCore/QSet>
#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<Table*> _tables;
QString _tableName;
Database *_database;
Table *_table;
QString _childClassName;
};
#endif // TABLESETBASE_H

BIN
test/.maintest.cpp.kate-swp Normal file

Binary file not shown.

6
test/comment.cpp Normal file
View File

@ -0,0 +1,6 @@
#include "comment.h"
Comment::Comment(QObject *parent) : Table(parent)
{
}

24
test/comment.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef COMMENT_H
#define COMMENT_H
#include <QtCore/qglobal.h>
#include <QtCore/QDateTime>
#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

131
test/maintest.cpp Normal file
View File

@ -0,0 +1,131 @@
#include <QtTest>
#include <QJsonDocument>
#include <QSqlError>
#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<Table*>();
qDebug() << "User type id:" << qRegisterMetaType<Post*>();
qDebug() << "Comment type id:" << qRegisterMetaType<Comment*>();
qDebug() << "DB type id:" << qRegisterMetaType<WeblogDatabase*>();
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()<<posts.at(0)->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)

30
test/maintest.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef MAINTEST_H
#define MAINTEST_H
#include <QtCore/QObject>
#include <QtCore/qglobal.h>
#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

9
test/post.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "comment.h"
#include "tableset.h"
#include "post.h"
Post::Post(QObject *parent) : Table(parent),
m_comments(new TableSet<Comment>(this)), m_id(0), m_title("")
{
}

34
test/post.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef USER_H
#define USER_H
#include <QtCore/qglobal.h>
#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

21
test/tst_nut.pro Normal file
View File

@ -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

9
test/weblogdatabase.cpp Normal file
View File

@ -0,0 +1,9 @@
#include <QDebug>
#include "post.h"
#include "comment.h"
#include "weblogdatabase.h"
WeblogDatabase::WeblogDatabase() : Database(), m_posts(new TableSet<Post>(this)), m_comments(new TableSet<Comment>(this))
{
}

23
test/weblogdatabase.h Normal file
View File

@ -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