diff --git a/README.md b/README.md index 3cb8a38..651f839 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,13 @@ ```cpp autoq = FROM(db.posts()) - WHERE(Post::id() == %1) - BIND(postId); + WHERE(Post::idField() == postId); + auto posts = q->toList(); // now posts is a QList contain all posts in // database that has id equal to postId variable +auto post = q->first(); +// post is first row in database that its id is equal to postId ``` ### Adding to database: @@ -41,8 +43,7 @@ db.saveChanges(); ### Modify database data: ```cpp auto q = FROM(db.posts()) - WHERE(Post::id() == %1) - BIND(postId); + WHERE(Post::idField() == postId); Post *post = q->first(); diff --git a/nut.pri b/nut.pri index d7bddb3..82e7dbe 100644 --- a/nut.pri +++ b/nut.pri @@ -10,7 +10,6 @@ HEADERS += \ $$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 \ @@ -18,14 +17,16 @@ HEADERS += \ $$PWD/src/tablesetbase_p.h \ $$PWD/src/querybase_p.h \ $$PWD/src/mysqlgenerator.h \ - $$PWD/src/sqlitegenerator.h + $$PWD/src/sqlitegenerator.h \ + $$PWD/src/tablemodel.h \ + $$PWD/src/sqlservergenerator.h \ + $$PWD/src/wherephrase.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 \ @@ -33,4 +34,7 @@ SOURCES += \ $$PWD/src/changelogtable.cpp \ $$PWD/src/querybase.cpp \ $$PWD/src/mysqlgenerator.cpp \ - $$PWD/src/sqlitegenerator.cpp + $$PWD/src/sqlitegenerator.cpp \ + $$PWD/src/tablemodel.cpp \ + $$PWD/src/sqlservergenerator.cpp \ + $$PWD/src/wherephrase.cpp diff --git a/src.tar.gz b/src.tar.gz new file mode 100644 index 0000000..77e0281 Binary files /dev/null and b/src.tar.gz differ diff --git a/src/changelogtable.cpp b/src/changelogtable.cpp index fe34e0c..1e8f9d2 100644 --- a/src/changelogtable.cpp +++ b/src/changelogtable.cpp @@ -25,6 +25,7 @@ QT_BEGIN_NAMESPACE ChangeLogTable::ChangeLogTable() { + } QT_END_NAMESPACE diff --git a/src/changelogtable.h b/src/changelogtable.h index 76bddaf..403418d 100644 --- a/src/changelogtable.h +++ b/src/changelogtable.h @@ -33,7 +33,7 @@ class ChangeLogTable : public Table NUT_PRIMARY_AUTO_INCREMENT(id) NUT_DECLARE_FIELD(int, id, id, setId) - NUT_DECLARE_FIELD(QByteArray, data, data, setData) + NUT_DECLARE_FIELD(QString, data, data, setData) NUT_DECLARE_FIELD(int, versionMajor, versionMajor, setVersionMajor) diff --git a/src/database.cpp b/src/database.cpp index 8131b82..c443093 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -33,10 +33,11 @@ #include "tableset.h" #include "database_p.h" #include "defines_p.h" -#include "tablescheema.h" +#include "tablemodel.h" #include "postgresqlgenerator.h" #include "mysqlgenerator.h" #include "sqlitegenerator.h" +#include "sqlservergenerator.h" #include "query.h" #include @@ -63,13 +64,18 @@ bool DatabasePrivate::open() qWarning("Could not connect to database"); qWarning(db.lastError().text().toLocal8Bit().data()); - if(db.lastError().text().contains("database \"" + databaseName + "\" does not exist")){ - db.setDatabaseName(""); + if(db.lastError().text().contains("database \"" + databaseName + "\" does not exist") + || db.lastError().text().contains("Cannot open database")){ + db.setDatabaseName(sqlGenertor->masterDatabaseName(databaseName)); ok = db.open(); qInfo("Creating database"); if(ok){ db.exec("CREATE DATABASE " + databaseName); db.close(); + + if(db.lastError().type() != QSqlError::NoError) + qWarning(db.lastError().text().prepend("Creating database error: ").toLatin1().data()); + return open(); }else{ qWarning(db.lastError().text().toLatin1().data()); @@ -84,26 +90,25 @@ bool DatabasePrivate::open() bool DatabasePrivate::updateDatabase() { DatabaseModel last = getLastScheema(); + DatabaseModel current = currentModel; - if(last == currentModel){ + if(last == current){ qInfo("Databse is up-to-date"); return true; } - if(!last.size()){ + if(!last.count()) qInfo("Databse is new"); - createChangeLogs(); - }else{ + else qInfo("Databse is changed"); - } - QStringList sql = sqlGenertor->getDiff(last, currentModel); + QStringList sql = sqlGenertor->diff(last, current); db.transaction(); foreach (QString s, sql){ qDebug() << "going to exec " << s; db.exec(s); - if(db.lastError().type() != QSqlError::NoError) + if(!db.lastError().type() == QSqlError::NoError) qWarning(db.lastError().text().toLatin1().data()); } bool ok = db.commit(); @@ -123,82 +128,94 @@ QVariantMap DatabasePrivate::getCurrectScheema() Q_Q(Database); tables.clear(); + //TODO: change logs must not be in model int changeLogTypeId = qRegisterMetaType(); - currentModel.append(new TableScheema(changeLogTypeId, "__change_logs")); + currentModel.append(new TableModel(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)) + QString ciName = QString(ci.name()).replace(__nut_NAME_PERFIX, ""); + if(ciName.startsWith(__nut_TABLE)) tables.insert(QString(ci.name()).replace(__nut_NAME_PERFIX, "").split(" ").at(1), ci.value()); + + if(ciName == __nut_DB_VERSION){ + QStringList version = QString(ci.value()).split('.'); + bool ok = false; + if(version.length() == 1){ + currentModel.setVersionMajor(version.at(0).toInt(&ok)); + } else if(version.length() == 2){ + currentModel.setVersionMajor(version.at(0).toInt(&ok)); + currentModel.setVersionMinor(version.at(1).toInt(&ok)); + } + + if(!ok) + qFatal("NUT_DB_VERSION macro accept version in format 'x' or 'x.y' only, and x[,y] must be integer values\n"); + } } QVariantMap databaseVariant; for(int i = 1; i < q->metaObject()->propertyCount(); i++){ QMetaProperty tableProperty = q->metaObject()->property(i); - int typeId = QMetaType::type(tableProperty.typeName()); + uint typeId = QMetaType::type(tableProperty.typeName()); if(tables.values().contains(tableProperty.name()) && typeId >= QVariant::UserType){ - TableScheema *sch = new TableScheema(typeId, tableProperty.name()); + TableModel *sch = new TableModel(typeId, tableProperty.name()); currentModel.append(sch); } } - foreach (TableScheema *sch, currentModel) - foreach (Relation *fk, sch->foregionKeys()) - fk->table = currentModel.scheemaByClass(fk->className); + foreach (TableModel *sch, currentModel) + foreach (RelationModel *fk, sch->foregionKeys()) + fk->table = currentModel.modelByClass(fk->className); + return databaseVariant; } DatabaseModel DatabasePrivate::getLastScheema() { -// ChangeLogTable *changeLog = FROM(_changeLogs) -// ORDERBY_DESC(id) -// FIRST(); + ChangeLogTable *u = changeLogs->createQuery()->orderBy("id", "desc")->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(); + QJsonObject json = QJsonDocument::fromJson(QByteArray(u->data().toLocal8Bit().data())).object(); foreach (QString key, json.keys()) { - TableScheema *sch = new TableScheema(json.value(key).toObject(), key); + TableModel *sch = new TableModel(json.value(key).toObject(), key); ret.append(sch); } + + u->deleteLater(); } return ret; } bool DatabasePrivate::storeScheemaInDB() { - /*int changeLogTypeId = qRegisterMetaType(); - TableScheema *changeLogModel = new TableScheema(changeLogTypeId, "__change_logs"); - - sqlGenertor->getDiff(0, changeLogModel);*/ - - /*Q_Q(Database); + Q_Q(Database); ChangeLogTable *changeLog = new ChangeLogTable(); changeLog->setData(QJsonDocument(currentModel.toJson()).toJson()); - q->saveChanges();*/ + changeLog->setVersionMajor(currentModel.versionMajor()); + changeLog->setVersionMinor(currentModel.versionMinor()); + changeLogs->append(changeLog); + q->saveChanges(); + changeLog->deleteLater(); - 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; + return true; +// 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(QString("storeScheemaInDB" + q.lastError().text()).toLatin1().data()); +// return ret; } void DatabasePrivate::createChangeLogs() { - Q_Q(Database); - - QString diff = sqlGenertor->getDiff(0, currentModel.scheema("__change_logs")); + QString diff = sqlGenertor->diff(0, currentModel.model("__change_logs")); db.exec(diff); } @@ -315,12 +332,22 @@ bool Database::open() { Q_D(Database); - if(driver() == "QPSQL") + if(d->driver == "QPSQL" || d->driver == "QPSQL7") d->sqlGenertor = new PostgreSqlGenerator; - else if (driver() == "QMYSQL") + else if (d->driver == "QMYSQL" || d->driver == "QMYSQL3") d->sqlGenertor = new MySqlGenerator; - else if (driver() == "QSQLITE") + else if (d->driver == "QSQLITE" || d->driver == "QSQLITE3") d->sqlGenertor = new SqliteGenerator; + else if(d->driver == "QODBC" || d->driver == "QODBC3"){ + QString driverName = QString::null; + QStringList parts = d->databaseName.toLower().split(';'); + foreach (QString p, parts) + if(p.trimmed().startsWith("driver=")) + driverName = p.split('=').at(1); + + if(driverName == "{sql server}") + d->sqlGenertor = new SqlServerGenerator; + } if(!d->sqlGenertor){ qWarning(QString("Sql generator for driver " + driver() + " not found").toLatin1().data()); @@ -330,23 +357,6 @@ bool Database::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); @@ -363,10 +373,14 @@ void Database::add(TableSetBase *t) void Database::saveChanges() { - Q_D(Database); - foreach(TableSetBase *ts, tableSets) ts->save(this); } +void Database::cleanUp() +{ + foreach(TableSetBase *ts, tableSets) + ts->clearChilds(); +} + QT_END_NAMESPACE diff --git a/src/database.h b/src/database.h index 94e2251..91896da 100644 --- a/src/database.h +++ b/src/database.h @@ -46,12 +46,11 @@ public: bool open(); - QString decodeQuery(QString sql); - QSqlQuery exec(QString sql); void add(TableSetBase *); void saveChanges(); + void cleanUp(); QString databaseName() const; QString hostName() const; diff --git a/src/databasemodel.cpp b/src/databasemodel.cpp index 83dea77..738d6b1 100644 --- a/src/databasemodel.cpp +++ b/src/databasemodel.cpp @@ -19,29 +19,31 @@ **************************************************************************/ #include "databasemodel.h" -#include "tablescheema.h" +#include "tablemodel.h" #include -DatabaseModel::DatabaseModel() : QList() +QT_BEGIN_NAMESPACE + +DatabaseModel::DatabaseModel() : QList(), _versionMajor(0), _versionMinor(0) { } -TableScheema *DatabaseModel::scheema(QString tableName) const +TableModel *DatabaseModel::model(QString tableName) const { for(int i = 0; i < size(); i++){ - TableScheema *s = at(i); + TableModel *s = at(i); if(s->name() == tableName) return s; } return 0; } -TableScheema *DatabaseModel::scheemaByClass(QString className) const +TableModel *DatabaseModel::modelByClass(QString className) const { for(int i = 0; i < size(); i++){ - TableScheema *s = at(i); + TableModel *s = at(i); if(s->className() == className) return s; } @@ -54,8 +56,8 @@ bool DatabaseModel::operator ==(const DatabaseModel &other) const return false; for(int i = 0; i < size(); i++){ - TableScheema *mine = at(i); - TableScheema *others = other.scheema(mine->name()); + TableModel *mine = at(i); + TableModel *others = other.model(mine->name()); if(!others) return false; @@ -71,36 +73,39 @@ QJsonObject DatabaseModel::toJson() const { QJsonObject obj; +// obj.insert(QT_STRINGIFY(versionMajor), QJsonValue(_versionMajor)); +// obj.insert(QT_STRINGIFY(versionMinor), QJsonValue(_versionMinor)); + for(int i = 0; i < size(); i++){ - TableScheema *s = at(i); + TableModel *s = at(i); obj.insert(s->name(), s->toJson()); } return obj; } -Relation *DatabaseModel::relationByClassNames(QString masterClassName, QString childClassName) +RelationModel *DatabaseModel::relationByClassNames(QString masterClassName, QString childClassName) { - TableScheema *childTable = scheemaByClass(childClassName); + TableModel *childTable = modelByClass(childClassName); if(!childTable) return 0; - foreach (Relation *rel, childTable->foregionKeys()) + foreach (RelationModel *rel, childTable->foregionKeys()) if(rel->className == masterClassName) return rel; return 0; } -Relation *DatabaseModel::relationByTableNames(QString masterTableName, QString childTableName) +RelationModel *DatabaseModel::relationByTableNames(QString masterTableName, QString childTableName) { - TableScheema *childTable = scheema(childTableName); + TableModel *childTable = model(childTableName); if(!childTable) return 0; - foreach (Relation *rel, childTable->foregionKeys()) + foreach (RelationModel *rel, childTable->foregionKeys()) if(rel->table->name() == masterTableName) return rel; @@ -110,9 +115,38 @@ Relation *DatabaseModel::relationByTableNames(QString masterTableName, QString c DatabaseModel DatabaseModel::fromJson(QJsonObject &json) { DatabaseModel model; + +// model.setVersionMajor(json.value(QT_STRINGIFY(versionMajor)).toInt()); +// model.setVersionMinor(json.value(QT_STRINGIFY(versionMinor)).toInt()); + foreach (QString key, json.keys()) { - TableScheema *sch = new TableScheema(json.value(key).toObject(), key); + if(!json.value(key).isObject()) + continue; + + TableModel *sch = new TableModel(json.value(key).toObject(), key); model.append(sch); } return model; } + +int DatabaseModel::versionMajor() const +{ + return _versionMajor; +} + +void DatabaseModel::setVersionMajor(int versionMajor) +{ + _versionMajor = versionMajor; +} + +int DatabaseModel::versionMinor() const +{ + return _versionMinor; +} + +void DatabaseModel::setVersionMinor(int versionMinor) +{ + _versionMinor = versionMinor; +} + +QT_END_NAMESPACE diff --git a/src/databasemodel.h b/src/databasemodel.h index 0bc4f8c..dd49512 100644 --- a/src/databasemodel.h +++ b/src/databasemodel.h @@ -23,25 +23,35 @@ #include -class TableScheema; -struct Relation; +QT_BEGIN_NAMESPACE + +class TableModel; +struct RelationModel; class QJsonObject; -class DatabaseModel : public QList +class DatabaseModel : public QList { + int _versionMajor, _versionMinor; public: DatabaseModel(); - TableScheema *scheema(QString tableName) const; - TableScheema *scheemaByClass(QString className) const; + TableModel *model(QString tableName) const; + TableModel *modelByClass(QString className) const; - Relation *relationByClassNames(QString masterClassName, QString childClassName); - Relation *relationByTableNames(QString masterTableName, QString childTableName); + RelationModel *relationByClassNames(QString masterClassName, QString childClassName); + RelationModel *relationByTableNames(QString masterTableName, QString childTableName); bool operator ==(const DatabaseModel &other) const; static DatabaseModel fromJson(QJsonObject &json); QJsonObject toJson() const; + int versionMajor() const; + void setVersionMajor(int versionMajor); + + int versionMinor() const; + void setVersionMinor(int versionMinor); }; +QT_END_NAMESPACE + #endif // DATABASEMODEL_H diff --git a/src/defines.h b/src/defines.h index 07e1903..a48a105 100644 --- a/src/defines.h +++ b/src/defines.h @@ -21,7 +21,7 @@ #ifndef SYNTAX_DEFINES_H #define SYNTAX_DEFINES_H - +#include "qglobal.h" #include "defines_p.h" #define QT_NAMESPACE Nut @@ -33,10 +33,10 @@ #endif // Database -#define NUT_DB_VERSION(major, minor) Q_CLASSINFO(__nut_NAME_PERFIX " " __nut_DB_VERSION, #major "." #minor) +#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_CLASSINFO(__nut_NAME_PERFIX __nut_TABLE " " #type, #name) \ Q_PROPERTY(type* name READ name) \ Q_PROPERTY(TableSet name##s READ name##s) \ type* m_##name; \ @@ -52,7 +52,10 @@ public: \ Q_CLASSINFO(__nut_NAME_PERFIX #name " " __nut_FIELD, #name) \ type m_##name; \ public: \ - static type type_##name; \ + static FieldPhrase name##Field(){ \ + static FieldPhrase f = FieldPhrase(staticMetaObject.className(), #name); \ + return f; \ + } \ type read() const{ \ return m_##name; \ } \ @@ -61,10 +64,10 @@ public: \ propertyChanged(#name); \ } -#define NUT_FOREGION_KEY(type, keytype, name, read, write) \ +#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) \ + 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 ; } \ @@ -72,10 +75,14 @@ public: \ m_##name = name; \ } -#define NUT_DECLARE_CHILD_TABLE(type, n) \ +#define NUT_DECLARE_CHILD_TABLE(type, n) \ private: \ TableSet *m_##n; \ public: \ + /*static type ## Field() const{ \ + static type t; \ + return t; \ + }*/ \ TableSet *n(){ \ return m_##n; \ } @@ -86,29 +93,22 @@ public: \ #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_UNIQUE(x) Q_CLASSINFO(__nut_NAME_PERFIX #x " " __nut_UNIQUE, #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 WHERE(x) ->setWhere(x) # define JOIN(x) ->join(#x) +# define ORDERBY(x) ->orderBy(#x, "ASC"); +# define ORDERBY_DESC(x) ->orderBy(#x, "DESC"); + # 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 index 2560bb2..9a74f26 100644 --- a/src/defines_p.h +++ b/src/defines_p.h @@ -27,9 +27,10 @@ #define __nut_FIELD "field" #define __nut_DB_VERSION "database_version" -#define __nut_NAME_PERFIX "nut_db_key:" +#define __nut_NAME_PERFIX "nut_db_key::" #define __nut_PRIMARY_KEY "primary_key" #define __nut_AUTO_INCREMENT "auto_increment" +#define __nut_UNIQUE "unique" #define __nut_TABLE "table" #define __nut_TABLE_NAME "table_name" diff --git a/src/mysqlgenerator.cpp b/src/mysqlgenerator.cpp index 0e7e21a..d18b132 100644 --- a/src/mysqlgenerator.cpp +++ b/src/mysqlgenerator.cpp @@ -1,16 +1,35 @@ +/************************************************************************** +** +** 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 "mysqlgenerator.h" -#include "tablescheema.h" +#include "tablemodel.h" QT_BEGIN_NAMESPACE -MySqlGenerator::MySqlGenerator() : SqlGeneratorBase() +MySqlGenerator::MySqlGenerator(QObject *parent) : SqlGeneratorBase(parent) { } -QString MySqlGenerator::getColumnDef(Field *field) +QString MySqlGenerator::fieldType(FieldModel *field) { - QString ret = field->name + " "; QString dbType; switch (field->type) { @@ -20,6 +39,17 @@ QString MySqlGenerator::getColumnDef(Field *field) case QVariant::ByteArray: dbType = "blob"; break; + case QVariant::DateTime: + dbType = "datetime"; + break; + + case QVariant::Date: + dbType = "date"; + break; + + case QVariant::Time: + dbType = "time"; + break; case QVariant::Double: dbType = "real"; break; @@ -35,22 +65,11 @@ QString MySqlGenerator::getColumnDef(Field *field) 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; + + return dbType; } QT_END_NAMESPACE diff --git a/src/mysqlgenerator.h b/src/mysqlgenerator.h index 736db1e..a33d35f 100644 --- a/src/mysqlgenerator.h +++ b/src/mysqlgenerator.h @@ -1,3 +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 . +** +**************************************************************************/ + #ifndef MYSQLGENERATOR_H #define MYSQLGENERATOR_H @@ -9,9 +29,9 @@ QT_BEGIN_NAMESPACE class MySqlGenerator : public SqlGeneratorBase { public: - MySqlGenerator(); + MySqlGenerator(QObject *parent = 0); - QString getColumnDef(Field *field); + QString fieldType(FieldModel *field); }; diff --git a/src/postgresqlgenerator.cpp b/src/postgresqlgenerator.cpp index ac8302f..42da93f 100644 --- a/src/postgresqlgenerator.cpp +++ b/src/postgresqlgenerator.cpp @@ -1,17 +1,36 @@ +/************************************************************************** +** +** 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 "postgresqlgenerator.h" #include "table.h" -#include "tablescheema.h" +#include "tablemodel.h" QT_BEGIN_NAMESPACE -PostgreSqlGenerator::PostgreSqlGenerator() : SqlGeneratorBase () +PostgreSqlGenerator::PostgreSqlGenerator(QObject *parent) : SqlGeneratorBase (parent) { } -QString PostgreSqlGenerator::getColumnDef(Field *field) +QString PostgreSqlGenerator::fieldType(FieldModel *field) { - QString ret = field->name + " "; QString dbType; switch (field->type) { @@ -27,8 +46,8 @@ QString PostgreSqlGenerator::getColumnDef(Field *field) case QVariant::DateTime: dbType = "timestamp"; break; - case QVariant::Double: - dbType = "real"; + case QVariant::Time: + dbType = "time"; break; case QVariant::Int: if(field->isAutoIncrement) @@ -36,36 +55,42 @@ QString PostgreSqlGenerator::getColumnDef(Field *field) else dbType = "integer"; break; + case QVariant::Double: + dbType = "real"; + 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; + + return dbType; } -//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) +QString PostgreSqlGenerator::diff(FieldModel *oldField, FieldModel *newField) { - return SqlGeneratorBase::deleteTableRows(tableName, where) + " RETURNING *"; + QString sql = ""; + if(oldField && newField) + if(*oldField == *newField) + return QString::null; + + if(!newField){ + sql = "DROP COLUMN " + oldField->name; + }else{ + if(oldField){ + sql = "ALTER COLUMN "; + sql.append(newField->name + " TYPE " + fieldType(newField)); + } else { + sql = "ADD COLUMN "; + sql.append(fieldDeclare(newField)); + } + } + return sql; } + QT_END_NAMESPACE diff --git a/src/postgresqlgenerator.h b/src/postgresqlgenerator.h index 976716a..e7181ec 100644 --- a/src/postgresqlgenerator.h +++ b/src/postgresqlgenerator.h @@ -1,3 +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 . +** +**************************************************************************/ + #ifndef POSTGRESQLGENERATOR_H #define POSTGRESQLGENERATOR_H @@ -6,16 +26,14 @@ QT_BEGIN_NAMESPACE -class Field; class PostgreSqlGenerator : public SqlGeneratorBase { public: - PostgreSqlGenerator(); + PostgreSqlGenerator(QObject *parent = 0); - QString getColumnDef(Field *field); -// QString saveSql(Table *t, QString tableName); + QString fieldType(FieldModel *field); - QString deleteTableRows(QString tableName, QString where); + QString diff(FieldModel *oldField, FieldModel *newField); }; QT_END_NAMESPACE diff --git a/src/query.h b/src/query.h index e8ab6b0..6e8ed72 100644 --- a/src/query.h +++ b/src/query.h @@ -31,6 +31,7 @@ #include "tablesetbase_p.h" #include "sqlgeneratorbase_p.h" #include "querybase_p.h" +#include "wherephrase.h" QT_BEGIN_NAMESPACE @@ -39,41 +40,48 @@ class NUT_EXPORT Query : public QueryBase { QString _tableName; QString _select; - QString _where; - QString _joinClassName; +// QString _where; Database *_database; TableSetBase *_tableSet; + QString _joinClassName; + QList _wheres; public: Query(Database *database, TableSetBase *tableSet); + Query(TableSet *tset){ + _database = tset->database(); + _tableName = _database->tableName(T::staticMetaObject.className()); + } + 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 *setWhere(FieldPhrase where); +// Query *setWhere(const QString &where); Query *orderBy(QString fieldName, QString type); private: static QHash _compiledCommands; QString compileCommand(QString command); QString queryText(); + QHash _orders; }; +//template +//inline Query createQuery(TableSet *tset) +//{ +// return Query(tset); +//} + template QHash Query::_compiledCommands; template -Q_OUTOFLINE_TEMPLATE Query::Query(Database *database, TableSetBase *tableSet) : QueryBase(database), _database(database), _tableSet(tableSet), - _joinClassName(QString::null) +Q_OUTOFLINE_TEMPLATE Query::Query(Database *database, TableSetBase *tableSet) : QueryBase(database), + _database(database), _tableSet(tableSet), _joinClassName(QString::null) { _tableName = _database->tableName(T::staticMetaObject.className()); } @@ -86,17 +94,17 @@ Q_OUTOFLINE_TEMPLATE QList Query::toList(int count) qDebug()<exec(queryText()); - QString pk =_database->model().scheema(_tableName)->primaryKey(); + QString pk =_database->model().model(_tableName)->primaryKey(); QVariant lastPkValue = QVariant(); int childTypeId = 0; T *lastRow = 0; TableSetBase *childTableSet; - QStringList masterFields = _database->model().scheema(_tableName)->fieldsNames(); + QStringList masterFields = _database->model().model(_tableName)->fieldsNames(); QStringList childFields; if(!_joinClassName.isNull()){ - childFields = _database->model().scheemaByClass(_joinClassName)->fieldsNames(); + childFields = _database->model().modelByClass(_joinClassName)->fieldsNames(); QString joinTableName = _database->tableName(_joinClassName); - childTypeId = _database->model().scheema(joinTableName)->typeId(); + childTypeId = _database->model().model(joinTableName)->typeId(); } while (q.next()) { @@ -168,8 +176,8 @@ Q_OUTOFLINE_TEMPLATE int Query::count() template Q_OUTOFLINE_TEMPLATE int Query::remove() { - QString sql = _database->sqlGenertor()->deleteTableRows(_tableName, _where); - sql = compileCommand(sql); + QString sql = _database->sqlGenertor()->deleteRecords(_tableName, queryText()); +// sql = compileCommand(sql); QSqlQuery q = _database->exec(sql); return q.numRowsAffected(); } @@ -182,45 +190,23 @@ Q_OUTOFLINE_TEMPLATE Query *Query::join(const QString &tableName) } template -Q_OUTOFLINE_TEMPLATE Query *Query::setWhere(const QString &where) +Q_OUTOFLINE_TEMPLATE Query *Query::setWhere(FieldPhrase where) { - _where = where; + _wheres.append(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::setWhere(const QString &where) +//{ +// _where = where; +// return this; +//} template Q_OUTOFLINE_TEMPLATE Query *Query::orderBy(QString fieldName, QString type) { + _orders.insert(fieldName, type); return this; } @@ -252,21 +238,26 @@ Q_OUTOFLINE_TEMPLATE QString Query::compileCommand(QString command) template Q_OUTOFLINE_TEMPLATE QString Query::queryText() { - QString orderby = ""; - QString q = compileCommand(_where); + QStringList orderby; + QString q = "";//compileCommand(_where); + foreach (FieldPhrase p, _wheres) { + if(q != "") + q.append(" AND "); + q.append(p.command(_database->sqlGenertor())); + } QString t = _tableName; if(!_joinClassName.isNull()){ QString joinTableName = _database->tableName(_joinClassName); - Relation *rel = _database->model().relationByTableNames(_tableName, joinTableName); + RelationModel *rel = _database->model().relationByTableNames(_tableName, joinTableName); if(rel){ - QString pk = _database->model().scheema(_tableName)->primaryKey(); + QString pk = _database->model().model(_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; + orderby.append(_tableName + "." + pk); }else{ qWarning(QString("Relation between table %1 and class %2 (%3) not exists!") .arg(_tableName) @@ -277,11 +268,23 @@ Q_OUTOFLINE_TEMPLATE QString Query::queryText() } } - return QString("SELECT %1 FROM %2 %3%4") + QString orderText = ""; + if(_orders.count()) + foreach (QString o, _orders.keys()) + orderby.append(o + " " + _orders.value(o)); + + if(orderby.count()) + orderText = " ORDER BY " + orderby.join(", "); + + QString command = QString("SELECT %1 FROM %2 %3%4") .arg(_select) .arg(t) .arg(q.isEmpty() ? "" : "WHERE " + q) - .arg(orderby); + .arg(orderText); + + for(int i = 0; i < _database->model().count(); i++) + command = command.replace(_database->model().at(i)->className() + "." , _database->model().at(i)->name() + "."); + return command; } QT_END_NAMESPACE diff --git a/src/querybase_p.h b/src/querybase_p.h index cd0c59b..dc78ffb 100644 --- a/src/querybase_p.h +++ b/src/querybase_p.h @@ -15,4 +15,4 @@ signals: public slots: }; -#endif // QUERYBASE_H \ No newline at end of file +#endif // QUERYBASE_H diff --git a/src/sqlgeneratorbase.cpp b/src/sqlgeneratorbase.cpp index 2d2d895..98cf3b4 100644 --- a/src/sqlgeneratorbase.cpp +++ b/src/sqlgeneratorbase.cpp @@ -1,11 +1,36 @@ +/************************************************************************** +** +** 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 "databasemodel.h" #include "sqlgeneratorbase_p.h" #include "table.h" -#include "tablescheema.h" +#include "tablemodel.h" QT_BEGIN_NAMESPACE -SqlGeneratorBase::SqlGeneratorBase() +SqlGeneratorBase::SqlGeneratorBase(QObject *parent) : QObject(parent) { } @@ -15,47 +40,60 @@ SqlGeneratorBase::~SqlGeneratorBase() } -QString SqlGeneratorBase::saveSql(Table *t, QString tableName) +QString SqlGeneratorBase::masterDatabaseName(QString databaseName) +{ + Q_UNUSED(databaseName); + return ""; +} + +QString SqlGeneratorBase::saveRecord(Table *t, QString tableName) { switch(t->status()){ case Table::Added: - return insertCommand(t, tableName); + return insertRecord(t, tableName); case Table::Deleted: - return deleteCommand(t, tableName); + return deleteRecord(t, tableName); case Table::Modified: - return updateCommand(t, tableName); + return updateRecord(t, tableName); case Table::NewCreated: case Table::FeatchedFromDB: // disable compiler warning - return "***"; + return ""; } + + return ""; } -QStringList SqlGeneratorBase::getDiff(DatabaseModel lastModel, DatabaseModel newModel) +QString SqlGeneratorBase::fieldDeclare(FieldModel *field) +{ + return field->name + " " + fieldType(field); +} + +QStringList SqlGeneratorBase::diff(DatabaseModel lastModel, DatabaseModel newModel) { QStringList ret; QSet tableNames; - foreach (TableScheema *table, lastModel) + foreach (TableModel *table, lastModel) tableNames.insert(table->name()); - foreach (TableScheema *table, newModel) + foreach (TableModel *table, newModel) tableNames.insert(table->name()); foreach (QString tableName, tableNames) { - TableScheema *oldTable = lastModel.scheema(tableName); - TableScheema *newTable = newModel.scheema(tableName); - ret << getDiff(oldTable, newTable); + TableModel *oldTable = lastModel.model(tableName); + TableModel *newTable = newModel.model(tableName); + ret << diff(oldTable, newTable); } return ret; } -QString SqlGeneratorBase::getDiff(Field *oldField, Field *newField) +QString SqlGeneratorBase::diff(FieldModel *oldField, FieldModel *newField) { QString sql = ""; if(oldField && newField) @@ -69,12 +107,12 @@ QString SqlGeneratorBase::getDiff(Field *oldField, Field *newField) sql = "ALTER COLUMN "; else sql = "ADD COLUMN "; - sql.append(getColumnDef(newField)); + sql.append(fieldDeclare(newField)); } return sql; } -QString SqlGeneratorBase::getDiff(TableScheema *oldTable, TableScheema *newTable) +QString SqlGeneratorBase::diff(TableModel *oldTable, TableModel *newTable) { if(oldTable && newTable) if(*oldTable == *newTable) @@ -86,23 +124,23 @@ QString SqlGeneratorBase::getDiff(TableScheema *oldTable, TableScheema *newTable QSet fieldNames; if(oldTable) - foreach (Field *f, oldTable->fields()) + foreach (FieldModel *f, oldTable->fields()) fieldNames.insert(f->name); - foreach (Field *f, newTable->fields()) + foreach (FieldModel *f, newTable->fields()) fieldNames.insert(f->name); QStringList columnSql; foreach (QString fieldName, fieldNames) { - Field *newField = newTable->field(fieldName); + FieldModel *newField = newTable->field(fieldName); if(oldTable){ - Field *oldField = oldTable->field(fieldName); + FieldModel *oldField = oldTable->field(fieldName); - QString buffer = getDiff(oldField, newField); + QString buffer = diff(oldField, newField); if(!buffer.isNull()) columnSql << buffer; }else{ - columnSql << getColumnDef(newField); + columnSql << fieldDeclare(newField); } } QString sql; @@ -124,7 +162,7 @@ QString SqlGeneratorBase::getDiff(TableScheema *oldTable, TableScheema *newTable return sql; } -QString SqlGeneratorBase::insertCommand(Table *t, QString tableName) +QString SqlGeneratorBase::insertRecord(Table *t, QString tableName) { QString sql = ""; QString key = t->primaryKey(); @@ -142,7 +180,7 @@ QString SqlGeneratorBase::insertCommand(Table *t, QString tableName) return sql; } -QString SqlGeneratorBase::updateCommand(Table *t, QString tableName) +QString SqlGeneratorBase::updateRecord(Table *t, QString tableName) { QString sql = ""; QString key = t->primaryKey(); @@ -160,7 +198,7 @@ QString SqlGeneratorBase::updateCommand(Table *t, QString tableName) return sql; } -QString SqlGeneratorBase::deleteCommand(Table *t, QString tableName) +QString SqlGeneratorBase::deleteRecord(Table *t, QString tableName) { return QString("DELETE FROM %1 WHERE %2='%3'") .arg(tableName) @@ -168,7 +206,7 @@ QString SqlGeneratorBase::deleteCommand(Table *t, QString tableName) .arg(t->primaryValue().toString()); } -QString SqlGeneratorBase::deleteTableRows(QString tableName, QString where) +QString SqlGeneratorBase::deleteRecords(QString tableName, QString where) { QString sql = ""; if(where.isEmpty() || where.isNull()) @@ -178,5 +216,38 @@ QString SqlGeneratorBase::deleteTableRows(QString tableName, QString where) return sql; } +QString SqlGeneratorBase::escapeFieldValue(QVariant &field) const +{ + switch (field.type()) { + case QVariant::Int: + case QVariant::Double: + return field.toString(); + break; + + case QVariant::String: + return "'" + field.toString() + "'"; + + case QVariant::DateTime: + return "'" + field.toDateTime().toString(Qt::ISODate) + "'"; + + case QVariant::Date: + return "'" + field.toDate().toString(Qt::ISODate) + "'"; + + case QVariant::Time: + return "'" + field.toTime().toString(Qt::ISODate) + "'"; + + case QVariant::StringList: + case QVariant::List: + return "['" + field.toStringList().join("', '") + "']"; + + case QVariant::Invalid: + qFatal("Invalud field value"); + return ""; + + default: + return ""; + } +} + QT_END_NAMESPACE diff --git a/src/sqlgeneratorbase_p.h b/src/sqlgeneratorbase_p.h index 4a25dfe..f0ff174 100644 --- a/src/sqlgeneratorbase_p.h +++ b/src/sqlgeneratorbase_p.h @@ -1,35 +1,60 @@ +/************************************************************************** +** +** 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 SQLGENERATORBASE_H #define SQLGENERATORBASE_H #include - -#include +#include +#include QT_BEGIN_NAMESPACE class Table; -class Field; +struct FieldModel; class DatabaseModel; -class TableScheema; -class SqlGeneratorBase +class TableModel; +class SqlGeneratorBase : public QObject { public: - SqlGeneratorBase(); + SqlGeneratorBase(QObject *parent = 0); 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 masterDatabaseName(QString databaseName); + + virtual QString fieldType(FieldModel *field) = 0; + virtual QString fieldDeclare(FieldModel *field); + + virtual QStringList diff(DatabaseModel lastModel, DatabaseModel newModel); + virtual QString diff(FieldModel *oldField, FieldModel *newField); + virtual QString diff(TableModel *oldTable, TableModel *newTable); - virtual QString insertCommand(Table *t, QString tableName); - virtual QString updateCommand(Table *t, QString tableName); - virtual QString deleteCommand(Table *t, QString tableName); + virtual QString saveRecord(Table *t, QString tableName); + virtual QString insertRecord(Table *t, QString tableName); + virtual QString updateRecord(Table *t, QString tableName); + virtual QString deleteRecord(Table *t, QString tableName); - virtual QString deleteTableRows(QString tableName, QString where); + virtual QString deleteRecords(QString tableName, QString where); + virtual QString escapeFieldValue(QVariant &field) const; }; QT_END_NAMESPACE diff --git a/src/sqlitegenerator.cpp b/src/sqlitegenerator.cpp index c04032d..e5f7699 100644 --- a/src/sqlitegenerator.cpp +++ b/src/sqlitegenerator.cpp @@ -1,13 +1,33 @@ +/************************************************************************** +** +** 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 "sqlitegenerator.h" #include "table.h" -#include "tablescheema.h" +#include "tablemodel.h" -SqliteGenerator::SqliteGenerator() : SqlGeneratorBase() +SqliteGenerator::SqliteGenerator(QObject *parent) : SqlGeneratorBase(parent) { } -QString SqliteGenerator::getColumnDef(Field *field) +QString SqliteGenerator::fieldType(FieldModel *field) { QString ret = field->name + " "; QString dbType; @@ -25,13 +45,16 @@ QString SqliteGenerator::getColumnDef(Field *field) case QVariant::DateTime: dbType = "datetime"; break; + case QVariant::Time: + dbType = "time"; + break; case QVariant::Double: dbType = "real"; break; case QVariant::Int: -// if(field->isAutoIncrement) -// dbType = "INTEGER PRIMARY KEY"; -// else + if(field->isPrimaryKey) + dbType = "INTEGER PRIMARY KEY"; + else dbType = "integer"; break; case QVariant::String: @@ -40,12 +63,9 @@ QString SqliteGenerator::getColumnDef(Field *field) else dbType = "text"; break; - case QVariant::Time: - dbType = "time"; - break; default: dbType = ""; } - ret.append(dbType); - return ret; + + return dbType; } diff --git a/src/sqlitegenerator.h b/src/sqlitegenerator.h index 7f7842d..59b817c 100644 --- a/src/sqlitegenerator.h +++ b/src/sqlitegenerator.h @@ -1,3 +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 . +** +**************************************************************************/ + #ifndef SQLITEGENERATOR_H #define SQLITEGENERATOR_H @@ -7,9 +27,9 @@ class SqliteGenerator : public SqlGeneratorBase { public: - SqliteGenerator(); + SqliteGenerator(QObject *parent = 0); - QString getColumnDef(Field *field); + QString fieldType(FieldModel *field); }; #endif // SQLITEGENERATOR_H diff --git a/src/sqlservergenerator.cpp b/src/sqlservergenerator.cpp index b2829af..40bc608 100644 --- a/src/sqlservergenerator.cpp +++ b/src/sqlservergenerator.cpp @@ -1,6 +1,105 @@ -#include "sqlservergenerator.h" +/************************************************************************** +** +** 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 . +** +**************************************************************************/ -SqlServerGenerator::SqlServerGenerator() +#include "sqlservergenerator.h" +#include "table.h" +#include "tablemodel.h" + +#include + +QT_BEGIN_NAMESPACE + +SqlServerGenerator::SqlServerGenerator(QObject *parent) : SqlGeneratorBase(parent) { } + +QString SqlServerGenerator::masterDatabaseName(QString databaseName) +{ + return databaseName.replace(QRegularExpression("DATABASE\\=(\\w+)", QRegularExpression::CaseInsensitiveOption), "DATABASE="); +} + +QString SqlServerGenerator::fieldType(FieldModel *field) +{ + QString dbType; + + switch (field->type) { + case QVariant::Bool: + dbType = "bit"; + break; + case QVariant::ByteArray: + dbType = "varbinary"; + + if(field->length) + dbType.append(" (" + QString::number(field->length) + ")"); + else + dbType.append(" (MAX)"); + break; + case QVariant::Date: + dbType = "date"; + break; + case QVariant::DateTime: + dbType = "datetime"; + break; + case QVariant::Time: + dbType = "time"; + break; + case QVariant::Double: + dbType = "real"; + break; + case QVariant::Int: + dbType = "int"; + if(field->isAutoIncrement) + dbType += " identity(1,1)"; + break; + case QVariant::String: + if(field->length) + dbType = QString("varchar(%1)").arg(field->length); + else + dbType = "text"; + break; + default: + dbType = ""; + } + + return dbType; +} + +QString SqlServerGenerator::diff(FieldModel *oldField, FieldModel *newField) +{ + QString sql = ""; + if(oldField && newField) + if(*oldField == *newField) + return QString::null; + + if(!newField){ + sql = "DROP COLUMN " + oldField->name; + }else{ + if(oldField) + sql = "MODIFY COLUMN "; + else + sql = "ADD "; + + sql.append(fieldDeclare(newField)); + } + return sql; +} + +QT_END_NAMESPACE diff --git a/src/sqlservergenerator.h b/src/sqlservergenerator.h index 65b3ce5..5d40f71 100644 --- a/src/sqlservergenerator.h +++ b/src/sqlservergenerator.h @@ -1,12 +1,42 @@ +/************************************************************************** +** +** 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 SQLSERVERGENERATOR_H #define SQLSERVERGENERATOR_H #include +#include "sqlgeneratorbase_p.h" -class SqlServerGenerator +QT_BEGIN_NAMESPACE + +class SqlServerGenerator : public SqlGeneratorBase { public: - SqlServerGenerator(); + SqlServerGenerator(QObject *parent = 0); + + QString masterDatabaseName(QString databaseName); + + QString fieldType(FieldModel *field); + QString diff(FieldModel *oldField, FieldModel *newField); }; -#endif // SQLSERVERGENERATOR_H \ No newline at end of file +QT_END_NAMESPACE + +#endif // SQLSERVERGENERATOR_H diff --git a/src/table.cpp b/src/table.cpp index 0325c0f..67e1bcc 100644 --- a/src/table.cpp +++ b/src/table.cpp @@ -39,58 +39,38 @@ void Table::add(TableSetBase *t) QString Table::primaryKey() const { - static QString ret = QString::null; +// 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(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.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(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 = ""; - } +// if(ret == QString::null) +// ret = ""; +// } - return ret; +// return ret; + return TableModel::model(metaObject()->className())->primaryKey(); } -QString Table::autoIncrementField() const +bool Table::isPrimaryKeyAutoIncrement() 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; + return TableModel::model(metaObject()->className())->field(primaryKey())->isAutoIncrement; } + QVariant Table::primaryValue() const { return property(primaryKey().toLatin1().data()); @@ -101,7 +81,6 @@ void Table::propertyChanged(QString propName) if(propName == primaryKey()) return; -// qDebug() << "Table::propertyChanged" << metaObject()->className() << propName; _changedProperties.insert(propName); if(_status == FeatchedFromDB) _status = Modified; @@ -118,14 +97,17 @@ QSet Table::changedProperties() const bool Table::setParentTable(Table *master) { QString masterClassName = master->metaObject()->className(); - TableScheema *myModel = TableScheema::findByClassName(metaObject()->className()); + TableModel *myModel = TableModel::findByClassName(metaObject()->className()); - foreach (Relation *r, myModel->foregionKeys()) + foreach (RelationModel *r, myModel->foregionKeys()) if(r->className == masterClassName) { setProperty(QString(r->localColumn).toLatin1().data(), master->primaryValue()); _changedProperties.insert(r->localColumn); + return true; } + + return false; } TableSetBase *Table::tableSet() const @@ -141,11 +123,9 @@ void Table::setTableSet(TableSetBase *parent) 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)); + QSqlQuery q = db->exec(db->sqlGenertor()->saveRecord(this, db->tableName(metaObject()->className()))); - if(status() == Added) + if(status() == Added && isPrimaryKeyAutoIncrement()) setProperty(primaryKey().toLatin1().data(), q.lastInsertId()); foreach(TableSetBase *ts, tableSets) diff --git a/src/table.h b/src/table.h index 348d30c..7c49bd2 100644 --- a/src/table.h +++ b/src/table.h @@ -25,9 +25,9 @@ #include #include -#include "tablescheema.h" +#include "tablemodel.h" #include "defines.h" -//#include "tableset.h" +#include "wherephrase.h" QT_BEGIN_NAMESPACE @@ -52,7 +52,7 @@ public: void save(Database *db); QString primaryKey() const; - QString autoIncrementField() const; + bool isPrimaryKeyAutoIncrement() const; QVariant primaryValue() const; Status status() const; void setStatus(const Status &status); @@ -77,14 +77,10 @@ private: 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/tablemodel.cpp b/src/tablemodel.cpp index bd0439b..de22f6e 100644 --- a/src/tablemodel.cpp +++ b/src/tablemodel.cpp @@ -25,100 +25,103 @@ #include #include -#include "tablescheema.h" +#include "tablemodel.h" #include "defines_p.h" -QSet TableScheema::_allModels; +QSet TableModel::_allModels; //QMap TableScheema::scheemas; -QString TableScheema::name() const +QString TableModel::name() const { return _name; } -void TableScheema::setName(const QString &name) +void TableModel::setName(const QString &name) { _name = name; } -QString TableScheema::className() const +QString TableModel::className() const { return _className; } -void TableScheema::setClassName(const QString &className) +void TableModel::setClassName(const QString &className) { _className = className; } -int TableScheema::typeId() const +int TableModel::typeId() const { return _typeId; } -void TableScheema::setTypeId(const int &typeId) +void TableModel::setTypeId(const int &typeId) { _typeId = typeId; } -Field *TableScheema::field(QString name) const +FieldModel *TableModel::field(QString name) const { - foreach (Field *f, _fields) + foreach (FieldModel *f, _fields) if(f->name == name) return f; return 0; } -QList TableScheema::fields() const +QList TableModel::fields() const { return _fields; } -QList TableScheema::foregionKeys() const +QList TableModel::foregionKeys() const { return _foregionKeys; } -QStringList TableScheema::fieldsNames() const +QStringList TableModel::fieldsNames() const { QStringList ret; - foreach (Field *f, _fields) + foreach (FieldModel *f, _fields) ret.append(f->name); return ret; } -TableScheema *TableScheema::findByTypeId(int typeId) +TableModel *TableModel::findByTypeId(int typeId) { - foreach (TableScheema *model, _allModels) + foreach (TableModel *model, _allModels) if(model->typeId() == typeId) return model; return 0; } -TableScheema *TableScheema::findByName(QString name) +TableModel *TableModel::findByName(QString name) { - foreach (TableScheema *model, _allModels) + foreach (TableModel *model, _allModels) if(model->name() == name) return model; return 0; } -TableScheema *TableScheema::findByClassName(QString className) +TableModel *TableModel::findByClassName(QString className) { - foreach (TableScheema *model, _allModels) + foreach (TableModel *model, _allModels) if(model->className() == className) return model; return 0; } -bool TableScheema::operator ==(const TableScheema &t) const{ +bool TableModel::operator ==(const TableModel &t) const{ if(_name != t.name()) return false; - foreach (Field *f, _fields) { - Field *tf = t.field(f->name); + if(fields().count() != t.fields().count()) + return false; + + foreach (FieldModel *f, _fields) { + FieldModel *tf = t.field(f->name); if(!tf) return false; @@ -129,12 +132,12 @@ bool TableScheema::operator ==(const TableScheema &t) const{ return true; } -bool TableScheema::operator !=(const TableScheema &t) const +bool TableModel::operator !=(const TableModel &t) const { return !(*this == t); } -TableScheema::TableScheema(int typeId, QString tableName) +TableModel::TableModel(int typeId, QString tableName) { const QMetaObject *tableMetaObject = QMetaType::metaObjectForType(typeId); @@ -153,7 +156,7 @@ TableScheema::TableScheema(int typeId, QString tableName) QString propName = parts.at(1); if(propName == __nut_FIELD){ - Field *f = new Field; + FieldModel *f = new FieldModel; f->name = parts.at(0); _fields.append(f); } @@ -163,13 +166,14 @@ TableScheema::TableScheema(int typeId, QString tableName) for(int j = 1; j < tableMetaObject->propertyCount(); j++){ QMetaProperty fieldProperty = tableMetaObject->property(j); - Field *f = field(fieldProperty.name()); - if(!f) + FieldModel *fieldObj = field(fieldProperty.name()); + foreach (FieldModel *f, _fields) + if(f->name == fieldProperty.name()) + f = fieldObj; + if(!fieldObj) continue; - f->type = fieldProperty.type(); - - _fields.append(f); + fieldObj->type = fieldProperty.type(); } // Browse class infos @@ -185,7 +189,7 @@ TableScheema::TableScheema(int typeId, QString tableName) QString propName = parts.at(1); if(propName == __nut_FOREGION_KEY){ - Relation *fk = new Relation; + RelationModel *fk = new RelationModel; fk->localColumn = parts.at(0); fk->foregionColumn = value; fk->className = value; @@ -197,7 +201,7 @@ TableScheema::TableScheema(int typeId, QString tableName) } - Field *f = field(parts.at(0)); + FieldModel *f = field(parts.at(0)); if(!f) continue; @@ -211,6 +215,8 @@ TableScheema::TableScheema(int typeId, QString tableName) f->isPrimaryKey = true; else if(propName == __nut_AUTO_INCREMENT) f->isAutoIncrement = true; + else if(propName == __nut_UNIQUE) + f->isUnique = true; } } @@ -234,14 +240,14 @@ TableScheema::TableScheema(int typeId, QString tableName) "primary_key": "id" }, */ -TableScheema::TableScheema(QJsonObject json, QString tableName) +TableModel::TableModel(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; + FieldModel *f = new FieldModel; f->name = fieldObject.value(__NAME).toString(); f->type = QVariant::nameToType(fieldObject.value(__TYPE).toString().toLatin1().data()); @@ -264,12 +270,12 @@ TableScheema::TableScheema(QJsonObject json, QString tableName) } -QJsonObject TableScheema::toJson() const +QJsonObject TableModel::toJson() const { QJsonObject obj; QJsonObject fieldsObj; - foreach (Field *f, _fields) { + foreach (FieldModel *f, _fields) { QJsonObject fieldObj; fieldObj.insert(__NAME, f->name); fieldObj.insert(__TYPE, QVariant::typeToName(f->type)); @@ -312,27 +318,27 @@ QJsonObject TableScheema::toJson() const // } //} -//TableScheema *TableScheema::scheema(QString className) -//{ -// foreach (TableScheema *s, scheemas) -// if(s->_className == className) -// return s; -// return 0; -//} - -Relation *TableScheema::foregionKey(QString otherTable) const +TableModel *TableModel::model(QString className) { - foreach (Relation *fk, _foregionKeys) + foreach (TableModel *s, _allModels) + if(s->_className == className) + return s; + return 0; +} + +RelationModel *TableModel::foregionKey(QString otherTable) const +{ + foreach (RelationModel *fk, _foregionKeys) if(fk->className == otherTable) return fk; return 0; } -QString TableScheema::toString() const +QString TableModel::toString() const { QStringList sl; - foreach (Field *f, _fields) + foreach (FieldModel *f, _fields) sl.append(f->name + " " + QVariant::typeToName(f->type)); QString ret = QString("%1 (%2)") @@ -341,9 +347,9 @@ QString TableScheema::toString() const return ret; } -QString TableScheema::primaryKey() const +QString TableModel::primaryKey() const { - foreach (Field *f, _fields) + foreach (FieldModel *f, _fields) if(f->isPrimaryKey) return f->name; return QString::null; diff --git a/src/tablemodel.h b/src/tablemodel.h index f1f646b..828ee2f 100644 --- a/src/tablemodel.h +++ b/src/tablemodel.h @@ -24,11 +24,11 @@ #include #include class QJsonObject; -class TableScheema; +class TableModel; -struct Field{ - Field() : name(QString::null), length(0), defaultValue(QString::null), - notNull(false), isPrimaryKey(false), isAutoIncrement(false) +struct FieldModel{ + FieldModel() : name(QString::null), length(0), defaultValue(QString::null), + notNull(false), isPrimaryKey(false), isAutoIncrement(false), isUnique(false) { } @@ -40,8 +40,9 @@ struct Field{ bool notNull; bool isPrimaryKey; bool isAutoIncrement; + bool isUnique; - bool operator ==(const Field &f) const{ + bool operator ==(const FieldModel &f) const{ bool b = name == f.name && type == f.type @@ -52,32 +53,32 @@ struct Field{ return b; } - bool operator !=(const Field &f) const{ + bool operator !=(const FieldModel &f) const{ return !(*this == f); } }; -struct Relation{ +struct RelationModel{ QString className; QString localColumn; - TableScheema *table; + TableModel *table; QString foregionColumn; }; -class TableScheema +class TableModel { public: - TableScheema(int typeId, QString tableName); - TableScheema(QJsonObject json, QString tableName); + TableModel(int typeId, QString tableName); + TableModel(QJsonObject json, QString tableName); QJsonObject toJson() const; // static TableScheema *registerTable(int typeId, QString tableName); // static void createForegionKeys(); -// static TableScheema* scheema(QString className); + static TableModel* model(QString className); - Field *field(QString name) const; - Relation *foregionKey(QString otherTable) const; + FieldModel *field(QString name) const; + RelationModel *foregionKey(QString otherTable) const; QString toString() const; @@ -91,24 +92,24 @@ public: int typeId() const; void setTypeId(const int &typeId); - QList fields() const; - QList foregionKeys() const; + QList fields() const; + QList foregionKeys() const; QStringList fieldsNames() const; - static TableScheema *findByTypeId(int typeId); - static TableScheema *findByName(QString name); - static TableScheema *findByClassName(QString className); + static TableModel *findByTypeId(int typeId); + static TableModel *findByName(QString name); + static TableModel *findByClassName(QString className); - bool operator ==(const TableScheema &t) const; - bool operator !=(const TableScheema &t) const; + bool operator ==(const TableModel &t) const; + bool operator !=(const TableModel &t) const; private: QString _name; QString _className; int _typeId; - QList _fields; - QList _foregionKeys; - static QSet_allModels; + QList _fields; + QList _foregionKeys; + static QSet_allModels; }; #endif // TABLESCHEEMA_H diff --git a/src/tableset.h b/src/tableset.h index 1e67384..d128ef1 100644 --- a/src/tableset.h +++ b/src/tableset.h @@ -47,6 +47,8 @@ public: void remove(T *t); void remove(QList t); + inline T type() const {} + int length() const; T *at(int i) const; const T &operator[](int i) const; @@ -82,19 +84,20 @@ Q_OUTOFLINE_TEMPLATE int TableSet::length() const template Q_OUTOFLINE_TEMPLATE T *TableSet::at(int i) const { - return (T*)_tables.values().at(i); + return (T*)_tablesList.at(i); } template Q_OUTOFLINE_TEMPLATE const T &TableSet::operator[](int i) const { - return _tables.values()[i]; + return _tablesList[i]; } template Q_OUTOFLINE_TEMPLATE void TableSet::append(T *t) { _tables.insert(t); + _tablesList.append(t); // rows.append(t); t->setTableSet(this); if(t->status() != Table::FeatchedFromDB) diff --git a/src/tablesetbase.cpp b/src/tablesetbase.cpp index 09849e1..a9d0c9f 100644 --- a/src/tablesetbase.cpp +++ b/src/tablesetbase.cpp @@ -35,7 +35,7 @@ TableSetBase::TableSetBase(Table *parent) : QObject(parent), _database(0), _tabl void TableSetBase::save(Database *db) { - foreach (Table *t, _tables) { + foreach (Table *t, _tablesList) { if(_table) t->setParentTable(_table); @@ -47,12 +47,31 @@ void TableSetBase::save(Database *db) } } +void TableSetBase::clearChilds() +{ + foreach (Table *t, _tablesList) + delete t; +} + void TableSetBase::add(Table *t) { - _tables.insert(t); + if(!_tables.contains(t)){ + _tables.insert(t); + _tablesList.append(t); + } } QString TableSetBase::childClassName() const { return _childClassName; } + +Database *TableSetBase::database() const +{ + return _database; +} + +void TableSetBase::setDatabase(Database *database) +{ + _database = database; +} diff --git a/src/tablesetbase_p.h b/src/tablesetbase_p.h index 9d47381..088777e 100644 --- a/src/tablesetbase_p.h +++ b/src/tablesetbase_p.h @@ -37,11 +37,16 @@ public: TableSetBase(Table *parent); virtual void save(Database *db); + void clearChilds(); void add(Table* t); QString childClassName() const; + Database *database() const; + void setDatabase(Database *database); + protected: QSet _tables; + QList _tablesList; QString _tableName; Database *_database; Table *_table; diff --git a/src/wherephrase.cpp b/src/wherephrase.cpp index a21312c..be2d8e0 100644 --- a/src/wherephrase.cpp +++ b/src/wherephrase.cpp @@ -1,6 +1,349 @@ +/************************************************************************** +** +** 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 "wherephrase.h" -WherePhrase::WherePhrase() -{ +QT_BEGIN_NAMESPACE +PhraseData::PhraseData(const char *className, const char *s){ + text = QString(className) + "." + s; + type = Field; } + +PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o) : left(l){ + operatorCond = o; + type = WithoutOperand; +} + +PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o, const PhraseData *r) : left(l), right(r){ + operatorCond = o; + type = WithOther; +} + +PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o, QVariant r) : left(l), operand(r){ + operatorCond = o; + type = WithVariant; +} + +PhraseData::~PhraseData(){ + // if(type == WithOther){ + // delete left; + // delete right; + // } + // if(type == WithVariant){ + //// qDebug() << operator + // delete left; +} + +QString PhraseData::operatorString() const +{ + switch (operatorCond){ + case PhraseData::Equal: + return "="; + case PhraseData::NotEqual: + return "<>"; + case PhraseData::Less: + return "<"; + case PhraseData::Greater: + return ">"; + case PhraseData::LessEqual: + return "<="; + case PhraseData::GreaterEqual: + return ">="; + case PhraseData::Null: + return "IS NULL"; + + case PhraseData::NotNull: + return "IS NOT NULL"; + + case PhraseData::In: + return "IN"; + + case PhraseData::NotIn: + return "NOT IN"; + + case PhraseData::And: + return "AND"; + case PhraseData::Or: + return "OR"; + + case PhraseData::Like: + return "LIKE"; + case PhraseData::NotLike: + return "NOT LIKE"; + + case PhraseData::Add: + return "+"; + case PhraseData::Minus: + return "-"; + case PhraseData::Multiple: + return "*"; + case PhraseData::Divide: + return "/"; + + case PhraseData::Set: + return "="; + + case PhraseData::Append: + return ","; + } + + return QString(""); +} + +QString PhraseData::escapeVariant() const +{ + switch (operand.type()) { + case QVariant::Int: + case QVariant::Double: + return operand.toString(); + break; + + case QVariant::String: + return "'" + operand.toString() + "'"; + + case QVariant::DateTime: + return "'" + operand.toDateTime().toString() + "'"; + + case QVariant::Date: + return "'" + operand.toDate().toString() + "'"; + + case QVariant::Time: + return "'" + operand.toTime().toString() + "'"; + + case QVariant::StringList: + case QVariant::List: + return "['" + operand.toStringList().join("', '") + "']"; + + case QVariant::Invalid: + return ""; + + default: + return ""; + } +} + +QString PhraseData::command(SqlGeneratorBase *generator) const +{ + QString ret = ""; + + switch(type){ + case Field: + ret = text; + break; + + case WithVariant: + ret = left->command(generator) + " " + operatorString() + " " + escapeVariant(); + break; + + case WithOther: + ret = left->command(generator) + " " + operatorString() + " " + right->command(generator); + break; + + case WithoutOperand: + ret = left->command(generator) + " " + operatorString(); + break; + } + + if(operatorCond == PhraseData::And || operatorCond == PhraseData::Or) + ret = "(" + ret + ")"; + + return ret; +} + + +FieldPhrase::FieldPhrase(const char *className, const char *s) : willDeleteData(false) +{ + data = new PhraseData(className, s); + text = QString(className) + "." + s; +} + +FieldPhrase::FieldPhrase(PhraseData *l) : willDeleteData(false) +{ + data = l; +} + +FieldPhrase::FieldPhrase(PhraseData *l, PhraseData::Condition o) : willDeleteData(false) +{ + data = new PhraseData(l, o); +} + + +FieldPhrase::FieldPhrase(PhraseData *l, PhraseData::Condition o, PhraseData *r) : willDeleteData(false) +{ + data = new PhraseData(l, o, r); +} + +FieldPhrase::FieldPhrase(PhraseData *l, PhraseData::Condition o, QVariant r) : willDeleteData(false) +{ + data = new PhraseData(l, o, r); +} + +FieldPhrase::~FieldPhrase() +{ +// if(willDeleteData) +// delete data; +} + + +QString FieldPhrase::command(SqlGeneratorBase *generator) +{ + willDeleteData = true; + return data->command(generator); +} + +void FieldPhrase::deleteData(PhraseData *d) +{ + deleteData(d); + if(d->type == PhraseData::WithOther){ + delete d->left; + delete d->right; + } + if(d->type == PhraseData::WithVariant) + delete d->left; +} + +FieldPhrase FieldPhrase::operator ==(const FieldPhrase &other){ + return FieldPhrase(this->data, PhraseData::Equal, other.data); +} + +FieldPhrase FieldPhrase::operator !=(const FieldPhrase &other){ + return FieldPhrase(this->data, PhraseData::NotEqual, other.data); +} + +FieldPhrase FieldPhrase::operator <(const FieldPhrase &other){ + return FieldPhrase(this->data, PhraseData::Less, other.data); +} + +FieldPhrase FieldPhrase::operator >(const FieldPhrase &other){ + return FieldPhrase(this->data, PhraseData::Greater, other.data); +} + +FieldPhrase FieldPhrase::operator <=(const FieldPhrase &other){ + return FieldPhrase(this->data, PhraseData::LessEqual, other.data); +} + +FieldPhrase FieldPhrase::operator >=(const FieldPhrase &other){ + return FieldPhrase(this->data, PhraseData::GreaterEqual, other.data); +} + +FieldPhrase FieldPhrase::operator =(const FieldPhrase &other) +{ + return FieldPhrase(this->data, PhraseData::Set, other.data); +} + +FieldPhrase FieldPhrase::operator +(const FieldPhrase &other){ + return FieldPhrase(this->data, PhraseData::Add, other.data); +} + +FieldPhrase FieldPhrase::operator -(const FieldPhrase &other){ + return FieldPhrase(this->data, PhraseData::Minus, other.data); +} + +FieldPhrase FieldPhrase::operator *(const FieldPhrase &other){ + return FieldPhrase(this->data, PhraseData::Multiple, other.data); +} + +FieldPhrase FieldPhrase::operator /(const FieldPhrase &other){ + return FieldPhrase(this->data, PhraseData::Divide, other.data); +} + +FieldPhrase FieldPhrase::operator &&(const FieldPhrase &other){ + return FieldPhrase(this->data, PhraseData::And, other.data); +} + +FieldPhrase FieldPhrase::operator ||(const FieldPhrase &other){ + return FieldPhrase(this->data, PhraseData::Or, other.data); +} + +FieldPhrase FieldPhrase::operator &(const FieldPhrase &other) +{ + return FieldPhrase(this->data, PhraseData::Append, other.data); +} + +FieldPhrase FieldPhrase::operator ,(const FieldPhrase &other) +{ + return FieldPhrase(this->data, PhraseData::Append, other.data); +} + +FieldPhrase FieldPhrase::operator !(){ + if(data->operatorCond < 20) + data->operatorCond = (PhraseData::Condition)((data->operatorCond + 10) % 20); + else + qFatal("Operator ! can not aplied to non condition statements"); + + return FieldPhrase(data); +} + +FieldPhrase FieldPhrase::operator ==(const QVariant &other){ + return FieldPhrase(this->data, PhraseData::Equal, other); +} + +FieldPhrase FieldPhrase::operator !=(const QVariant &other){ + return FieldPhrase(this->data, PhraseData::NotEqual, other); +} + +FieldPhrase FieldPhrase::operator <(const QVariant &other){ + return FieldPhrase(this->data, PhraseData::Less, other); +} + +FieldPhrase FieldPhrase::operator >(const QVariant &other){ + qDebug() << "var"; + return FieldPhrase(this->data, PhraseData::Greater, other); +} + +FieldPhrase FieldPhrase::operator <=(const QVariant &other){ + return FieldPhrase(this->data, PhraseData::LessEqual, other); +} + +FieldPhrase FieldPhrase::operator >=(const QVariant &other){ + return FieldPhrase(this->data, PhraseData::GreaterEqual, other); +} + +FieldPhrase FieldPhrase::operator =(const QVariant &other) +{ + return FieldPhrase(this->data, PhraseData::Set, other); +} + +FieldPhrase FieldPhrase::isNull(){ + return FieldPhrase(this->data, PhraseData::Null); +} + +FieldPhrase FieldPhrase::in(QVariantList list) +{ + return FieldPhrase(this->data, PhraseData::In, list); +} + +FieldPhrase FieldPhrase::in(QStringList list) +{ + return FieldPhrase(this->data, PhraseData::In, list); +} + +FieldPhrase FieldPhrase::like(QString pattern) +{ + return FieldPhrase(this->data, PhraseData::Like, pattern); +} + +QT_END_NAMESPACE + + diff --git a/src/wherephrase.h b/src/wherephrase.h index 5a8bed9..36a015c 100644 --- a/src/wherephrase.h +++ b/src/wherephrase.h @@ -1,12 +1,151 @@ -#ifndef WHEREPHRASE_H -#define WHEREPHRASE_H +/************************************************************************** +** +** 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 PHRASE_H +#define PHRASE_H #include -class WherePhrase -{ -public: - WherePhrase(); +#include +#include +#include +#include + + +QT_BEGIN_NAMESPACE + +class SqlGeneratorBase; +struct PhraseData{ + enum Condition + { + Equal = 0, + Less, + LessEqual, + Null, + In, + Like, + + NotEqual = 10, + GreaterEqual, + Greater, + NotNull, + NotIn, + NotLike, + + And = 20, + Or, + + Append, + Set, + + Add, + Minus, + Multiple, + Divide + }; + + enum Type{ + Field, + WithVariant, + WithOther, + WithoutOperand + }; + Type type; + + Condition operatorCond; + + QString text; + const PhraseData *left; + const PhraseData *right; + QVariant operand; + + PhraseData(const char *className, const char* s); + PhraseData(PhraseData *l, Condition o); + PhraseData(PhraseData *l, Condition o, const PhraseData *r); + PhraseData(PhraseData *l, Condition o, QVariant r); + + ~PhraseData(); + + QString operatorString() const; + QString escapeVariant() const; + QString command(SqlGeneratorBase *generator) const; }; -#endif // WHEREPHRASE_H \ No newline at end of file +class FieldPhrase{ + PhraseData *data; + bool willDeleteData; +public: + + QString text; + + FieldPhrase(const char *className, const char* s); + + FieldPhrase(PhraseData *l); + FieldPhrase(PhraseData *l, PhraseData::Condition o); + FieldPhrase(PhraseData *l, PhraseData::Condition o, PhraseData *r); + FieldPhrase(PhraseData *l, PhraseData::Condition o, QVariant r); + + ~FieldPhrase(); + + QString command(SqlGeneratorBase *generator); + + void deleteData(PhraseData *d); + + FieldPhrase operator ==(const FieldPhrase &other); + FieldPhrase operator !=(const FieldPhrase &other); + FieldPhrase operator <(const FieldPhrase &other); + FieldPhrase operator >(const FieldPhrase &other); + FieldPhrase operator <=(const FieldPhrase &other); + FieldPhrase operator >=(const FieldPhrase &other); + + FieldPhrase operator =(const FieldPhrase &other); + + FieldPhrase operator +(const FieldPhrase &other); + FieldPhrase operator -(const FieldPhrase &other); + FieldPhrase operator *(const FieldPhrase &other); + FieldPhrase operator /(const FieldPhrase &other); + + FieldPhrase operator &&(const FieldPhrase &other); + FieldPhrase operator ||(const FieldPhrase &other); + + FieldPhrase operator &(const FieldPhrase &other); + + FieldPhrase operator ,(const FieldPhrase &other); + + FieldPhrase operator !(); + + FieldPhrase operator ==(const QVariant &other); + FieldPhrase operator !=(const QVariant &other); + FieldPhrase operator <(const QVariant &other); + FieldPhrase operator >(const QVariant &other); + FieldPhrase operator <=(const QVariant &other); + FieldPhrase operator >=(const QVariant &other); + + FieldPhrase operator =(const QVariant &other); + + FieldPhrase isNull(); + FieldPhrase in(QVariantList list); + FieldPhrase in(QStringList list); + FieldPhrase like(QString pattern); +}; + +QT_END_NAMESPACE + +#endif // PHRASE_H diff --git a/test/.maintest.cpp.kate-swp b/test/.maintest.cpp.kate-swp deleted file mode 100644 index 1f932fa..0000000 Binary files a/test/.maintest.cpp.kate-swp and /dev/null differ diff --git a/test/comment.h b/test/comment.h index 7a81b13..5899fb6 100644 --- a/test/comment.h +++ b/test/comment.h @@ -14,6 +14,7 @@ class Comment : public Table NUT_DECLARE_FIELD(int, id, id, setId) NUT_DECLARE_FIELD(QString, message, message, setMessage) NUT_DECLARE_FIELD(QDateTime, saveDate, saveDate, setSaveDate) + NUT_DECLARE_FIELD(qreal, point, point, setPoint) NUT_FOREGION_KEY(Post, int, post, post, setPost) diff --git a/test/maintest.cpp b/test/maintest.cpp index cbd2be7..526e0d5 100644 --- a/test/maintest.cpp +++ b/test/maintest.cpp @@ -5,7 +5,7 @@ #include "maintest.h" #include "query.h" #include "tableset.h" -#include "tablescheema.h" +#include "tablemodel.h" #include "databasemodel.h" #include "post.h" @@ -18,18 +18,47 @@ MainTest::MainTest(QObject *parent) : QObject(parent) void MainTest::initTestCase() { - qDebug() << "Table type id:" << qRegisterMetaType(); +// qDebug() << "Table type id:" << qRegisterMetaType(); qDebug() << "User type id:" << qRegisterMetaType(); qDebug() << "Comment type id:" << qRegisterMetaType(); qDebug() << "DB type id:" << qRegisterMetaType(); - db.setDriver("QSQLITE"); + //sql server +// db.setDriver("QODBC"); +// db.setHostName("127.0.0.1"); +// db.setDatabaseName("DRIVER={SQL Server};Server=.;Database=Nut;Uid=sa;Port=1433;Pwd=qwe123!@#;WSID=."); +// db.setUserName("sa"); +// db.setPassword("qwe123!@#"); + + QStringList list; + list << "one" << "two" << "three"; + FieldPhrase q = (Post::idField() = 1) + & (Post::saveDateField() = QDateTime::currentDateTime()) + & (Post::saveDateField() < QDateTime::currentDateTime() +// (/*(Post::saveDateField() > Post::idField()) +// && */ +// !Post::saveDateField().isNull() +// && +// !Post::idField().in(list) +// || (Post::idField() == 4) +// && Post::saveDateField() >= QDateTime::currentDateTime() + /*|| Post::saveDateField().isNull()*/); + qDebug() << "Command="<< q.command(0); + QTEST_ASSERT(1==2); + // postgres + db.setDriver("QPSQL"); db.setHostName("127.0.0.1"); - db.setDatabaseName("nutdb"); -// db.setUserName("postgres"); -// db.setPassword("856856"); + db.setDatabaseName("nutdb3"); + db.setUserName("postgres"); + db.setPassword("856856"); + +// mysql +// db.setDriver("QMYSQL"); +// db.setHostName("127.0.0.1"); +// db.setDatabaseName("nutdb"); // db.setUserName("root"); // db.setPassword("onlyonlyi"); + bool ok = db.open(); QTEST_ASSERT(ok); @@ -49,13 +78,14 @@ void MainTest::createPost() { Post *newPost = new Post; newPost->setTitle("post title"); + newPost->setSaveDate(QDateTime::currentDateTime()); db.posts()->append(newPost); for(int i = 0 ; i < 3; i++){ Comment *comment = new Comment; comment->setMessage("comment #" + QString::number(i)); - + comment->setSaveDate(QDateTime::currentDateTime()); newPost->comments()->append(comment); } db.saveChanges(); @@ -68,22 +98,48 @@ void MainTest::createPost() void MainTest::selectPosts() { - auto q = FROM(db.posts()) - JOIN(Comment) - WHERE(Post::id() == %1) - BIND(postId); + q = FROM(db.posts()) + JOIN(Comment) + WHERE(Post::idField() == postId); + auto posts = q->toList(); + post = posts.at(0); + post->setBody(""); + 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"); + db.cleanUp(); } +void MainTest::testDate() +{ + QDateTime d = QDateTime::currentDateTime(); + QTime t = QTime(d.time().hour(), d.time().minute(), d.time().second()); + d.setTime(t); + + Post *newPost = new Post; + newPost->setTitle("post title"); + newPost->setSaveDate(d); + + db.posts()->append(newPost); + + db.saveChanges(); + + auto q = FROM(db.posts()) + WHERE(Post::idField() == newPost->id()) + FIRST(); + + qDebug() << d << q->saveDate(); + QTEST_ASSERT(q->saveDate() == d); +} + + void MainTest::selectWithInvalidRelation() { auto q = FROM(db.posts()) @@ -94,8 +150,7 @@ void MainTest::selectWithInvalidRelation() void MainTest::modifyPost() { auto q = FROM(db.posts()) - WHERE(Post::id() == %1) - BIND(postId); + WHERE(Post::idField() == postId); Post *post = q->first(); @@ -105,8 +160,8 @@ void MainTest::modifyPost() db.saveChanges(); q = FROM(db.posts()) - WHERE(Post::id() == %1) - BIND(postId); + WHERE(Post::idField() == postId); + post = q->first(); QTEST_ASSERT(post->title() == "new name"); } @@ -114,15 +169,13 @@ void MainTest::modifyPost() void MainTest::deletePost() { auto count = FROM(db.posts()) - WHERE(Post::id() == %1) - BIND(postId) + WHERE(Post::idField() == postId) DELETE(); QTEST_ASSERT(count == 1); count = FROM(db.posts()) - WHERE(Post::id() == %1) - BIND(postId) + WHERE(Post::idField() == postId) COUNT(); QTEST_ASSERT(count == 0); diff --git a/test/maintest.h b/test/maintest.h index f870330..b0daf10 100644 --- a/test/maintest.h +++ b/test/maintest.h @@ -5,12 +5,14 @@ #include #include "weblogdatabase.h" +class Post; class MainTest : public QObject { Q_OBJECT WeblogDatabase db; int postId; - + Post *post; + Query *q; public: explicit MainTest(QObject *parent = 0); @@ -22,6 +24,7 @@ private slots: void dataScheema(); void createPost(); void selectPosts(); + void testDate(); void selectWithInvalidRelation(); void modifyPost(); void deletePost(); diff --git a/test/post.h b/test/post.h index a295abd..7ece87e 100644 --- a/test/post.h +++ b/test/post.h @@ -18,6 +18,8 @@ class Post : public Table NUT_LEN(title, 50) NUT_DECLARE_FIELD(QString, title, title, setTitle) + NUT_DECLARE_FIELD(QDateTime, saveDate, saveDate, setSaveDate) + NUT_DECLARE_FIELD(QString, body, body, setBody) NUT_DECLARE_CHILD_TABLE(Comment, comments) diff --git a/test/weblogdatabase.h b/test/weblogdatabase.h index e059f7d..58eb7c8 100644 --- a/test/weblogdatabase.h +++ b/test/weblogdatabase.h @@ -9,7 +9,7 @@ class WeblogDatabase : public Database { Q_OBJECT - NUT_DB_VERSION(1, 0) + NUT_DB_VERSION(1, 1) NUT_DECLARE_TABLE(Post, post)