diff --git a/.gitignore b/.gitignore index 04b7a50..a150acc 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ Makefile* #QtCtreator Qml *.qmlproject.user *.qmlproject.user.* + +build diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..1421d09 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "3rdparty/serializer"] + path = 3rdparty/serializer + url = https://github.com/HamedMasafi/Serializer.git diff --git a/.travis.yml b/.travis.yml index dc854ff..31fe73e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,29 +13,23 @@ compiler: env: matrix: - - QT=5 BREW= - - QT=5 BREW=ex PPA=ubuntu-sdk-team/ppa - - QT=51 BREW=ex PPA=beineri/opt-qt511-trusty - - QT=52 BREW=ex PPA=beineri/opt-qt521-trusty - - QT=53 BREW=ex PPA=beineri/opt-qt532-trusty - - QT=54 BREW=ex PPA=beineri/opt-qt542-trusty - - QT=55 BREW=@5.5 PPA=beineri/opt-qt551-trusty - - QT=56 BREW=ex PPA=beineri/opt-qt562-trusty - - QT=56 BREW=ex PPA=beineri/opt-qt563-trusty - - QT=57 BREW=@5.7 PPA=beineri/opt-qt571-trusty - - QT=58 BREW=ex PPA=beineri/opt-qt58-trusty - - QT=59 BREW=@5.9 PPA=beineri/opt-qt591-trusty - + - QT=59 BREW=ex PPA=beineri/opt-qt591-trusty + - QT=59 BREW=ex PPA=beineri/opt-qt592-trusty + - QT=59 BREW=ex PPA=beineri/opt-qt593-trusty + - QT=59 BREW=ex PPA=beineri/opt-qt594-trusty + - QT=59 BREW=ex PPA=beineri/opt-qt595-trusty + - QT=59 BREW=ex PPA=beineri/opt-qt596-trusty + - QT=510 BREW=ex PPA=beineri/opt-qt-5.10.1-trusty + matrix: exclude: - - { os: osx, env: QT=5 BREW=ex PPA=ubuntu-sdk-team/ppa } - - { os: osx, env: QT=51 BREW=ex PPA=beineri/opt-qt511-trusty } - - { os: osx, env: QT=52 BREW=ex PPA=beineri/opt-qt521-trusty } - - { os: osx, env: QT=53 BREW=ex PPA=beineri/opt-qt532-trusty } - - { os: osx, env: QT=54 BREW=ex PPA=beineri/opt-qt542-trusty } - - { os: osx, env: QT=56 BREW=ex PPA=beineri/opt-qt562-trusty } - - { os: osx, env: QT=56 BREW=ex PPA=beineri/opt-qt563-trusty } - - { os: osx, env: QT=58 BREW=ex PPA=beineri/opt-qt58-trusty } + - { os: osx, env: QT=59 BREW=ex PPA=beineri/opt-qt591-trusty } + - { os: osx, env: QT=59 BREW=ex PPA=beineri/opt-qt592-trusty } + - { os: osx, env: QT=59 BREW=ex PPA=beineri/opt-qt593-trusty } + - { os: osx, env: QT=59 BREW=ex PPA=beineri/opt-qt594-trusty } + - { os: osx, env: QT=59 BREW=ex PPA=beineri/opt-qt595-trusty } + - { os: osx, env: QT=59 BREW=ex PPA=beineri/opt-qt596-trusty } + - { os: osx, env: QT=510 BREW=ex PPA=beineri/opt-qt-5.10.1-trusty } addons: coverity_scan: @@ -72,7 +66,8 @@ before_script: - '[[ "$TRAVIS_OS_NAME" != linux || "$PPA" != */opt-* ]] || . /opt/qt$QT/bin/qt$QT-env.sh' - '[[ "$TRAVIS_OS_NAME" != linux || "$PPA" == */opt-* ]] || export QT_SELECT=qt5' - mkdir -p "$TRAVIS_BUILD_DIR-build" - - qmake -o "$TRAVIS_BUILD_DIR-build" -r -Wall -Wlogic -Wparser CONFIG+=debug_and_release "$TRAVIS_BUILD_DIR" + - cd "$TRAVIS_BUILD_DIR-build" + - qmake -r -Wall -Wlogic -Wparser CONFIG+=debug_and_release "$TRAVIS_BUILD_DIR/nut.pro" script: - make -C "$TRAVIS_BUILD_DIR-build" all diff --git a/3rdparty/serializer b/3rdparty/serializer new file mode 160000 index 0000000..f9d81fc --- /dev/null +++ b/3rdparty/serializer @@ -0,0 +1 @@ +Subproject commit f9d81fcc6244271b3926891b0c86554e1e6b967e diff --git a/README.md b/README.md index 315ab32..8eacca4 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,17 @@ + + # Nut ## Build result -| Brancc name | Icon | +| Branch | Status | | ------------- |:-------------:| | master | [![Build Status](https://travis-ci.org/HamedMasafi/Nut.svg?branch=master)](https://travis-ci.org/HamedMasafi/Nut) | | dev | [![Build Status](https://travis-ci.org/HamedMasafi/Nut.svg?branch=dev)](https://travis-ci.org/HamedMasafi/Nut) | +[![GitLicense](https://gitlicense.com/badge/hamedmasafi/nut)](https://gitlicense.com/license/hamedmasafi/nut) + +[![Codacy +Badge](https://api.codacy.com/project/badge/Grade/f3802610beb946068f6cd2c2b6608a8b)](https://www.codacy.com/app/HamedMasafi/Nut?utm_source=github.com&utm_medium=referral&utm_content=HamedMasafi/Nut&utm_campaign=Badge_Grade) ## Advanced, Powerful and easy to use ORM for Qt5 @@ -17,6 +23,52 @@ - Automatically create and update database - IDE auto complete support, No hard-code nedded - Table join detect + - Supported types: + +| Type | Sqlite | MySql | Postgresql| Ms Sql server | +|--------|--------|--------|--------|--------| +| QBitArray | BLOB | VARBINARY | BYTEA | VARBINARY (MAX) | +| QByteArray | BLOB | BLOB | BYTEA | VARBINARY (MAX) | +| QChar | NCHAR(1) | CHAR(1) | CHAR(1) | CHAR(1) | +| QColor | TEXT | TEXT | TEXT | TEXT | +| QDate | DATE | DATE | DATE | DATE | +| QDateTime | DATETIME | DATETIME | TIMESTAMP | DATETIME | +| QJsonArray | TEXT | TEXT | JSON | TEXT | +| QJsonDocument | TEXT | TEXT | JSON | TEXT | +| QJsonObject | TEXT | TEXT | JSON | TEXT | +| QJsonValue | TEXT | TEXT | JSON | TEXT | +| QLine | TEXT | TEXT | LINE | TEXT | +| QLineF | TEXT | TEXT | LINE | TEXT | +| QPoint | TEXT | POINT | POINT | GEOMETRY | +| QPointF | TEXT | POINT | POINT | GEOMETRY | +| QPolygon | TEXT | POLYGON | POLYGON | TEXT | +| QPolygonF | TEXT | POLYGON | POLYGON | TEXT | +| QRect | TEXT | TEXT | BOX | TEXT | +| QRectF | TEXT | TEXT | BOX | TEXT | +| QSize | TEXT | TEXT | TEXT | TEXT | +| QSizeF | TEXT | TEXT | TEXT | TEXT | +| QString | TEXT | TEXT | TEXT | NVARCHAR(MAX) | +| QStringList | TEXT[^*] | TEXT | TEXT[] | TEXT | +| QTime | TIME | TIME | TIME | TIME | +| QUrl | TEXT | TEXT | TEXT | TEXT | +| QUuid | TEXT | VARCHAR(64) | UUID | UNIQUEIDENTIFIER | +| bool | BOOLEAN | BOOLEAN | BOOLEAN | BIT | +| char | TINYINT | CHAR(1) | CHAR(1) | CHAR(1) | +| double | DOUBLE | REAL | REAL | REAL | +| float | FLOAT | FLOAT | FLOAT | FLOAT(24) | +| int | INT | INT | INTEGER | INT | +| long | MEDIUMINT | BIGINT | BIGINT | BIGINT | +| qlonglong | BIGINT | BIGINT | BIGINT | BIGINT | +| qulonglong | BIGINT UNSIGNED | BIGINT | BIGINT | BIGINT | +| short | SMALLINT | SMALLINT | SMALLINT | SMALLINT | +| signed char | TINYINT | TINYINT | SMALLINT | TINYINT | +| uchar | TINYINT UNSIGNED | TINYINT | SMALLINT | TINYINT | +| uint | INT UNSIGNED | INT | INTEGER | INT | +| ulong | MEDIUMINT UNSIGNED | BIGINT | BIGINT | BIGINT | +| ushort | SMALLINT UNSIGNED | SMALLINT | SMALLINT | SMALLINT | + +[^*]: Using internal store/restore serialization + ## Sample Codes ### Read data from database: diff --git a/ci-test-init.pri b/ci-test-init.pri new file mode 100644 index 0000000..c3d1f7b --- /dev/null +++ b/ci-test-init.pri @@ -0,0 +1 @@ +#QT -= gui diff --git a/include/Nut b/include/Nut index 5b89c95..c5cf95c 100644 --- a/include/Nut +++ b/include/Nut @@ -1,5 +1,7 @@ #include "../src/table.h" #include "../src/database.h" +#include "../src/sqlmodel.h" #include "../src/tableset.h" +#include "../src/tablemodel.h" #include "../src/query.h" diff --git a/include/SqlModel b/include/SqlModel new file mode 100644 index 0000000..c15f192 --- /dev/null +++ b/include/SqlModel @@ -0,0 +1 @@ +#include "../src/sqlmodel.h" diff --git a/include/TableModel b/include/TableModel new file mode 100644 index 0000000..3bc108b --- /dev/null +++ b/include/TableModel @@ -0,0 +1 @@ +#include "../src/tablemodel.h" diff --git a/include/header_copier b/include/header_copier old mode 100644 new mode 100755 index e228c19..e4cd72b --- a/include/header_copier +++ b/include/header_copier @@ -3,11 +3,10 @@ src_dir="src" namespace_name="nut" -ns=$(echo $namespace_name|awk '{print tolower($0)}') +#ns=$(echo $namespace_name|awk '{print tolower($0)}') Ns="Nut" NS=$(echo $namespace_name|awk '{print toupper($0)}') -echo $NS -exit + create_sub_folder=true @@ -38,5 +37,6 @@ while read line; do echo "#include \"../$src_dir/$header.h\"" >> "$Ns" echo "#include \"../$src_dir/$header.h\"" >> "$ns.h" fi + echo $Ns done <&3 exec 3<&- diff --git a/include/nut.h b/include/nut.h index 2dfb0b9..c5cf95c 100644 --- a/include/nut.h +++ b/include/nut.h @@ -1,6 +1,7 @@ #include "../src/table.h" #include "../src/database.h" +#include "../src/sqlmodel.h" #include "../src/tableset.h" -#include "../src/dbgeography.h" +#include "../src/tablemodel.h" #include "../src/query.h" diff --git a/include/sqlmodel.h b/include/sqlmodel.h new file mode 100644 index 0000000..c15f192 --- /dev/null +++ b/include/sqlmodel.h @@ -0,0 +1 @@ +#include "../src/sqlmodel.h" diff --git a/include/tablemodel.h b/include/tablemodel.h new file mode 100644 index 0000000..3bc108b --- /dev/null +++ b/include/tablemodel.h @@ -0,0 +1 @@ +#include "../src/tablemodel.h" diff --git a/nut-dynamic.pro b/nut-dynamic.pro new file mode 100644 index 0000000..b19d9e8 --- /dev/null +++ b/nut-dynamic.pro @@ -0,0 +1,10 @@ +QT += sql gui + +TARGET = nut +TEMPLATE = lib +CONFIG += c++11 + +DEFINES += QT_DEPRECATED_WARNINGS NUT_COMPILE_STATIC + +include($$PWD/src/src.pri) +include($$PWD/3rdparty/serializer/src/src.pri) diff --git a/nut.pri b/nut.pri index 15b253b..b0931d8 100644 --- a/nut.pri +++ b/nut.pri @@ -3,6 +3,8 @@ QT += core sql CONFIG += c++11 INCLUDEPATH += $$PWD/include +DEFINES += NUT_SHARED_POINTER +include(3rdparty/serializer/src/src.pri) HEADERS += \ $$PWD/src/generators/sqlgeneratorbase_p.h \ @@ -27,7 +29,19 @@ HEADERS += \ $$PWD/src/serializableobject.h \ $$PWD/src/sqlmodel.h \ $$PWD/src/sqlmodel_p.h \ - $$PWD/src/phrase.h + $$PWD/src/phrase.h \ + $$PWD/src/tuple.h \ + $$PWD/src/phrases/conditionalphrase.h \ + $$PWD/src/phrases/abstractfieldphrase.h \ + $$PWD/src/phrases/fieldphrase.h \ + $$PWD/src/phrases/phraselist.h \ + $$PWD/src/phrases/assignmentphraselist.h \ + $$PWD/src/phrases/phrasedatalist.h \ + $$PWD/src/phrases/phrasedata.h \ + $$PWD/src/phrases/assignmentphrase.h \ + $$PWD/src/phrases/numericphrase.h \ + $$PWD/src/phrases/datephrase.h \ + $$PWD/src/bulkinserter.h SOURCES += \ $$PWD/src/generators/sqlgeneratorbase.cpp \ @@ -47,4 +61,16 @@ SOURCES += \ $$PWD/src/database.cpp \ $$PWD/src/serializableobject.cpp \ $$PWD/src/sqlmodel.cpp \ - $$PWD/src/phrase.cpp + $$PWD/src/phrase.cpp \ + $$PWD/src/tuple.cpp \ + $$PWD/src/phrases/conditionalphrase.cpp \ + $$PWD/src/phrases/abstractfieldphrase.cpp \ + $$PWD/src/phrases/fieldphrase.cpp \ + $$PWD/src/phrases/phraselist.cpp \ + $$PWD/src/phrases/assignmentphraselist.cpp \ + $$PWD/src/phrases/phrasedatalist.cpp \ + $$PWD/src/phrases/phrasedata.cpp \ + $$PWD/src/phrases/assignmentphrase.cpp \ + $$PWD/src/phrases/numericphrase.cpp \ + $$PWD/src/phrases/datephrase.cpp \ + $$PWD/src/bulkinserter.cpp diff --git a/nut.pro b/nut.pro index 6bc6b01..5a35529 100644 --- a/nut.pro +++ b/nut.pro @@ -1,53 +1,6 @@ -QT += sql -QT -= gui +TEMPLATE = subdirs -TARGET = nut -TEMPLATE = lib -CONFIG += c++11 +SUBDIRS += \ + src \ + test -DEFINES += QT_DEPRECATED_WARNINGS - -HEADERS += \ - $$PWD/src/generators/sqlgeneratorbase_p.h \ - $$PWD/src/generators/postgresqlgenerator.h \ - $$PWD/src/generators/mysqlgenerator.h \ - $$PWD/src/generators/sqlitegenerator.h \ - $$PWD/src/generators/sqlservergenerator.h \ - $$PWD/src/types/dbgeography.h \ - $$PWD/src/tableset.h \ - $$PWD/src/defines_p.h \ - $$PWD/src/defines.h \ - $$PWD/src/query.h \ - $$PWD/src/databasemodel.h \ - $$PWD/src/changelogtable.h \ - $$PWD/src/tablesetbase_p.h \ - $$PWD/src/querybase_p.h \ - $$PWD/src/tablemodel.h \ - $$PWD/src/query_p.h \ - $$PWD/src/table.h \ - $$PWD/src/database.h \ - $$PWD/src/database_p.h \ - $$PWD/src/serializableobject.h \ - $$PWD/src/sqlmodel.h \ - $$PWD/src/sqlmodel_p.h \ - $$PWD/src/phrase.h - -SOURCES += \ - $$PWD/src/generators/sqlgeneratorbase.cpp \ - $$PWD/src/generators/postgresqlgenerator.cpp \ - $$PWD/src/generators/mysqlgenerator.cpp \ - $$PWD/src/generators/sqlitegenerator.cpp \ - $$PWD/src/generators/sqlservergenerator.cpp \ - $$PWD/src/types/dbgeography.cpp \ - $$PWD/src/tableset.cpp \ - $$PWD/src/query.cpp \ - $$PWD/src/databasemodel.cpp \ - $$PWD/src/tablesetbase.cpp \ - $$PWD/src/changelogtable.cpp \ - $$PWD/src/querybase.cpp \ - $$PWD/src/tablemodel.cpp \ - $$PWD/src/table.cpp \ - $$PWD/src/database.cpp \ - $$PWD/src/serializableobject.cpp \ - $$PWD/src/sqlmodel.cpp \ - $$PWD/src/phrase.cpp diff --git a/src/bulkinserter.cpp b/src/bulkinserter.cpp new file mode 100644 index 0000000..ddebc4b --- /dev/null +++ b/src/bulkinserter.cpp @@ -0,0 +1,43 @@ +#include "bulkinserter.h" +#include "phrases/phraselist.h" +#include "database.h" +#include "generators/sqlgeneratorbase_p.h" +#include "databasemodel.h" + +#include + +Nut::BulkInserter::BulkInserter(Nut::Database *db, QString &className) + : _database(db), _fieldCount(0) +{ + foreach (TableModel *m, db->model()) + if (m->className() == className) + _className = m->name(); +} + +void Nut::BulkInserter::setFields(const Nut::PhraseList &ph) +{ + _fields = ph; + _fieldCount = static_cast(ph.data.count()); +} + +void Nut::BulkInserter::insert(std::initializer_list vars) +{ + if (vars.size() != _fieldCount) { + qInfo("Number of rows mistake"); + return; + } + + QVariantList list; + std::initializer_list::iterator it; + for (it = vars.begin(); it != vars.end(); ++it) + list.append(*it); + variants.append(list); +} + +int Nut::BulkInserter::apply() +{ + auto sql = _database->sqlGenertor()->insertBulk(_className, _fields, variants); + QSqlQuery q = _database->exec(sql); + return q.numRowsAffected(); +} + diff --git a/src/bulkinserter.h b/src/bulkinserter.h new file mode 100644 index 0000000..734689a --- /dev/null +++ b/src/bulkinserter.h @@ -0,0 +1,36 @@ +#ifndef BULKINSERTER_H +#define BULKINSERTER_H + +#include +#include +#include "defines.h" +#include "phrases/phraselist.h" +#include "phrases/fieldphrase.h" + +NUT_BEGIN_NAMESPACE + +class PhraseList; +class Database; +class BulkInserter +{ + Database *_database; + QString _className; + Nut::PhraseList _fields; + QList variants; + size_t _fieldCount; + +public: + BulkInserter(Database *db, QString &className); + void setFields(const PhraseList &ph); + + void insert(std::initializer_list vars); + template + void insert(Args... args) { + insert({args...}); + } + int apply(); +}; + +NUT_END_NAMESPACE + +#endif // BULKINSERTER_H diff --git a/src/changelogtable.h b/src/changelogtable.h index 84475a5..4d580de 100644 --- a/src/changelogtable.h +++ b/src/changelogtable.h @@ -35,7 +35,7 @@ class ChangeLogTable : public Table NUT_DECLARE_FIELD(QString, data, data, setData) - NUT_DECLARE_FIELD(QString, version, version, setVersion) + NUT_DECLARE_FIELD(int, version, version, setVersion) public: explicit ChangeLogTable(QObject *parentTableSet = Q_NULLPTR); diff --git a/src/database.cpp b/src/database.cpp index cc69a2a..e804ef4 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -45,7 +45,9 @@ #include #include -#define __CHANGE_LOG_TABLE_NAME "__change_logs" +#ifndef __CHANGE_LOG_TABLE_NAME +# define __CHANGE_LOG_TABLE_NAME "__change_logs" +#endif NUT_BEGIN_NAMESPACE @@ -53,7 +55,7 @@ qulonglong DatabasePrivate::lastId = 0; QMap DatabasePrivate::allTableMaps; DatabasePrivate::DatabasePrivate(Database *parent) : q_ptr(parent), - port(0), sqlGenertor(0), changeLogs(0), + port(0), sqlGenertor(nullptr), changeLogs(nullptr), isDatabaseNew(false) { } @@ -69,12 +71,13 @@ bool DatabasePrivate::open(bool update) db = QSqlDatabase::addDatabase(driver, connectionName); db.setHostName(hostName); - db.setPort(port); + if (port) + db.setPort(port); db.setDatabaseName(databaseName); db.setUserName(userName); db.setPassword(password); - if (driver.toLower().startsWith("qsqlite") + if (driver.startsWith("qsqlite", Qt::CaseInsensitive) && !QFile::exists(databaseName)) { //Force to execute update database isDatabaseNew = true; @@ -108,18 +111,18 @@ bool DatabasePrivate::open(bool update) isDatabaseNew = true; return open(update); - } else { - qWarning("Unknown error detecting change logs, %s", - db.lastError().text().toLatin1().data()); } + qWarning("Unknown error detecting change logs, %s", + db.lastError().text().toLatin1().data()); + } return false; } - if(update) +// if(update) return updateDatabase(); - else - return true; +// else +// return true; } bool DatabasePrivate::updateDatabase() @@ -134,24 +137,39 @@ bool DatabasePrivate::updateDatabase() if (last == current) { qDebug("Databse is up-to-date"); + //TODO: crash without this and I don't know why! + changeLogs->clearChilds(); return true; } + foreach (TableModel *tm, current) { + foreach (FieldModel *fm, tm->fields()) { + if (sqlGenertor->fieldType(fm).isEmpty()) { + qWarning("The type (%s) is not supported for field %s::%s", + QMetaType::typeName(fm->type), + qPrintable(tm->className()), + qPrintable(fm->name)); + return false; + } + } + } if (!last.count()) qDebug("Databse is new"); else qDebug("Databse is changed"); QStringList sql = sqlGenertor->diff(last, current); -qDebug()<<"database Sql =\n"<databaseUpdated(last.version(), current.version()); + if (!last.count()) + q->databaseCreated(); - for (int i = 0; i < q->metaObject()->methodCount(); i++) { - QMetaMethod m = q->metaObject()->method(i); - if (m.name() == "update" + current.version()) { - m.invoke(q, Qt::DirectConnection, - Q_ARG(QString, current.version())); - break; - } - } } else { qWarning("Unable update database, error = %s", db.lastError().text().toLatin1().data()); @@ -186,6 +198,7 @@ bool DatabasePrivate::getCurrectScheema() return false; } + QMap tables; tables.clear(); // TODO: change logs must not be in model @@ -205,7 +218,9 @@ bool DatabasePrivate::getCurrectScheema() if (!nutClassInfoString(q->metaObject()->classInfo(i), type, name, value)) { - qDebug() << "No valid table in" << q->metaObject()->classInfo(i).value(); + + errorMessage = QString("No valid table in %1") + .arg(q->metaObject()->classInfo(i).value()); continue; } if (type == __nut_TABLE) { @@ -222,23 +237,13 @@ bool DatabasePrivate::getCurrectScheema() currentModel.append(sch); } - if (type == __nut_DB_VERSION) - currentModel.setVersion(name); - - /* TODO: remove - QStringList version - = QString(ci.value()).replace("\"", "").split('.'); - bool ok = false; - if (version.length() == 1) { - currentModel.setVersion(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 (type == __nut_DB_VERSION) { + bool ok; + int version = value.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");*/ + qFatal("NUT_DB_VERSION macro accept version in format 'x'"); + currentModel.setVersion(version); + } } for (int i = 1; i < q->metaObject()->propertyCount(); i++) { @@ -252,42 +257,37 @@ bool DatabasePrivate::getCurrectScheema() } } - foreach (TableModel *table, currentModel) + foreach (TableModel *table, currentModel) { + foreach (FieldModel *f, table->fields()) { + if (f->isPrimaryKey && ! sqlGenertor->supportPrimaryKey(f->type)) + qFatal("The field of type %s does not support as primary key", + qPrintable(f->typeName)); + + if (f->isAutoIncrement && ! sqlGenertor->supportAutoIncrement(f->type)) + qFatal("The field of type %s does not support as auto increment", + qPrintable(f->typeName)); + } + foreach (RelationModel *fk, table->foregionKeys()) fk->masterTable = currentModel.tableByClassName(fk->masterClassName); + } allTableMaps.insert(q->metaObject()->className(), currentModel); return true; } -//bool DatabasePrivate::checkClassInfo(const QMetaClassInfo &classInfo, QString &type, QString &name, QString &value) -//{ -// if (!QString(classInfo.name()).startsWith(__nut_NAME_PERFIX)) { -// return false; -// } else { -// QStringList parts = QString(classInfo.value()).split("\n"); -// if (parts.count() != 3) -// return false; - -// type = parts[0]; -// name = parts[1]; -// value = parts[2]; -// return true; -// } -//} - DatabaseModel DatabasePrivate::getLastScheema() { - ChangeLogTable *u = changeLogs->query() + Row u = changeLogs->query() ->orderBy(!ChangeLogTable::idField()) ->first(); // DatabaseModel ret(q->metaObject()->className()); if (u) { + QJsonParseError e; QJsonObject json - = QJsonDocument::fromJson( - QByteArray(u->data().toLocal8Bit().data())).object(); + = QJsonDocument::fromJson(u->data().replace("\\\"", "\"").toUtf8(), &e).object(); DatabaseModel ret = json; return ret; @@ -321,11 +321,11 @@ bool DatabasePrivate::putModelToDatabase() DatabaseModel current = currentModel; /*current.remove(__CHANGE_LOG_TABLE_NAME)*/; - ChangeLogTable *changeLog = new ChangeLogTable(); - changeLog->setData(QJsonDocument(current.toJson()).toJson()); + auto changeLog = create(); + changeLog->setData(QJsonDocument(current.toJson()).toJson(QJsonDocument::Compact)); changeLog->setVersion(current.version()); changeLogs->append(changeLog); - q->saveChanges(); + q->saveChanges(true); changeLog->deleteLater(); return true; @@ -344,9 +344,10 @@ bool DatabasePrivate::putModelToDatabase() void DatabasePrivate::createChangeLogs() { // currentModel.model("change_log") - QString diff = sqlGenertor->diff(0, currentModel.tableByName("__change_log")); + QStringList diff = sqlGenertor->diff(nullptr, currentModel.tableByName("__change_log")); - db.exec(diff); + foreach (QString s, diff) + db.exec(s); } /*! @@ -357,6 +358,7 @@ void DatabasePrivate::createChangeLogs() Database::Database(QObject *parent) : QObject(parent), d_ptr(new DatabasePrivate(this)) { +// _d = new QSharedDataPointer(new DatabasePrivate(this)); DatabasePrivate::lastId++; } @@ -364,6 +366,7 @@ Database::Database(const Database &other) : QObject(other.parent()), d_ptr(new DatabasePrivate(this)) { DatabasePrivate::lastId++; +// _d = other._d; setDriver(other.driver()); setHostName(other.hostName()); @@ -393,8 +396,7 @@ Database::~Database() if (d->db.isOpen()) d->db.close(); - if (d_ptr) - delete d_ptr; + delete d_ptr; } QString Database::databaseName() const @@ -516,7 +518,12 @@ QSqlDatabase Database::database() return d->db; } -void Database::databaseUpdated(QString oldVersion, QString newVersion) +void Database::databaseCreated() +{ + +} + +void Database::databaseUpdated(int oldVersion, int newVersion) { Q_UNUSED(oldVersion); Q_UNUSED(newVersion); @@ -559,10 +566,9 @@ bool Database::open(bool updateDatabase) if (!d->sqlGenertor) { qFatal("Sql generator for driver %s not found", driver().toLatin1().constData()); - return false; - } else { - return d->open(updateDatabase); } + + return d->open(updateDatabase); } void Database::close() @@ -571,7 +577,7 @@ void Database::close() d->db.close(); } -QSqlQuery Database::exec(QString sql) +QSqlQuery Database::exec(const QString &sql) { Q_D(Database); @@ -592,6 +598,12 @@ void Database::add(TableSetBase *t) int Database::saveChanges(bool cleanUp) { Q_D(Database); + + if (!d->db.isOpen()) { + qWarning("Database is not open"); + return 0; + } + int rowsAffected = 0; foreach (TableSetBase *ts, d->tableSets) rowsAffected += ts->save(this, cleanUp); diff --git a/src/database.h b/src/database.h index 1885be5..58f611c 100644 --- a/src/database.h +++ b/src/database.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "defines.h" #include "tableset.h" @@ -39,11 +40,12 @@ class NUT_EXPORT Database : public QObject { Q_OBJECT +// QSharedDataPointer *_d; DatabasePrivate *d_ptr; Q_DECLARE_PRIVATE(Database) public: - explicit Database(QObject *parent = 0); + explicit Database(QObject *parent = nullptr); explicit Database(const Database &other); explicit Database(const QSqlDatabase &other); ~Database(); @@ -52,7 +54,7 @@ public: bool open(bool updateDatabase); void close(); - QSqlQuery exec(QString sql); + QSqlQuery exec(const QString& sql); int saveChanges(bool cleanUp = false); void cleanUp(); @@ -73,7 +75,8 @@ public: protected: //remove minor version - virtual void databaseUpdated(QString oldVersion, QString newVersion); + virtual void databaseCreated(); + virtual void databaseUpdated(int oldVersion, int newVersion); public slots: void setDatabaseName(QString databaseName); diff --git a/src/database_p.h b/src/database_p.h index cbe2379..d05d540 100644 --- a/src/database_p.h +++ b/src/database_p.h @@ -25,11 +25,12 @@ #include "databasemodel.h" #include +#include NUT_BEGIN_NAMESPACE class ChangeLogTable; -class DatabasePrivate +class DatabasePrivate //: public QSharedData { Database *q_ptr; Q_DECLARE_PUBLIC(Database) @@ -45,8 +46,6 @@ public: DatabaseModel getLastScheema(); bool getCurrectScheema(); -// bool checkClassInfo(const QMetaClassInfo &classInfo, -// QString &type, QString &name, QString &value); QSqlDatabase db; QString hostName; @@ -62,14 +61,14 @@ public: TableSet *changeLogs; - QT_DEPRECATED - QMap tables; static QMap allTableMaps; static qulonglong lastId; QSet tableSets; bool isDatabaseNew; + + QString errorMessage; }; NUT_END_NAMESPACE diff --git a/src/databasemodel.cpp b/src/databasemodel.cpp index 2a579f8..e9e3a13 100644 --- a/src/databasemodel.cpp +++ b/src/databasemodel.cpp @@ -31,13 +31,13 @@ QMap DatabaseModel::_models; #define NODE_VERSION "version" #define NODE_TABLES "tables" DatabaseModel::DatabaseModel(const QString &name) : - QList(), _databaseClassName(name), _version(QString()) + QList(), _databaseClassName(name), _version(0) { _models.insert(name, this); } DatabaseModel::DatabaseModel(const DatabaseModel &other) : - QList(other), _version(QString()) + QList(other), _version(0) { } @@ -45,7 +45,7 @@ DatabaseModel::DatabaseModel(const DatabaseModel &other) : DatabaseModel::DatabaseModel(const QJsonObject &json) : QList() { - setVersion(json.value(NODE_VERSION).toString()); + setVersion(json.value(NODE_VERSION).toInt()); QJsonObject tables = json.value(NODE_TABLES).toObject(); foreach (QString key, tables.keys()) { @@ -57,11 +57,7 @@ DatabaseModel::DatabaseModel(const QJsonObject &json) : } } -DatabaseModel::~DatabaseModel() -{ -} - -TableModel *DatabaseModel::tableByName(QString tableName) const +TableModel *DatabaseModel::tableByName(const QString &tableName) const { for(int i = 0; i < size(); i++){ TableModel *s = at(i); @@ -72,7 +68,7 @@ TableModel *DatabaseModel::tableByName(QString tableName) const // qWarning("Table with name '%s' not found in model", // qUtf8Printable(tableName)); - return 0; + return nullptr; } TableModel *DatabaseModel::tableByClassName(QString className) const @@ -86,7 +82,7 @@ TableModel *DatabaseModel::tableByClassName(QString className) const return s; } - return 0; + return nullptr; } bool DatabaseModel::operator ==(const DatabaseModel &other) const @@ -108,19 +104,19 @@ bool DatabaseModel::operator ==(const DatabaseModel &other) const return true; } -DatabaseModel DatabaseModel::operator +(const DatabaseModel &other) -{ - DatabaseModel model; - DatabaseModel::const_iterator i; +//DatabaseModel DatabaseModel::operator +(const DatabaseModel &other) +//{ +// DatabaseModel model; +// DatabaseModel::const_iterator i; - for (i = constBegin(); i != constEnd(); ++i) - model.append(*i); +// for (i = constBegin(); i != constEnd(); ++i) +// model.append(*i); - for (i = other.constBegin(); i != other.constEnd(); ++i) - model.append(*i); +// for (i = other.constBegin(); i != other.constEnd(); ++i) +// model.append(*i); - return model; -} +// return model; +//} QJsonObject DatabaseModel::toJson() const { @@ -148,13 +144,13 @@ RelationModel *DatabaseModel::relationByClassNames(const QString &masterClassNam TableModel *childTable = tableByClassName(childClassName); if(!childTable) - return 0; + return nullptr; foreach (RelationModel *rel, childTable->foregionKeys()) if(rel->masterClassName == masterClassName) return rel; - return 0; + return nullptr; } RelationModel *DatabaseModel::relationByTableNames(const QString &masterTableName, const QString &childTableName) @@ -162,20 +158,20 @@ RelationModel *DatabaseModel::relationByTableNames(const QString &masterTableNam TableModel *childTable = tableByName(childTableName); if(!childTable) - return 0; + return nullptr; foreach (RelationModel *rel, childTable->foregionKeys()) if(rel->masterTable->name() == masterTableName) return rel; - return 0; + return nullptr; } DatabaseModel DatabaseModel::fromJson(QJsonObject &json) { DatabaseModel model; - model.setVersion(json.value(NODE_VERSION).toString()); + model.setVersion(json.value(NODE_VERSION).toInt()); QJsonObject tables = json.value(NODE_TABLES).toObject(); foreach (QString key, tables.keys()) { @@ -188,12 +184,12 @@ DatabaseModel DatabaseModel::fromJson(QJsonObject &json) return model; } -QString DatabaseModel::version() const +int DatabaseModel::version() const { return _version; } -void DatabaseModel::setVersion(QString version) +void DatabaseModel::setVersion(int version) { _version = version; } @@ -236,7 +232,44 @@ void DatabaseModel::deleteAllModels() // qDeleteAll(i.value()); } // qDeleteAll(_models.values()); - _models.clear(); + _models.clear(); +} + +DatabaseModel operator +(const DatabaseModel &l, const DatabaseModel &r) +{ + DatabaseModel model; + DatabaseModel::const_iterator i; + + for (i = r.constBegin(); i != r.constEnd(); ++i) + model.append(*i); + + for (i = l.constBegin(); i != l.constEnd(); ++i) + model.append(*i); + + return model; +} + +DatabaseModel operator |(const DatabaseModel &l, const DatabaseModel &r) +{ + DatabaseModel ret; + DatabaseModel::const_iterator i; + QSet tables; + + for (i = r.constBegin(); i != r.constEnd(); ++i) { + if (tables.contains((*i)->name())) + continue; + ret.append(*i); + tables.insert((*i)->name()); + } + + for (i = l.constBegin(); i != l.constEnd(); ++i) { + if (tables.contains((*i)->name())) + continue; + ret.append(*i); + tables.insert((*i)->name()); + } + + return ret; } NUT_END_NAMESPACE diff --git a/src/databasemodel.h b/src/databasemodel.h index 6c73a3a..41a97e5 100644 --- a/src/databasemodel.h +++ b/src/databasemodel.h @@ -36,16 +36,16 @@ struct RelationModel; class DatabaseModel : public QList { QString _databaseClassName; - QString _version; + int _version; static QMap _models; public: DatabaseModel(const QString &name = QString()); DatabaseModel(const DatabaseModel &other); DatabaseModel(const QJsonObject &json); - ~DatabaseModel(); + ~DatabaseModel() = default; - TableModel *tableByName(QString tableName) const; + TableModel *tableByName(const QString &tableName) const; TableModel *tableByClassName(QString className) const; RelationModel *relationByClassNames(const QString &masterClassName, @@ -54,15 +54,15 @@ public: const QString &childClassName); bool operator==(const DatabaseModel &other) const; - DatabaseModel operator +(const DatabaseModel &other); +// DatabaseModel operator +(const DatabaseModel &other); Q_DECL_DEPRECATED static DatabaseModel fromJson(QJsonObject &json); QJsonObject toJson() const; operator QJsonObject(); - QString version() const; - void setVersion(QString version); + int version() const; + void setVersion(int version); bool remove(const QString &tableName); @@ -73,6 +73,9 @@ public: static void deleteAllModels(); }; +DatabaseModel operator +(const DatabaseModel &l, const DatabaseModel &r); +DatabaseModel operator |(const DatabaseModel &l, const DatabaseModel &r); + NUT_END_NAMESPACE #endif // DATABASEMODEL_H diff --git a/src/defines.h b/src/defines.h index 348ff6e..22efbb0 100644 --- a/src/defines.h +++ b/src/defines.h @@ -45,6 +45,83 @@ Q_CLASSINFO(__nut_NAME_PERFIX type #name #value, \ type "\n" #name "\n" value) +#define NUT_FIELD_PERFIX +#define NUT_FIELD_POSTFIX Field + +// Database +#define NUT_DB_VERSION(version) \ + NUT_INFO(__nut_DB_VERSION, version, 0) + +#define NUT_DECLARE_TABLE(type, name) \ + NUT_INFO(__nut_TABLE, type, name) \ + Q_PROPERTY(NUT_WRAP_NAMESPACE(TableSet) name READ name) \ + NUT_WRAP_NAMESPACE(TableSet) *m_##name; \ + public: \ + static const type *_##name; \ + NUT_WRAP_NAMESPACE(TableSet) *name() const \ + { return m_##name; } \ + private: + +//Table +#define NUT_DECLARE_FIELD(type, name, read, write) \ + Q_PROPERTY(type name READ read WRITE write) \ + NUT_INFO(__nut_FIELD, name, 0) \ + type m_##name; \ +public: \ + static NUT_WRAP_NAMESPACE(FieldPhrase)& name ## Field(){ \ + static NUT_WRAP_NAMESPACE(FieldPhrase) f = \ + NUT_WRAP_NAMESPACE(FieldPhrase) \ + (staticMetaObject.className(), #name); \ + return f; \ + } \ + 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(Nut::Row name READ read WRITE write) \ + NUT_DECLARE_FIELD(keytype, name##Id, read##Id, write##Id) \ + NUT_INFO(__nut_FOREGION_KEY, name, type) \ + Nut::Row m_##name; \ +public: \ + Nut::Row read() const { return m_##name ; } \ + void write(Nut::Row name){ \ + m_##name = name; \ + } + +#define NUT_DECLARE_CHILD_TABLE(type, n) \ + private: \ + NUT_WRAP_NAMESPACE(TableSet) *m_##n; \ + public: \ + static type *n##Table(); \ + NUT_WRAP_NAMESPACE(TableSet) *n(); + +#define NUT_IMPLEMENT_CHILD_TABLE(class, type, n) \ + type *class::n##Table(){ \ + static auto f = new type(); \ + return f; \ + } \ + NUT_WRAP_NAMESPACE(TableSet) *class::n(){ \ + return m_##n; \ + } + +#define NUT_FIELD(name) NUT_INFO(__nut_FIELD, name, 0) +#define NUT_PRIMARY_KEY(x) NUT_INFO(__nut_PRIMARY_KEY, x, 0) +#define NUT_AUTO_INCREMENT(x) NUT_INFO(__nut_AUTO_INCREMENT, x, 0) +#define NUT_PRIMARY_AUTO_INCREMENT(x) NUT_INFO(__nut_PRIMARY_KEY_AI, x, 0) +#define NUT_DISPLAY_NAME(field, name) NUT_INFO(__nut_DISPLAY, field, name) +#define NUT_UNIQUE(x) NUT_INFO(__nut_UNIQUE, x, 0) +#define NUT_LEN(field, len) NUT_INFO(__nut_LEN, field, len) +#define NUT_DEFAULT_VALUE(x, n) NUT_INFO(__nut_DEFAULT_VALUE, x, n) +#define NUT_NOT_NULL(x) NUT_INFO(__nut_NOT_NULL, x, 1) +#define NUT_INDEX(name, field, order) + +NUT_BEGIN_NAMESPACE + inline bool nutClassInfo(const QMetaClassInfo &classInfo, QString &type, QString &name, QVariant &value) { @@ -117,76 +194,57 @@ inline bool nutClassInfoInt(const QMetaClassInfo &classInfo, } } +#ifdef NUT_SHARED_POINTER +template +using RowList = QList>; -// Database -#define NUT_DB_VERSION(version) \ - NUT_INFO(__nut_DB_VERSION, version, 0) +template +using RowSet = QSet>; -#define NUT_DECLARE_TABLE(type, name) \ - NUT_INFO(__nut_TABLE, type, name) \ - Q_PROPERTY(NUT_WRAP_NAMESPACE(TableSet) name READ name) \ - NUT_WRAP_NAMESPACE(TableSet) *m_##name; \ - public: \ - static const type *_##name; \ - NUT_WRAP_NAMESPACE(TableSet) *name() const \ - { return m_##name; } \ - private: +template +using Row = QSharedPointer; -//Table -#define NUT_DECLARE_FIELD(type, name, read, write) \ - Q_PROPERTY(type name READ read WRITE write) \ - NUT_INFO(__nut_FIELD, name, 0) \ - type m_##name; \ -public: \ - static NUT_WRAP_NAMESPACE(FieldPhrase)& name ## Field(){ \ - static NUT_WRAP_NAMESPACE(FieldPhrase) f = \ - NUT_WRAP_NAMESPACE(FieldPhrase) \ - (staticMetaObject.className(), #name); \ - return f; \ - } \ - type read() const{ \ - return m_##name; \ - } \ - void write(type name){ \ - m_##name = name; \ - propertyChanged(#name); \ - } +template +inline Row create() { + return QSharedPointer(new T); +} -#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) \ - NUT_INFO(__nut_FOREGION_KEY, name, type) \ - type *m_##name; \ -public: \ - type *read() const { return m_##name ; } \ - void write(type *name){ \ - m_##name = name; \ - } +template +inline T *get(T *row) { + return row; +} +template +inline T *get(const QSharedPointer row) { + return row.data(); +} -#define NUT_DECLARE_CHILD_TABLE(type, n) \ - private: \ - NUT_WRAP_NAMESPACE(TableSet) *m_##n; \ - public: \ - static type *n##Table(); \ - NUT_WRAP_NAMESPACE(TableSet) *n(); +#else +template +using RowList = QList; -#define NUT_IMPLEMENT_CHILD_TABLE(class, type, n) \ - type *class::n##Table(){ \ - static type *f = new type(); \ - return f; \ - } \ - NUT_WRAP_NAMESPACE(TableSet) *class::n(){ \ - return m_##n; \ - } +template +using RowSet = QSet; -#define NUT_PRIMARY_KEY(x) NUT_INFO(__nut_PRIMARY_KEY, x, 0) -#define NUT_AUTO_INCREMENT(x) NUT_INFO(__nut_AUTO_INCREMENT, x, 0) -#define NUT_PRIMARY_AUTO_INCREMENT(x) NUT_INFO(__nut_PRIMARY_KEY_AI, x, 0) -#define NUT_DISPLAY_NAME(field, name) NUT_INFO(__nut_DISPLAY, field, name) -#define NUT_UNIQUE(x) NUT_INFO(__nut_UNIQUE, x, 0) -#define NUT_LEN(field, len) NUT_INFO(__nut_LEN, field, len) -#define NUT_DEFAULT_VALUE(x, n) NUT_INFO(__nut_DEFAULT_VALUE, x, n) -#define NUT_NOT_NULL(x) NUT_INFO(__nut_NOT_NULL, x, 1) -#define NUT_INDEX(name, field, order) +template +using Row = T*; + +template +inline Row create() { + return new T; +} + +template +inline T *get(const Row row) { + return row; +} + +template +inline T *get(const QSharedPointer row) { + return row.data(); +} + +#endif + +NUT_END_NAMESPACE #endif // SYNTAX_DEFINES_H diff --git a/src/generators/mysqlgenerator.cpp b/src/generators/mysqlgenerator.cpp index fb279fa..c33af38 100644 --- a/src/generators/mysqlgenerator.cpp +++ b/src/generators/mysqlgenerator.cpp @@ -23,6 +23,15 @@ #include #include +#include +#include +#include +#ifdef QT_GUI_LIB +#include +#include +#endif + +#include "sqlserializer.h" NUT_BEGIN_NAMESPACE @@ -36,52 +45,71 @@ QString MySqlGenerator::fieldType(FieldModel *field) QString dbType; switch (field->type) { - case QVariant::Bool: - dbType = "BOOLEAN"; - break; - 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; - case QVariant::Int: - dbType = "INT(4)"; + case QMetaType::Bool: return "BOOLEAN"; + case QMetaType::Char: + case QMetaType::QChar: return "CHAR(1)"; + case QMetaType::SChar: + case QMetaType::UChar: return "TINYINT"; + case QMetaType::Short: + case QMetaType::UShort: return "SMALLINT"; + case QMetaType::UInt: + case QMetaType::Int: + dbType = "INT"; if(field->isAutoIncrement) dbType += " AUTO_INCREMENT"; - break; - case QVariant::String: + case QMetaType::Long: + case QMetaType::ULong: + case QMetaType::LongLong: + case QMetaType::ULongLong: + return "BIGINT"; + + case QMetaType::Float: + return "FLOAT"; + + case QMetaType::Double: + return "REAL"; + + case QMetaType::QBitArray: return "VARBINARY"; + case QMetaType::QByteArray: return "BLOB"; + case QMetaType::QDate: return "DATE"; + case QMetaType::QTime: return "TIME"; + case QMetaType::QDateTime: return "DATETIME"; + + case QMetaType::QString: if(field->length) dbType = QString("VARCHAR(%1)").arg(field->length); else dbType = "TEXT"; break; - case QVariant::Point: - case QVariant::PointF: - dbType = "POINT"; - break; - case QVariant::Polygon: - case QVariant::PolygonF: - dbType = "POLYGON"; - break; + case QMetaType::QPolygon: + case QMetaType::QPolygonF: +// dbType = "POLYGON"; +// break; - case QVariant::Uuid: - dbType = "VARCHAR(64)"; - break; + case QMetaType::QUuid: +// dbType = "VARCHAR(64)"; +// break; + + case QMetaType::QPoint: + case QMetaType::QPointF: +// dbType = "POINT"; +// break; + case QMetaType::QSize: + case QMetaType::QSizeF: + case QMetaType::QRect: + case QMetaType::QRectF: + case QMetaType::QLine: + case QMetaType::QLineF: + case QMetaType::QColor: + case QMetaType::QUrl: + case QMetaType::QJsonArray: + case QMetaType::QJsonValue: + case QMetaType::QJsonObject: + case QMetaType::QJsonDocument: + case QMetaType::QStringList: return "TEXT"; default: qWarning("Type %s::%s(%d) is not supported", @@ -91,38 +119,146 @@ QString MySqlGenerator::fieldType(FieldModel *field) dbType = QString(); } - if(field->typeName == QStringLiteral("Nut::DbGeography")) - dbType = "GEOMETRY"; +// if(field->typeName == QStringLiteral("Nut::DbGeography")) +// dbType = "GEOMETRY"; return dbType; } QString MySqlGenerator::escapeValue(const QVariant &v) const { - switch (v.type()) { - case QVariant::Point: { - QPoint pt = v.toPoint(); - return QString("GeomFromText('POINT(%1 %2)',0)").arg(pt.x()).arg(pt.y()); - } + if (v.type() == QVariant::Bool) + return v.toBool() ? "1" : "0"; - case QVariant::PointF: { - QPointF pt = v.toPointF(); - return QString("GeomFromText('POINT(%1 %2)',0)").arg(pt.x()).arg(pt.y()); - } + if (v.type() == QVariant::Time) + return "'" + v.toTime().toString("HH:mm:ss") + "'"; - default: + if (v.type() == QVariant::Date) + return "'" + v.toDate().toString("yyyy-MM-dd") + "'"; + + if (v.type() == QVariant::DateTime) + return "'" + v.toDateTime().toString("yyyy-MM-dd HH:mm:ss") + "'"; + +//#ifdef QT_GUI_LIB +// if (v.type() == QVariant::Polygon) { +// QString ret; +// QPoint pt; +// QPolygon pol = v.value(); +// for (int i = 0; i < pol.size(); ++i) { +// pt = pol.at(i); +// if (!ret.isEmpty()) +// ret.append(", "); +// ret.append(QString::number(pt.x()) + " " + QString::number(pt.y())); +// } + +// return "GeomFromText('POLYGON(" + ret + "))')"; +// } +// if (v.type() == QVariant::PolygonF) { +// QString ret; +// QPointF pt; +// QPolygonF pol = v.value(); +// for (int i = 0; i < pol.size(); ++i) { +// pt = pol.at(i); +// if (!ret.isEmpty()) +// ret.append("),("); +// ret.append(QString::number(pt.x()) + " " + QString::number(pt.y())); +// } +// return "GeomFromText('POLYGON(" + ret + "))')"; +// } +//#endif +// switch (v.type()) { +// case QMetaType::QPoint: { +// QPoint pt = v.toPoint(); +// return QString("GeomFromText('POINT(%1 %2)',0)").arg(pt.x()).arg(pt.y()); +// } + +// case QMetaType::QPointF: { +// QPointF pt = v.toPointF(); +// return QString("GeomFromText('POINT(%1 %2)',0)").arg(pt.x()).arg(pt.y()); +// } + +// default: return SqlGeneratorBase::escapeValue(v); - } +// } } -QVariant MySqlGenerator::readValue(const QVariant::Type &type, const QVariant &dbValue) +QVariant MySqlGenerator::unescapeValue(const QMetaType::Type &type, const QVariant &dbValue) { - if (type == QVariant::PointF) { - qDebug() << "QVariant::PointF" << dbValue; - } - return SqlGeneratorBase::readValue(type, dbValue); + +//#ifdef QT_GUI_LIB +// if (type == QMetaType::QPolygon) { +// qDebug() << "p=" << dbValue; +// QString p; +// QString ref = dbValue.toString(); +// QPolygon pol; +// if (!readInsideParentese(ref, p)) +// return pol; +// QStringList parts = p.split(","); +// foreach (QString v, parts) { +// QList l = _serializer->toListInt(p.trimmed(), " "); +// if (l.count() != 2) +// return QPolygon(); +// pol.append(QPoint(l.at(0), l.at(1))); +// } +// return pol; +// } +// if (type == QMetaType::QPolygonF) { +// QString p; +// QString ref = dbValue.toString(); +// QPolygonF pol; +// if (!readInsideParentese(ref, p)) +// return pol; + +// QStringList parts = p.split(","); +// foreach (QString v, parts) { +// QList l = _serializer->toListReal(p.trimmed(), " "); +// if (l.count() != 2) +// return QPolygonF(); +// pol.append(QPointF(l.at(0), l.at(1))); +// } +// return pol; +// } +//#endif + + if (type == QMetaType::QDateTime) + return dbValue.toDateTime(); + + if (type == QMetaType::QTime) + return dbValue.toTime(); + + if (type == QMetaType::QDate) + return dbValue.toDate(); + + return SqlGeneratorBase::unescapeValue(type, dbValue); } +bool MySqlGenerator::readInsideParentese(QString &text, QString &out) +{ + int start = -1; + int end = -1; + int pc = 0; + for (int i = 0; i < text.length(); ++i) { + QChar ch = text.at(i); + + if (ch == '(') { + if (start == -1) + start = i; + pc++; + } + if (ch == ')') { + pc--; + + if (!pc && end == -1) + end = i; + } + if (start != -1 && end != -1){ + out = text.mid(start + 1, end - start - 1); + text = text.mid(end + 1); + return true; + } + } + return false; +} //QString MySqlGenerator::phrase(const PhraseData *d) const //{ // if (d->operatorCond == PhraseData::Distance) { diff --git a/src/generators/mysqlgenerator.h b/src/generators/mysqlgenerator.h index c5916ba..847b653 100644 --- a/src/generators/mysqlgenerator.h +++ b/src/generators/mysqlgenerator.h @@ -33,9 +33,11 @@ public: QString fieldType(FieldModel *field); QString escapeValue(const QVariant &v) const; - QVariant readValue(const QVariant::Type &type, const QVariant &dbValue); + QVariant unescapeValue(const QMetaType::Type &type, const QVariant &dbValue); // QString phrase(const PhraseData *d) const; -// QString selectCommand(AgregateType t, QString agregateArg, QString tableName, QList &wheres, QList &orders, QList joins, int skip, int take); + // QString selectCommand(AgregateType t, QString agregateArg, QString tableName, QList &wheres, QList &orders, QList joins, int skip, int take); +private: + bool readInsideParentese(QString &text, QString &out); }; NUT_END_NAMESPACE diff --git a/src/generators/postgresqlgenerator.cpp b/src/generators/postgresqlgenerator.cpp index 722fd1d..a6d368d 100644 --- a/src/generators/postgresqlgenerator.cpp +++ b/src/generators/postgresqlgenerator.cpp @@ -18,12 +18,60 @@ ** **************************************************************************/ +#include +#include +#ifdef QT_GUI_LIB +#include +#include +#endif +#include +#include + #include "postgresqlgenerator.h" #include "../table.h" #include "../tablemodel.h" +#include "sqlserializer.h" NUT_BEGIN_NAMESPACE +bool PostgreSqlGenerator::readInsideParentese(QString &text, QString &out) +{ + int start = -1; + int end = -1; + int pc = 0; + for (int i = 0; i < text.length(); ++i) { + QChar ch = text.at(i); + + if (ch == '(') { + if (start == -1) + start = i; + pc++; + } + if (ch == ')') { + pc--; + + if (!pc && end == -1) + end = i; + } + if (start != -1 && end != -1){ + out = text.mid(start + 1, end - start - 1); + text = text.mid(end + 1); + return true; + } + } + return false; +} + +bool PostgreSqlGenerator::isPostGisType(const QVariant::Type &t) const +{ + return t == QVariant::Point + || t == QVariant::PointF + || t == QVariant::Rect + || t == QVariant::RectF + || t == QVariant::Polygon + || t == QVariant::PolygonF; +} + PostgreSqlGenerator::PostgreSqlGenerator(Database *parent) : SqlGeneratorBase (parent) { @@ -34,70 +82,109 @@ QString PostgreSqlGenerator::fieldType(FieldModel *field) QString dbType; switch (field->type) { - case QVariant::Bool: + case QMetaType::Bool: dbType = "BOOLEAN"; break; - case QVariant::ByteArray: + + case QMetaType::QBitArray: + case QMetaType::QByteArray: dbType = "BYTEA"; break; - case QVariant::Date: + case QMetaType::QDate: dbType = "DATE"; break; - case QVariant::DateTime: + case QMetaType::QDateTime: dbType = "TIMESTAMP"; break; - case QVariant::Time: + case QMetaType::QTime: dbType = "TIME"; break; - case QVariant::Int: - case QVariant::UInt: + case QMetaType::SChar: + case QMetaType::UChar: + case QMetaType::Short: + case QMetaType::UShort: + dbType = "SMALLINT"; + break; + + case QMetaType::Float: + dbType = "FLOAT"; + break; + + case QMetaType::Double: + dbType = "REAL"; + break; + + case QMetaType::Int: + case QMetaType::UInt: if(field->isAutoIncrement) dbType = "SERIAL"; else dbType = "INTEGER"; break; - case QVariant::ULongLong: - case QVariant::LongLong: + case QMetaType::Long: + case QMetaType::ULong: + case QMetaType::LongLong: + case QMetaType::ULongLong: if(field->isAutoIncrement) dbType = "BIGSERIAL"; else dbType = "BIGINT"; break; - case QVariant::Double: - dbType = "REAL"; - break; - case QVariant::String: + case QMetaType::Char: + case QMetaType::QChar: + return "CHAR(1)"; + + case QMetaType::QString: if(field->length) dbType = QString("VARCHAR(%1)").arg(field->length); else dbType = "TEXT"; break; - case QVariant::Point: - case QVariant::PointF: + case QMetaType::QPoint: + case QMetaType::QPointF: dbType="POINT"; break; - case QVariant::Uuid: + case QMetaType::QUuid: dbType = "UUID"; break; - case QVariant::Polygon: - case QVariant::PolygonF: + case QMetaType::QPolygon: + case QMetaType::QPolygonF: dbType = "POLYGON"; break; + case QMetaType::QLine: + case QMetaType::QLineF: + return "LINE"; + + case QMetaType::QRect: + case QMetaType::QRectF: + return "BOX"; + + case QMetaType::QJsonArray: + case QMetaType::QJsonValue: + case QMetaType::QJsonObject: + case QMetaType::QJsonDocument: + return "JSON"; + + case QMetaType::QStringList: + return "TEXT[]"; + + case QMetaType::QSize: + case QMetaType::QSizeF: + case QMetaType::QUrl: + case QMetaType::QColor: + return "TEXT"; + default: - qDebug() << "Type for " << (int)field->type << field->type << "(" << QMetaType::typeName(field->type) << ")" << "nut supported"; dbType = QString(); } - if(field->type == (unsigned)QMetaType::type("Nut::DbGeography")) - dbType = "GEOGRAPHY"; - return dbType; } @@ -122,11 +209,134 @@ QString PostgreSqlGenerator::diff(FieldModel *oldField, FieldModel *newField) return sql; } -void PostgreSqlGenerator::replaceTableNames(QString &command) +QString PostgreSqlGenerator::escapeValue(const QVariant &v) const { - foreach (TableModel *m, TableModel::allModels()) - command = command - .replace("[" + m->className() + "]", m->name() ); + if (v.type() == QVariant::Time) + return "'" + v.toTime().toString("HH:mm:ss") + "'"; + + if (v.type() == QVariant::Date) + return "'" + v.toDate().toString("yyyy-MM-dd") + "'"; + + if (v.type() == QVariant::DateTime) + return "'" + v.toDateTime().toString("yyyy-MM-dd HH:mm:ss") + "'"; + + if (v.type() == QVariant::StringList) + return "'{" + v.toStringList().join(",") + "}'"; + + if (v.type() == QVariant::Point) { + QPoint pt = v.toPoint(); + return QString("point(%1, %2)").arg(pt.x()).arg(pt.y()); + } + if (v.type() == QVariant::PointF) { + QPointF pt = v.toPointF(); + return QString("point(%1, %2)").arg(pt.x()).arg(pt.y()); + } + if (v.userType() == QMetaType::QJsonDocument) { + return "'" + QString(v.toJsonDocument().toJson(QJsonDocument::Compact)) + "'"; + } + +#ifdef QT_GUI_LIB + if (v.type() == QVariant::Polygon) { + QString ret; + QPoint pt; + QPolygon pol = v.value(); + for (int i = 0; i < pol.size(); ++i) { + pt = pol.at(i); + if (!ret.isEmpty()) + ret.append("),("); + ret.append(QString::number(pt.x()) + ", " + QString::number(pt.y())); + } + return "'((" + ret + "))'"; + } + if (v.type() == QVariant::PolygonF) { + QString ret; + QPointF pt; + QPolygonF pol = v.value(); + for (int i = 0; i < pol.size(); ++i) { + pt = pol.at(i); + if (!ret.isEmpty()) + ret.append("),("); + ret.append(QString::number(pt.x()) + ", " + QString::number(pt.y())); + } + return "'((" + ret + "))'"; + } +#endif + + return SqlGeneratorBase::escapeValue(v); +} + +QVariant PostgreSqlGenerator::unescapeValue(const QMetaType::Type &type, const QVariant &dbValue) +{ + if (type == QMetaType::QDateTime) + return dbValue.toDateTime(); + + if (type == QMetaType::QTime) + return dbValue.toTime(); + + if (type == QMetaType::QDate) + return dbValue.toDate(); + + if (type == QMetaType::QPoint) + return SqlGeneratorBase::unescapeValue(QMetaType::QPoint, dbValue.toString() + .replace("(", "").replace(")", "")); + if (type == QMetaType::QPointF) + return SqlGeneratorBase::unescapeValue(QMetaType::QPointF, dbValue.toString() + .replace("(", "").replace(")", "")); + if (type == QMetaType::QStringList) + return dbValue.toString().replace("{", "").replace("}", "") + .split(","); + +#ifdef QT_GUI_LIB + if (type == QMetaType::QPolygon) { + QString p; + QString ref = dbValue.toString(); + QPolygon pol; + if (!readInsideParentese(ref, p)) + return pol; + + ref = p; + while (readInsideParentese(ref, p)) { + QList l = _serializer->toListInt(p); + if (l.count() != 2) + return QPolygon(); + pol.append(QPoint(l.at(0), l.at(1))); + } + return pol; + } + if (type == QMetaType::QPolygonF) { + QString p; + QString ref = dbValue.toString(); + QPolygonF pol; + if (!readInsideParentese(ref, p)) + return pol; + + ref = p; + while (readInsideParentese(ref, p)) { + QList l = _serializer->toListReal(p); + if (l.count() != 2) + return QPolygonF(); + pol.append(QPointF(l.at(0), l.at(1))); + } + return pol; + } +#endif + return SqlGeneratorBase::unescapeValue(type, dbValue); +} + +QString PostgreSqlGenerator::createConditionalPhrase(const PhraseData *d) const +{ + if (!d) + return QString(); + + if (d->type == PhraseData::WithVariant) { + if (isPostGisType(d->operand.type()) && d->operatorCond == PhraseData::Equal) { + return QString("%1 ~= %2") + .arg(SqlGeneratorBase::createConditionalPhrase(d->left), + escapeValue(d->operand)); + } + } + + return SqlGeneratorBase::createConditionalPhrase(d); } NUT_END_NAMESPACE diff --git a/src/generators/postgresqlgenerator.h b/src/generators/postgresqlgenerator.h index 5bc6c98..bf637f1 100644 --- a/src/generators/postgresqlgenerator.h +++ b/src/generators/postgresqlgenerator.h @@ -28,14 +28,24 @@ NUT_BEGIN_NAMESPACE class PostgreSqlGenerator : public SqlGeneratorBase { +private: + bool readInsideParentese(QString &ref, QString &out); + bool isPostGisType(const QVariant::Type &t) const; public: - explicit PostgreSqlGenerator(Database *parent); + explicit PostgreSqlGenerator(Database *parent = nullptr); - QString fieldType(FieldModel *field); + QString fieldType(FieldModel *field) override; - QString diff(FieldModel *oldField, FieldModel *newField); + QString diff(FieldModel *oldField, FieldModel *newField) override; - void replaceTableNames(QString &command); + // SqlGeneratorBase interface +public: + QString escapeValue(const QVariant &v) const override; + QVariant unescapeValue(const QMetaType::Type &type, const QVariant &dbValue) override; + + // SqlGeneratorBase interface +protected: + QString createConditionalPhrase(const PhraseData *d) const override; }; NUT_END_NAMESPACE diff --git a/src/generators/sqlgeneratorbase.cpp b/src/generators/sqlgeneratorbase.cpp index 4a404af..0f9590d 100644 --- a/src/generators/sqlgeneratorbase.cpp +++ b/src/generators/sqlgeneratorbase.cpp @@ -31,6 +31,7 @@ #include "../table.h" #include "../databasemodel.h" #include "../tablemodel.h" +#include "sqlserializer.h" NUT_BEGIN_NAMESPACE @@ -51,15 +52,28 @@ NUT_BEGIN_NAMESPACE * INNER JOIN dbo.GiftCards ON dbo.GiftTypes.GiftTypeID = dbo.GiftCards.GiftTypeID * INNER JOIN dbo.Entities ON dbo.GiftCards.GiftCardID = dbo.Entities.GiftCardID */ +bool SqlGeneratorBase::isNumeric(const QMetaType::Type &type) +{ + return type == QMetaType::SChar + || type == QMetaType::Char + || type == QMetaType::UChar + || type == QMetaType::Short + || type == QMetaType::UShort + || type == QMetaType::Int + || type == QMetaType::UInt + || type == QMetaType::Long + || type == QMetaType::ULong + || type == QMetaType::LongLong + || type == QMetaType::ULongLong; +} + SqlGeneratorBase::SqlGeneratorBase(Database *parent) - : QObject((QObject *)parent) + : QObject(parent) { if (parent) _database = parent; -} -SqlGeneratorBase::~SqlGeneratorBase() -{ + _serializer = new SqlSerializer; } QString SqlGeneratorBase::masterDatabaseName(QString databaseName) @@ -104,36 +118,65 @@ QString SqlGeneratorBase::recordsPhrase(TableModel *table) foreach (FieldModel *f, table->fields()) { if (!ret.isEmpty()) ret.append(", "); - ret.append(QString("%1.%2 AS [%1.%2]").arg(table->name()).arg(f->name)); + ret.append(QString("%1.%2 AS \"%1.%2\"").arg(table->name(), f->name)); } return ret; } +QString SqlGeneratorBase::insertBulk(const QString &tableName, const PhraseList &ph, const QList &vars) +{ + QString sql; + foreach (QVariantList list, vars) { + QStringList values; + foreach (QVariant v, list) + values.append(escapeValue(v)); + + if (!sql.isEmpty()) + sql.append(", "); + sql.append("(" + values.join(", ") + ")"); + } + sql = "INSERT INTO " + tableName + "(" + createFieldPhrase(ph) + + ") VALUES" + sql; + + removeTableNames(sql); + return sql; +} + QString SqlGeneratorBase::fieldDeclare(FieldModel *field) { - return field->name + " " + fieldType(field) + (field->notNull ? " NOT NULL" : ""); + QString type = fieldType(field); + if (type.isEmpty()) + return type; + return field->name + " " + type + (field->notNull ? " NOT NULL" : ""); +} + +QStringList SqlGeneratorBase::constraints(TableModel *table) +{ + Q_UNUSED(table); + return QStringList(); } QString SqlGeneratorBase::relationDeclare(const RelationModel *relation) { return QString("FOREIGN KEY (FK_%1) REFERENCES %2(%1)") - .arg(relation->localColumn) - .arg(relation->slaveTable->name()); + .arg(relation->localColumn, relation->slaveTable->name()); } -QStringList SqlGeneratorBase::diff(DatabaseModel lastModel, - DatabaseModel newModel) +QStringList SqlGeneratorBase::diff(const DatabaseModel &lastModel, + const DatabaseModel &newModel) { QStringList ret; - DatabaseModel unionModel = lastModel + newModel; + DatabaseModel unionModel = lastModel | newModel; + DatabaseModel::iterator i; - foreach (TableModel *table, unionModel) { - TableModel *oldTable = lastModel.tableByName(table->name()); - TableModel *newTable = newModel.tableByName(table->name()); - QString sql = diff(oldTable, newTable); + for (i = unionModel.begin(); i != unionModel.end(); ++i) { + TableModel *oldTable = lastModel.tableByName((*i)->name()); + TableModel *newTable = newModel.tableByName((*i)->name()); + QStringList sql = diff(oldTable, newTable); if (!sql.isEmpty()) ret << sql; + // QString sqlRel = diffRelation(oldTable, newTable); // if (!sqlRel.isEmpty()) // ret << sqlRel; @@ -147,7 +190,7 @@ QString SqlGeneratorBase::diff(FieldModel *oldField, FieldModel *newField) QString sql = QString(); if (oldField && newField) if (*oldField == *newField) - return QString(); + return sql; if (!newField) { sql = "DROP COLUMN " + oldField->name; @@ -161,14 +204,17 @@ QString SqlGeneratorBase::diff(FieldModel *oldField, FieldModel *newField) return sql; } -QString SqlGeneratorBase::diff(TableModel *oldTable, TableModel *newTable) +QStringList SqlGeneratorBase::diff(TableModel *oldTable, TableModel *newTable) { + if (!newTable && !oldTable) + return QStringList(); + if (oldTable && newTable) if (*oldTable == *newTable) - return QString(); + return QStringList(); if (!newTable) - return "DROP TABLE " + oldTable->name(); + return QStringList() << ("DROP TABLE " + oldTable->name()); QList fieldNames; QList relations; @@ -196,10 +242,13 @@ QString SqlGeneratorBase::diff(TableModel *oldTable, TableModel *newTable) FieldModel *oldField = oldTable->field(fieldName); QString buffer = diff(oldField, newField); - if (!buffer.isNull()) + if (!buffer.isEmpty()) columnSql << buffer; } else { - columnSql << fieldDeclare(newField); + QString declare = fieldDeclare(newField); + if (declare.isEmpty()) + return QStringList() << declare; + columnSql << declare; } } // foreach (QString fieldName, relations) { @@ -217,25 +266,27 @@ QString SqlGeneratorBase::diff(TableModel *oldTable, TableModel *newTable) QString sql; if (oldTable) { sql = QString("ALTER TABLE %1 \n%2") - .arg(newTable->name()) - .arg(columnSql.join(",\n")); + .arg(newTable->name(), columnSql.join(",\n")); } else { - if (!newTable->primaryKey().isNull()) - columnSql << QString("CONSTRAINT pk_%1 PRIMARY KEY (%2)") - .arg(newTable->name()) - .arg(newTable->primaryKey()); + if (!newTable->primaryKey().isNull()) { + QString pkCon = primaryKeyConstraint(newTable); + if (!pkCon.isEmpty()) + columnSql << pkCon; + columnSql << constraints(newTable); + } sql = QString("CREATE TABLE %1 \n(%2)") - .arg(newTable->name()) - .arg(columnSql.join(",\n")); + .arg(newTable->name(), columnSql.join(",\n")); + } - return sql; + return QStringList() << sql; } -QString SqlGeneratorBase::diffRelation(TableModel *oldTable, TableModel *newTable) +QStringList SqlGeneratorBase::diffRelation(TableModel *oldTable, TableModel *newTable) { + QStringList ret; if (!newTable) - return QString(); + return ret; QList relations; @@ -251,25 +302,25 @@ QString SqlGeneratorBase::diffRelation(TableModel *oldTable, TableModel *newTabl QStringList columnSql; foreach (QString fieldName, relations) { RelationModel *newRelation = newTable->foregionKeyByField(fieldName); - RelationModel *oldRelation = 0; + RelationModel *oldRelation = nullptr; if (oldTable) oldRelation = oldTable->foregionKeyByField(fieldName); - QString buffer = diff(oldRelation, newRelation); - if (!buffer.isNull()) - columnSql << buffer; + QStringList buffer = diff(oldRelation, newRelation); + if (!buffer.isEmpty()) + columnSql << buffer.at(0); } if (columnSql.count()) - return "ALTER TABLE " + newTable->name() + "\n" - + columnSql.join(",\n"); - else - return QString(); + ret.append("ALTER TABLE " + newTable->name() + "\n" + + columnSql.join(",\n")); + return ret; } -QString SqlGeneratorBase::diff(RelationModel *oldRel, RelationModel *newRel) +QStringList SqlGeneratorBase::diff(RelationModel *oldRel, RelationModel *newRel) { + QStringList ret; /* CONSTRAINT FK_PersonOrder FOREIGN KEY (PersonID) REFERENCES Persons(PersonID) @@ -283,25 +334,23 @@ QString SqlGeneratorBase::diff(RelationModel *oldRel, RelationModel *newRel) .arg(newRelation->foreignColumn); */ if (!oldRel) - return QString("ADD CONSTRAINT FK_%1 FOREIGN KEY (%1) " + ret.append(QString("ADD CONSTRAINT FK_%1 FOREIGN KEY (%1) " "REFERENCES %2(%3)") - .arg(newRel->localColumn) - .arg(newRel->masterTable->name()) - .arg(newRel->foreignColumn); + .arg(newRel->localColumn, newRel->masterTable->name(), + newRel->foreignColumn)); if (!newRel) - return QString("ADD CONSTRAINT FK_%1 FOREIGN KEY (%1) " + ret.append(QString("ADD CONSTRAINT FK_%1 FOREIGN KEY (%1) " "REFERENCES %2(%3)") - .arg(oldRel->localColumn) - .arg(oldRel->masterTable->name()) - .arg(oldRel->foreignColumn); + .arg(oldRel->localColumn, oldRel->masterTable->name(), + oldRel->foreignColumn)); // if (*oldRel == *newRel) - return QString(); + return ret; } QString SqlGeneratorBase::join(const QString &mainTable, - const QList list, + const QList &list, QStringList *order) { QString ret = mainTable; @@ -309,19 +358,19 @@ QString SqlGeneratorBase::join(const QString &mainTable, for (i = list.begin(); i != list.end(); ++i) { if ((*i)->masterTable->name() == mainTable) { ret.append(QString(" INNER JOIN %3 ON %1.%2 = %3.%4") - .arg((*i)->masterTable->name()) - .arg((*i)->masterTable->primaryKey()) - .arg((*i)->slaveTable->name()) - .arg((*i)->localColumn)); + .arg((*i)->masterTable->name(), + (*i)->masterTable->primaryKey(), + (*i)->slaveTable->name(), + (*i)->localColumn)); if (order != Q_NULLPTR) order->append((*i)->slaveTable->name() + "." + (*i)->slaveTable->primaryKey()); } else { ret.append(QString(" INNER JOIN %3 ON %1.%2 = %3.%4") - .arg(mainTable) - .arg((*i)->localColumn) - .arg((*i)->masterTable->name()) - .arg((*i)->masterTable->primaryKey())); + .arg(mainTable, + (*i)->localColumn, + (*i)->masterTable->name(), + (*i)->masterTable->primaryKey())); if (order != Q_NULLPTR) order->append((*i)->masterTable->name() + "." + (*i)->masterTable->primaryKey()); @@ -359,10 +408,8 @@ QString SqlGeneratorBase::join(const QStringList &list, QStringList *order) if (rel) { //mainTable is master of table ret.append(QString(" INNER JOIN [%1] ON %4.%2 = %1.%3") - .arg(table) - .arg(rel->masterTable->primaryKey()) - .arg(rel->localColumn) - .arg(mainTable)); + .arg(table, rel->masterTable->primaryKey(), + rel->localColumn, mainTable)); if (order != Q_NULLPTR) order->append(mainTable + "." + rel->masterTable->primaryKey()); @@ -372,10 +419,8 @@ QString SqlGeneratorBase::join(const QStringList &list, QStringList *order) if (rel) { // table is master of mainTable ret.append(QString(" INNER JOIN [%1] ON %4.%2 = %1.%3") - .arg(table) - .arg(rel->localColumn) - .arg(rel->masterTable->primaryKey()) - .arg(mainTable)); + .arg(table, rel->localColumn, + rel->masterTable->primaryKey(), mainTable)); if (order != Q_NULLPTR) order->append(mainTable + "." + rel->localColumn); @@ -395,7 +440,9 @@ QString SqlGeneratorBase::join(const QStringList &list, QStringList *order) QString SqlGeneratorBase::insertRecord(Table *t, QString tableName) { QString sql = QString(); - QString key = t->isPrimaryKeyAutoIncrement() ? t->primaryKey() : QString(); + auto model = _database->model().tableByName(tableName); + + QString key = model->isPrimaryKeyAutoIncrement() ? model->primaryKey() : QString(); QStringList values; @@ -411,9 +458,7 @@ QString SqlGeneratorBase::insertRecord(Table *t, QString tableName) changedPropertiesText.append(s); } sql = QString("INSERT INTO %1 (%2) VALUES (%3)") - .arg(tableName) - .arg(changedPropertiesText) - .arg(values.join(", ")); + .arg(tableName, changedPropertiesText, values.join(", ")); removeTableNames(sql); @@ -423,7 +468,8 @@ QString SqlGeneratorBase::insertRecord(Table *t, QString tableName) QString SqlGeneratorBase::updateRecord(Table *t, QString tableName) { QString sql = QString(); - QString key = t->primaryKey(); + auto model = _database->model().tableByName(tableName); + QString key = model->primaryKey(); QStringList values; foreach (QString f, t->changedProperties()) @@ -431,10 +477,8 @@ QString SqlGeneratorBase::updateRecord(Table *t, QString tableName) 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()); + .arg(tableName, values.join(", "), + key, t->property(key.toUtf8().data()).toString()); removeTableNames(sql); @@ -443,10 +487,10 @@ QString SqlGeneratorBase::updateRecord(Table *t, QString tableName) QString SqlGeneratorBase::deleteRecord(Table *t, QString tableName) { + auto model = _database->model().tableByName(tableName); + QString key = model->primaryKey(); QString sql = QString("DELETE FROM %1 WHERE %2='%3'") - .arg(tableName) - .arg(t->primaryKey()) - .arg(t->primaryValue().toString()); + .arg(tableName, key, t->property(key.toUtf8().data()).toString()); replaceTableNames(sql); return sql; } @@ -457,27 +501,20 @@ QString SqlGeneratorBase::agregateText(const AgregateType &t, switch (t) { case Min: return "MIN(" + arg + ")"; - break; case Max: return "MAX(" + arg + ")"; - break; case Average: - return "AVERAGE(" + arg + ")"; - break; + return "AVG(" + arg + ")"; case Count: return "COUNT(" + arg + ")"; - break; case SignleField: return arg; - break; - - default: - return QString(); } + return QString(); // never reach } QString SqlGeneratorBase::fromTableText(const QString &tableName, @@ -492,10 +529,8 @@ QString SqlGeneratorBase::fromTableText(const QString &tableName, if (rel) { QString pk = _database->model().tableByName(tableName)->primaryKey(); tableNameText = QString("%1 INNER JOIN %2 ON (%1.%3 = %2.%4)") - .arg(tableName) - .arg(joinTableName) - .arg(pk) - .arg(rel->localColumn); + .arg(tableName, joinTableName, + pk, rel->localColumn); orderBy = tableName + "." + pk; } else { qWarning("Relation between table %s and class %s (%s) not exists!", @@ -509,7 +544,7 @@ QString SqlGeneratorBase::fromTableText(const QString &tableName, return tableNameText; } -QString SqlGeneratorBase::deleteRecords(QString tableName, QString where) +QString SqlGeneratorBase::deleteRecords(const QString &tableName, const QString &where) { QString sql = QString(); if (where.isEmpty() || where.isNull()) @@ -526,7 +561,7 @@ QString SqlGeneratorBase::selectCommand(const QString &tableName, const PhraseList &fields, const ConditionalPhrase &where, const PhraseList &order, - const QList joins, + const QList &joins, const int skip, const int take) { @@ -563,9 +598,9 @@ QString SqlGeneratorBase::selectCommand(const QString &tableName, if (orderText != "") sql.append(" ORDER BY " + orderText); - for (int i = 0; i < _database->model().count(); i++) - sql = sql.replace(_database->model().at(i)->className() + ".", - _database->model().at(i)->name() + "."); +// for (int i = 0; i < _database->model().count(); i++) +// sql = sql.replace(_database->model().at(i)->className() + ".", +// _database->model().at(i)->name() + "."); appendSkipTake(sql, skip, take); replaceTableNames(sql); @@ -664,9 +699,7 @@ QString SqlGeneratorBase::insertCommand(const QString &tableName, const Assignme values.append(escapeValue(d->operand)); } return QString("INSERT INTO %1 (%2) VALUES (%3);") - .arg(tableName) - .arg(fieldNames) - .arg(values); + .arg(tableName, fieldNames, values); } //QString SqlGeneratorBase::selectCommand(SqlGeneratorBase::AgregateType t, @@ -740,14 +773,14 @@ QString SqlGeneratorBase::insertCommand(const QString &tableName, const Assignme void SqlGeneratorBase::replaceTableNames(QString &command) { - foreach (TableModel *m, TableModel::allModels()) + foreach (TableModel *m, _database->model()) command = command - .replace("[" + m->className() + "]", "`" + m->name() + "`"); + .replace("[" + m->className() + "]", m->name()); } void SqlGeneratorBase::removeTableNames(QString &command) { - foreach (TableModel *m, TableModel::allModels()) + foreach (TableModel *m, _database->model()) command = command.replace("[" + m->className() + "].", ""); } @@ -792,65 +825,25 @@ void SqlGeneratorBase::removeTableNames(QString &command) QString SqlGeneratorBase::escapeValue(const QVariant &v) const { - switch (v.type()) { - case QVariant::Bool: - return v.toBool() ? "1" : "0"; - break; + if (v.type() == QVariant::String && v.toString().isEmpty()) + return "''"; - case QVariant::Int: - case QVariant::UInt: - case QVariant::ULongLong: - case QVariant::LongLong: - case QVariant::Double: - return v.toString(); - break; - - case QVariant::Uuid: - return "'" + v.toUuid().toString() + "'"; - break; - - case QVariant::Char: - case QVariant::String: - return "'" + v.toString() + "'"; - - case QVariant::DateTime: - return "'" + v.toDateTime().toString(Qt::ISODate) + "'"; - - case QVariant::Date: - return "'" + v.toDate().toString(Qt::ISODate) + "'"; - - case QVariant::Time: - return "'" + v.toTime().toString(Qt::ISODate) + "'"; - - case QVariant::StringList: - case QVariant::List: - return "('" + v.toStringList().join("', '") + "')"; - - case QVariant::Point: { - QPoint pt = v.toPoint(); - return QString("POINT(%1 %2)").arg(pt.x()).arg(pt.y()); + QString serialized = _serializer->serialize(v); + if (serialized.isEmpty()) { + qWarning("No field escape rule for: %s", v.typeName()); + return QString(); } - case QVariant::PointF: { - QPointF pt = v.toPointF(); - return QString("POINT(%1 %2)").arg(pt.x()).arg(pt.y()); - } + if (v.type() == QVariant::List) + return serialized; - case QVariant::Invalid: - qFatal("Invalud field value"); - return ""; - - default: - Q_UNREACHABLE(); - return QString(); - } + return "'" + serialized + "'"; } -QVariant SqlGeneratorBase::readValue(const QVariant::Type &type, +QVariant SqlGeneratorBase::unescapeValue(const QMetaType::Type &type, const QVariant &dbValue) { - Q_UNUSED(type); - return dbValue; + return _serializer->deserialize(dbValue.toString(), type); } QString SqlGeneratorBase::phrase(const PhraseData *d) const @@ -875,9 +868,6 @@ QString SqlGeneratorBase::phrase(const PhraseData *d) const case PhraseData::WithoutOperand: ret = phrase(d->left) + " " + operatorString(d->operatorCond); break; - - default: - ret = ""; } if (d->operatorCond == PhraseData::And || d->operatorCond == PhraseData::Or) @@ -957,6 +947,12 @@ void SqlGeneratorBase::appendSkipTake(QString &sql, int skip, int take) Q_UNUSED(take); } +QString SqlGeneratorBase::primaryKeyConstraint(const TableModel *table) const +{ + return QString("CONSTRAINT pk_%1 PRIMARY KEY (%2)") + .arg(table->name(), table->primaryKey()); +} + QString SqlGeneratorBase::createConditionalPhrase(const PhraseData *d) const { if (!d) @@ -968,7 +964,7 @@ QString SqlGeneratorBase::createConditionalPhrase(const PhraseData *d) const //apply not (!) if (d->isNot) { if (op < 20) - op = (PhraseData::Condition)((op + 10) % 20); + op = static_cast((op + 10) % 20); } switch (d->type) { case PhraseData::Field: @@ -978,22 +974,40 @@ QString SqlGeneratorBase::createConditionalPhrase(const PhraseData *d) const case PhraseData::WithVariant: if (op == PhraseData::AddYears) ret = QString("DATEADD(year, %1, %2)") - .arg(d->operand.toString()).arg(createConditionalPhrase(d->left)); + .arg(d->operand.toString(), createConditionalPhrase(d->left)); else if (op == PhraseData::AddMonths) ret = QString("DATEADD(month, %1, %2)") - .arg(d->operand.toString()).arg(createConditionalPhrase(d->left)); + .arg(d->operand.toString(), createConditionalPhrase(d->left)); else if (op == PhraseData::AddDays) ret = QString("DATEADD(day, %1, %2)") - .arg(d->operand.toString()).arg(createConditionalPhrase(d->left)); + .arg(d->operand.toString(), createConditionalPhrase(d->left)); else if (op == PhraseData::AddHours) ret = QString("DATEADD(hour, %1, %2)") - .arg(d->operand.toString()).arg(createConditionalPhrase(d->left)); + .arg(d->operand.toString(), createConditionalPhrase(d->left)); else if (op == PhraseData::AddMinutes) ret = QString("DATEADD(minute, %1, %2)") - .arg(d->operand.toString()).arg(createConditionalPhrase(d->left)); + .arg(d->operand.toString(), createConditionalPhrase(d->left)); else if (op == PhraseData::AddSeconds) ret = QString("DATEADD(second, %1, %2)") - .arg(d->operand.toString()).arg(createConditionalPhrase(d->left)); + .arg(d->operand.toString(), createConditionalPhrase(d->left)); + else if (op == PhraseData::DatePartYear) + ret = QString("DATEPART(year, %1)") + .arg(d->operand.toString()); + else if (op == PhraseData::DatePartMonth) + ret = QString("DATEPART(month, %1)") + .arg(d->operand.toString()); + else if (op == PhraseData::DatePartDay) + ret = QString("DATEPART(day, %1)") + .arg(d->operand.toString()); + else if (op == PhraseData::DatePartHour) + ret = QString("DATEPART(hour, %1)") + .arg(d->operand.toString()); + else if (op == PhraseData::DatePartMinute) + ret = QString("DATEPART(minute, %1)") + .arg(d->operand.toString()); + else if (op == PhraseData::DatePartMilisecond) + ret = QString("DATEPART(milisecond, %1)") + .arg(d->operand.toString()); else ret = createConditionalPhrase(d->left) + " " + operatorString(op) + " " + escapeValue(d->operand); @@ -1007,9 +1021,6 @@ QString SqlGeneratorBase::createConditionalPhrase(const PhraseData *d) const case PhraseData::WithoutOperand: ret = createConditionalPhrase(d->left) + " " + operatorString(op); break; - - default: - ret = ""; } if (d->operatorCond == PhraseData::And || d->operatorCond == PhraseData::Or) @@ -1069,7 +1080,6 @@ void SqlGeneratorBase::createInsertPhrase(const AssignmentPhraseList &ph, QStrin case PhraseData::Field: case PhraseData::WithoutOperand: - default: qFatal("Invalid insert command"); } } diff --git a/src/generators/sqlgeneratorbase_p.h b/src/generators/sqlgeneratorbase_p.h index 643d82a..9c9bf1a 100644 --- a/src/generators/sqlgeneratorbase_p.h +++ b/src/generators/sqlgeneratorbase_p.h @@ -27,6 +27,8 @@ #include "../phrase.h" //#include "../wherephrase.h" +class SqlSerializer; + NUT_BEGIN_NAMESPACE class Table; @@ -40,6 +42,11 @@ class SqlGeneratorBase : public QObject // Q_OBJECT Database *_database; +protected: + SqlSerializer *_serializer; + + bool isNumeric(const QMetaType::Type &type); + public: //TODO: remove this enum enum CommandType{ @@ -58,24 +65,38 @@ public: }; explicit SqlGeneratorBase(Database *parent); - virtual ~SqlGeneratorBase(); + virtual ~SqlGeneratorBase() = default; + + virtual bool supportPrimaryKey(const QMetaType::Type &type) { + Q_UNUSED(type); + return true; + } + virtual bool supportAutoIncrement(const QMetaType::Type &type) { + Q_UNUSED(type); + return true; + } + + //fields + virtual QString fieldType(FieldModel *field) = 0; + virtual QString fieldDeclare(FieldModel *field); + virtual QStringList constraints(TableModel *table); + virtual QString escapeValue(const QVariant &v) const; + virtual QVariant unescapeValue(const QMetaType::Type &type, const QVariant &dbValue); virtual QString masterDatabaseName(QString databaseName); virtual QString createTable(TableModel *table); - virtual QString fieldType(FieldModel *field) = 0; - virtual QString fieldDeclare(FieldModel *field); virtual QString relationDeclare(const RelationModel *relation); - virtual QStringList diff(DatabaseModel lastModel, DatabaseModel newModel); + virtual QStringList diff(const DatabaseModel &lastModel, const DatabaseModel &newModel); virtual QString diff(FieldModel *oldField, FieldModel *newField); - virtual QString diff(TableModel *oldTable, TableModel *newTable); - virtual QString diffRelation(TableModel *oldTable, TableModel *newTable); - virtual QString diff(RelationModel *oldRel, RelationModel *newRel); + virtual QStringList diff(TableModel *oldTable, TableModel *newTable); + virtual QStringList diffRelation(TableModel *oldTable, TableModel *newTable); + virtual QStringList diff(RelationModel *oldRel, RelationModel *newRel); virtual QString join(const QString &mainTable, - const QList list, + const QList &list, QStringList *order = Q_NULLPTR); virtual QString join(const QStringList &list, QStringList *order = Q_NULLPTR); @@ -83,16 +104,17 @@ public: virtual QString recordsPhrase(TableModel *table); + virtual QString insertBulk(const QString &tableName, const PhraseList &ph, const QList &vars); virtual QString insertRecord(Table *t, QString tableName); virtual QString updateRecord(Table *t, QString tableName); virtual QString deleteRecord(Table *t, QString tableName); - virtual QString deleteRecords(QString tableName, QString where); + virtual QString deleteRecords(const QString &tableName, const QString &where); virtual QString selectCommand(const QString &tableName, const PhraseList &fields, const ConditionalPhrase &where, const PhraseList &order, - const QList joins, + const QList &joins, const int skip = -1, const int take = -1); @@ -123,18 +145,13 @@ public: // virtual QString updateCommand(WherePhrase &phrase, QList &wheres, QString tableName); - virtual QString escapeValue(const QVariant &v) const; - virtual QVariant readValue(const QVariant::Type &type, const QVariant &dbValue); virtual QString phrase(const PhraseData *d) const; virtual QString operatorString(const PhraseData::Condition &cond) const; virtual void appendSkipTake(QString &sql, int skip = -1, int take = -1); - - - virtual void replaceTableNames(QString &command); - virtual void removeTableNames(QString &command); + virtual QString primaryKeyConstraint(const TableModel *table) const; protected: - QString createConditionalPhrase(const PhraseData *d) const; + virtual QString createConditionalPhrase(const PhraseData *d) const; QString createFieldPhrase(const PhraseList &ph); QString createOrderPhrase(const PhraseList &ph); void createInsertPhrase(const AssignmentPhraseList &ph, QString &fields, QString &values); diff --git a/src/generators/sqlitegenerator.cpp b/src/generators/sqlitegenerator.cpp index fa18978..e97687b 100644 --- a/src/generators/sqlitegenerator.cpp +++ b/src/generators/sqlitegenerator.cpp @@ -31,55 +31,178 @@ SqliteGenerator::SqliteGenerator(Database *parent) : SqlGeneratorBase(parent) QString SqliteGenerator::fieldType(FieldModel *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::Time: - dbType = "time"; - break; - case QVariant::Double: - dbType = "real"; - break; - case QVariant::Int: - dbType = "integer"; + case QMetaType::Bool: return "BOOLEAN"; + case QMetaType::QBitArray: + case QMetaType::QByteArray: return "BLOB"; + case QMetaType::QDate: return "DATE"; + case QMetaType::QDateTime: return "DATETIME"; + case QMetaType::QTime: return "TIME"; + case QMetaType::Double: return "DOUBLE"; + case QMetaType::Float: return "FLOAT"; + + case QMetaType::SChar: + case QMetaType::Char: return "TINYINT"; + case QMetaType::UChar: return "TINYINT UNSIGNED"; + case QMetaType::Short: return "SMALLINT"; + case QMetaType::UShort: return "SMALLINT UNSIGNED"; + case QMetaType::Int: return "INT"; + case QMetaType::UInt: return "INT UNSIGNED"; + case QMetaType::Long: return "MEDIUMINT"; + case QMetaType::ULong: return "MEDIUMINT UNSIGNED"; + case QMetaType::LongLong: return "BIGINT"; + case QMetaType::ULongLong: return "BIGINT UNSIGNED"; + + case QMetaType::QChar: return "NCHAR(1)"; + + case QMetaType::QUrl: + case QMetaType::QJsonArray: + case QMetaType::QJsonValue: + case QMetaType::QJsonObject: + case QMetaType::QJsonDocument: + case QMetaType::QPoint: + case QMetaType::QPointF: + case QMetaType::QSize: + case QMetaType::QSizeF: + case QMetaType::QLine: + case QMetaType::QLineF: + case QMetaType::QRect: + case QMetaType::QRectF: + case QMetaType::QPolygon: + case QMetaType::QPolygonF: + case QMetaType::QStringList: + case QMetaType::QColor: + case QMetaType::QUuid: return "TEXT"; + // if (field->isAutoIncrement) // dbType.append(" PRIMARY KEY AUTOINCREMENT"); - break; - case QVariant::String: - if(field->length) - dbType = QString("varchar(%1)").arg(field->length); - else - dbType = "text"; - break; - case QVariant::Uuid: - dbType = "text"; - break; - default: - dbType = QString(); - } - return dbType; + case QMetaType::QString: + if(field->length) + return QString("VARCHAR(%1)").arg(field->length); + else + return "TEXT"; + default: +// qWarning("The type (%s) does not supported", +// QMetaType::typeName(field->type)); + return QString(); + } } +QString SqliteGenerator::fieldDeclare(FieldModel *field) +{ + QString type = fieldType(field); + if (type.isEmpty()) + return type; + + if (isNumeric(field->type) && field->isPrimaryKey) { + type = "INTEGER PRIMARY KEY"; + if (field->isAutoIncrement) + type.append(" AUTOINCREMENT"); + } + + if (field->notNull) + type.append(" NOT NULL"); + + return field->name + " " + type; +} + +bool SqliteGenerator::supportAutoIncrement(const QMetaType::Type &type) +{ + return isNumeric(type); +} + + +QStringList SqliteGenerator::diff(TableModel *oldTable, TableModel *newTable) +{ + QStringList ret; + + if (oldTable && newTable) + if (*oldTable == *newTable) + return ret; + + QStringList newTableSql = SqlGeneratorBase::diff(nullptr, newTable); + + if (!newTable) + return QStringList() << "DROP TABLE " + oldTable->name(); + + if (!oldTable) + return newTableSql; + + QList fieldNames; + QList relations; + + foreach (FieldModel *f, oldTable->fields()) + if (!fieldNames.contains(f->name)) + fieldNames.append(f->name); + foreach (RelationModel *r, oldTable->foregionKeys()) + if (!relations.contains(r->localColumn)) + relations.append(r->localColumn); + + foreach (FieldModel *f, newTable->fields()) + if (!fieldNames.contains(f->name)) + fieldNames.append(f->name); + foreach (RelationModel *r, newTable->foregionKeys()) + if (!relations.contains(r->localColumn)) + relations.append(r->localColumn); + + QString columns; + foreach (FieldModel *f, oldTable->fields()) { + if (!newTable->field(f->name)) + continue; + + if (!columns.isEmpty()) + columns.append(", "); + columns.append(f->name); + } + + /* + ALTER TABLE sampleTable RENAME TO sqlitestudio_temp_table; + + CREATE TABLE sampleTable ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + t BIGINT, + m CHAR + ); + + INSERT INTO sampleTable ( + id, + t, + m + ) + SELECT id, + t, + m + FROM sqlitestudio_temp_table; + + DROP TABLE sqlitestudio_temp_table; + */ + + ret.append("ALTER TABLE " + newTable->name() + " RENAME TO sqlitestudio_temp_table;"); + ret.append(newTableSql); + ret.append(QString("INSERT INTO %1 ( %2 ) SELECT %2 FROM sqlitestudio_temp_table;") + .arg(newTable->name(), columns)); + ret.append("DROP TABLE sqlitestudio_temp_table;"); + return ret; +} void SqliteGenerator::appendSkipTake(QString &sql, int skip, int take) { if (take != -1 && skip != -1) sql.append(QString(" LIMIT %1 OFFSET %2") .arg(take) - .arg(skip)); + .arg(skip)); +} + +QString SqliteGenerator::primaryKeyConstraint(const TableModel *table) const +{ + Q_UNUSED(table); + return QString(); +// QString sql = QString("CONSTRAINT pk_%1 PRIMARY KEY (%2)") +// .arg(table->name()) +// .arg(table->primaryKey()); +// if (table->field(table->primaryKey())->isAutoIncrement) +// sql += " AUTOINCREMENT"; +// return sql; } NUT_END_NAMESPACE diff --git a/src/generators/sqlitegenerator.h b/src/generators/sqlitegenerator.h index 5969cde..402ae41 100644 --- a/src/generators/sqlitegenerator.h +++ b/src/generators/sqlitegenerator.h @@ -29,11 +29,18 @@ NUT_BEGIN_NAMESPACE class SqliteGenerator : public SqlGeneratorBase { public: - explicit SqliteGenerator(Database *parent = 0); + explicit SqliteGenerator(Database *parent = nullptr); - QString fieldType(FieldModel *field); + QString fieldType(FieldModel *field) override; + QString fieldDeclare(FieldModel *field) override; + + bool supportAutoIncrement(const QMetaType::Type &type) override; + + void appendSkipTake(QString &sql, int skip, int take) override; + + QString primaryKeyConstraint(const TableModel *table) const override; + QStringList diff(TableModel *oldTable, TableModel *newTable) override; - void appendSkipTake(QString &sql, int skip, int take); }; NUT_END_NAMESPACE diff --git a/src/generators/sqlservergenerator.cpp b/src/generators/sqlservergenerator.cpp index de788d8..3b1d2e9 100644 --- a/src/generators/sqlservergenerator.cpp +++ b/src/generators/sqlservergenerator.cpp @@ -45,10 +45,44 @@ QString SqlServerGenerator::fieldType(FieldModel *field) QString dbType; switch (field->type) { - case QVariant::Bool: + case QMetaType::Bool: dbType = "BIT"; break; - case QVariant::ByteArray: + + case QMetaType::Char: + case QMetaType::QChar: + dbType = "CHAR(1)"; + break; + + case QMetaType::SChar: + case QMetaType::UChar: + return "tinyint"; + + case QMetaType::Short: + case QMetaType::UShort: + return "smallint"; + + case QMetaType::UInt: + case QMetaType::Int: + dbType = "INT"; + if (field->isAutoIncrement) + dbType += " IDENTITY(1,1)"; + break; + + case QMetaType::Long: + case QMetaType::ULong: + case QMetaType::LongLong: + case QMetaType::ULongLong: + return "bigint"; + + case QMetaType::Float: + return "FLOAT(24)"; + + case QMetaType::Double: + return "REAL"; + + case QMetaType::QBitArray: + case QMetaType::QByteArray: dbType = "VARBINARY"; if (field->length) @@ -56,42 +90,51 @@ QString SqlServerGenerator::fieldType(FieldModel *field) else dbType.append(" (MAX)"); break; - case QVariant::Date: + case QMetaType::QDate: dbType = "DATE"; break; - case QVariant::DateTime: + case QMetaType::QDateTime: dbType = "DATETIME"; break; - case QVariant::Time: + case QMetaType::QTime: dbType = "TIME"; break; - case QVariant::Double: - dbType = "REAL"; - break; - case QVariant::Int: - dbType = "INT"; - if (field->isAutoIncrement) - dbType += " IDENTITY(1,1)"; - break; - case QVariant::Point: - case QVariant::PointF: + case QMetaType::QPoint: + case QMetaType::QPointF: dbType = "GEOMETRY"; break; - case QVariant::String: + case QMetaType::QString: if (field->length) dbType = QString("NVARCHAR(%1)").arg(field->length); else dbType = "NVARCHAR(MAX)"; break; - case QVariant::Uuid: + case QMetaType::QUuid: dbType = "UNIQUEIDENTIFIER"; break; + case QMetaType::QPolygon: + case QMetaType::QPolygonF: + case QMetaType::QSize: + case QMetaType::QSizeF: + case QMetaType::QRect: + case QMetaType::QRectF: + case QMetaType::QLine: + case QMetaType::QLineF: + case QMetaType::QColor: + case QMetaType::QStringList: + case QMetaType::QJsonArray: + case QMetaType::QJsonValue: + case QMetaType::QJsonObject: + case QMetaType::QJsonDocument: + case QMetaType::QUrl: + return "TEXT"; + default: - Q_UNREACHABLE(); +// Q_UNREACHABLE(); dbType = QString(); } @@ -103,7 +146,7 @@ QString SqlServerGenerator::diff(FieldModel *oldField, FieldModel *newField) QString sql = QString(); if (oldField && newField) if (*oldField == *newField) - return QString(); + return sql; if (!newField) { sql = "DROP COLUMN " + oldField->name; @@ -120,13 +163,15 @@ QString SqlServerGenerator::diff(FieldModel *oldField, FieldModel *newField) QString SqlServerGenerator::escapeValue(const QVariant &v) const { - if (v.type() == QVariant::String || v.type() == QVariant::Char) + auto mid = static_cast(v.userType()); + + if (mid == QMetaType::QString || mid == QMetaType::QChar) return "N'" + v.toString() + "'"; - else if (v.type() == QVariant::Point) { + else if (mid == QMetaType::QPoint) { QPoint pt = v.toPoint(); return QString("geography::POINT(%1, %2, 4326)").arg(pt.x()).arg( pt.y()); - } else if (v.type() == QVariant::PointF) { + } else if (mid == QMetaType::QPointF) { QPointF pt = v.toPointF(); return QString("geography::POINT(%1, %2, 4326)").arg(pt.x()).arg( pt.y()); diff --git a/src/generators/sqlservergenerator.h b/src/generators/sqlservergenerator.h index 6bf5204..0935e21 100644 --- a/src/generators/sqlservergenerator.h +++ b/src/generators/sqlservergenerator.h @@ -29,7 +29,7 @@ NUT_BEGIN_NAMESPACE class SqlServerGenerator : public SqlGeneratorBase { public: - explicit SqlServerGenerator(Database *parent = 0); + explicit SqlServerGenerator(Database *parent = nullptr); QString masterDatabaseName(QString databaseName); diff --git a/src/phrase.cpp b/src/phrase.cpp index 65b2691..d575daf 100644 --- a/src/phrase.cpp +++ b/src/phrase.cpp @@ -24,275 +24,9 @@ NUT_BEGIN_NAMESPACE -PhraseData::PhraseData() : - className(""), fieldName(""), - type(Field), operatorCond(NotAssign), - left(0), right(0), operand(QVariant::Invalid), isNot(false), parents(1) -{ } - -PhraseData::PhraseData(const char *className, const char *fieldName) : - className(className), fieldName(fieldName), - type(Field), operatorCond(NotAssign), - left(0), right(0), operand(QVariant::Invalid), isNot(false), parents(1) -{ } - -PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o) - : className(0), fieldName(0), - type(WithoutOperand), operatorCond(o), left(l), right(0), - isNot(false), parents(1) -{ - l->parents++; -} - -PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o, - PhraseData *r) - : className(0), fieldName(0), - type(WithOther), operatorCond(o), - left(l), right(r), - isNot(false), parents(1) -{ - l->parents++; - r->parents++; -} - -PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o, QVariant r) - : className(0), fieldName(0), - type(WithVariant), operatorCond(o), left(l), - right(0), operand(r), isNot(false), parents(1) -{ } - -PhraseData *PhraseData::operator =(PhraseData *other) -{ - other->parents++; - return other; -} - -PhraseData &PhraseData::operator =(PhraseData &other) -{ - other.parents++; - return other; -} - -QString PhraseData::toString() const -{ - return QString("[%1].%2").arg(className).arg(fieldName); -} - -PhraseData::~PhraseData() -{ -} - -void PhraseData::cleanUp() -{ -} - -void PhraseData::cleanUp(PhraseData *d) -{ - if (d->left) - cleanUp(d->left); - if (d->right) - cleanUp(d->right); -} - -AbstractFieldPhrase::AbstractFieldPhrase(PhraseData *d) : data(d) -{ } - -AbstractFieldPhrase::AbstractFieldPhrase(const char *className, - const char *fieldName) - :data(new PhraseData(className, fieldName)) -{ - qDebug() <<"AbstractFieldPhrase created"<parents++; - qDebug() <<"Copy ctor"<toString()<parents; -} - -AbstractFieldPhrase::AbstractFieldPhrase(AbstractFieldPhrase &&other) -{ - data = other.data; - data->parents++; - other.data = 0; -} - -AbstractFieldPhrase::~AbstractFieldPhrase() -{ - if (data) { - --data->parents; - if (data->parents <= 0) - delete data; - } -} - -PhraseList AbstractFieldPhrase::operator |(const AbstractFieldPhrase &other) -{ - return PhraseList(this, other); -} - -ConditionalPhrase AbstractFieldPhrase::isNull() -{ - return ConditionalPhrase(this, PhraseData::Null); -} -ConditionalPhrase AbstractFieldPhrase::operator ==(const ConditionalPhrase - &other) -{ - return ConditionalPhrase(this, PhraseData::Equal, - const_cast(other)); -} -#define AbstractFieldPhraseOperatorVariant(class, op, cond) \ -ConditionalPhrase class::operator op(const QVariant &other) \ -{ \ - return ConditionalPhrase(this, cond, other); \ -} - -AbstractFieldPhraseOperatorVariant(AbstractFieldPhrase, ==, PhraseData::Equal) -AbstractFieldPhraseOperatorVariant(AbstractFieldPhrase, !=, PhraseData::NotEqual) - -#define AbstractFieldPhraseOperatorField(op, cond) \ -ConditionalPhrase AbstractFieldPhrase::operator op(const AbstractFieldPhrase &other) \ -{ \ - return ConditionalPhrase(this, cond, other); \ -} - -AbstractFieldPhraseOperatorField(==, PhraseData::Equal) -AbstractFieldPhraseOperatorField(!=, PhraseData::NotEqual) -AbstractFieldPhraseOperatorField(< , PhraseData::Less) -AbstractFieldPhraseOperatorField(<=, PhraseData::LessEqual) -AbstractFieldPhraseOperatorField(> , PhraseData::Greater) -AbstractFieldPhraseOperatorField(>=, PhraseData::GreaterEqual) - -AbstractFieldPhrase AbstractFieldPhrase::operator !() -{ - - AbstractFieldPhrase f(data->className, data->fieldName); - f.data->isNot = !data->isNot; - return f; -} - -AssignmentPhrase AbstractFieldPhrase::operator =(const QVariant &other) -{ - return AssignmentPhrase(this, other); -} - -AssignmentPhrase AbstractFieldPhrase::operator <<(const QVariant &other) -{ - return AssignmentPhrase(this, other); -} - -PhraseList::PhraseList() : isValid(false) -{ - -} - -PhraseList::PhraseList(const PhraseList &other) : isValid(true) -{ - data = qMove(other.data); - const_cast(other).data.clear(); -} - -PhraseList::PhraseList(PhraseList &&other) -{ - data = other.data; -} - -PhraseList::PhraseList(const AbstractFieldPhrase &other) : isValid(true) -{ - data.append(other.data); - incAllDataParents(); -} - -PhraseList::PhraseList(const AbstractFieldPhrase *left, - const AbstractFieldPhrase &right) - : isValid(true) -{ - data.append(left->data); - data.append(right.data); - incAllDataParents(); -} - -PhraseList::PhraseList(PhraseList *left, PhraseList *right) : isValid(true) -{ -// data = qMove(left->data + right->data); - data.append(left->data); - data.append(right->data); -// left->data.clear(); -// right->data.clear(); -} - -PhraseList::PhraseList(PhraseList *left, const AbstractFieldPhrase *right) - : isValid(true) -{ - data.append(left->data); - data.append(right->data); - incAllDataParents(); -} - -PhraseList::~PhraseList() -{ -} - -PhraseList &PhraseList::operator =(const PhraseList &other) -{ - data.append(const_cast(other).data); - return *this; -} - -PhraseList PhraseList::operator |(const AbstractFieldPhrase &other) { - return PhraseList(this, &other); -} - -void PhraseList::incAllDataParents() -{ -// foreach (PhraseData *d, data) -// d->parents++; -} - -PhraseList PhraseList::operator |(PhraseList &other) { - return PhraseList(this, &other); -} - -AssignmentPhrase::AssignmentPhrase(PhraseData *d) : data(d) -{ - d->parents++; -} - -AssignmentPhrase::AssignmentPhrase(AbstractFieldPhrase *l, const QVariant r) -{ - - data = new PhraseData(l->data, PhraseData::Equal, r); -// l->data = 0; -} - -AssignmentPhrase::AssignmentPhrase(AbstractFieldPhrase *l, - const AssignmentPhrase *r) -{ - data = new PhraseData(l->data, PhraseData::Equal, r->data); - // l->data = 0; -} - -AssignmentPhrase::AssignmentPhrase(AssignmentPhrase *ph, const QVariant &v) -{ - data = new PhraseData(ph->data, PhraseData::Equal, v); -} - -//AssignmentPhrase::AssignmentPhrase(AssignmentPhrase &other) -//{ -// data = other.data; -// other.data = 0; -//} - -AssignmentPhrase::~AssignmentPhrase() -{ - if (data) - if (!--data->parents) - delete data; -} //AssignmentPhrase::AssignmentPhrase(AssignmentPhrase *l, const AssignmentPhrase *r) //{ @@ -300,278 +34,10 @@ AssignmentPhrase::~AssignmentPhrase() // qFatal("SS"); //} -AssignmentPhraseList AssignmentPhrase::operator &(const AssignmentPhrase &other) -{ - return AssignmentPhraseList(this, &other); -} -AssignmentPhraseList::AssignmentPhraseList() -{ -} -AssignmentPhraseList::AssignmentPhraseList(const AssignmentPhrase &l) -{ - data.append(l.data); - incAllDataParents(); -} -AssignmentPhraseList::AssignmentPhraseList(AssignmentPhraseList *l, - const AssignmentPhrase *r) -{ - data.append(l->data); - data.append(r->data); - incAllDataParents(); -} - -AssignmentPhraseList::AssignmentPhraseList(AssignmentPhrase *l, - const AssignmentPhrase *r) -{ - data.append(l->data); - data.append(r->data); - incAllDataParents(); -} - -AssignmentPhraseList::AssignmentPhraseList(const AssignmentPhrase &r, - const AssignmentPhrase &l) -{ - data.append(l.data); - data.append(r.data); - incAllDataParents(); -} - -AssignmentPhraseList AssignmentPhraseList::operator &(const AssignmentPhrase - &ph) -{ - return AssignmentPhraseList(this, &ph); -} - -AssignmentPhraseList::~AssignmentPhraseList() -{ - foreach (PhraseData *d, data) - if (!--d->parents) - delete d; -// qDeleteAll(data); - // data.clear(); -} - -void AssignmentPhraseList::incAllDataParents() -{ - foreach (PhraseData *d, data) - d->parents++; -} - -ConditionalPhrase::ConditionalPhrase() : data(0) -{ } - -ConditionalPhrase::ConditionalPhrase(const ConditionalPhrase &other) -{ - qDebug() << "************* ctor called:"; - data = other.data; - data->parents++; -// const_cast(other).data = 0; -} - -#ifdef Q_COMPILER_RVALUE_REFS -ConditionalPhrase::ConditionalPhrase(const ConditionalPhrase &&other) -{ - qDebug() << "************* ctor called:"; - this->data = qMove(other.data); -} -#endif - -ConditionalPhrase::ConditionalPhrase(const PhraseData *data) -{ - this->data = const_cast(data); - this->data->parents++; -} - -ConditionalPhrase::ConditionalPhrase(AbstractFieldPhrase *l, - PhraseData::Condition cond) -{ - data = new PhraseData(l->data, cond); -} - -ConditionalPhrase::ConditionalPhrase(AbstractFieldPhrase *l, - PhraseData::Condition cond, - const QVariant &v) -{ - data = new PhraseData(l->data, cond, v); -} - -ConditionalPhrase::ConditionalPhrase(AbstractFieldPhrase *l, - PhraseData::Condition cond, - const AbstractFieldPhrase &other) -{ - data = new PhraseData(l->data, cond, other.data); -} - -ConditionalPhrase::ConditionalPhrase(AbstractFieldPhrase *l, - PhraseData::Condition cond, - ConditionalPhrase &r) -{ - data = new PhraseData(l->data, cond, r.data); - r.data = 0; -} - -ConditionalPhrase::ConditionalPhrase(ConditionalPhrase *l, - PhraseData::Condition cond, - const AbstractFieldPhrase &r) -{ - data = new PhraseData(l->data, cond, r.data); - l->data = 0; -} - -ConditionalPhrase::ConditionalPhrase(ConditionalPhrase *l, - PhraseData::Condition cond, - const QVariant &r) -{ - data = new PhraseData(l->data, cond, r); - l->data = 0; -} - -ConditionalPhrase::ConditionalPhrase(ConditionalPhrase *l, - PhraseData::Condition cond, - ConditionalPhrase &r) -{ - data = new PhraseData(l->data, cond, r.data); - l->data = 0; - r.data = 0; -} - -ConditionalPhrase::~ConditionalPhrase() -{ - if (data) { - data->cleanUp(); - if (!--data->parents) - delete data; - } -} - -ConditionalPhrase &ConditionalPhrase::operator =(const ConditionalPhrase &other) -{ - data = other.data; - data->parents++; - return *this; -} - -ConditionalPhrase ConditionalPhrase::operator ==(const QVariant &other) -{ - return ConditionalPhrase(this, PhraseData::Equal, other); -} - -//ConditionalPhrase ConditionalPhrase::operator ==(const AbstractFieldPhrase &other) -//{ -// return ConditionalPhrase(this, PhraseData::Equal, other); -//} - -//ConditionalPhrase ConditionalPhrase::operator &&(const ConditionalPhrase &other) -//{ -// return ConditionalPhrase(this, PhraseData::And, -// const_cast(other)); -//} - -//ConditionalPhrase ConditionalPhrase::operator ||(const ConditionalPhrase &other) -//{ -// return ConditionalPhrase(this, PhraseData::Or, -// const_cast(other)); -//} - -#define DECLARE_CONDITIONALPHRASE_OPERATORS_IMPL(op, cond) \ -ConditionalPhrase operator op(const ConditionalPhrase &l, \ - const ConditionalPhrase &r) \ -{ \ - ConditionalPhrase p; \ - p.data = new PhraseData; \ - p.data->operatorCond = cond; \ - p.data->left = l.data; \ - p.data->right = r.data; \ - l.data->parents++; \ - r.data->parents++; \ - return p; \ -} \ -ConditionalPhrase operator op(const ConditionalPhrase &l, \ - ConditionalPhrase &&r) \ -{ \ - ConditionalPhrase p; \ - p.data = new PhraseData; \ - p.data->operatorCond = cond; \ - p.data->left = l.data; \ - p.data->right = r.data; \ - l.data->parents++; \ - r.data->parents++; \ - return p; \ -} \ -ConditionalPhrase operator op(ConditionalPhrase &&l, \ - const ConditionalPhrase &r) \ -{ \ - ConditionalPhrase p; \ - p.data = new PhraseData; \ - p.data->operatorCond = cond; \ - p.data->left = l.data; \ - p.data->right = r.data; \ - l.data->parents++; \ - r.data->parents++; \ - return p; \ -} \ -ConditionalPhrase operator op(ConditionalPhrase &&l, ConditionalPhrase &&r) \ -{ \ - ConditionalPhrase p; \ - p.data = new PhraseData; \ - p.data->operatorCond = cond; \ - p.data->left = l.data; \ - p.data->right = r.data; \ - l.data->parents++; \ - r.data->parents++; \ - return p; \ -} - -DECLARE_CONDITIONALPHRASE_OPERATORS_IMPL(==, PhraseData::Equal) -DECLARE_CONDITIONALPHRASE_OPERATORS_IMPL(||, PhraseData::Or) -DECLARE_CONDITIONALPHRASE_OPERATORS_IMPL(&&, PhraseData::And) - -ConditionalPhrase ConditionalPhrase::operator !() -{ - ConditionalPhrase f(data); - f.data->isNot = !data->isNot; - return f; -} - -PhraseDataList::PhraseDataList() : QList() -{ - -} - -PhraseDataList::PhraseDataList(const PhraseDataList &other) : QList() -{ - PhraseDataList &o = const_cast(other); - PhraseDataList::iterator i; - for (i = o.begin(); i != o.end(); ++i) - append(*i); -} - -void PhraseDataList::append(PhraseData *d) -{ - d->parents++; - QList::append(d); -} - -void PhraseDataList::append(QList &dl) -{ - foreach (PhraseData *d, dl) - d->parents++; - QList::append(dl); -} - -PhraseDataList::~PhraseDataList() -{ - QList::iterator i; - for (i = begin(); i != end(); ++i) { - (*i)->cleanUp(); - if (!--(*i)->parents) - delete *i; - } -} //AssignmentPhraseList operator &(const AssignmentPhrase &l, const AssignmentPhrase &r) //{ diff --git a/src/phrase.h b/src/phrase.h index c466d9d..95178ce 100644 --- a/src/phrase.h +++ b/src/phrase.h @@ -21,15 +21,16 @@ #ifndef PHRASE_H #define PHRASE_H -#include -#include -#include -#include -#ifdef Q_COMPILER_INITIALIZER_LISTS -# include -#endif - -#include "defines.h" +#include "phrases/conditionalphrase.h" +#include "phrases/abstractfieldphrase.h" +#include "phrases/fieldphrase.h" +#include "phrases/phraselist.h" +#include "phrases/assignmentphraselist.h" +#include "phrases/phrasedatalist.h" +#include "phrases/phrasedata.h" +#include "phrases/assignmentphrase.h" +#include "phrases/numericphrase.h" +#include "phrases/datephrase.h" NUT_BEGIN_NAMESPACE @@ -43,426 +44,29 @@ class AbstractFieldPhrase; class AssignmentPhrase; class PhraseList; -class PhraseData -{ -public: - enum Condition { - NotAssign = 0, - Equal, - Less, - LessEqual, - Null, - In, - Like, - - Not = 10, - NotEqual, - GreaterEqual, - Greater, - NotNull, - NotIn, - NotLike, - - And = 20, - Or, - - Add, - Minus, - Multiple, - Divide, - Mod, - - Between, - - //date and time - AddYears, - AddMonths, - AddDays, - AddHours, - AddMinutes, - AddSeconds -// // special types -// Distance - }; - - enum Type { Field, WithVariant, WithOther, WithoutOperand }; - - const char *className; - const char *fieldName; - - Type type; - - PhraseData *left; - PhraseData *right; - - QVariant operand; - Condition operatorCond; - bool isNot; - quint16 parents; - - PhraseData(); - PhraseData(const char *className, const char *fieldName); - PhraseData(PhraseData *l, Condition o); - PhraseData(PhraseData *l, Condition o, PhraseData *r); - PhraseData(PhraseData *l, Condition o, QVariant r); -// explicit PhraseData(const PhraseData &other); -// explicit PhraseData(const PhraseData *other); - - PhraseData *operator =(PhraseData *other); - PhraseData &operator =(PhraseData &other); - - QString toString() const; - - ~PhraseData(); - - void cleanUp(); -private: - void cleanUp(PhraseData *d); -}; - -class PhraseDataList : public QList -{ -public: - PhraseDataList(); - PhraseDataList(const PhraseDataList &other); - void append(PhraseData *d); - void append(QList &dl); - virtual ~PhraseDataList(); -}; - -class AssignmentPhraseList -{ -public: - QList data; - explicit AssignmentPhraseList(); - AssignmentPhraseList(const AssignmentPhrase &l); - AssignmentPhraseList(AssignmentPhraseList *l, const AssignmentPhrase *r); - AssignmentPhraseList(AssignmentPhrase *l, const AssignmentPhrase *r); - AssignmentPhraseList(const AssignmentPhrase &r, const AssignmentPhrase &l); - - AssignmentPhraseList operator &(const AssignmentPhrase &ph); - - ~AssignmentPhraseList(); - -private: - void incAllDataParents(); -}; - -class AssignmentPhrase -{ -public: - PhraseData *data; - explicit AssignmentPhrase(PhraseData *d); - explicit AssignmentPhrase(AbstractFieldPhrase *l, const QVariant r); - explicit AssignmentPhrase(AbstractFieldPhrase *l, const AssignmentPhrase *r); - explicit AssignmentPhrase(AssignmentPhrase *ph, const QVariant &v); -// explicit AssignmentPhrase(AssignmentPhrase &other); - ~AssignmentPhrase(); -// AssignmentPhrase(AssignmentPhrase *l, const AssignmentPhrase *r); - - AssignmentPhraseList operator &(const AssignmentPhrase &other); - -}; - //AssignmentPhraseList operator &(const AssignmentPhrase &l, const AssignmentPhrase &r); //AssignmentPhraseList operator &(const AssignmentPhrase &l, AssignmentPhrase &&r); //AssignmentPhraseList operator &(AssignmentPhrase &&l, const AssignmentPhrase &r); //AssignmentPhraseList operator &(AssignmentPhrase &&l, AssignmentPhrase &&r); -class PhraseList{ -public: - bool isValid; - PhraseDataList data; - explicit PhraseList(); - PhraseList(const PhraseList &other); - PhraseList(PhraseList &&other); - PhraseList(const AbstractFieldPhrase &other); - PhraseList(const AbstractFieldPhrase *left, const AbstractFieldPhrase &right); - PhraseList(PhraseList *left, PhraseList *right); - PhraseList(PhraseList *left, const AbstractFieldPhrase *right); - virtual ~PhraseList(); - PhraseList &operator =(const PhraseList &other); - PhraseList operator |(PhraseList &other); - PhraseList operator |(const AbstractFieldPhrase &other); +//ConditionalPhrase operator <(AbstractFieldPhrase &l, ConditionalPhrase &&other) +//{ +// return ConditionalPhrase(&l, PhraseData::Less, other); +//} -private: - void incAllDataParents(); -}; +//template +//class FieldPhrase : public AbstractFieldPhrase +//{ +//public: +// FieldPhrase(const char *className, const char *s) : +// AbstractFieldPhrase(className, s) +// {} -class ConditionalPhrase -{ -public: - PhraseData *data; -// QSharedPointer leftDataPointer; -// QSharedPointer rightDataPointer; - ConditionalPhrase(); - ConditionalPhrase(const ConditionalPhrase &other); -#ifdef Q_COMPILER_RVALUE_REFS - ConditionalPhrase(const ConditionalPhrase &&other); -#endif - ConditionalPhrase(const PhraseData *data); - ConditionalPhrase(AbstractFieldPhrase *, PhraseData::Condition); - ConditionalPhrase(AbstractFieldPhrase *, PhraseData::Condition, const QVariant &v); - ConditionalPhrase(AbstractFieldPhrase *, PhraseData::Condition, const AbstractFieldPhrase &v); - ConditionalPhrase(AbstractFieldPhrase *l, PhraseData::Condition cond, ConditionalPhrase &r); - ConditionalPhrase(ConditionalPhrase *l, PhraseData::Condition cond, const AbstractFieldPhrase &r); - ConditionalPhrase(ConditionalPhrase *l, PhraseData::Condition cond, const QVariant &r); - ConditionalPhrase(ConditionalPhrase *l, PhraseData::Condition cond, ConditionalPhrase &r); - virtual ~ConditionalPhrase(); - - ConditionalPhrase &operator =(const ConditionalPhrase &other); - ConditionalPhrase operator ==(const QVariant &other); -// ConditionalPhrase operator ==(const AbstractFieldPhrase &other); -// ConditionalPhrase operator &&(const ConditionalPhrase &other); -// ConditionalPhrase operator ||(const ConditionalPhrase &other); - ConditionalPhrase operator !(); - - SPECIALIZATION_NUMERIC_MEMBER(type, <, PhraseData::Less) - SPECIALIZATION_NUMERIC_MEMBER(type, <=, PhraseData::LessEqual) - SPECIALIZATION_NUMERIC_MEMBER(type, >, PhraseData::Greater) - SPECIALIZATION_NUMERIC_MEMBER(type, >=, PhraseData::GreaterEqual) -}; - -#define DECLARE_CONDITIONALPHRASE_OPERATORS(op) \ -ConditionalPhrase operator op(const ConditionalPhrase &l, const ConditionalPhrase &r); \ -ConditionalPhrase operator op(const ConditionalPhrase &l, ConditionalPhrase &&r); \ -ConditionalPhrase operator op(ConditionalPhrase &&l, const ConditionalPhrase &r); \ -ConditionalPhrase operator op(ConditionalPhrase &&l, ConditionalPhrase &&r); - -DECLARE_CONDITIONALPHRASE_OPERATORS(==) -DECLARE_CONDITIONALPHRASE_OPERATORS(&&) -DECLARE_CONDITIONALPHRASE_OPERATORS(||) - -class AbstractFieldPhrase -{ -public: - PhraseData *data; - explicit AbstractFieldPhrase(PhraseData *d); - AbstractFieldPhrase(const char *className, const char *fieldName); - AbstractFieldPhrase(const AbstractFieldPhrase &other); - AbstractFieldPhrase(AbstractFieldPhrase &&other); - - virtual ~AbstractFieldPhrase(); - - PhraseList operator |(const AbstractFieldPhrase &other); - - template - ConditionalPhrase in(QList list) - { - QVariantList vlist; - foreach (T t, list) - vlist.append(QVariant::fromValue(t)); - - return ConditionalPhrase(this, PhraseData::In, vlist); - } -#ifdef Q_COMPILER_INITIALIZER_LISTS - ConditionalPhrase in(std::initializer_list list) { - QVariantList vlist; - std::initializer_list::iterator it; - for (it = list.begin(); it != list.end(); ++it) - vlist.append(*it); - return ConditionalPhrase(this, PhraseData::In, vlist); - } -#endif - - ConditionalPhrase isNull(); - - ConditionalPhrase operator ==(const QVariant &other); - ConditionalPhrase operator ==(const ConditionalPhrase &other); - ConditionalPhrase operator !=(const QVariant &other); - - ConditionalPhrase operator ==(const AbstractFieldPhrase &other); - ConditionalPhrase operator !=(const AbstractFieldPhrase &other); - ConditionalPhrase operator <(const AbstractFieldPhrase &other); - ConditionalPhrase operator >(const AbstractFieldPhrase &other); - ConditionalPhrase operator <=(const AbstractFieldPhrase &other); - ConditionalPhrase operator >=(const AbstractFieldPhrase &other); - - AbstractFieldPhrase operator !(); - AssignmentPhrase operator =(const QVariant &other); - AssignmentPhrase operator <<(const QVariant &other); -}; - -template -class FieldPhrase : public AbstractFieldPhrase -{ -public: - FieldPhrase(const char *className, const char *s) : - AbstractFieldPhrase(className, s) - {} - - AssignmentPhrase operator =(const QVariant &other) { - return AssignmentPhrase(this, other); - } -}; - -template<> -class FieldPhrase : public AbstractFieldPhrase -{ -public: - FieldPhrase(const char *className, const char *s) : - AbstractFieldPhrase(className, s) - {} - - ConditionalPhrase like(const QString &term) { - return ConditionalPhrase(this, PhraseData::Like, term); - } - - AssignmentPhrase operator =(const QVariant &v) { - return AssignmentPhrase(this, v); - } -}; - -#define SPECIALIZATION_NUMERIC(type) \ -template<> \ -class FieldPhrase : public AbstractFieldPhrase \ -{ \ - public: \ - FieldPhrase(const char *className, const char *s) : \ - AbstractFieldPhrase(className, s) \ - {} \ - SPECIALIZATION_NUMERIC_MEMBER(type, <, PhraseData::Less) \ - SPECIALIZATION_NUMERIC_MEMBER(type, <=, PhraseData::LessEqual) \ - SPECIALIZATION_NUMERIC_MEMBER(type, >, PhraseData::Greater) \ - SPECIALIZATION_NUMERIC_MEMBER(type, >=, PhraseData::GreaterEqual) \ - SPECIALIZATION_NUMERIC_MEMBER(type, %, PhraseData::Mod) \ - \ - SPECIALIZATION_NUMERIC_MEMBER(type, +, PhraseData::Add) \ - SPECIALIZATION_NUMERIC_MEMBER(type, -, PhraseData::Minus) \ - SPECIALIZATION_NUMERIC_MEMBER(type, *, PhraseData::Multiple) \ - SPECIALIZATION_NUMERIC_MEMBER(type, /, PhraseData::Divide) \ - AssignmentPhrase operator =(const QVariant &other) { \ - return AssignmentPhrase(this, other); \ - } \ - ConditionalPhrase between(const QVariant &min, const QVariant &max) \ - { \ - return ConditionalPhrase(this, PhraseData::Between, \ - QVariantList() << min << max); \ - } \ -}; - -SPECIALIZATION_NUMERIC(qint8) -SPECIALIZATION_NUMERIC(qint16) -SPECIALIZATION_NUMERIC(qint32) -SPECIALIZATION_NUMERIC(qint64) - -SPECIALIZATION_NUMERIC(quint8) -SPECIALIZATION_NUMERIC(quint16) -SPECIALIZATION_NUMERIC(quint32) -SPECIALIZATION_NUMERIC(quint64) - -SPECIALIZATION_NUMERIC(qreal) - -//Date and time -#define CONDITIONAL_VARIANT_METHOD(name, cond) \ - ConditionalPhrase name(int val) \ - { \ - return ConditionalPhrase(this, cond, val); \ - } - -template<> -class FieldPhrase : public AbstractFieldPhrase -{ -public: - FieldPhrase(const char *className, const char *s) : - AbstractFieldPhrase(className, s) - {} - - AssignmentPhrase operator =(const bool &other) { - return AssignmentPhrase(this, other); - } - - FieldPhrase operator !() - { - FieldPhrase f(data->className, data->fieldName); -// f.data = new PhraseData(data); - f.data->isNot = !data->isNot; - return f; - } - - operator ConditionalPhrase() - { - return ConditionalPhrase(this, PhraseData::Equal, !data->isNot); - } -}; - -template<> -class FieldPhrase : public AbstractFieldPhrase -{ -public: - FieldPhrase(const char *className, const char *s) : - AbstractFieldPhrase(className, s) - {} - SPECIALIZATION_NUMERIC_MEMBER(type, <, PhraseData::Less) - SPECIALIZATION_NUMERIC_MEMBER(type, <=, PhraseData::LessEqual) - SPECIALIZATION_NUMERIC_MEMBER(type, >, PhraseData::Greater) - SPECIALIZATION_NUMERIC_MEMBER(type, >=, PhraseData::GreaterEqual) - AssignmentPhrase operator =(const QDate &other) { - return AssignmentPhrase(this, other); - } - ConditionalPhrase between(const QDate &min, const QDate &max) - { - return ConditionalPhrase(this, PhraseData::Between, - QVariantList() << min << max); - } - CONDITIONAL_VARIANT_METHOD(addYears, PhraseData::AddYears) - CONDITIONAL_VARIANT_METHOD(addMonths, PhraseData::AddMonths) - CONDITIONAL_VARIANT_METHOD(addDays, PhraseData::AddDays) -}; - -template<> -class FieldPhrase : public AbstractFieldPhrase -{ -public: - FieldPhrase(const char *className, const char *s) : - AbstractFieldPhrase(className, s) - {} - SPECIALIZATION_NUMERIC_MEMBER(type, <, PhraseData::Less) - SPECIALIZATION_NUMERIC_MEMBER(type, <=, PhraseData::LessEqual) - SPECIALIZATION_NUMERIC_MEMBER(type, >, PhraseData::Greater) - SPECIALIZATION_NUMERIC_MEMBER(type, >=, PhraseData::GreaterEqual) - AssignmentPhrase operator =(const QTime &other) { - return AssignmentPhrase(this, other); - } - ConditionalPhrase between(const QTime &min, const QTime &max) - { - return ConditionalPhrase(this, PhraseData::Between, - QVariantList() << min << max); - } - - CONDITIONAL_VARIANT_METHOD(addHours, PhraseData::AddHours) - CONDITIONAL_VARIANT_METHOD(addMinutes, PhraseData::AddMinutes) - CONDITIONAL_VARIANT_METHOD(addSeconds, PhraseData::AddSeconds) -}; - -template<> -class FieldPhrase : public AbstractFieldPhrase -{ -public: - FieldPhrase(const char *className, const char *s) : - AbstractFieldPhrase(className, s) - {} - SPECIALIZATION_NUMERIC_MEMBER(type, <, PhraseData::Less) - SPECIALIZATION_NUMERIC_MEMBER(type, <=, PhraseData::LessEqual) - SPECIALIZATION_NUMERIC_MEMBER(type, >, PhraseData::Greater) - SPECIALIZATION_NUMERIC_MEMBER(type, >=, PhraseData::GreaterEqual) - AssignmentPhrase operator =(const QDateTime &other) { - return AssignmentPhrase(this, other); - } - ConditionalPhrase between(const QDateTime &min, const QDateTime &max) - { - return ConditionalPhrase(this, PhraseData::Between, - QVariantList() << min << max); - } - CONDITIONAL_VARIANT_METHOD(addYears, PhraseData::AddYears) - CONDITIONAL_VARIANT_METHOD(addMonths, PhraseData::AddMonths) - CONDITIONAL_VARIANT_METHOD(addDays, PhraseData::AddDays) - - CONDITIONAL_VARIANT_METHOD(addHours, PhraseData::AddHours) - CONDITIONAL_VARIANT_METHOD(addMinutes, PhraseData::AddMinutes) - CONDITIONAL_VARIANT_METHOD(addSeconds, PhraseData::AddSeconds) -}; +// AssignmentPhrase operator =(const QVariant &other) { +// return AssignmentPhrase(this, other); +// } +//}; NUT_END_NAMESPACE diff --git a/src/phrases/abstractfieldphrase.cpp b/src/phrases/abstractfieldphrase.cpp new file mode 100644 index 0000000..616384e --- /dev/null +++ b/src/phrases/abstractfieldphrase.cpp @@ -0,0 +1,126 @@ +/************************************************************************** +** +** 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 "abstractfieldphrase.h" + +NUT_BEGIN_NAMESPACE + +AbstractFieldPhrase::AbstractFieldPhrase(PhraseData *d) : data(d) +{ } + +AbstractFieldPhrase::AbstractFieldPhrase(const char *className, + const char *fieldName) + :data(new PhraseData(className, fieldName)) +{ +} + +AbstractFieldPhrase::AbstractFieldPhrase(const AbstractFieldPhrase &other) +{ + data = other.data; + data->parents++; +} + +AbstractFieldPhrase::AbstractFieldPhrase(AbstractFieldPhrase &&other) +{ + data = other.data; + data->parents++; + other.data = nullptr; +} + +AbstractFieldPhrase::~AbstractFieldPhrase() +{ + if (data) { + --data->parents; + if (data->parents <= 0) + delete data; + } +} + +PhraseList AbstractFieldPhrase::operator |(const AbstractFieldPhrase &other) +{ + return PhraseList(this, other); +} + +ConditionalPhrase AbstractFieldPhrase::isNull() +{ + return ConditionalPhrase(this, PhraseData::Null); +} + + +ConditionalPhrase AbstractFieldPhrase::operator ==(const ConditionalPhrase + &other) +{ + return ConditionalPhrase(this, PhraseData::Equal, + const_cast(other)); +} + + +AssignmentPhrase AbstractFieldPhrase::operator =(const QVariant &other) +{ + return AssignmentPhrase(this, other); +} + +AssignmentPhrase AbstractFieldPhrase::operator =(const ConditionalPhrase &other) +{ + return AssignmentPhrase(new PhraseData(data, PhraseData::Equal, other.data)); +} + +AssignmentPhrase AbstractFieldPhrase::operator <<(const QVariant &other) +{ + return AssignmentPhrase(this, other); +} + +#define AbstractFieldPhraseOperatorVariant(class, op, cond) \ +ConditionalPhrase class::operator op(const QVariant &other) \ +{ \ + return ConditionalPhrase(this, cond, other); \ +} + +AbstractFieldPhraseOperatorVariant(AbstractFieldPhrase, ==, PhraseData::Equal) +AbstractFieldPhraseOperatorVariant(AbstractFieldPhrase, !=, PhraseData::NotEqual) + +#define AbstractFieldPhraseOperatorField(op, cond) \ +ConditionalPhrase AbstractFieldPhrase::operator op(const AbstractFieldPhrase &other) \ +{ \ + return ConditionalPhrase(this, cond, other); \ +} + +AbstractFieldPhraseOperatorField(==, PhraseData::Equal) +AbstractFieldPhraseOperatorField(!=, PhraseData::NotEqual) +AbstractFieldPhraseOperatorField(< , PhraseData::Less) +AbstractFieldPhraseOperatorField(<=, PhraseData::LessEqual) +AbstractFieldPhraseOperatorField(> , PhraseData::Greater) +AbstractFieldPhraseOperatorField(>=, PhraseData::GreaterEqual) + +AbstractFieldPhrase AbstractFieldPhrase::operator ~() +{ + AbstractFieldPhrase f(data->className, data->fieldName); + f.data->isNot = !data->isNot; + return f; +} + +AbstractFieldPhrase AbstractFieldPhrase::operator !() +{ + AbstractFieldPhrase f(data->className, data->fieldName); + f.data->isNot = !data->isNot; + return f; +} + +NUT_END_NAMESPACE diff --git a/src/phrases/abstractfieldphrase.h b/src/phrases/abstractfieldphrase.h new file mode 100644 index 0000000..2ddd7be --- /dev/null +++ b/src/phrases/abstractfieldphrase.h @@ -0,0 +1,88 @@ +/************************************************************************** +** +** 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 ABSTRACTFIELDPHRASE_H +#define ABSTRACTFIELDPHRASE_H + +#include "../defines.h" + +#include "assignmentphrase.h" +#include "conditionalphrase.h" +#include "phraselist.h" + +NUT_BEGIN_NAMESPACE + +class PhraseData; +class NUT_EXPORT AbstractFieldPhrase +{ +public: + PhraseData *data; + explicit AbstractFieldPhrase(PhraseData *d); + AbstractFieldPhrase(const char *className, const char *fieldName); + AbstractFieldPhrase(const AbstractFieldPhrase &other); + AbstractFieldPhrase(AbstractFieldPhrase &&other); + + virtual ~AbstractFieldPhrase(); + + PhraseList operator |(const AbstractFieldPhrase &other); + + template + ConditionalPhrase in(QList list) + { + QVariantList vlist; + foreach (T t, list) + vlist.append(QVariant::fromValue(t)); + + return ConditionalPhrase(this, PhraseData::In, vlist); + } +#ifdef Q_COMPILER_INITIALIZER_LISTS + ConditionalPhrase in(std::initializer_list list) { + QVariantList vlist; + std::initializer_list::iterator it; + for (it = list.begin(); it != list.end(); ++it) + vlist.append(*it); + return ConditionalPhrase(this, PhraseData::In, vlist); + } +#endif + + ConditionalPhrase isNull(); + + ConditionalPhrase operator ==(const QVariant &other); + ConditionalPhrase operator ==(const ConditionalPhrase &other); + //why? + ConditionalPhrase operator !=(const QVariant &other); + + ConditionalPhrase operator ==(const AbstractFieldPhrase &other); + ConditionalPhrase operator !=(const AbstractFieldPhrase &other); + ConditionalPhrase operator <(const AbstractFieldPhrase &other); + ConditionalPhrase operator >(const AbstractFieldPhrase &other); + ConditionalPhrase operator <=(const AbstractFieldPhrase &other); + ConditionalPhrase operator >=(const AbstractFieldPhrase &other); + + AbstractFieldPhrase operator ~(); + AbstractFieldPhrase operator !(); + AssignmentPhrase operator =(const QVariant &other); + AssignmentPhrase operator =(const ConditionalPhrase &other); + AssignmentPhrase operator <<(const QVariant &other); +}; + +NUT_END_NAMESPACE + +#endif // ABSTRACTFIELDPHRASE_H diff --git a/src/phrases/assignmentphrase.cpp b/src/phrases/assignmentphrase.cpp new file mode 100644 index 0000000..6b49426 --- /dev/null +++ b/src/phrases/assignmentphrase.cpp @@ -0,0 +1,64 @@ +/************************************************************************** +** +** 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 "abstractfieldphrase.h" +#include "assignmentphrase.h" +#include "phrasedata.h" + +NUT_BEGIN_NAMESPACE + +AssignmentPhrase::AssignmentPhrase(PhraseData *d) : data(d) +{ + d->parents++; +} + +AssignmentPhrase::AssignmentPhrase(AbstractFieldPhrase *l, const QVariant r) +{ + + data = new PhraseData(l->data, PhraseData::Equal, r); +// l->data = 0; +} + +AssignmentPhrase::AssignmentPhrase(AbstractFieldPhrase *l, + const AssignmentPhrase *r) +{ + data = new PhraseData(l->data, PhraseData::Equal, r->data); + // l->data = 0; +} + +AssignmentPhrase::AssignmentPhrase(AssignmentPhrase *ph, const QVariant &v) +{ + data = new PhraseData(ph->data, PhraseData::Equal, v); +} + +//AssignmentPhrase::AssignmentPhrase(AssignmentPhrase &other) +//{ +// data = other.data; +// other.data = 0; +//} + +AssignmentPhrase::~AssignmentPhrase() +{ + if (data) + if (!--data->parents) + delete data; +} + +NUT_END_NAMESPACE diff --git a/src/phrases/assignmentphrase.h b/src/phrases/assignmentphrase.h new file mode 100644 index 0000000..84f7b1c --- /dev/null +++ b/src/phrases/assignmentphrase.h @@ -0,0 +1,50 @@ +/************************************************************************** +** +** 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 ASSIGNMENTPHRASE_H +#define ASSIGNMENTPHRASE_H + +#include "../defines.h" + +#include "assignmentphraselist.h" + +NUT_BEGIN_NAMESPACE + +class PhraseData; +class AbstractFieldPhrase; +class NUT_EXPORT AssignmentPhrase +{ +public: + PhraseData *data; + explicit AssignmentPhrase(PhraseData *d); + explicit AssignmentPhrase(AbstractFieldPhrase *l, const QVariant r); + explicit AssignmentPhrase(AbstractFieldPhrase *l, const AssignmentPhrase *r); + explicit AssignmentPhrase(AssignmentPhrase *ph, const QVariant &v); +// explicit AssignmentPhrase(AssignmentPhrase &other); + ~AssignmentPhrase(); +// AssignmentPhrase(AssignmentPhrase *l, const AssignmentPhrase *r); + + AssignmentPhraseList operator &(const AssignmentPhrase &other); + +}; + +NUT_END_NAMESPACE + +#endif // ASSIGNMENTPHRASE_H diff --git a/src/phrases/assignmentphraselist.cpp b/src/phrases/assignmentphraselist.cpp new file mode 100644 index 0000000..b65c8fa --- /dev/null +++ b/src/phrases/assignmentphraselist.cpp @@ -0,0 +1,85 @@ +/************************************************************************** +** +** 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 "assignmentphraselist.h" +#include "phrasedata.h" +#include "assignmentphrase.h" + +NUT_BEGIN_NAMESPACE + + +AssignmentPhraseList AssignmentPhrase::operator &(const AssignmentPhrase &other) +{ + return AssignmentPhraseList(this, &other); +} + +AssignmentPhraseList::AssignmentPhraseList(const AssignmentPhrase &l) +{ + data.append(l.data); + incAllDataParents(); +} + +AssignmentPhraseList::AssignmentPhraseList(AssignmentPhraseList *l, + const AssignmentPhrase *r) +{ + data.append(l->data); + data.append(r->data); + incAllDataParents(); +} + +AssignmentPhraseList::AssignmentPhraseList(AssignmentPhrase *l, + const AssignmentPhrase *r) +{ + data.append(l->data); + data.append(r->data); + incAllDataParents(); +} + +AssignmentPhraseList::AssignmentPhraseList(const AssignmentPhrase &r, + const AssignmentPhrase &l) +{ + data.append(l.data); + data.append(r.data); + incAllDataParents(); +} + +AssignmentPhraseList AssignmentPhraseList::operator &(const AssignmentPhrase + &ph) +{ + return AssignmentPhraseList(this, &ph); +} + +AssignmentPhraseList::~AssignmentPhraseList() +{ + foreach (PhraseData *d, data) + if (!--d->parents) + delete d; +// qDeleteAll(data); + // data.clear(); +} + +void AssignmentPhraseList::incAllDataParents() +{ + foreach (PhraseData *d, data) + d->parents++; +} + + +NUT_END_NAMESPACE diff --git a/src/phrases/assignmentphraselist.h b/src/phrases/assignmentphraselist.h new file mode 100644 index 0000000..adb5399 --- /dev/null +++ b/src/phrases/assignmentphraselist.h @@ -0,0 +1,50 @@ +/************************************************************************** +** +** 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 ASSIGNMENTPHRASELIST_H +#define ASSIGNMENTPHRASELIST_H + +#include "../defines.h" + +NUT_BEGIN_NAMESPACE + +class PhraseData; +class AssignmentPhrase; +class NUT_EXPORT AssignmentPhraseList +{ +public: + QList data; + explicit AssignmentPhraseList() = default; + AssignmentPhraseList(const AssignmentPhrase &l); + AssignmentPhraseList(AssignmentPhraseList *l, const AssignmentPhrase *r); + AssignmentPhraseList(AssignmentPhrase *l, const AssignmentPhrase *r); + AssignmentPhraseList(const AssignmentPhrase &r, const AssignmentPhrase &l); + + AssignmentPhraseList operator &(const AssignmentPhrase &ph); + + ~AssignmentPhraseList(); + +private: + void incAllDataParents(); +}; + +NUT_END_NAMESPACE + +#endif // ASSIGNMENTPHRASELIST_H diff --git a/src/phrases/conditionalphrase.cpp b/src/phrases/conditionalphrase.cpp new file mode 100644 index 0000000..ec210b6 --- /dev/null +++ b/src/phrases/conditionalphrase.cpp @@ -0,0 +1,245 @@ +/************************************************************************** +** +** 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 "abstractfieldphrase.h" +#include "conditionalphrase.h" +#include "phrasedata.h" + +NUT_BEGIN_NAMESPACE + +ConditionalPhrase::ConditionalPhrase() : data(nullptr) +{ } + +ConditionalPhrase::ConditionalPhrase(const ConditionalPhrase &other) +{ + data = other.data; + data->parents++; +// const_cast(other).data = 0; +} + +#ifdef Q_COMPILER_RVALUE_REFS +ConditionalPhrase::ConditionalPhrase(const ConditionalPhrase &&other) +{ + this->data = qMove(other.data); +} +#endif + +ConditionalPhrase::ConditionalPhrase(const PhraseData *data) +{ + this->data = const_cast(data); + this->data->parents++; +} + +ConditionalPhrase::ConditionalPhrase(AbstractFieldPhrase *l, + PhraseData::Condition cond) +{ + data = new PhraseData(l->data, cond); +} + +ConditionalPhrase::ConditionalPhrase(AbstractFieldPhrase *l, + PhraseData::Condition cond, + const QVariant &v) +{ + data = new PhraseData(l->data, cond, v); +} + +ConditionalPhrase::ConditionalPhrase(AbstractFieldPhrase *l, + PhraseData::Condition cond, + const AbstractFieldPhrase &other) +{ + data = new PhraseData(l->data, cond, other.data); +} + +ConditionalPhrase::ConditionalPhrase(AbstractFieldPhrase *l, + PhraseData::Condition cond, + ConditionalPhrase &r) +{ + data = new PhraseData(l->data, cond, r.data); + r.data = nullptr; +} + +ConditionalPhrase::ConditionalPhrase(ConditionalPhrase *l, + PhraseData::Condition cond, + const AbstractFieldPhrase &r) +{ + data = new PhraseData(l->data, cond, r.data); + l->data = nullptr; +} + +ConditionalPhrase::ConditionalPhrase(ConditionalPhrase *l, + PhraseData::Condition cond, + const QVariant &r) +{ + data = new PhraseData(l->data, cond, r); + l->data = nullptr; +} + +ConditionalPhrase::ConditionalPhrase(ConditionalPhrase *l, + PhraseData::Condition cond, + ConditionalPhrase &r) +{ + data = new PhraseData(l->data, cond, r.data); + l->data = nullptr; + r.data = nullptr; +} + +ConditionalPhrase::~ConditionalPhrase() +{ + if (data) { + data->cleanUp(); + if (!--data->parents) + delete data; + } +} + +ConditionalPhrase &ConditionalPhrase::operator =(const ConditionalPhrase &other) +{ + data = other.data; + data->parents++; + return *this; +} + +ConditionalPhrase ConditionalPhrase::operator ==(const QVariant &other) +{ + return ConditionalPhrase(this, PhraseData::Equal, other); +} + +//ConditionalPhrase ConditionalPhrase::operator ==(const AbstractFieldPhrase &other) +//{ +// return ConditionalPhrase(this, PhraseData::Equal, other); +//} + +//ConditionalPhrase ConditionalPhrase::operator &&(const ConditionalPhrase &other) +//{ +// return ConditionalPhrase(this, PhraseData::And, +// const_cast(other)); +//} + +//ConditionalPhrase ConditionalPhrase::operator ||(const ConditionalPhrase &other) +//{ +// return ConditionalPhrase(this, PhraseData::Or, +// const_cast(other)); +//} + +#define DECLARE_CONDITIONALPHRASE_OPERATORS_IMPL(op, cond) \ +ConditionalPhrase operator op(const ConditionalPhrase &l, \ + const ConditionalPhrase &r) \ +{ \ + ConditionalPhrase p; \ + p.data = new PhraseData; \ + p.data->type = PhraseData::WithOther; \ + p.data->operatorCond = cond; \ + p.data->left = l.data; \ + p.data->right = r.data; \ + l.data->parents++; \ + r.data->parents++; \ + return p; \ +} \ +ConditionalPhrase operator op(const ConditionalPhrase &l, \ + ConditionalPhrase &&r) \ +{ \ + ConditionalPhrase p; \ + p.data = new PhraseData; \ + p.data->type = PhraseData::WithOther; \ + p.data->operatorCond = cond; \ + p.data->left = l.data; \ + p.data->right = r.data; \ + l.data->parents++; \ + r.data->parents++; \ + return p; \ +} \ +ConditionalPhrase operator op(ConditionalPhrase &&l, \ + const ConditionalPhrase &r) \ +{ \ + ConditionalPhrase p; \ + p.data = new PhraseData; \ + p.data->type = PhraseData::WithOther; \ + p.data->operatorCond = cond; \ + p.data->left = l.data; \ + p.data->right = r.data; \ + l.data->parents++; \ + r.data->parents++; \ + return p; \ +} \ +ConditionalPhrase operator op(ConditionalPhrase &&l, ConditionalPhrase &&r) \ +{ \ + ConditionalPhrase p; \ + p.data = new PhraseData; \ + p.data->type = PhraseData::WithOther; \ + p.data->operatorCond = cond; \ + p.data->left = l.data; \ + p.data->right = r.data; \ + l.data->parents++; \ + r.data->parents++; \ + return p; \ +} + +DECLARE_CONDITIONALPHRASE_OPERATORS_IMPL(==, PhraseData::Equal) +DECLARE_CONDITIONALPHRASE_OPERATORS_IMPL(||, PhraseData::Or) +DECLARE_CONDITIONALPHRASE_OPERATORS_IMPL(&&, PhraseData::And) + +ConditionalPhrase ConditionalPhrase::operator !() +{ + ConditionalPhrase f(data); + f.data->isNot = !data->isNot; + return f; +} + +ConditionalPhrase operator <(AbstractFieldPhrase &l, ConditionalPhrase &&r) +{ + return ConditionalPhrase(&l, PhraseData::Less, r); +} + +ConditionalPhrase operator <=(AbstractFieldPhrase &l, ConditionalPhrase &&r) +{ + return ConditionalPhrase(&l, PhraseData::LessEqual, r); +} + +ConditionalPhrase operator >(AbstractFieldPhrase &l, ConditionalPhrase &&r) +{ + return ConditionalPhrase(&l, PhraseData::Greater, r); +} + +ConditionalPhrase operator >=(AbstractFieldPhrase &l, ConditionalPhrase &&r) +{ + return ConditionalPhrase(&l, PhraseData::GreaterEqual, r); +} + +ConditionalPhrase operator <(ConditionalPhrase &&l, ConditionalPhrase &&r) +{ + return ConditionalPhrase(&l, PhraseData::Less, r); +} + +ConditionalPhrase operator <=(ConditionalPhrase &&l, ConditionalPhrase &&r) +{ + return ConditionalPhrase(&l, PhraseData::LessEqual, r); +} + +ConditionalPhrase operator >(ConditionalPhrase &&l, ConditionalPhrase &&r) +{ + return ConditionalPhrase(&l, PhraseData::Greater, r); +} + +ConditionalPhrase operator >=(ConditionalPhrase &&l, ConditionalPhrase &&r) +{ + return ConditionalPhrase(&l, PhraseData::GreaterEqual, r); +} + +NUT_END_NAMESPACE diff --git a/src/phrases/conditionalphrase.h b/src/phrases/conditionalphrase.h new file mode 100644 index 0000000..0902693 --- /dev/null +++ b/src/phrases/conditionalphrase.h @@ -0,0 +1,94 @@ +/************************************************************************** +** +** 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 CONDITIONALPHRASE_H +#define CONDITIONALPHRASE_H + +#include "phrasedata.h" + +NUT_BEGIN_NAMESPACE + +class PhraseData; +class AbstractFieldPhrase; + +#define SPECIALIZATION_NUMERIC_MEMBER(type, op, cond) \ +ConditionalPhrase operator op(const QVariant &other) \ +{ \ + return ConditionalPhrase(this, cond, other); \ +} +class NUT_EXPORT ConditionalPhrase +{ +public: + PhraseData *data; +// QSharedPointer leftDataPointer; +// QSharedPointer rightDataPointer; + ConditionalPhrase(); + ConditionalPhrase(const ConditionalPhrase &other); +#ifdef Q_COMPILER_RVALUE_REFS + ConditionalPhrase(const ConditionalPhrase &&other); +#endif + explicit ConditionalPhrase(const PhraseData *data); + ConditionalPhrase(AbstractFieldPhrase *, PhraseData::Condition); + ConditionalPhrase(AbstractFieldPhrase *, PhraseData::Condition, const QVariant &v); + ConditionalPhrase(AbstractFieldPhrase *, PhraseData::Condition, const AbstractFieldPhrase &v); + ConditionalPhrase(AbstractFieldPhrase *l, PhraseData::Condition cond, ConditionalPhrase &r); + ConditionalPhrase(ConditionalPhrase *l, PhraseData::Condition cond, const AbstractFieldPhrase &r); + ConditionalPhrase(ConditionalPhrase *l, PhraseData::Condition cond, const QVariant &r); + ConditionalPhrase(ConditionalPhrase *l, PhraseData::Condition cond, ConditionalPhrase &r); + virtual ~ConditionalPhrase(); + + ConditionalPhrase &operator =(const ConditionalPhrase &other); + ConditionalPhrase operator ==(const QVariant &other); +// ConditionalPhrase operator ==(const AbstractFieldPhrase &other); +// ConditionalPhrase operator &&(const ConditionalPhrase &other); +// ConditionalPhrase operator ||(const ConditionalPhrase &other); + ConditionalPhrase operator !(); + + SPECIALIZATION_NUMERIC_MEMBER(type, <, PhraseData::Less) + SPECIALIZATION_NUMERIC_MEMBER(type, <=, PhraseData::LessEqual) + SPECIALIZATION_NUMERIC_MEMBER(type, >, PhraseData::Greater) + SPECIALIZATION_NUMERIC_MEMBER(type, >=, PhraseData::GreaterEqual) +}; + + +#define DECLARE_CONDITIONALPHRASE_OPERATORS(op) \ +ConditionalPhrase operator op(const ConditionalPhrase &l, const ConditionalPhrase &r); \ +ConditionalPhrase operator op(const ConditionalPhrase &l, ConditionalPhrase &&r); \ +ConditionalPhrase operator op(ConditionalPhrase &&l, const ConditionalPhrase &r); \ +ConditionalPhrase operator op(ConditionalPhrase &&l, ConditionalPhrase &&r); + +DECLARE_CONDITIONALPHRASE_OPERATORS(==) +DECLARE_CONDITIONALPHRASE_OPERATORS(&&) +DECLARE_CONDITIONALPHRASE_OPERATORS(||) + +ConditionalPhrase operator <(AbstractFieldPhrase &l, ConditionalPhrase &&r); +ConditionalPhrase operator <=(AbstractFieldPhrase &l, ConditionalPhrase &&r); +ConditionalPhrase operator >(AbstractFieldPhrase &l, ConditionalPhrase &&r); +ConditionalPhrase operator >=(AbstractFieldPhrase &l, ConditionalPhrase &&r); + +ConditionalPhrase operator <(ConditionalPhrase &&l, ConditionalPhrase &&r); +ConditionalPhrase operator <=(ConditionalPhrase &&l, ConditionalPhrase &&r); +ConditionalPhrase operator >(ConditionalPhrase &&l, ConditionalPhrase &&r); +ConditionalPhrase operator >=(ConditionalPhrase &&l, ConditionalPhrase &&r); + + +NUT_END_NAMESPACE + +#endif // CONDITIONALPHRASE_H diff --git a/src/phrases/datephrase.cpp b/src/phrases/datephrase.cpp new file mode 100644 index 0000000..a9c8d9b --- /dev/null +++ b/src/phrases/datephrase.cpp @@ -0,0 +1,22 @@ +/************************************************************************** +** +** 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 "datephrase.h" + diff --git a/src/phrases/datephrase.h b/src/phrases/datephrase.h new file mode 100644 index 0000000..5db2186 --- /dev/null +++ b/src/phrases/datephrase.h @@ -0,0 +1,173 @@ +/************************************************************************** +** +** 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 DATEPHRASE_H +#define DATEPHRASE_H + +#include "fieldphrase.h" +#include +#include + +NUT_BEGIN_NAMESPACE + + +template +struct __is_date_helper + : public std::false_type { }; + +template<> +struct __is_date_helper + : public std::true_type { }; + +template<> +struct __is_date_helper + : public std::true_type { }; + +template<> +struct __is_date_helper + : public std::true_type { }; + +template +struct is_date + : public __is_date_helper::type>::type +{ }; + + +template +inline bool is_valid_template() {return false;} + +template <> +inline bool is_valid_template() {return true;} + +template <> +inline bool is_valid_template() {return true;} + +template <> +inline bool is_valid_template() {return true;} + +template <> +inline bool is_valid_template() {return true;} + +template +class NUT_EXPORT FieldPhrase::value>::type> + : public AbstractFieldPhrase +{ +public: + FieldPhrase(const char *className, const char *s) : + AbstractFieldPhrase(className, s) + {} + + AssignmentPhrase operator =(const T &other) { + return AssignmentPhrase(this, other); + } + + ConditionalPhrase operator <(const QVariant &other) { + return ConditionalPhrase(this, PhraseData::Less, other); + } + ConditionalPhrase operator <=(const QVariant &other) { + return ConditionalPhrase(this, PhraseData::LessEqual, other); + } + ConditionalPhrase operator >(const QVariant &other) { + return ConditionalPhrase(this, PhraseData::Greater, other); + } + ConditionalPhrase operator >=(const QVariant &other) { + return ConditionalPhrase(this, PhraseData::GreaterEqual, other); + } + + ConditionalPhrase between(const QVariant &min, const QVariant &max) + { + return ConditionalPhrase(this, PhraseData::Between, + QVariantList() << min << max); + } + +// template::value, int>::type = 0> + ConditionalPhrase addYears(int val) { + if (!is_valid_template()) + return ConditionalPhrase(); + return ConditionalPhrase(this, PhraseData::AddYears, val); + } + ConditionalPhrase addMonths(int val) { + if (!is_valid_template()) + return ConditionalPhrase(); + return ConditionalPhrase(this, PhraseData::AddMonths, val); + } + ConditionalPhrase addDays(int val) { + if (!is_valid_template()) + return ConditionalPhrase(); + return ConditionalPhrase(this, PhraseData::AddDays, val); + } + + ConditionalPhrase addHours(int val) { + if (!is_valid_template()) + return ConditionalPhrase(); + return ConditionalPhrase(this, PhraseData::AddHours, val); + } + ConditionalPhrase addMinutes(int val) { + if (!is_valid_template()) + return ConditionalPhrase(); + return ConditionalPhrase(this, PhraseData::AddMinutes, val); + } + ConditionalPhrase addSeconds(int val) { + if (!is_valid_template()) + return ConditionalPhrase(); + return ConditionalPhrase(this, PhraseData::AddSeconds, val); + } + + ConditionalPhrase year() { + if (!is_valid_template()) + return ConditionalPhrase(); + return ConditionalPhrase(this, PhraseData::DatePartYear); + } + ConditionalPhrase month() { + if (!is_valid_template()) + return ConditionalPhrase(); + return ConditionalPhrase(this, PhraseData::DatePartMonth); + } + ConditionalPhrase day() { + if (!is_valid_template()) + return ConditionalPhrase(); + return ConditionalPhrase(this, PhraseData::DatePartDay); + } + ConditionalPhrase hour() { + if (!is_valid_template()) + return ConditionalPhrase(); + return ConditionalPhrase(this, PhraseData::DatePartHour); + } + ConditionalPhrase minute() { + if (!is_valid_template()) + return ConditionalPhrase(); + return ConditionalPhrase(this, PhraseData::DatePartMinute); + } + ConditionalPhrase second() { + if (!is_valid_template()) + return ConditionalPhrase(); + return ConditionalPhrase(this, PhraseData::DatePartSecond); + } + ConditionalPhrase msec() { + if (!is_valid_template()) + return ConditionalPhrase(); + return ConditionalPhrase(this, PhraseData::DatePartMilisecond); + } +}; + +NUT_END_NAMESPACE + +#endif // DATEPHRASE_H diff --git a/src/phrases/fieldphrase.cpp b/src/phrases/fieldphrase.cpp new file mode 100644 index 0000000..ed06b83 --- /dev/null +++ b/src/phrases/fieldphrase.cpp @@ -0,0 +1,21 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#include "fieldphrase.h" diff --git a/src/phrases/fieldphrase.h b/src/phrases/fieldphrase.h new file mode 100644 index 0000000..6ec3bf4 --- /dev/null +++ b/src/phrases/fieldphrase.h @@ -0,0 +1,105 @@ +/************************************************************************** +** +** 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 FIELDPHRASE_H +#define FIELDPHRASE_H + +#include "../defines.h" + +#include "abstractfieldphrase.h" + +NUT_BEGIN_NAMESPACE + +template +class NUT_EXPORT FieldPhrase : public AbstractFieldPhrase +{ +public: + FieldPhrase(const char *className, const char *s) : + AbstractFieldPhrase(className, s) + {} + + AssignmentPhrase operator =(const QVariant &other) { + return AssignmentPhrase(this, other); + } + + ConditionalPhrase operator ==(const QVariant &other) { + return ConditionalPhrase(this, PhraseData::Equal, other); + } + +}; + + +template<> +class NUT_EXPORT FieldPhrase : public AbstractFieldPhrase +{ +public: + FieldPhrase(const char *className, const char *s) : + AbstractFieldPhrase(className, s) + {} + + ConditionalPhrase like(const QString &term) { + return ConditionalPhrase(this, PhraseData::Like, term); + } + + ConditionalPhrase contains(const QString &term) { + return ConditionalPhrase(this, PhraseData::Like, "%" + term + "%"); + } + + AssignmentPhrase operator =(const QVariant &v) { + return AssignmentPhrase(this, v); + } +}; + +//Date and time +#define CONDITIONAL_VARIANT_METHOD(name, cond) \ + ConditionalPhrase name(int val) \ + { \ + return ConditionalPhrase(this, cond, val); \ + } + +template<> +class NUT_EXPORT FieldPhrase : public AbstractFieldPhrase +{ +public: + FieldPhrase(const char *className, const char *s) : + AbstractFieldPhrase(className, s) + {} + + AssignmentPhrase operator =(const bool &other) { + return AssignmentPhrase(this, other); + } + + FieldPhrase operator !() + { + FieldPhrase f(data->className, data->fieldName); +// f.data = new PhraseData(data); + f.data->isNot = !data->isNot; + return f; + } + + operator ConditionalPhrase() + { + return ConditionalPhrase(this, PhraseData::Equal, !data->isNot); + } +}; + +NUT_END_NAMESPACE + +#endif // FIELDPHRASE_H diff --git a/src/phrases/numericphrase.cpp b/src/phrases/numericphrase.cpp new file mode 100644 index 0000000..78c7275 --- /dev/null +++ b/src/phrases/numericphrase.cpp @@ -0,0 +1,2 @@ +#include "numericphrase.h" + diff --git a/src/phrases/numericphrase.h b/src/phrases/numericphrase.h new file mode 100644 index 0000000..ab9db31 --- /dev/null +++ b/src/phrases/numericphrase.h @@ -0,0 +1,90 @@ +#ifndef NUMERICPHRASE_H +#define NUMERICPHRASE_H + +#include "fieldphrase.h" +#include + +NUT_BEGIN_NAMESPACE + +#define SPECIALIZATION_NUMERIC_MEMBER(type, op, cond) \ + ConditionalPhrase operator op(const QVariant &other) \ +{ \ + return ConditionalPhrase(this, cond, other); \ +} + +template +class FieldPhrase::value || std::is_integral::value + >::type> + : public AbstractFieldPhrase +{ +public: + FieldPhrase(const char *className, const char *s) : + AbstractFieldPhrase(className, s) + {} + + AssignmentPhrase operator =(const QVariant &other) { + return AssignmentPhrase(this, other); + } + AssignmentPhrase operator =(ConditionalPhrase &&other) { + return AssignmentPhrase(new PhraseData(data, PhraseData::Equal, other.data)); + } + ConditionalPhrase between(const QVariant &min, const QVariant &max) + { + return ConditionalPhrase(this, PhraseData::Between, + QVariantList() << min << max); + } + ConditionalPhrase operator ++() + { + return ConditionalPhrase(this, PhraseData::Add, 1); + } + ConditionalPhrase operator --() + { + return ConditionalPhrase(this, PhraseData::Minus, 1); + } + ConditionalPhrase operator ++(int) + { + return ConditionalPhrase(this, PhraseData::Add, 1); + } + ConditionalPhrase operator --(int) + { + return ConditionalPhrase(this, PhraseData::Minus, 1); + } + + SPECIALIZATION_NUMERIC_MEMBER(type, <, PhraseData::Less) + SPECIALIZATION_NUMERIC_MEMBER(type, <=, PhraseData::LessEqual) + SPECIALIZATION_NUMERIC_MEMBER(type, >, PhraseData::Greater) + SPECIALIZATION_NUMERIC_MEMBER(type, >=, PhraseData::GreaterEqual) + SPECIALIZATION_NUMERIC_MEMBER(type, %, PhraseData::Mod) + + SPECIALIZATION_NUMERIC_MEMBER(type, +, PhraseData::Add) + SPECIALIZATION_NUMERIC_MEMBER(type, -, PhraseData::Minus) + SPECIALIZATION_NUMERIC_MEMBER(type, *, PhraseData::Multiple) + SPECIALIZATION_NUMERIC_MEMBER(type, /, PhraseData::Divide) +}; + +#define SPECIALIZATION_NUMERIC_TYPE(type) \ + template<> \ + class FieldPhrase : public NumericPhrase \ +{ \ +public: \ +FieldPhrase(const char *className, const char *s) : \ + NumericPhrase(className, s) \ +{} \ +}; + +//SPECIALIZATION_NUMERIC_TYPE(qint8) +//SPECIALIZATION_NUMERIC_TYPE(qint16) +//SPECIALIZATION_NUMERIC_TYPE(qint32) +//SPECIALIZATION_NUMERIC_TYPE(qint64) + +//SPECIALIZATION_NUMERIC_TYPE(quint8) +//SPECIALIZATION_NUMERIC_TYPE(quint16) +//SPECIALIZATION_NUMERIC_TYPE(quint32) +//SPECIALIZATION_NUMERIC_TYPE(quint64) + +//SPECIALIZATION_NUMERIC_TYPE(qreal) + +NUT_END_NAMESPACE + +#endif // NUMERICPHRASE_H diff --git a/src/phrases/phrasedata.cpp b/src/phrases/phrasedata.cpp new file mode 100644 index 0000000..69a40d5 --- /dev/null +++ b/src/phrases/phrasedata.cpp @@ -0,0 +1,91 @@ +/************************************************************************** +** +** 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 "phrasedata.h" + +NUT_BEGIN_NAMESPACE + +PhraseData::PhraseData() : + className(""), fieldName(""), + type(Field), operatorCond(NotAssign), + left(nullptr), right(nullptr), operand(QVariant::Invalid), isNot(false), parents(1) +{ } + +PhraseData::PhraseData(const char *className, const char *fieldName) : + className(className), fieldName(fieldName), + type(Field), operatorCond(NotAssign), + left(nullptr), right(nullptr), operand(QVariant::Invalid), isNot(false), parents(1) +{ } + +PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o) + : className(nullptr), fieldName(nullptr), + type(WithoutOperand), operatorCond(o), left(l), right(nullptr), + isNot(false), parents(1) +{ + l->parents++; +} + +PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o, + PhraseData *r) + : className(nullptr), fieldName(nullptr), + type(WithOther), operatorCond(o), + left(l), right(r), + isNot(false), parents(1) +{ + l->parents++; + r->parents++; +} + +PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o, QVariant r) + : className(nullptr), fieldName(nullptr), + type(WithVariant), operatorCond(o), left(l), + right(nullptr), operand(r), isNot(false), parents(1) +{ } + +PhraseData *PhraseData::operator =(PhraseData *other) +{ + other->parents++; + return other; +} + +PhraseData &PhraseData::operator =(PhraseData &other) +{ + other.parents++; + return other; +} + +QString PhraseData::toString() const +{ + return QString("[%1].%2").arg(className, fieldName); +} + +void PhraseData::cleanUp() +{ +} + +void PhraseData::cleanUp(PhraseData *d) +{ + if (d->left) + cleanUp(d->left); + if (d->right) + cleanUp(d->right); +} + +NUT_END_NAMESPACE diff --git a/src/phrases/phrasedata.h b/src/phrases/phrasedata.h new file mode 100644 index 0000000..d1ae6f0 --- /dev/null +++ b/src/phrases/phrasedata.h @@ -0,0 +1,116 @@ +/************************************************************************** +** +** 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 PHRASEDATA_H +#define PHRASEDATA_H + +#include "../defines.h" + +NUT_BEGIN_NAMESPACE + +class NUT_EXPORT PhraseData +{ +public: + enum Condition { + NotAssign = 0, + Equal, + Less, + LessEqual, + Null, + In, + Like, + + Not = 10, + NotEqual, + GreaterEqual, + Greater, + NotNull, + NotIn, + NotLike, + + And = 20, + Or, + + Add, + Minus, + Multiple, + Divide, + Mod, + + Between, + + //date and time + AddYears, + AddMonths, + AddDays, + AddHours, + AddMinutes, + AddSeconds, + + DatePartYear, + DatePartMonth, + DatePartDay, + DatePartHour, + DatePartMinute, + DatePartSecond, + DatePartMilisecond +// // special types +// Distance + }; + + enum Type { Field, WithVariant, WithOther, WithoutOperand }; + + const char *className; + const char *fieldName; + + Type type; + + Condition operatorCond; + + PhraseData *left; + PhraseData *right; + + QVariant operand; + bool isNot; + quint16 parents; + + PhraseData(); + PhraseData(const char *className, const char *fieldName); + PhraseData(PhraseData *l, Condition o); + PhraseData(PhraseData *l, Condition o, PhraseData *r); + PhraseData(PhraseData *l, Condition o, QVariant r); +// explicit PhraseData(const PhraseData &other); +// explicit PhraseData(const PhraseData *other); + + PhraseData *operator =(PhraseData *other); + PhraseData &operator =(PhraseData &other); + + QString toString() const; + + ~PhraseData() = default; + + void cleanUp(); +private: + void cleanUp(PhraseData *d); +}; + +NUT_END_NAMESPACE + +#endif // PHRASEDATA_H diff --git a/src/phrases/phrasedatalist.cpp b/src/phrases/phrasedatalist.cpp new file mode 100644 index 0000000..e1858ed --- /dev/null +++ b/src/phrases/phrasedatalist.cpp @@ -0,0 +1,61 @@ +/************************************************************************** +** +** 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 "phrasedatalist.h" + +NUT_BEGIN_NAMESPACE + +PhraseDataList::PhraseDataList() : QList() +{ + +} + +PhraseDataList::PhraseDataList(const PhraseDataList &other) : QList() +{ +// auto &o = const_cast(other); + PhraseDataList::const_iterator i; + for (i = other.constBegin(); i != other.constEnd(); ++i) + append(*i); +} + +void PhraseDataList::append(PhraseData *d) +{ + d->parents++; + QList::append(d); +} + +void PhraseDataList::append(QList &dl) +{ + foreach (PhraseData *d, dl) + d->parents++; + QList::append(dl); +} + +PhraseDataList::~PhraseDataList() +{ + QList::iterator i; + for (i = begin(); i != end(); ++i) { + (*i)->cleanUp(); + if (!--(*i)->parents) + delete *i; + } +} + +NUT_END_NAMESPACE diff --git a/src/phrases/phrasedatalist.h b/src/phrases/phrasedatalist.h new file mode 100644 index 0000000..9566ccb --- /dev/null +++ b/src/phrases/phrasedatalist.h @@ -0,0 +1,40 @@ +/************************************************************************** +** +** 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 PHRASEDATALIST_H +#define PHRASEDATALIST_H + +#include "phrasedata.h" + +NUT_BEGIN_NAMESPACE + +class NUT_EXPORT PhraseDataList : public QList +{ +public: + PhraseDataList(); + PhraseDataList(const PhraseDataList &other); + void append(PhraseData *d); + void append(QList &dl); + virtual ~PhraseDataList(); +}; + +NUT_END_NAMESPACE + +#endif // PHRASEDATALIST_H diff --git a/src/phrases/phraselist.cpp b/src/phrases/phraselist.cpp new file mode 100644 index 0000000..a20c5b6 --- /dev/null +++ b/src/phrases/phraselist.cpp @@ -0,0 +1,94 @@ +/************************************************************************** +** +** 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 "abstractfieldphrase.h" +#include "phraselist.h" + +NUT_BEGIN_NAMESPACE + +PhraseList::PhraseList() : isValid(false) +{ + +} + +PhraseList::PhraseList(const PhraseList &other) : isValid(true) +{ + data = qMove(other.data); + const_cast(other).data.clear(); +} + +PhraseList::PhraseList(PhraseList &&other) +{ + data = other.data; +} + +PhraseList::PhraseList(const AbstractFieldPhrase &other) : isValid(true) +{ + data.append(other.data); + incAllDataParents(); +} + +PhraseList::PhraseList(const AbstractFieldPhrase *left, + const AbstractFieldPhrase &right) + : isValid(true) +{ + data.append(left->data); + data.append(right.data); + incAllDataParents(); +} + +PhraseList::PhraseList(PhraseList *left, PhraseList *right) : isValid(true) +{ +// data = qMove(left->data + right->data); + data.append(left->data); + data.append(right->data); +// left->data.clear(); +// right->data.clear(); +} + +PhraseList::PhraseList(PhraseList *left, const AbstractFieldPhrase *right) + : isValid(true) +{ + data.append(left->data); + data.append(right->data); + incAllDataParents(); +} + +PhraseList &PhraseList::operator =(const PhraseList &other) +{ + data.append(const_cast(other).data); + return *this; +} + +PhraseList PhraseList::operator |(const AbstractFieldPhrase &other) { + return PhraseList(this, &other); +} + +void PhraseList::incAllDataParents() +{ +// foreach (PhraseData *d, data) +// d->parents++; +} + +PhraseList PhraseList::operator |(PhraseList &other) { + return PhraseList(this, &other); +} + +NUT_END_NAMESPACE diff --git a/src/phrases/phraselist.h b/src/phrases/phraselist.h new file mode 100644 index 0000000..cddb61b --- /dev/null +++ b/src/phrases/phraselist.h @@ -0,0 +1,55 @@ +/************************************************************************** +** +** 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 PHRASELIST_H +#define PHRASELIST_H + +#include "../defines.h" + +#include "phrasedatalist.h" + +NUT_BEGIN_NAMESPACE + +class AbstractFieldPhrase; +class NUT_EXPORT PhraseList +{ +public: + bool isValid; + PhraseDataList data; + explicit PhraseList(); + PhraseList(const PhraseList &other); + PhraseList(PhraseList &&other); + PhraseList(const AbstractFieldPhrase &other); + PhraseList(const AbstractFieldPhrase *left, const AbstractFieldPhrase &right); + PhraseList(PhraseList *left, PhraseList *right); + PhraseList(PhraseList *left, const AbstractFieldPhrase *right); + virtual ~PhraseList() = default; + + PhraseList &operator =(const PhraseList &other); + PhraseList operator |(PhraseList &other); + PhraseList operator |(const AbstractFieldPhrase &other); + +private: + void incAllDataParents(); +}; + +NUT_END_NAMESPACE + +#endif // PHRASELIST_H diff --git a/src/query.cpp b/src/query.cpp index f8c79fd..4ba466c 100644 --- a/src/query.cpp +++ b/src/query.cpp @@ -23,15 +23,11 @@ NUT_BEGIN_NAMESPACE QueryPrivate::QueryPrivate(QueryBase *parent) : q_ptr(parent), - database(0), tableSet(0), skip(-1), take(-1) + database(nullptr), tableSet(nullptr), skip(-1), take(-1) { } -QueryPrivate::~QueryPrivate() -{ -} - /*! * \class Query * \brief This class hold a query. A query can be used for getting database rows, editing or deleting without row fetching. diff --git a/src/query.h b/src/query.h index 77e2c36..57b6a1d 100644 --- a/src/query.h +++ b/src/query.h @@ -29,7 +29,13 @@ #include #include #include +#include +#ifdef NUT_SHARED_POINTER +#include +#endif + +#include "table.h" #include "query_p.h" #include "database.h" #include "databasemodel.h" @@ -38,6 +44,7 @@ #include "querybase_p.h" #include "phrase.h" #include "tablemodel.h" +#include "sqlmodel.h" NUT_BEGIN_NAMESPACE @@ -50,6 +57,14 @@ template bool m_autoDelete; public: +//#ifdef NUT_SHARED_POINTER +// typedef QList> RowList; +// typedef QSharedPointer Row; +//#else +// typedef QList RowList; +// typedef T* Row; +//#endif + explicit Query(Database *database, TableSetBase *tableSet, bool autoDelete); ~Query(); @@ -74,16 +89,20 @@ public: Query *setWhere(const ConditionalPhrase &ph); //data selecting - T *first(); - QList toList(int count = -1); + Row first(); + RowList toList(int count = -1); template QList select(const FieldPhrase f); + + template + QList select(const std::function allocator); + int count(); QVariant max(const FieldPhrase &f); QVariant min(const FieldPhrase &f); QVariant average(const FieldPhrase &f); - QVariant insert(AssignmentPhraseList p); + QVariant insert(const AssignmentPhraseList &p); //data mailpulation int update(const AssignmentPhraseList &ph); @@ -91,17 +110,46 @@ public: int remove(); QSqlQueryModel *toModel(); + void toModel(QSqlQueryModel *model); + void toModel(SqlModel *model); //debug purpose QString sqlCommand() const; }; -template -inline Query *createQuery(TableSet *tableSet) +template +template +Q_OUTOFLINE_TEMPLATE QList Query::select(const std::function allocator) { - return tableSet->query(); + Q_D(Query); + QList ret; + + d->joins.prepend(d->tableName); + d->sql = d->database->sqlGenertor()->selectCommand( + d->tableName, + SqlGeneratorBase::SignleField, "*", + d->wherePhrase, + d->relations, + d->skip, d->take); + + QSqlQuery q = d->database->exec(d->sql); + + while (q.next()) { + O obj = allocator(q); + ret.append(obj); + } + + if (m_autoDelete) + deleteLater(); + return ret; } +//template +//inline Query *createQuery(TableSet *tableSet) +//{ +// return tableSet->query(); +//} + template Q_OUTOFLINE_TEMPLATE Query::Query(Database *database, TableSetBase *tableSet, bool autoDelete) @@ -115,7 +163,7 @@ Q_OUTOFLINE_TEMPLATE Query::Query(Database *database, TableSetBase *tableSet, d->className = T::staticMetaObject.className(); d->tableName = d->database->model() - .tableByClassName(T::staticMetaObject.className()) + .tableByClassName(d->className) ->name(); } @@ -127,17 +175,17 @@ Q_OUTOFLINE_TEMPLATE Query::~Query() } template -Q_OUTOFLINE_TEMPLATE QList Query::toList(int count) +Q_OUTOFLINE_TEMPLATE RowList Query::toList(int count) { Q_UNUSED(count); Q_D(Query); - QList returnList; + RowList returnList; d->select = "*"; d->sql = d->database->sqlGenertor()->selectCommand( d->tableName, d->fieldPhrase, d->wherePhrase, d->orderPhrase, d->relations, d->skip, d->take); - +qDebug()<sql; QSqlQuery q = d->database->exec(d->sql); if (q.lastError().isValid()) { qDebug() << q.lastError().text(); @@ -217,7 +265,6 @@ Q_OUTOFLINE_TEMPLATE QList Query::toList(int count) int p = levels.count(); int n = -1; - int lastP = p; while (p) { // Q_ASSERT(p != lastP); @@ -252,24 +299,29 @@ Q_OUTOFLINE_TEMPLATE QList Query::toList(int count) Table *table; if (data.table->className() == d->className) { table = new T(); - table->setParentTableSet(d->tableSet); +#ifdef NUT_SHARED_POINTER + auto shp = QSharedPointer(qobject_cast(table)); + returnList.append(shp); +#else returnList.append(dynamic_cast(table)); +#endif + table->setParentTableSet(d->tableSet); } else { const QMetaObject *childMetaObject = QMetaType::metaObjectForType(data.table->typeId()); table = qobject_cast(childMetaObject->newInstance()); - if (!table) qFatal("Could not create instance of %s", qPrintable(data.table->name())); - qDebug() << data.table->name() << "created"; } - QStringList childFields = data.table->fieldsNames(); - foreach (QString field, childFields) - table->setProperty(field.toLatin1().data(), - q.value(data.table->name() + "." + field)); + QList childFields = data.table->fields(); + foreach (FieldModel *field, childFields) + table->setProperty(field->name.toLatin1().data(), + d->database->sqlGenertor()->unescapeValue( + field->type, + q.value(data.table->name() + "." + field->name))); for (int i = 0; i < data.masters.count(); ++i) { int master = data.masters[i]; @@ -287,13 +339,13 @@ Q_OUTOFLINE_TEMPLATE QList Query::toList(int count) //set last created row data.lastRow = table; - - lastP = p; } //while } // while + +#ifndef NUT_SHARED_POINTER if (m_autoDelete) deleteLater(); - +#endif return returnList; } @@ -325,11 +377,11 @@ Q_OUTOFLINE_TEMPLATE QList Query::select(const FieldPhrase f) } template -Q_OUTOFLINE_TEMPLATE T *Query::first() +Q_OUTOFLINE_TEMPLATE Row Query::first() { skip(0); take(1); - QList list = toList(1); + RowList list = toList(1); if (list.count()) return list.first(); @@ -412,7 +464,7 @@ Q_OUTOFLINE_TEMPLATE QVariant Query::average(const FieldPhrase &f) } template -Q_OUTOFLINE_TEMPLATE QVariant Query::insert(AssignmentPhraseList p) +Q_OUTOFLINE_TEMPLATE QVariant Query::insert(const AssignmentPhraseList &p) { Q_D(Query); d->sql = d->database->sqlGenertor() @@ -520,6 +572,7 @@ Q_OUTOFLINE_TEMPLATE int Query::update(const AssignmentPhraseList &ph) d->tableName, ph, d->wherePhrase); + QSqlQuery q = d->database->exec(d->sql); if (m_autoDelete) @@ -543,6 +596,14 @@ Q_OUTOFLINE_TEMPLATE int Query::remove() template Q_OUTOFLINE_TEMPLATE QSqlQueryModel *Query::toModel() +{ + QSqlQueryModel *model = new QSqlQueryModel; + toModel(model); + return model; +} + +template +Q_OUTOFLINE_TEMPLATE void Query::toModel(QSqlQueryModel *model) { Q_D(Query); @@ -551,9 +612,8 @@ Q_OUTOFLINE_TEMPLATE QSqlQueryModel *Query::toModel() d->fieldPhrase, d->wherePhrase, d->orderPhrase, d->relations, d->skip, d->take); -qDebug() << d->sql; + DatabaseModel dbModel = d->database->model(); - QSqlQueryModel *model = new QSqlQueryModel; model->setQuery(d->sql, d->database->database()); int fieldIndex = 0; @@ -563,8 +623,6 @@ qDebug() << d->sql; QString displayName = dbModel.tableByClassName(pd->className) ->field(pd->fieldName)->displayName; - qDebug() << "Display name for"<className<fieldName - <<"="<setHeaderData(fieldIndex++, Qt::Horizontal, displayName); @@ -577,8 +635,43 @@ qDebug() << d->sql; f->displayName); } } +} - return model; +template +Q_OUTOFLINE_TEMPLATE void Query::toModel(SqlModel *model) +{ + Q_D(Query); + + d->sql = d->database->sqlGenertor()->selectCommand( + d->tableName, + d->fieldPhrase, + d->wherePhrase, d->orderPhrase, d->relations, + d->skip, d->take); + + model->setTable(toList()); + /* + DatabaseModel dbModel = d->database->model(); + model->setQuery(d->sql, d->database->database()); + + int fieldIndex = 0; + + if (d->fieldPhrase.data.count()) { + foreach (const PhraseData *pd, d->fieldPhrase.data) { + QString displayName = dbModel.tableByClassName(pd->className) + ->field(pd->fieldName)->displayName; + + model->setHeaderData(fieldIndex++, + Qt::Horizontal, + displayName); + } + } else { + TableModel *tbl = d->database->model().tableByName(d->tableName); + foreach (FieldModel *f, tbl->fields()) { + model->setHeaderData(fieldIndex++, + Qt::Horizontal, + f->displayName); + } + }*/ } template @@ -588,6 +681,15 @@ Q_OUTOFLINE_TEMPLATE QString Query::sqlCommand() const return d->sql; } +//TODO: complete this class later +//class RawQuery : public Query +//{ +//public: +// void setRawCommand(const QString &sql) { + +// } +//}; + NUT_END_NAMESPACE #endif // QUERY_H diff --git a/src/query_p.h b/src/query_p.h index f7127cc..b0b3c4a 100644 --- a/src/query_p.h +++ b/src/query_p.h @@ -23,8 +23,9 @@ #include "phrase.h" -#include -#include +#include +#include +#include NUT_BEGIN_NAMESPACE @@ -32,13 +33,13 @@ class Database; class TableSetBase; class QueryBase; struct RelationModel; -class QueryPrivate{ +class QueryPrivate : public QSharedData { QueryBase *q_ptr; Q_DECLARE_PUBLIC(QueryBase) public: explicit QueryPrivate(QueryBase *parent); - ~QueryPrivate(); + ~QueryPrivate() = default; QString sql; QString className; diff --git a/src/querybase.cpp b/src/querybase.cpp index 5a9526d..f14f16e 100644 --- a/src/querybase.cpp +++ b/src/querybase.cpp @@ -11,9 +11,9 @@ QueryBase::QueryBase(QObject *parent) : QObject(parent) } -void QueryBase::addTableToSet(TableSetBase *set, Table *table) -{ - set->add(table); -} +//void QueryBase::addTableToSet(TableSetBase *set, Table *table) +//{ +// set->add(table); +//} NUT_END_NAMESPACE diff --git a/src/querybase_p.h b/src/querybase_p.h index 4d53ce3..2ef0386 100644 --- a/src/querybase_p.h +++ b/src/querybase_p.h @@ -23,7 +23,10 @@ #include #include +#include + #include "defines.h" +#include "query_p.h" NUT_BEGIN_NAMESPACE @@ -33,11 +36,15 @@ class TableSetBase; class QueryBase : public QObject { Q_OBJECT + +protected: + QExplicitlySharedDataPointer d; + public: explicit QueryBase(QObject *parent = 0); protected: - void addTableToSet(TableSetBase *set, Table *table); +// void addTableToSet(TableSetBase *set, Table *table); public slots: }; diff --git a/src/serializableobject.cpp b/src/serializableobject.cpp index 667c57b..0a09db5 100644 --- a/src/serializableobject.cpp +++ b/src/serializableobject.cpp @@ -1,6 +1,2 @@ #include "serializableobject.h" -SerializableObject::SerializableObject() -{ - -} diff --git a/src/serializableobject.h b/src/serializableobject.h index ffc65b1..aac7a23 100644 --- a/src/serializableobject.h +++ b/src/serializableobject.h @@ -6,7 +6,7 @@ class SerializableObject { public: - SerializableObject(); + SerializableObject() = default; virtual void load(const QVariant &value) = 0; virtual QVariant save() = 0; diff --git a/src/sqlmodel.cpp b/src/sqlmodel.cpp index 9f1a870..153b892 100644 --- a/src/sqlmodel.cpp +++ b/src/sqlmodel.cpp @@ -21,20 +21,33 @@ #include "database.h" #include "tablesetbase_p.h" #include "databasemodel.h" - +#include "tablemodel.h" +#include "table.h" #include "sqlmodel_p.h" #include "sqlmodel.h" +#include "query.h" NUT_BEGIN_NAMESPACE +//SqlModel::SqlModel(Query *q) : QAbstractItemModel(q.) +//{ + +//} + +void SqlModel::setRenderer(const std::function &renderer) +{ + _renderer = renderer; +} + SqlModel::SqlModel(Database *database, TableSetBase *tableSet, QObject *parent) : - QAbstractTableModel(parent) + QAbstractTableModel(parent), d_ptr(new SqlModelPrivate(this)), _renderer(nullptr) { Q_D(SqlModel); d->model = database->model() .tableByClassName(tableSet->childClassName()); d->tableName = d->model->name(); + // setQuery("SELECT * FROM " + d->tableName, database->databaseName()); } @@ -62,8 +75,12 @@ QVariant SqlModel::data(const QModelIndex &index, int role) const return QVariant("-"); if (role == Qt::DisplayRole) { - Table *t = d->rows.at(index.row()); - return t->property(d->model->field(index.column())->name.toLocal8Bit().data()); + Row
t = d->rows.at(index.row()); + QVariant v = t->property(d->model->field(index.column())->name.toLocal8Bit().data()); +// emit beforeShowText(index.column(), v); + if (_renderer != nullptr) + v = _renderer(index.column(), v); + return v; // LogData *d = dataList.at(index.row()); // switch (index.column()) { @@ -85,9 +102,49 @@ QVariant SqlModel::data(const QModelIndex &index, int role) const return QVariant(); } -SqlModelPrivate::SqlModelPrivate() +void SqlModel::setRows(RowList
rows) +{ + Q_D(SqlModel); + beginRemoveRows(QModelIndex(), 0, d->rows.count()); + d->rows.clear(); + endRemoveRows(); + beginInsertRows(QModelIndex(), 0, rows.count()); + d->rows = rows; + endInsertRows(); +} + +void SqlModel::append(Row
table) +{ + Q_D(SqlModel); + beginInsertRows(QModelIndex(), d->rows.count(), d->rows.count()); + d->rows.append(table); + endInsertRows(); +} + +//void SqlModel::append(Table *table) +//{ +// append(TableType
::Row(table)); +//} + +QVariant SqlModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + Q_D(const SqlModel); + return d->model->field(section)->displayName; + } + return QAbstractItemModel::headerData(section, orientation, role); +} + +Row
SqlModel::at(const int &i) const +{ + Q_D(const SqlModel); + return d->rows.at(i); +} + +SqlModelPrivate::SqlModelPrivate(SqlModel *parent) : q_ptr(parent) { } + NUT_END_NAMESPACE diff --git a/src/sqlmodel.h b/src/sqlmodel.h index e8c2590..3539576 100644 --- a/src/sqlmodel.h +++ b/src/sqlmodel.h @@ -23,6 +23,8 @@ #include #include "defines.h" +#include +#include NUT_BEGIN_NAMESPACE @@ -31,22 +33,52 @@ class TableSetBase; class SqlModelPrivate; class Table; class TableModel; -class SqlModel : public QAbstractTableModel + +class NUT_EXPORT SqlModel : public QAbstractTableModel { Q_OBJECT + std::function _renderer; + public: +// explicit SqlModel(Query *q); explicit SqlModel(Database *database, TableSetBase *tableSet, QObject *parent = Q_NULLPTR); int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; + template + void setTable(RowList rows); + + void setRows(RowList
rows); + void append(Row
table); +// void append(Table *table); + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + Row at(const int &i) const; + + void setRenderer(const std::function &renderer); + private: SqlModelPrivate *d_ptr; Q_DECLARE_PRIVATE(SqlModel) + +signals: + void beforeShowText(int col, QVariant &value); }; +template +Q_OUTOFLINE_TEMPLATE void SqlModel::setTable(RowList rows) +{ + Q_D(SqlModel); + + RowList
tab; + foreach (auto t, rows) + tab.append(t); + setRows(tab); +} + + NUT_END_NAMESPACE #endif // SQLMODEL_H diff --git a/src/sqlmodel_p.h b/src/sqlmodel_p.h index 7b68e88..1991d9c 100644 --- a/src/sqlmodel_p.h +++ b/src/sqlmodel_p.h @@ -1,6 +1,7 @@ #ifndef SQLMODEL_P_H #define SQLMODEL_P_H +#include #include #include "defines.h" @@ -13,11 +14,11 @@ class SqlModelPrivate { SqlModel *q_ptr; Q_DECLARE_PUBLIC(SqlModel) public: - explicit SqlModelPrivate(); + explicit SqlModelPrivate(SqlModel *parent); QString tableName; - QList rows; + RowList
rows; TableModel *model; }; diff --git a/src/src.pri b/src/src.pri new file mode 100644 index 0000000..79319e3 --- /dev/null +++ b/src/src.pri @@ -0,0 +1,69 @@ +DEFINES += NUT_SHARED_POINTER + +HEADERS += \ + $$PWD/generators/sqlgeneratorbase_p.h \ + $$PWD/generators/postgresqlgenerator.h \ + $$PWD/generators/mysqlgenerator.h \ + $$PWD/generators/sqlitegenerator.h \ + $$PWD/generators/sqlservergenerator.h \ + $$PWD/tablesetbasedata.h \ + $$PWD/types/dbgeography.h \ + $$PWD/tableset.h \ + $$PWD/defines_p.h \ + $$PWD/defines.h \ + $$PWD/query.h \ + $$PWD/databasemodel.h \ + $$PWD/changelogtable.h \ + $$PWD/tablesetbase_p.h \ + $$PWD/querybase_p.h \ + $$PWD/tablemodel.h \ + $$PWD/query_p.h \ + $$PWD/table.h \ + $$PWD/database.h \ + $$PWD/database_p.h \ + $$PWD/serializableobject.h \ + $$PWD/sqlmodel.h \ + $$PWD/sqlmodel_p.h \ + $$PWD/phrase.h \ + $$PWD/phrases/abstractfieldphrase.h \ + $$PWD/phrases/assignmentphrase.h \ + $$PWD/phrases/assignmentphraselist.h \ + $$PWD/phrases/conditionalphrase.h \ + $$PWD/phrases/fieldphrase.h \ + $$PWD/phrases/phrasedata.h \ + $$PWD/phrases/phrasedatalist.h \ + $$PWD/phrases/phraselist.h \ + $$PWD/phrases/datephrase.h \ + $$PWD/table_p.h + +SOURCES += \ + $$PWD/generators/sqlgeneratorbase.cpp \ + $$PWD/generators/postgresqlgenerator.cpp \ + $$PWD/generators/mysqlgenerator.cpp \ + $$PWD/generators/sqlitegenerator.cpp \ + $$PWD/generators/sqlservergenerator.cpp \ + $$PWD/types/dbgeography.cpp \ + $$PWD/tableset.cpp \ + $$PWD/query.cpp \ + $$PWD/databasemodel.cpp \ + $$PWD/tablesetbase.cpp \ + $$PWD/changelogtable.cpp \ + $$PWD/querybase.cpp \ + $$PWD/tablemodel.cpp \ + $$PWD/table.cpp \ + $$PWD/database.cpp \ + $$PWD/serializableobject.cpp \ + $$PWD/sqlmodel.cpp \ + $$PWD/phrase.cpp \ + $$PWD/phrases/abstractfieldphrase.cpp \ + $$PWD/phrases/assignmentphrase.cpp \ + $$PWD/phrases/assignmentphraselist.cpp \ + $$PWD/phrases/conditionalphrase.cpp \ + $$PWD/phrases/fieldphrase.cpp \ + $$PWD/phrases/phrasedata.cpp \ + $$PWD/phrases/phrasedatalist.cpp \ + $$PWD/phrases/phraselist.cpp \ + $$PWD/phrases/datephrase.cpp + + +include($$PWD/../3rdparty/serializer/src/src.pri) diff --git a/src/src.pro b/src/src.pro new file mode 100644 index 0000000..6f3372d --- /dev/null +++ b/src/src.pro @@ -0,0 +1,11 @@ +QT += sql gui + +TARGET = nut +TEMPLATE = lib +CONFIG += c++11 +CONFIG += staticlib + +DEFINES += QT_DEPRECATED_WARNINGS NUT_COMPILE_STATIC + +include($$PWD/src.pri) +include($$PWD/../ci-test-init.pri) diff --git a/src/table.cpp b/src/table.cpp index afb4033..6bcc750 100644 --- a/src/table.cpp +++ b/src/table.cpp @@ -20,10 +20,14 @@ #include #include +#include + #include "table.h" +#include "table_p.h" #include "database.h" #include "databasemodel.h" #include "generators/sqlgeneratorbase_p.h" +#include "tablesetbase_p.h" NUT_BEGIN_NAMESPACE @@ -39,73 +43,100 @@ NUT_BEGIN_NAMESPACE * This should be fixed to v1.2 */ -Table::Table(QObject *parent) : QObject(parent), myModel(0), _parentTableSet(0) +Table::Table(QObject *parent) : QObject(parent), + d_ptr(new TablePrivate(this)) +{ } + +Table::~Table() { - setStatus(NewCreated); + Q_D(Table); + + if (d->parentTableSet) + d->parentTableSet->remove(this); } void Table::add(TableSetBase *t) { - this->childTableSets.insert(t); + Q_D(Table); + d->childTableSets.insert(t); } +//QString Table::primaryKey() const +//{ +// Q_D(const Table); +// return d->model->primaryKey(); +//} -QString Table::primaryKey() const +//bool Table::isPrimaryKeyAutoIncrement() const +//{ +// Q_D(const Table); +// FieldModel *pk = d->model->field(d->model->primaryKey()); +// if (!pk) +// return false; +// return pk->isAutoIncrement; +//} + + +//QVariant Table::primaryValue() const +//{ +// return property(primaryKey().toLatin1().data()); +//} + +void Table::propertyChanged(const QString &propName) { - return myModel->primaryKey(); + Q_D(Table); +// if (!d->model) +// d->model = TableModel::findByClassName(metaObject()->className()); + +// if (!d->model) +// qFatal ("model for class '%s' not found", qPrintable(metaObject()->className())); + +// foreach (FieldModel *f, d->model->fields()) +// if(f->isPrimaryKey && propName == f->name && f->isAutoIncrement) +// return; + + d->changedProperties.insert(propName); + if (d->status == FeatchedFromDB) + d->status = Modified; + + if (d->status == NewCreated) + d->status = Added; } -bool Table::isPrimaryKeyAutoIncrement() const +void Table::setModel(TableModel *model) { - return myModel->field(myModel->primaryKey())->isAutoIncrement; -} - - -QVariant Table::primaryValue() const -{ - return property(primaryKey().toLatin1().data()); -} - -void Table::propertyChanged(QString propName) -{ - if (!myModel) - myModel = TableModel::findByClassName(metaObject()->className()); - - if (!myModel) - qFatal ("model for this class not found"); - - foreach (FieldModel *f, myModel->fields()) - if(f->isPrimaryKey && propName == f->name && f->isAutoIncrement) - return; - - _changedProperties.insert(propName); - if (_status == FeatchedFromDB) - _status = Modified; - - if (_status == NewCreated) - _status = Added; + Q_D(Table); + d->model = model; } void Table::clear() { - _changedProperties.clear(); + Q_D(Table); + d->changedProperties.clear(); } QSet Table::changedProperties() const { - return _changedProperties; + Q_D(const Table); + return d->changedProperties; } -bool Table::setParentTable(Table *master) +bool Table::setParentTable(Table *master, TableModel *masterModel, TableModel *model) { - QString masterClassName = master->metaObject()->className(); + Q_D(Table); - foreach (RelationModel *r, myModel->foregionKeys()) + QString masterClassName = master->metaObject()->className(); + d->refreshModel(); + +// if (!d->model) +// d->model = TableModel::findByClassName(metaObject()->className()); + + foreach (RelationModel *r, model->foregionKeys()) if(r->masterClassName == masterClassName) { setProperty(QString(r->localColumn).toLatin1().data(), - master->primaryValue()); - _changedProperties.insert(r->localColumn); + master->property(masterModel->primaryKey().toUtf8().data())); + d->changedProperties.insert(r->localColumn); return true; } @@ -114,18 +145,23 @@ bool Table::setParentTable(Table *master) TableSetBase *Table::parentTableSet() const { - return _parentTableSet; + Q_D(const Table); + return d->parentTableSet; } void Table::setParentTableSet(TableSetBase *parent) { - _parentTableSet = parent; - _parentTableSet->add(this); + Q_D(Table); + d->parentTableSet = parent; + + if (parent) + d->parentTableSet->add(this); } TableSetBase *Table::childTableSet(const QString &name) const { - foreach (TableSetBase *t, childTableSets) + Q_D(const Table); + foreach (TableSetBase *t, d->childTableSets) if (t->childClassName() == name) return t; return Q_NULLPTR; @@ -133,12 +169,15 @@ TableSetBase *Table::childTableSet(const QString &name) const int Table::save(Database *db) { + Q_D(Table); + QSqlQuery q = db->exec(db->sqlGenertor()->saveRecord(this, db->tableName(metaObject()->className()))); - if(status() == Added && isPrimaryKeyAutoIncrement()) - setProperty(primaryKey().toLatin1().data(), q.lastInsertId()); + auto model = db->model().tableByClassName(metaObject()->className()); + if(status() == Added && model->isPrimaryKeyAutoIncrement()) + setProperty(model->primaryKey().toLatin1().data(), q.lastInsertId()); - foreach(TableSetBase *ts, childTableSets) + foreach(TableSetBase *ts, d->childTableSets) ts->save(db); setStatus(FeatchedFromDB); @@ -147,12 +186,29 @@ int Table::save(Database *db) Table::Status Table::status() const { - return _status; + Q_D(const Table); + return static_cast(d->status); } void Table::setStatus(const Status &status) { - _status = status; + Q_D(Table); + d->status = status; +} + + + +TablePrivate::TablePrivate(Table *parent) : q_ptr(parent), + model(nullptr), status(Table::NewCreated), parentTableSet(nullptr) +{ + +} + +void TablePrivate::refreshModel() +{ + Q_Q(Table); +// if (!model) +// model = TableModel::findByClassName(q->metaObject()->className()); } NUT_END_NAMESPACE diff --git a/src/table.h b/src/table.h index 858a405..d661784 100644 --- a/src/table.h +++ b/src/table.h @@ -34,12 +34,16 @@ NUT_BEGIN_NAMESPACE class Database; class TableSetBase; class TableModel; +class TablePrivate; class NUT_EXPORT Table : public QObject { Q_OBJECT + TablePrivate *d_ptr; + Q_DECLARE_PRIVATE(Table) public: explicit Table(QObject *parentTableSet = nullptr); + virtual ~Table(); enum Status{ NewCreated, @@ -51,9 +55,15 @@ public: int save(Database *db); - QString primaryKey() const; - bool isPrimaryKeyAutoIncrement() const; - QVariant primaryValue() const; +// Q_DECL_DEPRECATED +// QString primaryKey() const; + +// Q_DECL_DEPRECATED +// bool isPrimaryKeyAutoIncrement() const; + +// Q_DECL_DEPRECATED +// QVariant primaryValue() const; + Status status() const; void setStatus(const Status &status); @@ -64,28 +74,31 @@ public: QSet changedProperties() const; - bool setParentTable(Table *master); + bool setParentTable(Table *master, TableModel *masterModel, TableModel *model); signals: public slots: protected: - void propertyChanged(QString propName); + void propertyChanged(const QString &propName); private: - TableModel *myModel; - Status _status; - QSet _changedProperties; + void setModel(TableModel *model); +// TableModel *myModel; +// Status _status; +// QSet _changedProperties; //TODO: is this removable? - TableSetBase *_parentTableSet; +// TableSetBase *_parentTableSet; - QSet childTableSets; +// QSet childTableSets; void clear(); void add(TableSetBase *); template friend class Query; + template + friend class TableSet; friend class TableSetBase; }; diff --git a/src/table_p.h b/src/table_p.h new file mode 100644 index 0000000..5f40ed3 --- /dev/null +++ b/src/table_p.h @@ -0,0 +1,32 @@ +#ifndef TABLEPRIVATE_H +#define TABLEPRIVATE_H + +#include "defines.h" + +#include + +NUT_BEGIN_NAMESPACE + +class TableModel; +class Table; +class TableSetBase; +class TablePrivate { + Table *q_ptr; + Q_DECLARE_PUBLIC(Table) + +public: + TablePrivate(Table *parent); + + + TableModel *model; + int status; + QSet changedProperties; + TableSetBase *parentTableSet; + QSet childTableSets; + + void refreshModel(); +}; + +NUT_END_NAMESPACE + +#endif // TABLEPRIVATE_H diff --git a/src/tablemodel.cpp b/src/tablemodel.cpp index 2908f26..22ddfeb 100644 --- a/src/tablemodel.cpp +++ b/src/tablemodel.cpp @@ -1,4 +1,4 @@ -/************************************************************************** +/************************************************************************** ** ** This file is part of Nut project. ** https://github.com/HamedMasafi/Nut @@ -69,18 +69,18 @@ void TableModel::setTypeId(const int &typeId) FieldModel *TableModel::field(int n) const { if (n < 0 || n >= _fields.count()) - return 0; + return nullptr; return _fields.at(n); } -FieldModel *TableModel::field(QString name) const +FieldModel *TableModel::field(const QString &name) const { foreach (FieldModel *f, _fields) if(f->name == name) return f; - return 0; + return nullptr; } QList TableModel::fields() const @@ -101,37 +101,6 @@ QStringList TableModel::fieldsNames() const return ret; } -QSet TableModel::allModels() -{ - return _allModels; -} - -/* - * This is not used anywhere - */ -TableModel *TableModel::findByTypeId(int typeId) -{ - foreach (TableModel *model, _allModels) - if(model->typeId() == typeId) - return model; - return 0; -} - -/** - * @brief TableModel::findByClassName - * Find a table model by class name - * @param className - * @return - */ -TableModel *TableModel::findByClassName(QString className) -{ - foreach (TableModel *model, _allModels) - if(model->className() == className) - return model; - - return 0; -} - bool TableModel::operator ==(const TableModel &t) const{ if(_name != t.name()) return false; @@ -156,24 +125,7 @@ bool TableModel::operator !=(const TableModel &t) const return !(*this == t); } -//bool TableModel::checkClassInfo(const QMetaClassInfo &classInfo, -// QString &type, QString &name, QString &value) -//{ -// if (!QString(classInfo.name()).startsWith(__nut_NAME_PERFIX)) { -// return false; -// } else { -// QStringList parts = QString(classInfo.value()).split("\n"); -// if (parts.count() != 3) -// return false; - -// type = parts[0]; -// name = parts[1]; -// value = parts[2]; -// return true; -// } -//} - -TableModel::TableModel(int typeId, QString tableName) +TableModel::TableModel(int typeId, const QString &tableName) { //TODO: check that // if (findByTypeId(typeId)) @@ -202,7 +154,7 @@ TableModel::TableModel(int typeId, QString tableName) } if(type == __nut_FIELD){ - FieldModel *f = new FieldModel; + auto *f = new FieldModel; f->name = f->displayName = name; _fields.append(f); } @@ -217,7 +169,7 @@ TableModel::TableModel(int typeId, QString tableName) f = fieldObj; if(!fieldObj) continue; - fieldObj->type = fieldProperty.type(); + fieldObj->type = static_cast(fieldProperty.type()); fieldObj->typeName = QString(fieldProperty.typeName()); } @@ -233,7 +185,7 @@ TableModel::TableModel(int typeId, QString tableName) } if(type == __nut_FOREGION_KEY){ - RelationModel *fk = new RelationModel; + auto *fk = new RelationModel; fk->slaveTable = this; fk->localColumn = name + "Id"; fk->localProperty = name; @@ -270,9 +222,6 @@ TableModel::TableModel(int typeId, QString tableName) f->isAutoIncrement = true; } } - - if(!findByTypeId(typeId) && !tableName.isNull()) - _allModels.insert(this); } /* @@ -291,7 +240,7 @@ TableModel::TableModel(int typeId, QString tableName) "primary_key": "id" }, */ -TableModel::TableModel(QJsonObject json, QString tableName) +TableModel::TableModel(const QJsonObject &json, const QString &tableName) : _typeId(0) { _name = tableName; @@ -299,9 +248,11 @@ TableModel::TableModel(QJsonObject json, QString tableName) QJsonObject relations = json.value(__FOREIGN_KEYS).toObject(); foreach (QString key, fields.keys()) { QJsonObject fieldObject = fields.value(key).toObject(); - FieldModel *f = new FieldModel; + //TODO: use FieldModel(QJsonObject) ctor + auto *f = new FieldModel; f->name = fieldObject.value(__NAME).toString(); - f->type = QVariant::nameToType(fieldObject.value(__TYPE).toString().toLatin1().data()); + f->type = static_cast(QMetaType::type(fieldObject.value(__TYPE).toString().toLatin1().data())); + f->typeName = QMetaType::typeName(f->type); if(fieldObject.contains(__nut_NOT_NULL)) f->notNull = fieldObject.value(__nut_NOT_NULL).toBool(); @@ -377,7 +328,7 @@ RelationModel *TableModel::foregionKey(const QString &otherTable) const if(fk->masterClassName == otherTable) return fk; - return 0; + return nullptr; } RelationModel *TableModel::foregionKeyByField(const QString &fieldName) const @@ -386,7 +337,7 @@ RelationModel *TableModel::foregionKeyByField(const QString &fieldName) const if(fk->localColumn == fieldName) return fk; - return 0; + return nullptr; } QString TableModel::toString() const @@ -396,8 +347,7 @@ QString TableModel::toString() const sl.append(f->name + " " + QVariant::typeToName(f->type)); QString ret = QString("%1 (%2)") - .arg(_name) - .arg(sl.join(", ")); + .arg(_name, sl.join(", ")); return ret; } @@ -409,10 +359,18 @@ QString TableModel::primaryKey() const return QString(); } +bool TableModel::isPrimaryKeyAutoIncrement() const +{ + FieldModel *pk = field(primaryKey()); + if (!pk) + return false; + return pk->isAutoIncrement; +} + FieldModel::FieldModel(const QJsonObject &json) { name = json.value(__NAME).toString(); - type = static_cast(json.value(__TYPE).toInt()); + type = static_cast(json.value(__TYPE).toInt()); length = json.value(__nut_LEN).toInt(); notNull = json.value(__nut_NOT_NULL).toBool(); isAutoIncrement = json.value(__nut_AUTO_INCREMENT).toBool(); @@ -440,7 +398,7 @@ RelationModel::RelationModel(const QJsonObject &obj) localProperty = obj.value("localProperty").toString(); masterClassName = obj.value("masterClassName").toString(); foreignColumn = obj.value("foreignColumn").toString(); - slaveTable = masterTable = 0; + slaveTable = masterTable = nullptr; } QJsonObject RelationModel::toJson() const diff --git a/src/tablemodel.h b/src/tablemodel.h index 345374f..6b43071 100644 --- a/src/tablemodel.h +++ b/src/tablemodel.h @@ -40,8 +40,7 @@ struct FieldModel{ explicit FieldModel(const QJsonObject &json); QString name; - //TODO: QMetaType::Type?? - QVariant::Type type; + QMetaType::Type type; QString typeName; int length; QString defaultValue; @@ -70,8 +69,9 @@ struct FieldModel{ }; struct RelationModel{ - RelationModel() : localColumn(QString()), localProperty(QString()), slaveTable(0), - foreignColumn(QString()), masterTable(0), masterClassName(QString()) + RelationModel() : localColumn(QString()), localProperty(QString()), + slaveTable(nullptr), foreignColumn(QString()), masterTable(nullptr), + masterClassName(QString()) {} explicit RelationModel(const QJsonObject &obj); @@ -87,29 +87,28 @@ struct RelationModel{ QJsonObject toJson() const; }; + bool operator ==(const RelationModel &l, const RelationModel &r); bool operator !=(const RelationModel &l, const RelationModel &r); -class TableModel + +class NUT_EXPORT TableModel { public: - explicit TableModel(int typeId, QString tableName = QString()); - explicit TableModel(QJsonObject json, QString tableName); + explicit TableModel(int typeId, const QString &tableName = QString()); + explicit TableModel(const QJsonObject &json, const QString &tableName); virtual ~TableModel(); QJsonObject toJson() const; -// static TableScheema *registerTable(int typeId, QString tableName); -// static void createForegionKeys(); -// static TableModel* model(QString className); - FieldModel *field(int n) const; - FieldModel *field(QString name) const; + FieldModel *field(const QString &name) const; RelationModel *foregionKey(const QString &otherTable) const; RelationModel *foregionKeyByField(const QString &fieldName) const; QString toString() const; QString primaryKey() const; + bool isPrimaryKeyAutoIncrement() const; QString name() const; void setName(const QString &name); @@ -123,11 +122,6 @@ public: QList foregionKeys() const; QStringList fieldsNames() const; - static QSet allModels(); - static TableModel *findByTypeId(int typeId); -// static TableModel *findByName(QString name); - static TableModel *findByClassName(QString className); - bool operator ==(const TableModel &t) const; bool operator !=(const TableModel &t) const; @@ -137,9 +131,9 @@ private: int _typeId; QList _fields; QList _foreignKeys; + + Q_DECL_DEPRECATED static QSet_allModels; -// bool checkClassInfo(const QMetaClassInfo &classInfo, -// QString &type, QString &name, QString &value); }; NUT_END_NAMESPACE diff --git a/src/tableset.h b/src/tableset.h index a6bfd2c..d85e1d6 100644 --- a/src/tableset.h +++ b/src/tableset.h @@ -29,102 +29,117 @@ #include "tablesetbase_p.h" #include "table.h" +#include "bulkinserter.h" +#include "databasemodel.h" +#include "tablesetbasedata.h" NUT_BEGIN_NAMESPACE template class Query; +class BulkInserter; +class Database; + template class NUT_EXPORT TableSet : public TableSetBase { public: + typedef T value_type; + typedef T *pointer; + typedef T &reference; + explicit TableSet(Database *parent); explicit TableSet(Table *parent); - void append(T *t); - void append(QList t); + void append(Row t); + void append(RowList t); 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; - Query *query(); - Query *query(bool autoDelete); + Query *query(bool autoDelete = true); + BulkInserter *bulkInserter(); }; template Q_OUTOFLINE_TEMPLATE TableSet::TableSet(Database *parent) : TableSetBase(parent) { - _childClassName = T::staticMetaObject.className(); + data->childClassName = T::staticMetaObject.className(); } template Q_OUTOFLINE_TEMPLATE TableSet::TableSet(Table *parent) : TableSetBase(parent) { - _childClassName = T::staticMetaObject.className(); -} - -template -Q_OUTOFLINE_TEMPLATE Query *TableSet::query() -{ - Query *q = new Query(_database, this, true); - - return q; + data->childClassName = T::staticMetaObject.className(); } template Q_OUTOFLINE_TEMPLATE Query *TableSet::query(bool autoDelete) { - Query *q = new Query(_database, this, autoDelete); + Query *q = new Query(data->database, this, autoDelete); return q; } +template +Q_OUTOFLINE_TEMPLATE BulkInserter *TableSet::bulkInserter() +{ + BulkInserter *bi = new BulkInserter(data->database, data->childClassName); + return bi; +} + template Q_OUTOFLINE_TEMPLATE int TableSet::length() const { - return _tables.count(); + return data->tables.count(); } template Q_OUTOFLINE_TEMPLATE T *TableSet::at(int i) const { - return (T*)_tablesList.at(i); + //TODO: check + return reinterpret_cast(data->childRows.at(i)); } template Q_OUTOFLINE_TEMPLATE const T &TableSet::operator[](int i) const { - return _tablesList[i]; + return data->childRows[i]; } template -Q_OUTOFLINE_TEMPLATE void TableSet::append(T *t) +Q_OUTOFLINE_TEMPLATE void TableSet::append(Row t) { - _tables.insert(t); - _tablesList.append(t); -// rows.append(t); + data.detach(); + data->childs.append(t); + data->tables.insert(t.data()); + data->childRows.append(t.data()); + +// if (_database) +// t->setModel(_database->model().tableByClassName(t->metaObject()->className())); + t->setParentTableSet(this); if(t->status() != Table::FeatchedFromDB) t->setStatus(Table::Added); } template -Q_OUTOFLINE_TEMPLATE void TableSet::append(QList t) +Q_OUTOFLINE_TEMPLATE void TableSet::append(RowList t) { - foreach (T* i, t) + foreach (Row i, t) append(i); } template Q_OUTOFLINE_TEMPLATE void TableSet::remove(T *t) { - _tables.remove(t); + data.detach(); + data->childs.removeOne(t); + data->tables.remove(t); t->setStatus(Table::Deleted); } diff --git a/src/tablesetbase.cpp b/src/tablesetbase.cpp index f5bde89..1cf0b0d 100644 --- a/src/tablesetbase.cpp +++ b/src/tablesetbase.cpp @@ -22,27 +22,40 @@ #include "database.h" #include "tablesetbase_p.h" #include "databasemodel.h" +#include "tablesetbasedata.h" NUT_BEGIN_NAMESPACE -TableSetBase::TableSetBase(Database *parent) : QObject(parent), _database(parent), _table(0), - _tableName(QString()) +TableSetBase::TableSetBase(Database *parent) : QObject(parent), + data(new TableSetBaseData(parent)) { parent->add(this); } -TableSetBase::TableSetBase(Table *parent) : QObject(parent), _database(0), _table(parent), - _tableName(QString()) +TableSetBase::TableSetBase(Table *parent) : QObject(parent), + data(new TableSetBaseData(parent)) { parent->add(this); } +TableSetBase::~TableSetBase() +{ + foreach (Table *t, data->tables) + t->setParentTableSet(nullptr); +} + int TableSetBase::save(Database *db, bool cleanUp) { int rowsAffected = 0; - foreach (Table *t, _tablesList) { - if(_table) - t->setParentTable(_table); + TableModel *masterModel = nullptr; + if (data->table) + masterModel = db->model().tableByClassName(data->table->metaObject()->className()); + + foreach (Table *t, data->childRows) { + if(data->table) + t->setParentTable(data->table, + masterModel, + db->model().tableByClassName(t->metaObject()->className())); if(t->status() == Table::Added || t->status() == Table::Modified @@ -55,39 +68,50 @@ int TableSetBase::save(Database *db, bool cleanUp) } if (cleanUp) - _tablesList.clear(); + data->childRows.clear(); return rowsAffected; } void TableSetBase::clearChilds() { - foreach (Table *t, _tablesList) +#ifndef NUT_SHARED_POINTER + foreach (Table *t, data->_childRows) t->deleteLater(); - _tablesList.clear(); +#endif + data->childRows.clear(); } void TableSetBase::add(Table *t) { - if(!_tables.contains(t)){ - _tables.insert(t); - _tablesList.append(t); + if(!data->tables.contains(get(t))){ + data.detach(); + data->tables.insert(get(t)); + data->childRows.append(get(t)); } } +void TableSetBase::remove(Table *t) +{ + data.detach(); + data->tables.remove(get(t)); + data->childRows.removeOne(get(t)); +} + QString TableSetBase::childClassName() const { - return _childClassName; + return data->childClassName; } Database *TableSetBase::database() const { - return _database; + return data->database; } void TableSetBase::setDatabase(Database *database) { - _database = database; + data.detach(); + data->database = database; } NUT_END_NAMESPACE diff --git a/src/tablesetbase_p.h b/src/tablesetbase_p.h index df95a71..cb8c6d2 100644 --- a/src/tablesetbase_p.h +++ b/src/tablesetbase_p.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "defines.h" @@ -31,12 +32,14 @@ NUT_BEGIN_NAMESPACE class Table; class Database; +class TableSetBaseData; class TableSetBase : public QObject { public: explicit TableSetBase(Database *parent); explicit TableSetBase(Table *parent); + virtual ~TableSetBase(); virtual int save(Database *db, bool cleanUp = false); void clearChilds(); @@ -46,15 +49,17 @@ public: void setDatabase(Database *database); protected: - QSet _tables; - QList _tablesList; - QString _tableName; - Database *_database; - Table *_table; - QString _childClassName; +// QSet _tables; +// RowList
_childRows; +// Database *_database; +// Table *_table; +//// QString _tableName; +// QString _childClassName; + QExplicitlySharedDataPointer data; private: void add(Table* t); + void remove(Table *t); friend class Table; friend class QueryBase; diff --git a/src/tablesetbasedata.h b/src/tablesetbasedata.h new file mode 100644 index 0000000..f0fe881 --- /dev/null +++ b/src/tablesetbasedata.h @@ -0,0 +1,53 @@ +/************************************************************************** +** +** 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 TABLESETBASEDATA_H +#define TABLESETBASEDATA_H + +#include +#include "defines.h" + +NUT_BEGIN_NAMESPACE + +class Table; +class Database; +class TableSetBaseData : public QSharedData +{ +public: + TableSetBaseData(Database *parent) : + database(parent), table(nullptr) + { } + + TableSetBaseData(Table *parent) : + database(nullptr), table(parent) + { } + + QSet tables; + QList childRows; + RowList
childs; + + Database *database; + Table *table; + QString childClassName; +}; + +NUT_END_NAMESPACE + +#endif // TABLESETBASEDATA_H diff --git a/src/tuple.cpp b/src/tuple.cpp new file mode 100644 index 0000000..c87288d --- /dev/null +++ b/src/tuple.cpp @@ -0,0 +1,2 @@ +#include "tuple.h" + diff --git a/src/tuple.h b/src/tuple.h new file mode 100644 index 0000000..e3131fa --- /dev/null +++ b/src/tuple.h @@ -0,0 +1,18 @@ +#ifndef TUPLE_H +#define TUPLE_H + +#include + +//class AbstractTuple +//{ +// Q_GADGET +//}; + +//template +//class Tuple +//{ +//public: +// T _1; +//}; + +#endif // TUPLE_H diff --git a/test/benckmark/tst_benchmark.pro b/test/benckmark/tst_benchmark.pro deleted file mode 100644 index 6400e16..0000000 --- a/test/benckmark/tst_benchmark.pro +++ /dev/null @@ -1,21 +0,0 @@ -QT += qml quick testlib sql - -QT -= gui - -TARGET = tst_nut -CONFIG += warn_on qmltestcase c++11 -INCLUDEPATH += $$PWD/../../src $$PWD/../common -include(../../nut.pri) -TEMPLATE = app -IMPORTPATH += $$OUT_PWD/../src/imports -SOURCES += \ - maintest.cpp \ - ../common/comment.cpp \ - ../common/post.cpp \ - ../common/weblogdatabase.cpp - -HEADERS += \ - maintest.h \ - ../common/comment.h \ - ../common/post.h \ - ../common/weblogdatabase.h diff --git a/test/common/comment.h b/test/common/comment.h index 808b521..437aa20 100644 --- a/test/common/comment.h +++ b/test/common/comment.h @@ -3,7 +3,6 @@ #include #include -#include #include "table.h" #ifdef NUT_NAMESPACE @@ -16,14 +15,14 @@ class Comment : public Table { Q_OBJECT - NUT_PRIMARY_KEY(id) - NUT_DECLARE_FIELD(QUuid, id, id, setId) + 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_DECLARE_FIELD(qreal, point, point, setPoint) NUT_FOREGION_KEY(Post, int, post, post, setPost) - NUT_FOREGION_KEY(User, QUuid, author, author, setAuthor) + NUT_FOREGION_KEY(User, int, author, author, setAuthor) public: Q_INVOKABLE explicit Comment(QObject *parentTableSet = nullptr); diff --git a/test/common/consts.h b/test/common/consts.h index 9c0c228..ca49e2e 100644 --- a/test/common/consts.h +++ b/test/common/consts.h @@ -1,29 +1,21 @@ #ifndef CONSTS_H #define CONSTS_H -#define PRINT(x) qDebug() << #x "=" << x; +#include + +#define REGISTER(x) qDebug() << (#x) << "type id:" << qMetaTypeId() +#define PRINT(x) +//qDebug() << (#x "=") << (x); #define TIC() QElapsedTimer timer; timer.start() #define TOC() qDebug() << QString("Elapsed time: %1ms for %2") \ .arg(timer.elapsed() / 1000.) \ .arg(__func__) -//#define DRIVER "QPSQL" -//#define HOST "127.0.0.1" -//#define DATABASE "nutdb2" -//#define USERNAME "postgres" -//#define PASSWORD "856856" - #define DRIVER "QSQLITE" +#define DATABASE QString("nut_test_%1_db").arg(metaObject()->className()).toLower() #define HOST "127.0.0.1" -#define DATABASE "nutdb1" -#define USERNAME "root" -#define PASSWORD "onlyonlyi" - -//#define DRIVER "QODBC" -//#define HOST "127.0.0.1" -//#define DATABASE "DRIVER={SQL Server};Server=.;Database=Nut2;Uid=sa;Port=1433;Pwd=qwe123!@#;WSID=." -//#define USERNAME "sa" -//#define PASSWORD "qwe123!@#" +#define USERNAME "postgres" +#define PASSWORD "856856" #ifdef Q_OS_LINUX # define OS "Linux" @@ -46,4 +38,5 @@ << "\n\tTest:" << metaObject()->className() \ << "\n****************************\n"; + #endif // CONSTS_H diff --git a/test/common/nut-lib.pri b/test/common/nut-lib.pri new file mode 100644 index 0000000..ade3e61 --- /dev/null +++ b/test/common/nut-lib.pri @@ -0,0 +1,12 @@ +win32 { + CONFIG(debug,debug|release): LIBDIR = $$absolute_path($$OUT_PWD/../../src/debug) + CONFIG(release,debug|release): LIBDIR = $$absolute_path($$OUT_PWD/../../src/release) +} else { + LIBDIR = $$absolute_path($$OUT_PWD/../../src) +} + +LIBS += -L$$LIBDIR -lnut +INCLUDEPATH += $$PWD/../../src $$PWD/../common +#include(../../src/src.pri) + +DEFINES += NUT_SHARED_POINTER diff --git a/test/common/post.h b/test/common/post.h index 7759b8c..cca0944 100644 --- a/test/common/post.h +++ b/test/common/post.h @@ -33,7 +33,7 @@ class Post : public Table NUT_DECLARE_CHILD_TABLE(Score, scores) public: - Q_INVOKABLE Post(QObject *parentTableSet = 0); + Q_INVOKABLE Post(QObject *parentTableSet = nullptr); signals: diff --git a/test/common/user.h b/test/common/user.h index c16f6ec..ad7a2c3 100644 --- a/test/common/user.h +++ b/test/common/user.h @@ -17,8 +17,8 @@ class User : public Nut::Table { Q_OBJECT - NUT_PRIMARY_KEY(id) - NUT_DECLARE_FIELD(QUuid, id, id, setId) + NUT_PRIMARY_AUTO_INCREMENT(id) + NUT_DECLARE_FIELD(int, id, id, setId) NUT_NOT_NULL(username) NUT_LEN(username, 50) diff --git a/test/phrases/maintest.cpp b/test/phrases/maintest.cpp deleted file mode 100644 index 3e94a69..0000000 --- a/test/phrases/maintest.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include -#include - -#include "maintest.h" -#include "phrase.h" - -using namespace Nut; - -MainTest::MainTest(QObject *parent) : QObject(parent) -{ -} - -void MainTest::initTestCase() -{ - -} - -void MainTest::no1() -{ - FieldPhrase id("main", "id"); - FieldPhrase name("main", "name"); - FieldPhrase last_name("main", "last_name"); - FieldPhrase date("main", "date"); - auto w = (id == 4 && name == "hi"); -} - -QTEST_MAIN(MainTest) diff --git a/test/phrases/maintest.h b/test/phrases/maintest.h deleted file mode 100644 index 78d83bc..0000000 --- a/test/phrases/maintest.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef MAINTEST_H -#define MAINTEST_H - -#include -#include - -class Post; -class User; -class MainTest : public QObject -{ - Q_OBJECT - -public: - explicit MainTest(QObject *parent = nullptr); - -signals: - -private slots: - void initTestCase(); - void no1(); -}; - -#endif // MAINTEST_H diff --git a/test/phrases/tst_phrases.pro b/test/phrases/tst_phrases.pro deleted file mode 100644 index 5dd3829..0000000 --- a/test/phrases/tst_phrases.pro +++ /dev/null @@ -1,15 +0,0 @@ -QT += qml quick testlib sql -QT -= gui - -TARGET = tst_phrases -TEMPLATE = app - -CONFIG += warn_on qmltestcase c++11 -INCLUDEPATH += $$PWD/../../src $$PWD/../common -include(../../nut.pri) -IMPORTPATH += $$OUT_PWD/../src/imports -SOURCES += \ - maintest.cpp - -HEADERS += \ - maintest.h diff --git a/test/quuid/maintest.cpp b/test/quuid/maintest.cpp deleted file mode 100644 index dc43140..0000000 --- a/test/quuid/maintest.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include -#include -#include -#include - -#include "consts.h" - -#include "maintest.h" -#include "query.h" -#include "tableset.h" -#include "tablemodel.h" - -#include "test.h" - -MainTest::MainTest(QObject *parent) : QObject(parent) -{ -} - -void MainTest::initTestCase() -{ - qDebug() << "Test type id:" << qRegisterMetaType(); - qDebug() << "DB type id:" << qRegisterMetaType(); - - db.setDriver(DRIVER); - db.setHostName(HOST); - db.setDatabaseName("nut_tst_quuid"); - db.setUserName(USERNAME); - db.setPassword(PASSWORD); - - bool ok = db.open(); - - db.tests()->query()->remove(); - - QTEST_ASSERT(ok); -} - -void MainTest::add() -{ - TIC(); - QUuid uuid = QUuid::createUuid(); - Test t; - t.setId(uuid); - t.setUsername("test username"); - db.tests()->append(&t); - db.saveChanges(); - TOC(); - - Test *t2 = db.tests()->query() - ->where(Test::idField() == uuid) - ->first(); - - TOC(); - QTEST_ASSERT(t2->id() == uuid); -} - -void MainTest::cleanupTestCase() -{ - qDeleteAll(Nut::TableModel::allModels()); -// Nut::DatabaseModel::deleteAllModels(); -} - -QTEST_MAIN(MainTest) diff --git a/test/quuid/tst_quuid.pro b/test/quuid/tst_quuid.pro deleted file mode 100644 index 0a702dc..0000000 --- a/test/quuid/tst_quuid.pro +++ /dev/null @@ -1,20 +0,0 @@ -QT += qml quick testlib sql -QT -= gui - -TARGET = tst_nut -TEMPLATE = app - -CONFIG += warn_on qmltestcase c++11 -INCLUDEPATH += $$PWD/../../src $$PWD/../common -include(../../nut.pri) -IMPORTPATH += $$OUT_PWD/../src/imports -SOURCES += \ - maintest.cpp \ - testdatabase.cpp \ - test.cpp - -HEADERS += \ - maintest.h \ - ../common/consts.h \ - testdatabase.h \ - test.h diff --git a/test/test.pro b/test/test.pro new file mode 100644 index 0000000..504a9c5 --- /dev/null +++ b/test/test.pro @@ -0,0 +1,14 @@ +TEMPLATE = subdirs + +SUBDIRS += \ + tst_basic \ + tst_benckmark \ +# tst_commands \ + tst_datatypes \ + #tst_join \ + tst_phrases \ + tst_quuid \ + tst_generators \ + tst_upgrades \ + tst_json + diff --git a/test/basic/maintest.cpp b/test/tst_basic/tst_basic.cpp similarity index 74% rename from test/basic/maintest.cpp rename to test/tst_basic/tst_basic.cpp index 5cc87d8..f728887 100644 --- a/test/basic/maintest.cpp +++ b/test/tst_basic/tst_basic.cpp @@ -5,7 +5,7 @@ #include "consts.h" -#include "maintest.h" +#include "tst_basic.h" #include "query.h" #include "tableset.h" #include "tablemodel.h" @@ -16,19 +16,11 @@ #include "comment.h" #include "score.h" -#define PRINT(x) qDebug() << #x "=" << x; -#define TIC() QElapsedTimer timer; timer.start() -#define TOC() qDebug() << QString("Elapsed time: %1ms for %2") \ - .arg(timer.elapsed() / 1000.) \ - .arg(__func__) - -#define REGISTER(x) qDebug() << #x << "type id:" << qRegisterMetaType() - -MainTest::MainTest(QObject *parent) : QObject(parent) +BasicTest::BasicTest(QObject *parent) : QObject(parent) { } -void MainTest::initTestCase() +void BasicTest::initTestCase() { //register all entities with Qt-MetaType mechanism REGISTER(User); @@ -39,7 +31,7 @@ void MainTest::initTestCase() db.setDriver(DRIVER); db.setHostName(HOST); - db.setDatabaseName("nut_tst_basic"); + db.setDatabaseName(DATABASE); db.setUserName(USERNAME); db.setPassword(PASSWORD); @@ -52,7 +44,7 @@ void MainTest::initTestCase() db.scores()->query()->remove(); } -void MainTest::dataScheema() +void BasicTest::dataScheema() { // auto json = db.model().toJson(); // auto model = DatabaseModel::fromJson(json); @@ -62,20 +54,19 @@ void MainTest::dataScheema() // QTEST_ASSERT(model == db.model()); } -void MainTest::createUser() +void BasicTest::createUser() { - user = new User; - user->setId(QUuid::createUuid()); + user = Nut::create(); user->setUsername("admin"); user->setPassword("123456"); db.users()->append(user); db.saveChanges(); } -void MainTest::createPost() +void BasicTest::createPost() { TIC(); - Post *newPost = new Post; + auto newPost = Nut::create(); newPost->setTitle("post title"); newPost->setSaveDate(QDateTime::currentDateTime()); newPost->setPublic(false); @@ -83,15 +74,14 @@ void MainTest::createPost() db.posts()->append(newPost); for(int i = 0 ; i < 3; i++){ - Comment *comment = new Comment; - comment->setId(QUuid::createUuid()); + auto comment = Nut::create(); comment->setMessage("comment #" + QString::number(i)); comment->setSaveDate(QDateTime::currentDateTime()); comment->setAuthorId(user->id()); - newPost->comments()->append(comment); + db.comments()->append(comment); } for (int i = 0; i < 10; ++i) { - Score *score = new Score; + auto score = Nut::create(); score->setScore(i % 5); newPost->scores()->append(score); } @@ -105,19 +95,18 @@ void MainTest::createPost() qDebug() << "New post inserted with id:" << newPost->id(); } -void MainTest::createPost2() +void BasicTest::createPost2() { //create post on the fly QVariant postIdVar = db.posts()->query()->insert( (Post::titleField() = "This is a sample") & (Post::isPublicField() = true)); - QTEST_ASSERT(postIdVar.type() == QVariant::LongLong); + QTEST_ASSERT(postIdVar.type() == QVariant::LongLong || postIdVar.type() == QVariant::ULongLong); int postId = postIdVar.toInt(); for(int i = 0 ; i < 3; i++){ - Comment *comment = new Comment; - comment->setId(QUuid::createUuid()); + auto comment = Nut::create(); comment->setMessage("comment #" + QString::number(i + 2)); comment->setSaveDate(QDateTime::currentDateTime()); comment->setAuthor(user); @@ -130,7 +119,7 @@ void MainTest::createPost2() QTEST_ASSERT(postId != 0); } -void MainTest::updatePostOnTheFly() +void BasicTest::updatePostOnTheFly() { auto c = db.posts()->query() ->where(Post::idField() == postId) @@ -139,7 +128,7 @@ void MainTest::updatePostOnTheFly() QTEST_ASSERT(c == 1); } -void MainTest::selectPublicts() +void BasicTest::selectPublicts() { auto q = db.posts()->query() ->where(Post::isPublicField()) @@ -153,7 +142,7 @@ void MainTest::selectPublicts() QTEST_ASSERT(q2 == 1); } -void MainTest::selectPosts() +void BasicTest::selectPosts() { auto q = db.posts()->query() ->join() @@ -177,7 +166,7 @@ void MainTest::selectPosts() db.cleanUp(); } -void MainTest::selectScoreAverage() +void BasicTest::selectScoreAverage() { auto a = db.scores()->query() ->join() @@ -186,7 +175,7 @@ void MainTest::selectScoreAverage() qDebug() << a; } -void MainTest::selectFirst() +void BasicTest::selectFirst() { auto posts = db.posts()->query() ->first(); @@ -194,7 +183,7 @@ void MainTest::selectFirst() QTEST_ASSERT(posts != Q_NULLPTR); } -void MainTest::selectPostsWithoutTitle() +void BasicTest::selectPostsWithoutTitle() { auto q = db.posts()->query(); q->setWhere(Post::titleField().isNull()); @@ -202,7 +191,7 @@ void MainTest::selectPostsWithoutTitle() QTEST_ASSERT(count == 0); } -void MainTest::selectPostIds() +void BasicTest::selectPostIds() { auto q = db.posts()->query(); auto ids = q->select(Post::idField()); @@ -210,13 +199,13 @@ qDebug() << ids.count(); QTEST_ASSERT(ids.count() == 2); } -void MainTest::testDate() +void BasicTest::testDate() { QDateTime d = QDateTime::currentDateTime(); QTime t = QTime(d.time().hour(), d.time().minute(), d.time().second()); d.setTime(t); - Post *newPost = new Post; + auto newPost = Nut::create(); newPost->setTitle("post title"); newPost->setSaveDate(d); @@ -228,38 +217,39 @@ void MainTest::testDate() ->setWhere(Post::idField() == newPost->id()) ->first(); + qDebug() << q->saveDate() << d; QTEST_ASSERT(q->saveDate() == d); } -void MainTest::join() +void BasicTest::join() { - TIC(); - auto q = db.comments()->query() - ->join() - ->join(); +// TIC(); +// auto q = db.comments()->query() +// ->join() +// ->join(); - auto comments = q->toList(); +// auto comments = q->toList(); - TOC(); - QTEST_ASSERT(comments.length()); - QTEST_ASSERT(comments[0]->author()); - QTEST_ASSERT(comments[0]->author()->username() == "admin"); +// TOC(); +// QTEST_ASSERT(comments.length()); +// QTEST_ASSERT(comments[0]->author()); +// QTEST_ASSERT(comments[0]->author()->username() == "admin"); } -void MainTest::selectWithInvalidRelation() +void BasicTest::selectWithInvalidRelation() { auto q = db.posts()->query(); q->join("Invalid_Class_Name"); q->toList(); } -void MainTest::modifyPost() +void BasicTest::modifyPost() { auto q = db.posts()->query(); q->setWhere(Post::idField() == postId); - Post *post = q->first(); + Nut::Row post = q->first(); QTEST_ASSERT(post != nullptr); @@ -274,7 +264,7 @@ void MainTest::modifyPost() QTEST_ASSERT(post->title() == "new name"); } -void MainTest::emptyDatabase() +void BasicTest::emptyDatabase() { // auto commentsCount = db.comments()->query()->remove(); // auto postsCount = db.posts()->query()->remove(); @@ -282,15 +272,18 @@ void MainTest::emptyDatabase() // QTEST_ASSERT(commentsCount == 6); } -void MainTest::cleanupTestCase() +void BasicTest::cleanupTestCase() { - post->deleteLater(); - user->deleteLater(); +// post->deleteLater(); +// user->deleteLater(); //release models before exiting - qDeleteAll(TableModel::allModels()); +// qDeleteAll(TableModel::allModels()); + +// if (QFile::remove("nut_tst_basic")) +// qDebug() << "database removed"; PRINT_FORM(db); } -QTEST_MAIN(MainTest) +QTEST_MAIN(BasicTest) diff --git a/test/basic/maintest.h b/test/tst_basic/tst_basic.h similarity index 84% rename from test/basic/maintest.h rename to test/tst_basic/tst_basic.h index c5e9699..89b59a9 100644 --- a/test/basic/maintest.h +++ b/test/tst_basic/tst_basic.h @@ -7,16 +7,16 @@ #include "weblogdatabase.h" class Post; class User; -class MainTest : public QObject +class BasicTest : public QObject { Q_OBJECT WeblogDatabase db; int postId; - Post *post; - User *user; + Nut::Row post; + Nut::Row user; public: - explicit MainTest(QObject *parent = nullptr); + explicit BasicTest(QObject *parent = nullptr); signals: diff --git a/test/tst_basic/tst_basic.pro b/test/tst_basic/tst_basic.pro new file mode 100644 index 0000000..24ffb66 --- /dev/null +++ b/test/tst_basic/tst_basic.pro @@ -0,0 +1,27 @@ +QT += testlib sql + +TARGET = tst_basic +TEMPLATE = app + +CONFIG += warn_on c++11 + +include(../common/nut-lib.pri) + +SOURCES += \ + ../common/comment.cpp \ + ../common/post.cpp \ + ../common/user.cpp \ + ../common/weblogdatabase.cpp \ + ../common/score.cpp \ + tst_basic.cpp + +HEADERS += \ + ../common/consts.h \ + ../common/comment.h \ + ../common/post.h \ + ../common/user.h \ + ../common/weblogdatabase.h \ + ../common/score.h \ + tst_basic.h + +include($$PWD/../../ci-test-init.pri) diff --git a/test/benckmark/maintest.h b/test/tst_benckmark/maintest.h similarity index 78% rename from test/benckmark/maintest.h rename to test/tst_benckmark/maintest.h index b98b7ce..f287e86 100644 --- a/test/benckmark/maintest.h +++ b/test/tst_benckmark/maintest.h @@ -6,7 +6,7 @@ #include "weblogdatabase.h" class Post; -class MainTest : public QObject +class BenchmarkTest : public QObject { Q_OBJECT WeblogDatabase db; @@ -14,7 +14,7 @@ class MainTest : public QObject Post *post; Query *q; public: - explicit MainTest(QObject *parent = nullptr); + explicit BenchmarkTest(QObject *parent = nullptr); signals: diff --git a/test/benckmark/maintest.cpp b/test/tst_benckmark/tst_benchmark.cpp similarity index 60% rename from test/benckmark/maintest.cpp rename to test/tst_benckmark/tst_benchmark.cpp index f8b8877..2618f14 100644 --- a/test/benckmark/maintest.cpp +++ b/test/tst_benckmark/tst_benchmark.cpp @@ -10,23 +10,27 @@ #include "tablemodel.h" #include "databasemodel.h" +#include "user.h" #include "post.h" #include "comment.h" +#include "score.h" -MainTest::MainTest(QObject *parent) : QObject(parent) +BenchmarkTest::BenchmarkTest(QObject *parent) : QObject(parent) { } -void MainTest::initTestCase() +void BenchmarkTest::initTestCase() { - qDebug() << "User type id:" << qRegisterMetaType(); - qDebug() << "Comment type id:" << qRegisterMetaType(); - qDebug() << "DB type id:" << qRegisterMetaType(); + REGISTER(User); + REGISTER(Post); + REGISTER(Score); + REGISTER(Comment); + REGISTER(WeblogDatabase); db.setDriver(DRIVER); db.setHostName(HOST); - db.setDatabaseName(DATABASE); + db.setDatabaseName("tst_benchmark_db"); db.setUserName(USERNAME); db.setPassword(PASSWORD); @@ -35,21 +39,21 @@ void MainTest::initTestCase() QTEST_ASSERT(ok); } -void MainTest::insert1kPost() +void BenchmarkTest::insert1kPost() { QTime t; t.start(); - for (int i = 0; i < 1000; ++i) { - Post *newPost = new Post; + for (int i = 0; i < 100; ++i) { + auto newPost = Nut::create(); newPost->setTitle("post title"); newPost->setSaveDate(QDateTime::currentDateTime()); db.posts()->append(newPost); } db.saveChanges(); - qDebug("1k post inserted in %d ms", t.elapsed()); + } -QTEST_MAIN(MainTest) +QTEST_MAIN(BenchmarkTest) diff --git a/test/basic/tst_basic.pro b/test/tst_benckmark/tst_benckmark.pro similarity index 55% rename from test/basic/tst_basic.pro rename to test/tst_benckmark/tst_benckmark.pro index a274bdc..104ecda 100644 --- a/test/basic/tst_basic.pro +++ b/test/tst_benckmark/tst_benckmark.pro @@ -1,20 +1,19 @@ -QT += qml quick testlib sql -QT -= gui +QT += testlib sql -TARGET = tst_nut +TARGET = tst_benchmark TEMPLATE = app -CONFIG += warn_on qmltestcase c++11 -INCLUDEPATH += $$PWD/../../src $$PWD/../common -include(../../nut.pri) -IMPORTPATH += $$OUT_PWD/../src/imports +CONFIG += warn_on c++11 + +include(../common/nut-lib.pri) + SOURCES += \ - maintest.cpp \ ../common/comment.cpp \ ../common/post.cpp \ ../common/user.cpp \ ../common/weblogdatabase.cpp \ - ../common/score.cpp + ../common/score.cpp \ + tst_benchmark.cpp HEADERS += \ maintest.h \ @@ -24,3 +23,5 @@ HEADERS += \ ../common/user.h \ ../common/weblogdatabase.h \ ../common/score.h + +include($$PWD/../../ci-test-init.pri) diff --git a/test/commands/maintest.cpp b/test/tst_commands/tst_commands.cpp similarity index 69% rename from test/commands/maintest.cpp rename to test/tst_commands/tst_commands.cpp index a5c46fd..ee234d2 100644 --- a/test/commands/maintest.cpp +++ b/test/tst_commands/tst_commands.cpp @@ -4,7 +4,7 @@ #include "consts.h" -#include "maintest.h" +#include "tst_commands.h" #include "query.h" #include "tableset.h" #include "tablemodel.h" @@ -12,17 +12,19 @@ #include "post.h" #include "comment.h" +#include "user.h" +#include "score.h" -MainTest::MainTest(QObject *parent) : QObject(parent) +CommandsTest::CommandsTest(QObject *parent) : QObject(parent) { } -void MainTest::initTestCase() +void CommandsTest::initTestCase() { - qDebug() << "User type id:" << qRegisterMetaType(); - qDebug() << "Comment type id:" << qRegisterMetaType(); - qDebug() << "DB type id:" << qRegisterMetaType(); + REGISTER(Post); + REGISTER(Comment); + REGISTER(WeblogDatabase); db.setDriver(DRIVER); db.setHostName(HOST); @@ -35,7 +37,7 @@ void MainTest::initTestCase() QTEST_ASSERT(ok); } -void MainTest::cmd1() +void CommandsTest::cmd1() { Query *q = db.posts()->query() ->setWhere(Post::titleField() == "test" && Post::idField() < 4 + 5); @@ -45,7 +47,7 @@ void MainTest::cmd1() qDebug() << q->sqlCommand(); } -void MainTest::cmd2() +void CommandsTest::cmd2() { Query *q = db.posts()->query() ->setWhere(!Post::idField().in({1, 2, 3, 4})); @@ -56,7 +58,7 @@ void MainTest::cmd2() qDebug() << q->sqlCommand(); } -void MainTest::join() +void CommandsTest::join() { auto q = db.posts()->query() ->join() @@ -64,4 +66,4 @@ void MainTest::join() } -QTEST_MAIN(MainTest) +QTEST_MAIN(CommandsTest) diff --git a/test/commands/maintest.h b/test/tst_commands/tst_commands.h similarity index 80% rename from test/commands/maintest.h rename to test/tst_commands/tst_commands.h index 0f19910..fa2ad06 100644 --- a/test/commands/maintest.h +++ b/test/tst_commands/tst_commands.h @@ -6,7 +6,7 @@ #include "weblogdatabase.h" class Post; -class MainTest : public QObject +class CommandsTest : public QObject { Q_OBJECT WeblogDatabase db; @@ -14,7 +14,7 @@ class MainTest : public QObject Post *post; Query *q; public: - explicit MainTest(QObject *parent = nullptr); + explicit CommandsTest(QObject *parent = nullptr); signals: diff --git a/test/commands/tst_commands.pro b/test/tst_commands/tst_commands.pro similarity index 80% rename from test/commands/tst_commands.pro rename to test/tst_commands/tst_commands.pro index a1d0878..7a0473d 100644 --- a/test/commands/tst_commands.pro +++ b/test/tst_commands/tst_commands.pro @@ -1,7 +1,5 @@ QT += qml quick testlib sql -QT -= gui - TARGET = tst_nut CONFIG += warn_on qmltestcase c++11 INCLUDEPATH += $$PWD/../../src $$PWD/../common @@ -9,15 +7,15 @@ include(../../nut.pri) TEMPLATE = app IMPORTPATH += $$OUT_PWD/../src/imports SOURCES += \ - maintest.cpp \ ../common/comment.cpp \ ../common/post.cpp \ ../common/weblogdatabase.cpp \ - ../common/user.cpp + ../common/user.cpp \ + tst_commands.cpp HEADERS += \ - maintest.h \ ../common/comment.h \ ../common/post.h \ ../common/weblogdatabase.h \ - ../common/user.h + ../common/user.h \ + tst_commands.h diff --git a/test/tst_datatypes/db.cpp b/test/tst_datatypes/db.cpp new file mode 100644 index 0000000..e570806 --- /dev/null +++ b/test/tst_datatypes/db.cpp @@ -0,0 +1,9 @@ +#include "db.h" + +#include "sampletable.h" + +DB::DB() : Nut::Database (), + m_sampleTables(new Nut::TableSet(this)) +{ + +} diff --git a/test/tst_datatypes/db.h b/test/tst_datatypes/db.h new file mode 100644 index 0000000..401ac8c --- /dev/null +++ b/test/tst_datatypes/db.h @@ -0,0 +1,21 @@ +#ifndef DB_H +#define DB_H + +#include "database.h" + +class SampleTable; +class DB : public Nut::Database +{ + Q_OBJECT + + NUT_DB_VERSION(1) + + NUT_DECLARE_TABLE(SampleTable, sampleTables) + +public: + DB(); +}; + +Q_DECLARE_METATYPE(DB*) + +#endif // DB_H diff --git a/test/tst_datatypes/sampletable.cpp b/test/tst_datatypes/sampletable.cpp new file mode 100644 index 0000000..68a9c9d --- /dev/null +++ b/test/tst_datatypes/sampletable.cpp @@ -0,0 +1,6 @@ +#include "sampletable.h" + +SampleTable::SampleTable(QObject *parent) : Nut::Table (parent) +{ + +} diff --git a/test/tst_datatypes/sampletable.h b/test/tst_datatypes/sampletable.h new file mode 100644 index 0000000..db88673 --- /dev/null +++ b/test/tst_datatypes/sampletable.h @@ -0,0 +1,72 @@ +#ifndef SAMPLETABLE_H +#define SAMPLETABLE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef QT_GUI_LIB +#include +#include +#endif + +#include "table.h" + +#define FIELD_Q(type) NUT_DECLARE_FIELD(q##type, f##type, f##type, setF##type) + +class SampleTable : public Nut::Table +{ + Q_OBJECT + + NUT_PRIMARY_AUTO_INCREMENT(id) + NUT_DECLARE_FIELD(int, id, id, setId) + + NUT_DECLARE_FIELD(qint8, f_int8, f_int8, setInt8) + NUT_DECLARE_FIELD(qint16, f_int16, f_int16, setInt16) + NUT_DECLARE_FIELD(qint32, f_int32, f_int32, setInt32) + NUT_DECLARE_FIELD(qint64, f_int64, f_int64, setInt64) + + NUT_DECLARE_FIELD(quint8, f_uint8, f_uint8, setUint8) + NUT_DECLARE_FIELD(quint16, f_uint16, f_uint16, setUint16) + NUT_DECLARE_FIELD(quint32, f_uint32, f_uint32, setUint32) + NUT_DECLARE_FIELD(quint64, f_uint64, f_uint64, setUint64) + + NUT_DECLARE_FIELD(qreal, f_real, f_real, setReal) + NUT_DECLARE_FIELD(float, f_float, f_float, setFloat) +// NUT_DECLARE_FIELD(long double, fldouble, fldouble, setFldouble) + NUT_DECLARE_FIELD(QString, f_string, f_string, setString) + + NUT_DECLARE_FIELD(QTime, f_time, f_time, setTime) + NUT_DECLARE_FIELD(QDate, f_date, f_date, setDate) + NUT_DECLARE_FIELD(QDateTime, f_dateTime, f_dateTime, setDateTime) + + NUT_DECLARE_FIELD(QUuid, f_uuid, f_uuid, setUuid) + + NUT_DECLARE_FIELD(QUrl, f_url, f_url, setUrl) + + NUT_DECLARE_FIELD(QJsonDocument, f_jsonDoc, f_jsonDoc, setJsonDoc) + NUT_DECLARE_FIELD(QJsonObject, f_jsonObj, f_jsonObj, setJsonObj) + NUT_DECLARE_FIELD(QJsonArray, f_jsonArray, f_jsonArray, setJsonArray) + NUT_DECLARE_FIELD(QJsonValue, f_jsonValue, f_jsonValue, setJsonValue) + + NUT_DECLARE_FIELD(QStringList, f_stringList, f_stringList, setStringList) + NUT_DECLARE_FIELD(QChar, f_qchar, f_qchar, setQchar) +#ifdef QT_GUI_LIB + NUT_DECLARE_FIELD(QPoint, f_point, f_point, setPoint) + NUT_DECLARE_FIELD(QPointF, f_pointf, f_pointf, setPointf) + NUT_DECLARE_FIELD(QPolygon, f_polygon, f_polygon, setPolygon) + NUT_DECLARE_FIELD(QPolygonF, f_polygonf, f_polygonf, setPolygonf) + NUT_DECLARE_FIELD(QColor, f_color, f_color, setColor) +#endif +public: + Q_INVOKABLE SampleTable(QObject *parent = Q_NULLPTR); +}; + +Q_DECLARE_METATYPE(SampleTable*) + +#endif // SAMPLETABLE_H diff --git a/test/tst_datatypes/tst_datatypes.cpp b/test/tst_datatypes/tst_datatypes.cpp new file mode 100644 index 0000000..2016137 --- /dev/null +++ b/test/tst_datatypes/tst_datatypes.cpp @@ -0,0 +1,229 @@ +#include +#include +#include +#include + +#include "consts.h" + +#include "tst_datatypes.h" +#include "query.h" +#include "tableset.h" +#include "tablemodel.h" +#include "databasemodel.h" + +#include "sampletable.h" + +#include "generators/sqlitegenerator.h" +#include "generators/sqlservergenerator.h" + +DataTypesTest::DataTypesTest(QObject *parent) : QObject(parent) +{ +} + +void DataTypesTest::initTestCase() +{ + //register all entities with Qt-MetaType mechanism + REGISTER(SampleTable); + REGISTER(DB); + + db.setDriver(DRIVER); + db.setHostName(HOST); + db.setDatabaseName(DATABASE); + db.setUserName(USERNAME); + db.setPassword(PASSWORD); + + QFile::remove(DATABASE); + bool ok = db.open(); + f_int8 = 8; + f_int16 = 16; + f_int32 = 32l; + f_int64 = 64ll; + f_uint8 = 8u; + f_uint16 = 16u; + f_uint32 = 32ul; + f_uint64 = 64ull; + f_real = 1.2; + f_float = 2.3f; + + f_url = QUrl("http://google.com/search?q=nut"); + + f_time = QTime::currentTime(); + f_time.setHMS(f_time.hour(), f_time.minute(), f_time.second()); + + f_date = QDate::currentDate(); + f_dateTime = QDateTime::currentDateTime(); + f_dateTime.setTime(f_time); + + f_uuid = QUuid::createUuid(); + f_jsonDoc = QJsonDocument::fromJson("{\"a\": 1}"); + f_jsonObj = f_jsonDoc.object(); + f_jsonArray.insert(0, QJsonValue(1)); + f_jsonArray.insert(1, QJsonValue("Hi")); + f_jsonArray.insert(2, QJsonValue(true)); + + f_jsonValue = QJsonValue(true); + + f_stringList.append("One"); + f_stringList.append("Two"); + f_stringList.append("Three"); + f_string = "this is \n sample ' unescapped \r\n text"; + + f_qchar = QChar('z'); + +#ifdef QT_GUI_LIB + f_point = QPoint(1, 2); + f_pointf = QPointF(1.2, 3.4); + f_polygon = QPolygon() << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); + f_polygonf = QPolygonF() << QPointF(1.2, 2.3) << QPointF(3.4, 4.5) << QPointF(5.6, 6.7); + f_color = Qt::red; +#endif + + QTEST_ASSERT(ok); + + db.sampleTables()->query()->remove(); +} + +void DataTypesTest::insert() +{ + auto t = Nut::create(); + + t->setInt8(f_int8); + t->setInt16(f_int16); + t->setInt32(f_int32); + t->setInt64(f_int64); + + t->setUint8(f_uint8); + t->setUint16(f_uint16); + t->setUint32(f_uint32); + t->setUint64(f_uint64); + + t->setReal(f_real); + t->setFloat(f_float); + + t->setUrl(f_url); + + t->setTime(f_time); + t->setDate(f_date); + t->setDateTime(f_dateTime); + t->setUuid(f_uuid); + + t->setJsonDoc(f_jsonDoc); + t->setJsonObj(f_jsonObj); + t->setJsonArray(f_jsonArray); + t->setJsonValue(f_jsonValue); + + t->setString(f_string); + t->setStringList(f_stringList); + t->setQchar(f_qchar); +#ifdef QT_GUI_LIB + t->setColor(f_color); + + t->setPoint(f_point); + t->setPointf(f_pointf); + + t->setPolygon(f_polygon); + t->setPolygonf(f_polygonf); +#endif + db.sampleTables()->append(t); + db.saveChanges(); +} + +void DataTypesTest::retrive() +{ + Nut::RowList list = db.sampleTables()->query()->toList(); + QTEST_ASSERT(list.count() == 1); + Nut::Row t = list.first(); + + QTEST_ASSERT(t->f_int8() == f_int8); + QTEST_ASSERT(t->f_int16() == f_int16); + QTEST_ASSERT(t->f_int32() == f_int32); + QTEST_ASSERT(t->f_int64() == f_int64); + + QTEST_ASSERT(t->f_uint8() == f_uint8); + QTEST_ASSERT(t->f_uint16() == f_uint16); + QTEST_ASSERT(t->f_uint32() == f_uint32); + QTEST_ASSERT(t->f_uint64() == f_uint64); + + QTEST_ASSERT(qFuzzyCompare(t->f_real(), f_real)); + QTEST_ASSERT(qFuzzyCompare(t->f_float(), f_float)); + + + QTEST_ASSERT(t->f_url() == f_url); + QTEST_ASSERT(t->f_uuid() == f_uuid); + + QTEST_ASSERT(t->f_time() == f_time); + QTEST_ASSERT(t->f_date() == f_date); + QTEST_ASSERT(t->f_dateTime() == f_dateTime); + + QTEST_ASSERT(t->f_jsonDoc() == f_jsonDoc); + QTEST_ASSERT(t->f_jsonObj() == f_jsonObj); + QTEST_ASSERT(t->f_jsonArray() == f_jsonArray); + QTEST_ASSERT(t->f_jsonValue() == f_jsonValue); + + QTEST_ASSERT(t->f_string() == f_string); + QTEST_ASSERT(t->f_stringList() == f_stringList); + QTEST_ASSERT(t->f_qchar() == f_qchar); +#ifdef QT_GUI_LIB + QTEST_ASSERT(t->f_point() == f_point); + QTEST_ASSERT(t->f_pointf() == f_pointf); + + QTEST_ASSERT(t->f_polygon() == f_polygon); + QTEST_ASSERT(t->f_polygonf() == f_polygonf); + QTEST_ASSERT(t->f_color() == f_color); +#endif +} + +#define CHECK(name) \ + c = db.sampleTables()->query() \ + ->where(SampleTable::f_ ## name ## Field() == f_ ## name) \ + ->count(); \ + QTEST_ASSERT(c == 1); + +void DataTypesTest::check() +{ + int c; + + CHECK(int8) + CHECK(int16) + CHECK(int32) + CHECK(int64) + CHECK(uint8) + CHECK(uint16) + CHECK(uint32) + CHECK(uint64) + CHECK(real) + CHECK(float) + CHECK(url) + + CHECK(time) + CHECK(date) + CHECK(dateTime) + + CHECK(uuid) +// CHECK(jsonDoc) +// CHECK(jsonObj) +// CHECK(jsonArray) +// CHECK(jsonValue) + + CHECK(string) + CHECK(stringList) + + CHECK(qchar) +#ifdef QT_GUI_LIB + CHECK(point) + CHECK(pointf) + CHECK(polygon) + CHECK(polygonf) + CHECK(color) +#endif +} + +void DataTypesTest::cleanupTestCase() +{ + db.sampleTables()->query()->remove(); + db.close(); + + PRINT_FORM(db); +} + +QTEST_MAIN(DataTypesTest) diff --git a/test/tst_datatypes/tst_datatypes.h b/test/tst_datatypes/tst_datatypes.h new file mode 100644 index 0000000..04188d6 --- /dev/null +++ b/test/tst_datatypes/tst_datatypes.h @@ -0,0 +1,72 @@ +#ifndef MAINTEST_H +#define MAINTEST_H + +#include +#include + +#include +#include +#include +#include +#ifdef QT_GUI_LIB +#include +#include +#endif +#include +#include + +#include "db.h" +class DataTypesTest : public QObject +{ + Q_OBJECT + DB db; + + qint8 f_int8; + qint16 f_int16; + qint32 f_int32; + qint64 f_int64; + quint8 f_uint8; + quint16 f_uint16; + quint32 f_uint32; + quint64 f_uint64; + qreal f_real; + float f_float; + + QTime f_time; + QDate f_date; + QDateTime f_dateTime; + + QJsonDocument f_jsonDoc; + QJsonObject f_jsonObj; + QJsonArray f_jsonArray; + QJsonValue f_jsonValue; + + QString f_string; + QStringList f_stringList; + + QChar f_qchar; + QUrl f_url; + QUuid f_uuid; +#ifdef QT_GUI_LIB + QPoint f_point; + QPointF f_pointf; + QPolygon f_polygon; + QPolygonF f_polygonf; + QColor f_color; +#endif + +public: + explicit DataTypesTest(QObject *parent = nullptr); + +signals: + +private slots: + void initTestCase(); + + void insert(); + void retrive(); + void check(); + void cleanupTestCase(); +}; + +#endif // MAINTEST_H diff --git a/test/tst_datatypes/tst_datatypes.pro b/test/tst_datatypes/tst_datatypes.pro new file mode 100644 index 0000000..a690739 --- /dev/null +++ b/test/tst_datatypes/tst_datatypes.pro @@ -0,0 +1,20 @@ +QT += testlib sql gui + +TARGET = tst_datatypes +TEMPLATE = app + +CONFIG += warn_on c++11 + +include(../common/nut-lib.pri) + +SOURCES += \ + db.cpp \ + sampletable.cpp \ + tst_datatypes.cpp + +HEADERS += \ + db.h \ + sampletable.h \ + tst_datatypes.h + +include($$PWD/../../ci-test-init.pri) diff --git a/test/tst_generators/.gitignore b/test/tst_generators/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/test/tst_generators/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/test/tst_generators/tst_generators.cpp b/test/tst_generators/tst_generators.cpp new file mode 100644 index 0000000..285cd05 --- /dev/null +++ b/test/tst_generators/tst_generators.cpp @@ -0,0 +1,147 @@ +#include + +#include +#include +#include +#include + +#include "tablemodel.h" +#include "generators/sqlitegenerator.h" +#include "generators/sqlservergenerator.h" +#include "generators/mysqlgenerator.h" +#include "generators/postgresqlgenerator.h" + +#include "tst_generators.h" + +GeneratorsTest::GeneratorsTest(QObject *parent) : QObject(parent) +{ + +} + + +void GeneratorsTest::types(Nut::SqlGeneratorBase *g, QString name) +{ + QList types; + types + << QMetaType::Bool + + << QMetaType::Char + << QMetaType::SChar + << QMetaType::UChar + << QMetaType::QChar + + << QMetaType::Short + << QMetaType::UShort + << QMetaType::Int + << QMetaType::UInt + << QMetaType::Long + << QMetaType::LongLong + << QMetaType::ULong + << QMetaType::ULongLong + + << QMetaType::Double + << QMetaType::Float + + << QMetaType::QString + << QMetaType::QStringList + + << QMetaType::QBitArray + << QMetaType::QByteArray + + << QMetaType::QDate + << QMetaType::QTime + << QMetaType::QDateTime + + << QMetaType::QUrl + << QMetaType::QColor + + << QMetaType::QPoint + << QMetaType::QPointF + << QMetaType::QPolygon + << QMetaType::QPolygonF + << QMetaType::QSize + << QMetaType::QSizeF + << QMetaType::QRect + << QMetaType::QRectF + << QMetaType::QLine + << QMetaType::QLineF + + // << QMetaType::QRegion + // << QMetaType::QImage + // << QMetaType::QPixmap + // << QMetaType::QLocale + // << QMetaType::QMatrix + // << QMetaType::QMatrix4x4 + // << QMetaType::QVector2D + // << QMetaType::QVector3D + // << QMetaType::QVector4D + << QMetaType::QJsonValue + << QMetaType::QJsonObject + << QMetaType::QJsonArray + << QMetaType::QJsonDocument + + << QMetaType::QUuid + // << QMetaType::QByteArrayList + ; + + Nut::FieldModel m; + foreach (QMetaType::Type t, types) { + m.type = t; + QString fn = g->fieldType(&m); + + QString tn = QString(QMetaType::typeName(t)); + if (!table.contains(tn)) + table.insert(tn, row()); + + table[tn].set(name, fn); + + if (fn.isEmpty()) + qDebug() << "No rule for" << t << "(" << QMetaType::typeName(t) << ")"; + Q_ASSERT(!fn.isEmpty()); + } +} + +void GeneratorsTest::test_sqlite() +{ + auto g = new Nut::SqliteGenerator; + types(g, "sqlite"); + g->deleteLater(); +} + +void GeneratorsTest::test_sqlserver() +{ + auto g = new Nut::SqlServerGenerator; + types(g, "mssql"); + g->deleteLater(); +} + +void GeneratorsTest::test_psql() +{ + auto g = new Nut::PostgreSqlGenerator; + types(g, "psql"); + g->deleteLater(); +} + +void GeneratorsTest::test_mysql() +{ + auto g = new Nut::MySqlGenerator; + types(g, "mysql"); + g->deleteLater(); +} + +void GeneratorsTest::cleanupTestCase() +{ + QMap::const_iterator i; + QString p = "\n| Type | Sqlite | MySql | Postgresql| Ms Sql server |" + "\n|--------|--------|--------|--------|--------|"; + for (i = table.constBegin(); i != table.constEnd(); ++i) { + p.append(QString("\n|%1|%2|%3|%4|%5|") + .arg(i.key(), i.value().sqlite, i.value().mysql, + i.value().psql, i.value().mssql)); + } + qDebug() << p.toStdString().c_str(); +} + +QTEST_MAIN(GeneratorsTest) + +//#include "tst_GeneratorsTest.moc" diff --git a/test/tst_generators/tst_generators.h b/test/tst_generators/tst_generators.h new file mode 100644 index 0000000..3f65973 --- /dev/null +++ b/test/tst_generators/tst_generators.h @@ -0,0 +1,48 @@ +#ifndef TST_GENERATORS_H +#define TST_GENERATORS_H + +#include +#include + +namespace Nut { +class SqlGeneratorBase; +} + +class GeneratorsTest : public QObject +{ + Q_OBJECT + + struct row { + QString sqlite; + QString psql; + QString mysql; + QString mssql; + void set(QString name, QString value) { + if (name == "sqlite") + sqlite = value.toUpper(); + else if (name == "psql") + psql = value.toUpper(); + else if (name == "mysql") + mysql = value.toUpper(); + else if (name == "mssql") + mssql = value.toUpper(); + } + }; + QMap table; + +public: + explicit GeneratorsTest(QObject *parent = nullptr); + + void types(Nut::SqlGeneratorBase *g, QString name); + +private slots: + void test_sqlite(); + void test_psql(); + void test_sqlserver(); + void test_mysql(); + + void cleanupTestCase(); + +}; + +#endif // TST_GENERATORS_H diff --git a/test/tst_generators/tst_generators.pro b/test/tst_generators/tst_generators.pro new file mode 100644 index 0000000..dc81002 --- /dev/null +++ b/test/tst_generators/tst_generators.pro @@ -0,0 +1,16 @@ +QT += testlib sql + +CONFIG += qt console warn_on depend_includepath testcase +CONFIG -= app_bundle + +TEMPLATE = app + +include(../common/nut-lib.pri) + +SOURCES += \ + tst_generators.cpp + +HEADERS += \ + tst_generators.h + +include($$PWD/../../ci-test-init.pri) diff --git a/test/join/jointest.cpp b/test/tst_join/jointest.cpp similarity index 82% rename from test/join/jointest.cpp rename to test/tst_join/jointest.cpp index 05918cb..87c2530 100644 --- a/test/join/jointest.cpp +++ b/test/tst_join/jointest.cpp @@ -22,11 +22,11 @@ JoinTest::JoinTest(QObject *parent) : QObject(parent) void JoinTest::initTestCase() { - qDebug() << "User type id:" << qRegisterMetaType(); - qDebug() << "Post type id:" << qRegisterMetaType(); - qDebug() << "Comment type id:" << qRegisterMetaType(); - qDebug() << "Score type id:" << qRegisterMetaType(); - qDebug() << "DB type id:" << qRegisterMetaType(); + REGISTER(User); + REGISTER(Post); + REGISTER(Comment); + REGISTER(Score); + REGISTER(WeblogDatabase); db.setDriver(DRIVER); db.setHostName(HOST); diff --git a/test/join/jointest.h b/test/tst_join/jointest.h similarity index 100% rename from test/join/jointest.h rename to test/tst_join/jointest.h diff --git a/test/join/tst_join.pro b/test/tst_join/tst_join.pro similarity index 97% rename from test/join/tst_join.pro rename to test/tst_join/tst_join.pro index 78533c7..ee955a0 100644 --- a/test/join/tst_join.pro +++ b/test/tst_join/tst_join.pro @@ -1,5 +1,4 @@ QT += qml quick testlib sql -QT -= gui TARGET = tst_nut TEMPLATE = app diff --git a/test/tst_json/db.cpp b/test/tst_json/db.cpp new file mode 100644 index 0000000..598f17c --- /dev/null +++ b/test/tst_json/db.cpp @@ -0,0 +1,9 @@ +#include "db.h" + +#include "sampletable.h" + +DB::DB() : Nut::Database (), + m_sampleTable(new Nut::TableSet
(this)) +{ + +} diff --git a/test/tst_json/db.h b/test/tst_json/db.h new file mode 100644 index 0000000..d31adb7 --- /dev/null +++ b/test/tst_json/db.h @@ -0,0 +1,22 @@ +#ifndef DB1_H +#define DB1_H + +#include "database.h" + +class Table; + +class DB : public Nut::Database +{ + Q_OBJECT + + NUT_DB_VERSION(1) + + NUT_DECLARE_TABLE(Table, sampleTable) + +public: + DB(); +}; + +Q_DECLARE_METATYPE(DB*) + +#endif // DB1_H diff --git a/test/tst_json/sampletable.cpp b/test/tst_json/sampletable.cpp new file mode 100644 index 0000000..3ba992b --- /dev/null +++ b/test/tst_json/sampletable.cpp @@ -0,0 +1,7 @@ +#include "sampletable.h" + + +Table::Table(QObject *parent) : Nut::Table (parent) +{ + +} diff --git a/test/tst_json/sampletable.h b/test/tst_json/sampletable.h new file mode 100644 index 0000000..57ac136 --- /dev/null +++ b/test/tst_json/sampletable.h @@ -0,0 +1,23 @@ +#ifndef TABLE1_H +#define TABLE1_H + +#include "table.h" +#include + +class Table : public Nut::Table +{ + Q_OBJECT + + NUT_PRIMARY_AUTO_INCREMENT(id) + NUT_DECLARE_FIELD(int, id, id, setId) + + NUT_DECLARE_FIELD(QJsonDocument, doc, doc, setDoc) + +public: + Q_INVOKABLE Table(QObject *parent = Q_NULLPTR); + +}; + +Q_DECLARE_METATYPE(Table*) + +#endif // TABLE1_H diff --git a/test/tst_json/tst_json.cpp b/test/tst_json/tst_json.cpp new file mode 100644 index 0000000..81c2977 --- /dev/null +++ b/test/tst_json/tst_json.cpp @@ -0,0 +1,65 @@ +#include + +#include "db.h" +#include "sampletable.h" +#include "query.h" + +#include "tst_json.h" +#include "consts.h" + +void TestJson::initDb(Nut::Database &db) +{ + db.setDriver(DRIVER); + db.setHostName(HOST); + db.setDatabaseName(DATABASE); + db.setUserName(USERNAME); + db.setPassword(PASSWORD); +} + +TestJson::TestJson() +{ + +} + +TestJson::~TestJson() +{ + +} + +void TestJson::initTestCase() +{ + QFile::remove(DATABASE); + REGISTER(DB); + REGISTER(Table); +} + +void TestJson::store() +{ + initDb(db); + + db.open(); + + auto t = Nut::create
(); + QJsonParseError e; + QJsonDocument doc = QJsonDocument::fromJson(R"({"a": 4, "b":3.14})", &e); + qDebug() << e.errorString(); + t->setDoc(doc); + db.sampleTable()->append(t); + db.saveChanges(true); + + int id = t->id(); + auto newObj = db.sampleTable()->query() + ->where(Table::idField() == id) + ->first(); + + Q_ASSERT(newObj != nullptr); + Q_ASSERT(newObj->doc() == t->doc()); +} + +void TestJson::cleanupTestCase() +{ + PRINT_FORM(db); +} + +QTEST_APPLESS_MAIN(TestJson) + diff --git a/test/tst_json/tst_json.h b/test/tst_json/tst_json.h new file mode 100644 index 0000000..2672db1 --- /dev/null +++ b/test/tst_json/tst_json.h @@ -0,0 +1,33 @@ +#ifndef TST_TESTJSON_H +#define TST_TESTJSON_H + +#include "db.h" + +#include + +namespace Nut { +class Database; +} +class TestJson : public QObject +{ + Q_OBJECT + + DB db; + + void initDb(Nut::Database &db); + + int id; +public: + TestJson(); + ~TestJson(); + +private slots: + void initTestCase(); + + void store(); + + void cleanupTestCase(); + +}; + +#endif // TST_TESTJSON_H diff --git a/test/tst_json/tst_json.pro b/test/tst_json/tst_json.pro new file mode 100644 index 0000000..04f053c --- /dev/null +++ b/test/tst_json/tst_json.pro @@ -0,0 +1,19 @@ +QT += testlib sql + +TARGET = tst_upgrades +TEMPLATE = app +CONFIG += warn_on c++11 + +include(../common/nut-lib.pri) + +SOURCES += \ + tst_json.cpp \ + db.cpp \ + sampletable.cpp + +HEADERS += \ + tst_json.h \ + db.h \ + sampletable.h + +include($$PWD/../../ci-test-init.pri) diff --git a/test/tst_phrases/tst_phrases.cpp b/test/tst_phrases/tst_phrases.cpp new file mode 100644 index 0000000..4455330 --- /dev/null +++ b/test/tst_phrases/tst_phrases.cpp @@ -0,0 +1,154 @@ +#include +#include + +#include "tst_phrases.h" +#include "phrase.h" + +using namespace Nut; + +MainTest::MainTest(QObject *parent) : QObject(parent) +{ +} + +void MainTest::initTestCase() +{ + +} + +void MainTest::no1() +{ + FieldPhrase id("main", "id"); + FieldPhrase name("main", "name"); + FieldPhrase last_name("main", "last_name"); + FieldPhrase date("main", "date"); + auto w = (id == 4 && name == "hi"); +} + +void MainTest::numeric() +{ + FieldPhrase n("main", "int"); + FieldPhrase f("main", "float"); + + auto p1 = n == 1; + auto p2 = n <= 1; + auto p3 = n >= 1; + auto p4 = n < 1; + auto p5 = n > 1; + auto p6 = n != 1; + auto p7 = n = n + 1; + auto p8 = n < n + 1; + auto p9 = n <= n + 1; + auto p10 = n > n + 1; + auto p11 = n >= n + 1; + auto p12 = n + 1 > n - 2; + auto p13 = ++n; + auto p14 = n++; + auto p15 = n.between(1, 2); + auto p16 = n + 1 < n + 2; + + auto p21 = p1 && p2; + auto p22 = p3 == p4; + auto p23 = f == n + 1; + + auto p24 = n = 4; + auto p26 = (n = 4) & (n = 5); + auto p27 = n | f; +} + +void MainTest::string() +{ + FieldPhrase str("main", "string"); + + auto p1 = str == "salam"; + auto p2 = str.like("%hi%"); + auto p3 = str.isNull(); + auto p4 = str.in(QStringList() << "one" << "two" << "three"); + auto p5 = str != "hi" && str.like("%s"); +} + +void MainTest::boolean() +{ + FieldPhrase b("main", "bool"); + + auto p1 = b; + auto p2 = !b; + auto p3 = b == false; + + QTEST_ASSERT(p1.data); + QTEST_ASSERT(p2.data); + QTEST_ASSERT(p3.data); +} + +void MainTest::datetime() +{ + FieldPhrase time("main", "time"); + FieldPhrase date("main", "date"); + FieldPhrase datetime("main", "datetime"); + + auto p1 = time <= QTime::currentTime(); + auto p2 = time.addHours(2) < QTime::currentTime(); + auto p3 = date == QDate::currentDate(); + auto p4 = date.addDays(1) == QDate::currentDate(); + auto p5 = datetime > QDateTime::currentDateTime(); + auto p6 = datetime.addMonths(1) >= QDateTime::currentDateTime(); + auto p7 = time.between(QTime::currentTime().addSecs(-100), QTime::currentTime()); + auto p8 = time.hour() == 3; + auto p9 = time = QTime::currentTime(); + +// auto pi1 = time.addYears(1); +// auto pi2 = date.addMinutes(3); + +// QTEST_ASSERT(!pi1.data); +// QTEST_ASSERT(!pi2.data); +} + +void MainTest::extra() +{ + FieldPhrase url("main", "url"); + + auto p1 = url == QUrl(); + auto p2 = url == "http://google.com"; +} + +void MainTest::mix() +{ + FieldPhrase id("", ""); + FieldPhrase name("", ""); + FieldPhrase lastName("", ""); + FieldPhrase birthDate("", ""); + + select(id); + select(id | name | lastName); + update((name = "john") & (lastName = "snow")); + insert(id = 0); + insert((id = 4) & (name = "john")); + order_by(id); + order_by(id | !name); +} + +void MainTest::select(const PhraseList &ph) +{ + QTEST_ASSERT(ph.data.count()); +} + +void MainTest::where(const ConditionalPhrase &ph) +{ + QTEST_ASSERT(ph.data); +} + +void MainTest::update(const AssignmentPhraseList &p) +{ + QTEST_ASSERT(p.data.count()); +} + +void MainTest::insert(const AssignmentPhraseList &p) +{ + QTEST_ASSERT(p.data.count()); +} + +void MainTest::order_by(const PhraseList &ph) +{ + QTEST_ASSERT(ph.data.count()); +} + +QTEST_MAIN(MainTest) diff --git a/test/tst_phrases/tst_phrases.h b/test/tst_phrases/tst_phrases.h new file mode 100644 index 0000000..308fa85 --- /dev/null +++ b/test/tst_phrases/tst_phrases.h @@ -0,0 +1,43 @@ +#ifndef MAINTEST_H +#define MAINTEST_H + +#include +#include + +class Post; +class User; + +namespace Nut { +class PhraseList; +class AssignmentPhraseList; +class ConditionalPhrase; +} +class MainTest : public QObject +{ + Q_OBJECT + +public: + explicit MainTest(QObject *parent = nullptr); + +signals: + +private slots: + void initTestCase(); + void no1(); + + void numeric(); + void string(); + void boolean(); + void datetime(); + void extra(); + void mix(); + +private: + void select(const Nut::PhraseList &ph); + void where(const Nut::ConditionalPhrase &ph); + void update(const Nut::AssignmentPhraseList &p); + void insert(const Nut::AssignmentPhraseList &p); + void order_by(const Nut::PhraseList &ph); +}; + +#endif // MAINTEST_H diff --git a/test/tst_phrases/tst_phrases.pro b/test/tst_phrases/tst_phrases.pro new file mode 100644 index 0000000..d287bac --- /dev/null +++ b/test/tst_phrases/tst_phrases.pro @@ -0,0 +1,16 @@ +QT += testlib sql + +TARGET = tst_phrases +TEMPLATE = app + +CONFIG += warn_on c++11 + +include(../common/nut-lib.pri) + +SOURCES += \ + tst_phrases.cpp + +HEADERS += \ + tst_phrases.h + +include($$PWD/../../ci-test-init.pri) diff --git a/test/quuid/test.cpp b/test/tst_quuid/test.cpp similarity index 100% rename from test/quuid/test.cpp rename to test/tst_quuid/test.cpp diff --git a/test/quuid/test.h b/test/tst_quuid/test.h similarity index 58% rename from test/quuid/test.h rename to test/tst_quuid/test.h index edff96c..e960c5f 100644 --- a/test/quuid/test.h +++ b/test/tst_quuid/test.h @@ -11,13 +11,10 @@ class Test : public Nut::Table NUT_PRIMARY_KEY(id) NUT_DECLARE_FIELD(QUuid, id, id, setId) - - NUT_NOT_NULL(username) - NUT_LEN(username, 50) - NUT_DECLARE_FIELD(QString, username, username, setUsername) + NUT_DECLARE_FIELD(QUuid, uuid, uuid, setUuid) public: - Q_INVOKABLE Test(QObject *parentTableSet = 0); + Q_INVOKABLE Test(QObject *parentTableSet = nullptr); }; Q_DECLARE_METATYPE(Test*) diff --git a/test/quuid/testdatabase.cpp b/test/tst_quuid/testdatabase.cpp similarity index 100% rename from test/quuid/testdatabase.cpp rename to test/tst_quuid/testdatabase.cpp diff --git a/test/quuid/testdatabase.h b/test/tst_quuid/testdatabase.h similarity index 92% rename from test/quuid/testdatabase.h rename to test/tst_quuid/testdatabase.h index 1e257e6..1fa20d2 100644 --- a/test/quuid/testdatabase.h +++ b/test/tst_quuid/testdatabase.h @@ -1,7 +1,7 @@ #ifndef TESTDATABASE_H #define TESTDATABASE_H -#include +#include "database.h" class Test; class TestDatabase : public Nut::Database diff --git a/test/tst_quuid/tst_quuid.pro b/test/tst_quuid/tst_quuid.pro new file mode 100644 index 0000000..da1afd9 --- /dev/null +++ b/test/tst_quuid/tst_quuid.pro @@ -0,0 +1,20 @@ +QT += testlib sql + +TARGET = tst_uuid +TEMPLATE = app +CONFIG += warn_on c++11 + +include(../common/nut-lib.pri) + +SOURCES += \ + testdatabase.cpp \ + test.cpp \ + tst_uuid.cpp + +HEADERS += \ + ../common/consts.h \ + testdatabase.h \ + test.h \ + tst_uuid.h + +include($$PWD/../../ci-test-init.pri) diff --git a/test/tst_quuid/tst_uuid.cpp b/test/tst_quuid/tst_uuid.cpp new file mode 100644 index 0000000..21dadae --- /dev/null +++ b/test/tst_quuid/tst_uuid.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +#include "consts.h" + +#include "tst_uuid.h" +#include "query.h" +#include "tableset.h" +#include "tablemodel.h" + +#include "test.h" + +UuidTest::UuidTest(QObject *parent) : QObject(parent) +{ +} + +void UuidTest::initTestCase() +{ + REGISTER(Test); + REGISTER(TestDatabase); + + QFile::remove(DATABASE); + + db.setDriver(DRIVER); + db.setHostName(HOST); + db.setDatabaseName(DATABASE); + db.setUserName(USERNAME); + db.setPassword(PASSWORD); + + bool ok = db.open(); + + db.tests()->query()->remove(); + uuid = QUuid::createUuid(); + + QTEST_ASSERT(ok); +} + +void UuidTest::save() +{ + TIC(); + auto t = Nut::create(); + t->setId(QUuid::createUuid()); + t->setUuid(uuid); + db.tests()->append(t); + int n = db.saveChanges(); + TOC(); + + QTEST_ASSERT(n == 1); +} + +void UuidTest::restore() +{ + TIC(); + auto test = db.tests()->query()->first(); + TOC(); + QTEST_ASSERT(!test->id().isNull()); + QTEST_ASSERT(test->uuid() == uuid); +} + +void UuidTest::cleanupTestCase() +{ +// qDeleteAll(Nut::TableModel::allModels()); +// Nut::DatabaseModel::deleteAllModels(); +} + +QTEST_MAIN(UuidTest) diff --git a/test/quuid/maintest.h b/test/tst_quuid/tst_uuid.h similarity index 64% rename from test/quuid/maintest.h rename to test/tst_quuid/tst_uuid.h index 32fad84..42300f0 100644 --- a/test/quuid/maintest.h +++ b/test/tst_quuid/tst_uuid.h @@ -4,21 +4,25 @@ #include #include +#include + #include "testdatabase.h" class Test; -class MainTest : public QObject +class UuidTest : public QObject { Q_OBJECT TestDatabase db; + QUuid uuid; public: - explicit MainTest(QObject *parent = 0); + explicit UuidTest(QObject *parent = nullptr); signals: private slots: void initTestCase(); - void add(); + void save(); + void restore(); void cleanupTestCase(); }; diff --git a/test/tst_upgrades/.gitignore b/test/tst_upgrades/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/test/tst_upgrades/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/test/tst_upgrades/db1.cpp b/test/tst_upgrades/db1.cpp new file mode 100644 index 0000000..3171155 --- /dev/null +++ b/test/tst_upgrades/db1.cpp @@ -0,0 +1,9 @@ +#include "db1.h" + +#include "table1.h" + +DB1::DB1() : Nut::Database (), + m_sampleTable(new Nut::TableSet(this)) +{ + +} diff --git a/test/tst_upgrades/db1.h b/test/tst_upgrades/db1.h new file mode 100644 index 0000000..0f7e74b --- /dev/null +++ b/test/tst_upgrades/db1.h @@ -0,0 +1,22 @@ +#ifndef DB1_H +#define DB1_H + +#include "database.h" + +class Table1; + +class DB1 : public Nut::Database +{ + Q_OBJECT + + NUT_DB_VERSION(1) + + NUT_DECLARE_TABLE(Table1, sampleTable) + +public: + DB1(); +}; + +Q_DECLARE_METATYPE(DB1*) + +#endif // DB1_H diff --git a/test/tst_upgrades/db2.cpp b/test/tst_upgrades/db2.cpp new file mode 100644 index 0000000..de05611 --- /dev/null +++ b/test/tst_upgrades/db2.cpp @@ -0,0 +1,9 @@ +#include "db2.h" + +#include "table2.h" + +DB2::DB2() : Nut::Database (), + m_sampleTable(new Nut::TableSet(this)) +{ + +} diff --git a/test/tst_upgrades/db2.h b/test/tst_upgrades/db2.h new file mode 100644 index 0000000..5f72875 --- /dev/null +++ b/test/tst_upgrades/db2.h @@ -0,0 +1,22 @@ +#ifndef DB2_H +#define DB2_H + +#include "database.h" + +class Table2; + +class DB2 : public Nut::Database +{ + Q_OBJECT + + NUT_DB_VERSION(1) + + NUT_DECLARE_TABLE(Table2, sampleTable) + +public: + DB2(); +}; + +Q_DECLARE_METATYPE(DB2*) + +#endif // DB2_H diff --git a/test/tst_upgrades/db3.cpp b/test/tst_upgrades/db3.cpp new file mode 100644 index 0000000..ab4c987 --- /dev/null +++ b/test/tst_upgrades/db3.cpp @@ -0,0 +1,9 @@ +#include "db3.h" + +#include "table3.h" + +DB3::DB3() : Nut::Database (), + m_sampleTable(new Nut::TableSet(this)) +{ + +} diff --git a/test/tst_upgrades/db3.h b/test/tst_upgrades/db3.h new file mode 100644 index 0000000..018a4d7 --- /dev/null +++ b/test/tst_upgrades/db3.h @@ -0,0 +1,22 @@ +#ifndef DB3_H +#define DB3_H + +#include "database.h" + +class Table3; + +class DB3 : public Nut::Database +{ + Q_OBJECT + + NUT_DB_VERSION(1) + + NUT_DECLARE_TABLE(Table3, sampleTable) + +public: + DB3(); +}; + +Q_DECLARE_METATYPE(DB3*) + +#endif // DB3_H diff --git a/test/tst_upgrades/table1.cpp b/test/tst_upgrades/table1.cpp new file mode 100644 index 0000000..d74ed3b --- /dev/null +++ b/test/tst_upgrades/table1.cpp @@ -0,0 +1,7 @@ +#include "table1.h" + + +Table1::Table1(QObject *parent) : Nut::Table (parent) +{ + +} diff --git a/test/tst_upgrades/table1.h b/test/tst_upgrades/table1.h new file mode 100644 index 0000000..727b73a --- /dev/null +++ b/test/tst_upgrades/table1.h @@ -0,0 +1,20 @@ +#ifndef TABLE1_H +#define TABLE1_H + +#include "table.h" + +class Table1 : public Nut::Table +{ + Q_OBJECT + + NUT_PRIMARY_AUTO_INCREMENT(id) + NUT_DECLARE_FIELD(int, id, id, setId) + +public: + Q_INVOKABLE Table1(QObject *parent = Q_NULLPTR); + +}; + +Q_DECLARE_METATYPE(Table1*) + +#endif // TABLE1_H diff --git a/test/tst_upgrades/table2.cpp b/test/tst_upgrades/table2.cpp new file mode 100644 index 0000000..3118a19 --- /dev/null +++ b/test/tst_upgrades/table2.cpp @@ -0,0 +1,7 @@ +#include "table2.h" + + +Table2::Table2(QObject *parent) : Nut::Table (parent) +{ + +} diff --git a/test/tst_upgrades/table2.h b/test/tst_upgrades/table2.h new file mode 100644 index 0000000..1b4f06e --- /dev/null +++ b/test/tst_upgrades/table2.h @@ -0,0 +1,23 @@ +#ifndef TABLE2_H +#define TABLE2_H + +#include "table.h" + +class Table2 : public Nut::Table +{ + Q_OBJECT + + NUT_PRIMARY_AUTO_INCREMENT(id) + NUT_DECLARE_FIELD(int, id, id, setId) + + NUT_DECLARE_FIELD(QString, str, str, setStr) + NUT_DECLARE_FIELD(int, grade, grade, setGrade) + +public: + Q_INVOKABLE Table2(QObject *parent = Q_NULLPTR); + +}; + +Q_DECLARE_METATYPE(Table2*) + +#endif // TABLE2_H diff --git a/test/tst_upgrades/table3.cpp b/test/tst_upgrades/table3.cpp new file mode 100644 index 0000000..fa07443 --- /dev/null +++ b/test/tst_upgrades/table3.cpp @@ -0,0 +1,7 @@ +#include "table3.h" + + +Table3::Table3(QObject *parent) : Nut::Table (parent) +{ + +} diff --git a/test/tst_upgrades/table3.h b/test/tst_upgrades/table3.h new file mode 100644 index 0000000..b0c3458 --- /dev/null +++ b/test/tst_upgrades/table3.h @@ -0,0 +1,22 @@ +#ifndef TABLE3_H +#define TABLE3_H + +#include "table.h" + +class Table3 : public Nut::Table +{ + Q_OBJECT + + NUT_PRIMARY_AUTO_INCREMENT(id) + NUT_DECLARE_FIELD(int, id, id, setId) + + NUT_DECLARE_FIELD(QString, grade, grade, setGrade) + +public: + Q_INVOKABLE Table3(QObject *parent = Q_NULLPTR); + +}; + +Q_DECLARE_METATYPE(Table3*) + +#endif // TABLE3_H diff --git a/test/tst_upgrades/tst_upgrades.cpp b/test/tst_upgrades/tst_upgrades.cpp new file mode 100644 index 0000000..c91576b --- /dev/null +++ b/test/tst_upgrades/tst_upgrades.cpp @@ -0,0 +1,88 @@ +#include + +#include "db1.h" +#include "db2.h" +#include "db3.h" + +#include "table1.h" +#include "table2.h" +#include "table3.h" +#include "query.h" + +#include "tst_upgrades.h" +#include "consts.h" + +void Upgrades::initDb(Nut::Database &db) +{ + db.setDriver(DRIVER); + db.setHostName(HOST); + db.setDatabaseName(DATABASE); + db.setUserName(USERNAME); + db.setPassword(PASSWORD); +} + +Upgrades::Upgrades() +{ + +} + +Upgrades::~Upgrades() +{ + +} + +void Upgrades::initTestCase() +{ + QFile::remove(DATABASE); + + REGISTER(DB1); + REGISTER(DB2); + REGISTER(DB3); + + REGISTER(Table1); + REGISTER(Table2); + REGISTER(Table3); +} + +void Upgrades::version1() +{ + DB1 db; + initDb(db); + QTEST_ASSERT(db.open()); + db.sampleTable()->query()->remove(); +} + +void Upgrades::version2() +{ + DB2 db; + initDb(db); + QTEST_ASSERT(db.open()); + + auto t = Nut::create(); + t->setStr("0"); + db.sampleTable()->append(t); + db.saveChanges(); + id = t->id(); +} + +void Upgrades::version3() +{ + DB3 db; + initDb(db); + QTEST_ASSERT(db.open()); + + auto t = db.sampleTable()->query() + ->first(); + QTEST_ASSERT(id == t->id()); +} + +void Upgrades::cleanupTestCase() +{ + DB1 db; + initDb(db); + PRINT_FORM(db); +} + + +QTEST_APPLESS_MAIN(Upgrades) + diff --git a/test/tst_upgrades/tst_upgrades.h b/test/tst_upgrades/tst_upgrades.h new file mode 100644 index 0000000..9c78885 --- /dev/null +++ b/test/tst_upgrades/tst_upgrades.h @@ -0,0 +1,31 @@ +#ifndef TST_UPGRADES_H +#define TST_UPGRADES_H + +#include + +namespace Nut { +class Database; +} +class Upgrades : public QObject +{ + Q_OBJECT + + void initDb(Nut::Database &db); + + int id; +public: + Upgrades(); + ~Upgrades(); + +private slots: + void initTestCase(); + + void version1(); + void version2(); + void version3(); + + void cleanupTestCase(); + +}; + +#endif // TST_UPGRADES_H diff --git a/test/tst_upgrades/tst_upgrades.pro b/test/tst_upgrades/tst_upgrades.pro new file mode 100644 index 0000000..8493a14 --- /dev/null +++ b/test/tst_upgrades/tst_upgrades.pro @@ -0,0 +1,26 @@ +QT += testlib sql + +TARGET = tst_upgrades +TEMPLATE = app +CONFIG += warn_on c++11 + +include(../common/nut-lib.pri) + +SOURCES += tst_upgrades.cpp \ + db1.cpp \ + table1.cpp \ + db2.cpp \ + table2.cpp \ + db3.cpp \ + table3.cpp + +HEADERS += \ + tst_upgrades.h \ + db1.h \ + table1.h \ + db2.h \ + table2.h \ + db3.h \ + table3.h + +include($$PWD/../../ci-test-init.pri)