From ec449b0c8f9216b5dddfe00665f38861dcf1001d Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Wed, 19 Jun 2019 17:38:09 +0430 Subject: [PATCH 01/25] all supported datatypes in full list markdown [skip ci] --- README.md | 46 +-------- doc/datatypes.md | 41 ++++++++ test/common/comment.h | 4 +- test/common/score.h | 5 +- test/test.pro | 5 +- .../tst_supported_datatypes.cpp | 98 +++++++++++++++++++ .../tst_supported_datatypes.h | 38 +++++++ .../tst_supported_datatypes.pro | 18 ++++ 8 files changed, 203 insertions(+), 52 deletions(-) create mode 100644 doc/datatypes.md create mode 100644 test/tst_supported_datatypes/tst_supported_datatypes.cpp create mode 100644 test/tst_supported_datatypes/tst_supported_datatypes.h create mode 100644 test/tst_supported_datatypes/tst_supported_datatypes.pro diff --git a/README.md b/README.md index 8eacca4..d08d7e9 100644 --- a/README.md +++ b/README.md @@ -23,51 +23,7 @@ Badge](https://api.codacy.com/project/badge/Grade/f3802610beb946068f6cd2c2b6608a - 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 + - Supported types: [Full list](doc/datatypes.md) ## Sample Codes diff --git a/doc/datatypes.md b/doc/datatypes.md new file mode 100644 index 0000000..a4bc9e7 --- /dev/null +++ b/doc/datatypes.md @@ -0,0 +1,41 @@ +| Type | Sqlite | MySql | Postgresql| Ms Sql server | +|--------|--------|--------|--------|--------| +| bool | BOOLEAN | BOOLEAN | BOOLEAN | BIT | +| QBitArray | BLOB | VARBINARY | BYTEA | VARBINARY (MAX) | +| QByteArray | BLOB | BLOB | BYTEA | VARBINARY (MAX) | +| QDate | DATE | DATE | DATE | DATE | +| QDateTime | DATETIME | DATETIME | TIMESTAMP | DATETIME | +| QTime | TIME | TIME | TIME | TIME | +| double | DOUBLE | REAL | REAL | REAL | +| float | FLOAT | FLOAT | FLOAT | FLOAT(24) | +| signed char | TINYINT | TINYINT | SMALLINT | tinyint | +| char | TINYINT | CHAR(1) | CHAR(1) | CHAR(1) | +| uchar | TINYINT UNSIGNED | TINYINT | SMALLINT | tinyint | +| short | SMALLINT | SMALLINT | SMALLINT | smallint | +| ushort | SMALLINT UNSIGNED | SMALLINT | SMALLINT | smallint | +| int | INT | INT | INTEGER | INT | +| uint | INT UNSIGNED | INT | INTEGER | INT | +| long | MEDIUMINT | BIGINT | BIGINT | bigint | +| ulong | MEDIUMINT UNSIGNED | BIGINT | BIGINT | bigint | +| qlonglong | BIGINT | BIGINT | BIGINT | bigint | +| qulonglong | BIGINT UNSIGNED | BIGINT | BIGINT | bigint | +| QChar | NCHAR(1) | CHAR(1) | CHAR(1) | CHAR(1) | +| QUrl | TEXT | TEXT | TEXT | TEXT | +| QJsonArray | TEXT | TEXT | JSONB | TEXT | +| QJsonValue | TEXT | TEXT | JSONB | TEXT | +| QJsonObject | TEXT | TEXT | JSONB | TEXT | +| QJsonDocument | TEXT | TEXT | JSONB | TEXT | +| QPoint | TEXT | TEXT | POINT | GEOMETRY | +| QPointF | TEXT | TEXT | POINT | GEOMETRY | +| QSize | TEXT | TEXT | TEXT | TEXT | +| QSizeF | TEXT | TEXT | TEXT | TEXT | +| QLine | TEXT | TEXT | LINE | TEXT | +| QLineF | TEXT | TEXT | LINE | TEXT | +| QRect | TEXT | TEXT | BOX | TEXT | +| QRectF | TEXT | TEXT | BOX | TEXT | +| QPolygon | TEXT | TEXT | POLYGON | TEXT | +| QPolygonF | TEXT | TEXT | POLYGON | TEXT | +| QStringList | TEXT | TEXT | TEXT[] | TEXT | +| QColor | TEXT | TEXT | TEXT | TEXT | +| QUuid | TEXT | TEXT | UUID | UNIQUEIDENTIFIER | +| QString | TEXT | TEXT | TEXT | NVARCHAR(MAX) | diff --git a/test/common/comment.h b/test/common/comment.h index 437aa20..83fd55b 100644 --- a/test/common/comment.h +++ b/test/common/comment.h @@ -21,8 +21,8 @@ class Comment : public Table 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, int, author, author, setAuthor) + NUT_FOREIGN_KEY(Post, int, post, post, setPost) + NUT_FOREIGN_KEY(User, int, author, author, setAuthor) public: Q_INVOKABLE explicit Comment(QObject *parentTableSet = nullptr); diff --git a/test/common/score.h b/test/common/score.h index 72d907b..a87fa08 100644 --- a/test/common/score.h +++ b/test/common/score.h @@ -1,6 +1,7 @@ #ifndef SCORE_H #define SCORE_H +#include #include "table.h" class User; @@ -14,8 +15,8 @@ class Score : public Nut::Table NUT_DECLARE_FIELD(int, score, score, setScore) - NUT_FOREGION_KEY(Post, int, post, post, setPost) - NUT_FOREGION_KEY(User, QUuid, author, author, setAuthor) + NUT_FOREIGN_KEY(Post, int, post, post, setPost) + NUT_FOREIGN_KEY(User, QUuid, author, author, setAuthor) public: Q_INVOKABLE Score(QObject *parent = Q_NULLPTR); diff --git a/test/test.pro b/test/test.pro index 504a9c5..8f182da 100644 --- a/test/test.pro +++ b/test/test.pro @@ -3,12 +3,11 @@ TEMPLATE = subdirs SUBDIRS += \ tst_basic \ tst_benckmark \ -# tst_commands \ tst_datatypes \ - #tst_join \ tst_phrases \ tst_quuid \ tst_generators \ tst_upgrades \ - tst_json + tst_json \ + tst_supported_datatypes diff --git a/test/tst_supported_datatypes/tst_supported_datatypes.cpp b/test/tst_supported_datatypes/tst_supported_datatypes.cpp new file mode 100644 index 0000000..f9222b2 --- /dev/null +++ b/test/tst_supported_datatypes/tst_supported_datatypes.cpp @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include + +#include "tst_supported_datatypes.h" + +#include "generators/sqlitegenerator.h" +#include "generators/postgresqlgenerator.h" +#include "generators/mysqlgenerator.h" +#include "generators/sqlservergenerator.h" + +SupportedDataTypesTest::SupportedDataTypesTest(QObject *parent) : QObject(parent) +{ + types + << QMetaType::Bool + << QMetaType::QBitArray + << QMetaType::QByteArray + << QMetaType::QDate + << QMetaType::QDateTime + << QMetaType::QTime + << QMetaType::Double + << QMetaType::Float + + << QMetaType::SChar + << QMetaType::Char + << QMetaType::UChar + << QMetaType::Short + << QMetaType::UShort + << QMetaType::Int + << QMetaType::UInt + << QMetaType::Long + << QMetaType::ULong + << QMetaType::LongLong + << QMetaType::ULongLong + + << QMetaType::QChar + + << QMetaType::QUrl + << QMetaType::QJsonArray + << QMetaType::QJsonValue + << QMetaType::QJsonObject + << QMetaType::QJsonDocument + << QMetaType::QPoint + << QMetaType::QPointF + << QMetaType::QSize + << QMetaType::QSizeF + << QMetaType::QLine + << QMetaType::QLineF + << QMetaType::QRect + << QMetaType::QRectF + << QMetaType::QPolygon + << QMetaType::QPolygonF + << QMetaType::QStringList + << QMetaType::QColor + << QMetaType::QUuid + + << QMetaType::QString; +} + +void SupportedDataTypesTest::initTestCase() +{ + Nut::SqliteGenerator sqlite; + Nut::SqlServerGenerator mssql; + Nut::PostgreSqlGenerator pgsql; + Nut::MySqlGenerator mysql; + + Nut::FieldModel *field = new Nut::FieldModel; + foreach (QMetaType::Type type, types) { + field->type = type; + result.append(Result( + type, + mssql.fieldType(field), + sqlite.fieldType(field), + pgsql.fieldType(field), + mysql.fieldType(field) + )); + } +} + +void SupportedDataTypesTest::cleanupTestCase() +{ + qDebug() << DOC_PATH; + QString md("| Type | Sqlite | MySql | Postgresql| Ms Sql server |\n"); + md.append("|--------|--------|--------|--------|--------|\n"); + foreach (Result r, result) + md.append(QString("| %1 | %2 | %3 | %4 | %5 |\n") + .arg(QMetaType::typeName(r.type), r.sqlite, r.mysql, r.pgsql, r.mssql)); + + QFile file(DOC_PATH "/datatypes.md"); + if (file.open(QIODevice::WriteOnly)) { + file.write(md.toUtf8()); + file.close(); + } +} + +QTEST_MAIN(SupportedDataTypesTest) diff --git a/test/tst_supported_datatypes/tst_supported_datatypes.h b/test/tst_supported_datatypes/tst_supported_datatypes.h new file mode 100644 index 0000000..2ce7f03 --- /dev/null +++ b/test/tst_supported_datatypes/tst_supported_datatypes.h @@ -0,0 +1,38 @@ +#ifndef MAINTEST_H +#define MAINTEST_H + +#include +#include + +class SupportedDataTypesTest : public QObject +{ + Q_OBJECT + QList types; + + struct Result + { + QMetaType::Type type; + QString mssql; + QString sqlite; + QString pgsql; + QString mysql; + + Result(QMetaType::Type type, QString mssql, QString sqlite, + QString pgsql, QString mysql) + : type(type), mssql(mssql), sqlite(sqlite), pgsql(pgsql), mysql(mysql) + {} + }; + QList result; + +public: + explicit SupportedDataTypesTest(QObject *parent = nullptr); + +signals: + +private slots: + void initTestCase(); + + void cleanupTestCase(); +}; + +#endif // MAINTEST_H diff --git a/test/tst_supported_datatypes/tst_supported_datatypes.pro b/test/tst_supported_datatypes/tst_supported_datatypes.pro new file mode 100644 index 0000000..eb8b996 --- /dev/null +++ b/test/tst_supported_datatypes/tst_supported_datatypes.pro @@ -0,0 +1,18 @@ +QT += testlib sql gui + +TARGET = tst_datatypes +TEMPLATE = app + +CONFIG += warn_on c++11 + +DEFINES += DOC_PATH=\\\"$$PWD/../../doc\\\" + +include(../common/nut-lib.pri) + +SOURCES += \ + tst_supported_datatypes.cpp + +HEADERS += \ + tst_supported_datatypes.h + +include($$PWD/../../ci-test-init.pri) From 35eb05ab8e4b964ebf2e3d4b76fde2bacf2e284a Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Wed, 19 Jun 2019 18:02:30 +0430 Subject: [PATCH 02/25] auto generate md file for supported data types [skip ci] --- ci-test-init.pri | 2 + test/test.pro | 3 +- test/tst_generators/tst_generators.cpp | 7 +- .../tst_supported_datatypes.cpp | 98 ------------------- .../tst_supported_datatypes.h | 38 ------- .../tst_supported_datatypes.pro | 18 ---- 6 files changed, 9 insertions(+), 157 deletions(-) delete mode 100644 test/tst_supported_datatypes/tst_supported_datatypes.cpp delete mode 100644 test/tst_supported_datatypes/tst_supported_datatypes.h delete mode 100644 test/tst_supported_datatypes/tst_supported_datatypes.pro diff --git a/ci-test-init.pri b/ci-test-init.pri index c3d1f7b..a204047 100644 --- a/ci-test-init.pri +++ b/ci-test-init.pri @@ -1 +1,3 @@ #QT -= gui + +DEFINES += DOC_PATH=\\\"$$PWD/../../doc\\\" diff --git a/test/test.pro b/test/test.pro index 8f182da..6fad6dd 100644 --- a/test/test.pro +++ b/test/test.pro @@ -8,6 +8,5 @@ SUBDIRS += \ tst_quuid \ tst_generators \ tst_upgrades \ - tst_json \ - tst_supported_datatypes + tst_json diff --git a/test/tst_generators/tst_generators.cpp b/test/tst_generators/tst_generators.cpp index 285cd05..140a6e1 100644 --- a/test/tst_generators/tst_generators.cpp +++ b/test/tst_generators/tst_generators.cpp @@ -139,7 +139,12 @@ void GeneratorsTest::cleanupTestCase() .arg(i.key(), i.value().sqlite, i.value().mysql, i.value().psql, i.value().mssql)); } - qDebug() << p.toStdString().c_str(); + + QFile file(DOC_PATH "/datatypes.md"); + if (file.open(QIODevice::WriteOnly)) { + file.write(p.toUtf8()); + file.close(); + } } QTEST_MAIN(GeneratorsTest) diff --git a/test/tst_supported_datatypes/tst_supported_datatypes.cpp b/test/tst_supported_datatypes/tst_supported_datatypes.cpp deleted file mode 100644 index f9222b2..0000000 --- a/test/tst_supported_datatypes/tst_supported_datatypes.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include -#include -#include -#include - -#include "tst_supported_datatypes.h" - -#include "generators/sqlitegenerator.h" -#include "generators/postgresqlgenerator.h" -#include "generators/mysqlgenerator.h" -#include "generators/sqlservergenerator.h" - -SupportedDataTypesTest::SupportedDataTypesTest(QObject *parent) : QObject(parent) -{ - types - << QMetaType::Bool - << QMetaType::QBitArray - << QMetaType::QByteArray - << QMetaType::QDate - << QMetaType::QDateTime - << QMetaType::QTime - << QMetaType::Double - << QMetaType::Float - - << QMetaType::SChar - << QMetaType::Char - << QMetaType::UChar - << QMetaType::Short - << QMetaType::UShort - << QMetaType::Int - << QMetaType::UInt - << QMetaType::Long - << QMetaType::ULong - << QMetaType::LongLong - << QMetaType::ULongLong - - << QMetaType::QChar - - << QMetaType::QUrl - << QMetaType::QJsonArray - << QMetaType::QJsonValue - << QMetaType::QJsonObject - << QMetaType::QJsonDocument - << QMetaType::QPoint - << QMetaType::QPointF - << QMetaType::QSize - << QMetaType::QSizeF - << QMetaType::QLine - << QMetaType::QLineF - << QMetaType::QRect - << QMetaType::QRectF - << QMetaType::QPolygon - << QMetaType::QPolygonF - << QMetaType::QStringList - << QMetaType::QColor - << QMetaType::QUuid - - << QMetaType::QString; -} - -void SupportedDataTypesTest::initTestCase() -{ - Nut::SqliteGenerator sqlite; - Nut::SqlServerGenerator mssql; - Nut::PostgreSqlGenerator pgsql; - Nut::MySqlGenerator mysql; - - Nut::FieldModel *field = new Nut::FieldModel; - foreach (QMetaType::Type type, types) { - field->type = type; - result.append(Result( - type, - mssql.fieldType(field), - sqlite.fieldType(field), - pgsql.fieldType(field), - mysql.fieldType(field) - )); - } -} - -void SupportedDataTypesTest::cleanupTestCase() -{ - qDebug() << DOC_PATH; - QString md("| Type | Sqlite | MySql | Postgresql| Ms Sql server |\n"); - md.append("|--------|--------|--------|--------|--------|\n"); - foreach (Result r, result) - md.append(QString("| %1 | %2 | %3 | %4 | %5 |\n") - .arg(QMetaType::typeName(r.type), r.sqlite, r.mysql, r.pgsql, r.mssql)); - - QFile file(DOC_PATH "/datatypes.md"); - if (file.open(QIODevice::WriteOnly)) { - file.write(md.toUtf8()); - file.close(); - } -} - -QTEST_MAIN(SupportedDataTypesTest) diff --git a/test/tst_supported_datatypes/tst_supported_datatypes.h b/test/tst_supported_datatypes/tst_supported_datatypes.h deleted file mode 100644 index 2ce7f03..0000000 --- a/test/tst_supported_datatypes/tst_supported_datatypes.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef MAINTEST_H -#define MAINTEST_H - -#include -#include - -class SupportedDataTypesTest : public QObject -{ - Q_OBJECT - QList types; - - struct Result - { - QMetaType::Type type; - QString mssql; - QString sqlite; - QString pgsql; - QString mysql; - - Result(QMetaType::Type type, QString mssql, QString sqlite, - QString pgsql, QString mysql) - : type(type), mssql(mssql), sqlite(sqlite), pgsql(pgsql), mysql(mysql) - {} - }; - QList result; - -public: - explicit SupportedDataTypesTest(QObject *parent = nullptr); - -signals: - -private slots: - void initTestCase(); - - void cleanupTestCase(); -}; - -#endif // MAINTEST_H diff --git a/test/tst_supported_datatypes/tst_supported_datatypes.pro b/test/tst_supported_datatypes/tst_supported_datatypes.pro deleted file mode 100644 index eb8b996..0000000 --- a/test/tst_supported_datatypes/tst_supported_datatypes.pro +++ /dev/null @@ -1,18 +0,0 @@ -QT += testlib sql gui - -TARGET = tst_datatypes -TEMPLATE = app - -CONFIG += warn_on c++11 - -DEFINES += DOC_PATH=\\\"$$PWD/../../doc\\\" - -include(../common/nut-lib.pri) - -SOURCES += \ - tst_supported_datatypes.cpp - -HEADERS += \ - tst_supported_datatypes.h - -include($$PWD/../../ci-test-init.pri) From 126f2141463804f13a51d7b63bd2a0ddc24314f7 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Thu, 20 Jun 2019 12:39:34 +0430 Subject: [PATCH 03/25] wip: more documents [skip ci] --- README.md | 54 +++----------------------------------- doc/database.md | 39 +++++++++++++++++++++++++++ doc/query.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++ doc/start.md | 61 ++++++++++++++++++++++++++++++++++++++++++ doc/table.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 240 insertions(+), 51 deletions(-) create mode 100644 doc/database.md create mode 100644 doc/query.md create mode 100644 doc/start.md create mode 100644 doc/table.md diff --git a/README.md b/README.md index d08d7e9..50da158 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,8 @@ # Nut -## Build result -| 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) | - +[![Build Status](https://travis-ci.org/HamedMasafi/Nut.svg?branch=master)](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) @@ -23,51 +17,9 @@ Badge](https://api.codacy.com/project/badge/Grade/f3802610beb946068f6cd2c2b6608a - Automatically create and update database - IDE auto complete support, No hard-code nedded - Table join detect - - Supported types: [Full list](doc/datatypes.md) + - Suppor every Qt types. [Full list](doc/datatypes.md) - -## Sample Codes -### Read data from database: - -```cpp -auto q = db.posts()->createQuery(); -q->setWhere(Post::idField() == postId); -auto posts = q->toList(); -// now posts is a QList contain all posts in -// database that has id equal to postId variable -auto post = q->first(); -// post is first row in database that its id is equal to postId -``` - -### Adding to database: -```cpp -Post *newPost = new Post; -newPost->setTitle("post title"); - -db.posts()->append(newPost); - -for(int i = 0 ; i < 3; i++){ - Comment *comment = new Comment; - comment->setMessage("comment #" + QString::number(i)); - - newPost->comments()->append(comment); -} -db.saveChanges(); -``` - -### Modify database data: -```cpp -auto q = db.posts()->createQuery(); -q->setWhere(Post::idField() == postId); -Post *post = q->first(); - -if(post) { - post->setTitle("new name"); - db.saveChanges(); -} else { - qWarning("No post found!"); -} -``` +[Getting start](doc/start.md) ### Donate Butcoin address: 1Dn1WHKkaxanXe4cTGDk4cFRRABxLUpEVj diff --git a/doc/database.md b/doc/database.md new file mode 100644 index 0000000..399ae72 --- /dev/null +++ b/doc/database.md @@ -0,0 +1,39 @@ +Database class must inherits from Nut::Database class. +Database class can have NUT_DB_VERSION for declaring version number, version will be stored in database if upgrade needed. +```cpp +NUT_DB_VERSION(major, minor) +``` + +for every table in database NUT_DECLARE_TABLE macro should be use, usage: +```cpp +NUT_DECLARE_TABLE(class_name, table_name) +``` + +Sample database class: +```cpp +#include + +class Post; +class Comment; +class WeblogDatabase : public Nut::Database +{ + Q_OBJECT + + NUT_DB_VERSION(1) + + NUT_DECLARE_TABLE(Post, post) + NUT_DECLARE_TABLE(Comment, comment) + +public: + WeblogDatabase(); +}; +``` + +Child tables should initalize in constructor, Example: +```cpp +WeblogDatabase::WeblogDatabase() : Nut::Database() + , m_posts(new TableSet(this)) + , m_comments(new TableSet(this)) +{ +} +``` \ No newline at end of file diff --git a/doc/query.md b/doc/query.md new file mode 100644 index 0000000..b752f34 --- /dev/null +++ b/doc/query.md @@ -0,0 +1,67 @@ +# Creating query +```cpp +auto q = db.posts().query(); +``` + +You can also create query in one command: +```cpp +auto result = db.posts().query() + ->where(Post::idField() == 1) + ->toList(); +``` +Now, _result_ contains **QList\\>** and can be used in code. query has other commands like: sum, avg, max, min and etc + +## Getting first record in query +```cpp +auto post = db.posts().query() + ->setWhete(Post::idField() == 1) + ->first(); + +if(post) + qDebug() << "Post found in database"; +else + qDebug() << "No post found!"; + +``` +## Sorting result +```cpp +auto posts = db.posts().query() + ->whete(Post::idField() == 1) + ->orderBy(Post::idField()) + ->toList(); +``` +Also you can sort descending by adding **!** to field name +```cpp +auto posts = db.posts().query() + ->whete(Post::idField() == 1) + ->orderBy(!Post::idField()) + ->toList(); +``` + +## Selecting single field +```cpp +auto ids = db.posts().query() + ->select(Post::idField()); +//ids is type of QList +``` +## Getting sum, count, min, max +```cpp +auto q = db.posts().query(); +auto sum = q.sum(Post::idField()); +auto max = q.max(Post::idField()); +auto min = q.min(Post::idField()); +auto count = q.count(Post::idField()); +``` + +## Checking field exists in list of values +```cpp +auto post = db.posts().query() + ->setWhete(Post::idField().in(QList() << 1 << 2 << 3 << 4) || Post::isAccepted()) + ->first(); +``` +Or +```cpp +auto post = db.posts().query() + ->setWhete(Post::idField().in({1, 2, 3, 4}) || Post::isAccepted()) + ->first(); +``` \ No newline at end of file diff --git a/doc/start.md b/doc/start.md new file mode 100644 index 0000000..a980903 --- /dev/null +++ b/doc/start.md @@ -0,0 +1,61 @@ +Welcome to the Nut wiki! + +# What is Nut + +Nut is advanced, Powerful and easy to use ORM for Qt5 + +## Sample Codes +### Read data from database: + +```cpp +auto posts = db.posts()->query() + where(Post::idField() == postId) + toList(); +// now posts is a QList contain all posts in +// database that has id equal to postId variable +auto post = q->first(); +// post is first row in database that its id is equal to postId +``` + +### Adding to database: +```cpp +auto newPost = Nut::create(); +newPost->setTitle("post title"); + +db.posts()->append(newPost); + +for(int i = 0 ; i < 3; i++){ + // Below line same as new Comment in non shared pointer mode + // or QSharedPointer(new Comment) in shared_pointer mode + + auto comment = Nut::create(); + comment->setMessage("comment #" + QString::number(i)); + + newPost->comments()->append(comment); +} +db.saveChanges(); +``` + +### Modify database data: +```cpp +auto post = db.posts()->query() + ->where(Post::idField() == postId) + ->first(); + +if(post) { + post->setTitle("new name"); + db.saveChanges(); +} else { + qWarning("No post found!"); +} +``` + +## How to use nut + +* [Create database class](database.md) + +* [Create table class](table.md) + +* [Using queries](query.md) + +* [SUpported data types](datatypes.md) \ No newline at end of file diff --git a/doc/table.md b/doc/table.md new file mode 100644 index 0000000..37e82cb --- /dev/null +++ b/doc/table.md @@ -0,0 +1,70 @@ +The class must inherits from Table class + +## Add primary key field +Primary key can be auto increment + +```cpp +NUT_PRIMARY_AUTO_INCREMENT(id) +NUT_DECLARE_FIELD(int, id, id, setId) +``` + +for declaring primary key use _NUT_PRIMARY_KEY_ macro, if primary key is auto increment use _NUT_PRIMARY_AUTO_INCREMENT_ + +| Macro | Description | +| ----------------------------- |:------------------------------------------------| +| NUT_PRIMARY_KEY(x) | The field *x* is primary key | +| NUT_AUTO_INCREMENT(x) | The field *x* is auto increment | +| NUT_PRIMARY_AUTO_INCREMENT(x) | The field *x* is primary key and auto increment | + +## Declare field +```cpp +NUT_DECLARE_FIELD(type, property_name, read_method_name, write_method_name) +``` +## Additional meta data +| Macro | Description | +| ----------------------------- |:-------------------------------------------------| +| NUT_NOT_NULL(x) | The field *x* is not allowed to store NULL value | +| NUT_LEN(x, len) | Max length of *x* is *len* in string types and in numeric typed field *x* will be store in *len* bytes | +| NUT_DEFAULT_VALUE(x, def) | Default value of *x* is *def* | +| NUT_UNIQUE(x) | Field *x* is unique (Not imlemented yet!) | +| NUT_DISPLAY_NAME(field, name) | Sets display name for field (used in model creation | + +## Sample table +```cpp +class Post : public Table +{ + Q_OBJECT + + NUT_PRIMARY_AUTO_INCREMENT(id) + NUT_DECLARE_FIELD(int, id, id, setId) + + NUT_NOT_NULL(title) + NUT_LEN(title, 50) + NUT_DECLARE_FIELD(QString, title, title, setTitle) + + NUT_DECLARE_FIELD(QDateTime, saveDate, saveDate, setSaveDate) + + NUT_LEN(body, 200) + NUT_DECLARE_FIELD(QString, body, body, setBody) +public: + explicit Post(QObject *tableSet = 0); + +}; +``` + +## Declare child table +If current table has one-to-many relation ship it must be declared. For example post table has a slave table named comment, every post has many comment: +```cpp +NUT_DECLARE_CHILD_TABLE(Comment, comments) +``` + +First argument id table name and second is field name, m_comments must be initalized in constructor: +```cpp +Post::Post(QObject *parent) : Table(parent), + m_comments(new TableSet(this)), m_id(0), m_title("") +{ + +} +``` + +For more example take a look at _tests/common_ folder \ No newline at end of file From 97a94241630690f6d6255cd3947c8124623ccfe9 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Thu, 20 Jun 2019 12:59:57 +0430 Subject: [PATCH 04/25] few lines of doc & travis build only master branch --- .travis.yml | 4 ++ .../ipch/38f51fe5930ec860/mmap_address.bin | Bin 0 -> 8 bytes README.md | 8 ++- doc/query.md | 4 +- doc/sharedpointer.md | 59 ++++++++++++++++++ doc/start.md | 16 ----- 6 files changed, 72 insertions(+), 19 deletions(-) create mode 100644 .vscode/ipch/38f51fe5930ec860/mmap_address.bin create mode 100644 doc/sharedpointer.md diff --git a/.travis.yml b/.travis.yml index 31fe73e..1502709 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,10 @@ #Based on https://github.com/pcolby/libqtaws/blob/master/.travis.yml language: cpp +branches: + only: + - master + os: - linux - osx diff --git a/.vscode/ipch/38f51fe5930ec860/mmap_address.bin b/.vscode/ipch/38f51fe5930ec860/mmap_address.bin new file mode 100644 index 0000000000000000000000000000000000000000..f015e039298aa6a178e9be8160337a72102bb65a GIT binary patch literal 8 PcmZQzU|`u-&%gix1d#zk literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 50da158..0b5a8a0 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,13 @@ Badge](https://api.codacy.com/project/badge/Grade/f3802610beb946068f6cd2c2b6608a - Table join detect - Suppor every Qt types. [Full list](doc/datatypes.md) -[Getting start](doc/start.md) +##Getting start +* [Sample codes](doc/start.md) +* [Shared pointer and regular mode](sharedpointer.md) +* [Create database class](database.md) +* [Create table class](table.md) +* [Using queries](query.md) +* [Supported data types](datatypes.md) ### Donate Butcoin address: 1Dn1WHKkaxanXe4cTGDk4cFRRABxLUpEVj diff --git a/doc/query.md b/doc/query.md index b752f34..ffb8d0c 100644 --- a/doc/query.md +++ b/doc/query.md @@ -56,12 +56,12 @@ auto count = q.count(Post::idField()); ## Checking field exists in list of values ```cpp auto post = db.posts().query() - ->setWhete(Post::idField().in(QList() << 1 << 2 << 3 << 4) || Post::isAccepted()) + ->where(Post::idField().in(QList() << 1 << 2 << 3 << 4) || Post::isAccepted()) ->first(); ``` Or ```cpp auto post = db.posts().query() - ->setWhete(Post::idField().in({1, 2, 3, 4}) || Post::isAccepted()) + ->where(Post::idField().in({1, 2, 3, 4}) || Post::isAccepted()) ->first(); ``` \ No newline at end of file diff --git a/doc/sharedpointer.md b/doc/sharedpointer.md new file mode 100644 index 0000000..85195f1 --- /dev/null +++ b/doc/sharedpointer.md @@ -0,0 +1,59 @@ +Nut can compile in *shared pointer* mode or *regular* mode + +In *shared pointer* mode reqults of queries is QList> and in *regular* mode results are QList + +Almost in every case shared pointer mode is better, But nut support regular mode for backward comptability. + +To compiling in *shared pointer* define **NUT_SHARED_POINTER** macro + +Nut has template alias + +```cpp +#ifdef NUT_SHARED_POINTER + template + using RowList = QList>; + + template + using Row = QSharedPointer; +#else + template + using RowList = QList; + + template + using Row = T*; +#endif +``` + +In other words these types are defined by this table: + +| Mode | Nut::Row | Nut::RowList | +|------ |----- |--------- | +|Regular|T* | QList\ | +|Shared pointer|QSharedPointer\ | QList\\> | + +For the integration of your source, you can use these aliases. + +Ans also Nut::create() method are defined for two mode + +```cpp +#ifdef NUT_SHARED_POINTER + template + inline Row create(QObject *parent) { + return QSharedPointer(new T(parent)); + } +#else + template + inline Row create() { + return new T; + } +#endif +``` + +So you can use the Nut::create function without considering in what way the library is being compiled. Example: +```cpp +auto post = Nut::create(); +``` + +In above example if *NUT_SHARED_POINTER* is defined *post* is *QSharedPointer* else is *Post\** + +I recommand use *NUT_SHARED_POINTER* always! \ No newline at end of file diff --git a/doc/start.md b/doc/start.md index a980903..8ce946d 100644 --- a/doc/start.md +++ b/doc/start.md @@ -1,10 +1,3 @@ -Welcome to the Nut wiki! - -# What is Nut - -Nut is advanced, Powerful and easy to use ORM for Qt5 - -## Sample Codes ### Read data from database: ```cpp @@ -50,12 +43,3 @@ if(post) { } ``` -## How to use nut - -* [Create database class](database.md) - -* [Create table class](table.md) - -* [Using queries](query.md) - -* [SUpported data types](datatypes.md) \ No newline at end of file From 60a779806c10b87a9405f04d335abde61a65120b Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Thu, 20 Jun 2019 13:02:17 +0430 Subject: [PATCH 05/25] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b5a8a0..673d65f 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Badge](https://api.codacy.com/project/badge/Grade/f3802610beb946068f6cd2c2b6608a - Table join detect - Suppor every Qt types. [Full list](doc/datatypes.md) -##Getting start +## Getting start * [Sample codes](doc/start.md) * [Shared pointer and regular mode](sharedpointer.md) * [Create database class](database.md) From 32429ed10bedfe086f97da443cc5a3fa81b165d4 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Thu, 20 Jun 2019 19:39:54 +0430 Subject: [PATCH 06/25] small fix on readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b5a8a0..673d65f 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Badge](https://api.codacy.com/project/badge/Grade/f3802610beb946068f6cd2c2b6608a - Table join detect - Suppor every Qt types. [Full list](doc/datatypes.md) -##Getting start +## Getting start * [Sample codes](doc/start.md) * [Shared pointer and regular mode](sharedpointer.md) * [Create database class](database.md) From 74b2a4524ac058f9db043db013e15811a198febe Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Sat, 22 Jun 2019 01:32:37 +0430 Subject: [PATCH 07/25] initial --- 3rdparty/serializer | 2 +- ci-test-init.pri | 2 +- src/defines.h | 65 +++++++++++++++++++++++--- src/table.h | 10 +--- test/common/comment.cpp | 3 +- test/tst_generators/tst_generators.cpp | 2 +- 6 files changed, 65 insertions(+), 19 deletions(-) diff --git a/3rdparty/serializer b/3rdparty/serializer index f9d81fc..b3c550c 160000 --- a/3rdparty/serializer +++ b/3rdparty/serializer @@ -1 +1 @@ -Subproject commit f9d81fcc6244271b3926891b0c86554e1e6b967e +Subproject commit b3c550c5bb7c570b1b10492fcedf287c1915af39 diff --git a/ci-test-init.pri b/ci-test-init.pri index a204047..a24cc9d 100644 --- a/ci-test-init.pri +++ b/ci-test-init.pri @@ -1,3 +1,3 @@ #QT -= gui -DEFINES += DOC_PATH=\\\"$$PWD/../../doc\\\" +DEFINES += NUT_PATH=\\\"$$PWD/../../\\\" diff --git a/src/defines.h b/src/defines.h index 144ae10..a547688 100644 --- a/src/defines.h +++ b/src/defines.h @@ -84,15 +84,33 @@ public: \ #define NUT_FOREIGN_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_DECLARE_FIELD(keytype, name##Id, read##Id, write##Id)*/ \ NUT_INFO(__nut_FOREIGN_KEY, name, type) \ - Nut::Row m_##name; \ + Nut::ForeignKeyData m_##name; \ public: \ - Nut::Row read() const { return m_##name ; } \ + Nut::Row read() const { return m_##name.table() ; } \ void write(Nut::Row name){ \ - m_##name = name; \ + m_##name.setTable(name); \ + }\ + \ + Q_PROPERTY(keytype name##Id READ read##Id WRITE write##Id) \ + NUT_INFO(__nut_FIELD, name##Id, 0) \ +public: \ + static NUT_WRAP_NAMESPACE(FieldPhrase)& name##Id ## Field(){ \ + static NUT_WRAP_NAMESPACE(FieldPhrase) f = \ + NUT_WRAP_NAMESPACE(FieldPhrase) \ + (staticMetaObject.className(), #name); \ + return f; \ + } \ + keytype read##Id() const{ \ + return m_##name.value(); \ + } \ + void write##Id(keytype name##Id){ \ + m_##name.setValue(name##Id); \ + propertyChanged(QT_STRINGIFY2(name##Id)); \ } + #define NUT_DECLARE_CHILD_TABLE(type, n) \ private: \ NUT_WRAP_NAMESPACE(TableSet) *m_##n; \ @@ -110,9 +128,18 @@ public: \ } #define NUT_FIELD(name) NUT_INFO(__nut_FIELD, name, 0) -#define NUT_PRIMARY_KEY(x) NUT_INFO(__nut_PRIMARY_KEY, x, 0) +#define NUT_PRIMARY_KEY(x) NUT_INFO(__nut_PRIMARY_KEY, x, 0) \ + QVariant primaryValue() const override { \ + return property(#x); \ + } \ + void setPrimaryValue(const QVariant &value) override { \ + setProperty(#x, value); \ + } + + #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_PRIMARY_AUTO_INCREMENT(x) NUT_INFO(__nut_PRIMARY_KEY_AI, x, 0)\ + NUT_PRIMARY_KEY(X) NUT_AUTO_INCREMENT(X) #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) @@ -250,6 +277,32 @@ inline T *get(const QSharedPointer row) { #endif +template +struct ForeignKeyData { + Nut::Row _table; + T _id; + + ForeignKeyData() : _table(nullptr) + {} + + void setTable(Nut::Row t) { + _table = t; + _id = t->primaryValue().value(); + } + Nut::Row table() const { + return _table; + } + void setValue(const T& val) { + _table = nullptr; + _id = val; + } + T value() const { + if (_table) + return _table->primaryValue().value(); + return _id; + } +}; + NUT_END_NAMESPACE #endif // SYNTAX_DEFINES_H diff --git a/src/table.h b/src/table.h index d661784..16dfbd3 100644 --- a/src/table.h +++ b/src/table.h @@ -55,14 +55,8 @@ public: int save(Database *db); -// Q_DECL_DEPRECATED -// QString primaryKey() const; - -// Q_DECL_DEPRECATED -// bool isPrimaryKeyAutoIncrement() const; - -// Q_DECL_DEPRECATED -// QVariant primaryValue() const; + virtual QVariant primaryValue() const = 0; + virtual void setPrimaryValue(const QVariant &value) = 0; Status status() const; void setStatus(const Status &status); diff --git a/test/common/comment.cpp b/test/common/comment.cpp index d732145..87ec017 100644 --- a/test/common/comment.cpp +++ b/test/common/comment.cpp @@ -1,7 +1,6 @@ #include "comment.h" -Comment::Comment(QObject *parent) : Table(parent), - m_author(Q_NULLPTR), m_post(Q_NULLPTR) +Comment::Comment(QObject *parent) : Table(parent) { } diff --git a/test/tst_generators/tst_generators.cpp b/test/tst_generators/tst_generators.cpp index 140a6e1..8eb4d5f 100644 --- a/test/tst_generators/tst_generators.cpp +++ b/test/tst_generators/tst_generators.cpp @@ -140,7 +140,7 @@ void GeneratorsTest::cleanupTestCase() i.value().psql, i.value().mssql)); } - QFile file(DOC_PATH "/datatypes.md"); + QFile file(NUT_PATH "/doc/datatypes.md"); if (file.open(QIODevice::WriteOnly)) { file.write(p.toUtf8()); file.close(); From f696ffc5c4befcd89f2fe9c05b6e67f6bb38e475 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Sat, 22 Jun 2019 09:49:06 +0430 Subject: [PATCH 08/25] working on it --- 3rdparty/serializer | 2 +- src/defines.h | 90 ++++++++++++++++++++---------------- test/common/comment.cpp | 5 ++ test/common/score.cpp | 5 ++ test/tst_basic/tst_basic.cpp | 10 ++-- 5 files changed, 67 insertions(+), 45 deletions(-) diff --git a/3rdparty/serializer b/3rdparty/serializer index b3c550c..f9d81fc 160000 --- a/3rdparty/serializer +++ b/3rdparty/serializer @@ -1 +1 @@ -Subproject commit b3c550c5bb7c570b1b10492fcedf287c1915af39 +Subproject commit f9d81fcc6244271b3926891b0c86554e1e6b967e diff --git a/src/defines.h b/src/defines.h index a547688..50e3cbe 100644 --- a/src/defines.h +++ b/src/defines.h @@ -83,30 +83,40 @@ public: \ } #define NUT_FOREIGN_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_FOREIGN_KEY, name, type) \ - Nut::ForeignKeyData m_##name; \ -public: \ - Nut::Row read() const { return m_##name.table() ; } \ - void write(Nut::Row name){ \ - m_##name.setTable(name); \ - }\ - \ - Q_PROPERTY(keytype name##Id READ read##Id WRITE write##Id) \ NUT_INFO(__nut_FIELD, name##Id, 0) \ + NUT_INFO(__nut_FOREIGN_KEY, name, type) \ + Nut::Row m_##name; \ + keytype m_##name##Id; \ + Q_PROPERTY(keytype name##Id READ read##Id WRITE write##Id) \ public: \ + Nut::Row read() const; \ + void write(Nut::Row name); \ static NUT_WRAP_NAMESPACE(FieldPhrase)& name##Id ## Field(){ \ static NUT_WRAP_NAMESPACE(FieldPhrase) f = \ NUT_WRAP_NAMESPACE(FieldPhrase) \ (staticMetaObject.className(), #name); \ return f; \ } \ - keytype read##Id() const{ \ - return m_##name.value(); \ + keytype read##Id() const; \ + void write##Id(keytype name##Id); + +#define NUT_FOREIGN_KEY_IMPLEMENT(class, type, keytype, name, read, write) \ + \ + Nut::Row class::read() const { return m_##name ; } \ + void class::write(Nut::Row name){ \ + propertyChanged(QT_STRINGIFY2(name##Id)); \ + m_##name = name; \ + m_##name##Id = name->primaryValue().value(); \ + } \ + \ + keytype class::read##Id() const{ \ + if (m_##name) \ + return m_##name->primaryValue().value(); \ + return m_##name##Id; \ } \ - void write##Id(keytype name##Id){ \ - m_##name.setValue(name##Id); \ + void class::write##Id(keytype name##Id){ \ + m_##name##Id = name##Id; \ + m_##name = nullptr; \ propertyChanged(QT_STRINGIFY2(name##Id)); \ } @@ -129,12 +139,14 @@ public: \ #define NUT_FIELD(name) NUT_INFO(__nut_FIELD, name, 0) #define NUT_PRIMARY_KEY(x) NUT_INFO(__nut_PRIMARY_KEY, x, 0) \ + public: \ QVariant primaryValue() const override { \ return property(#x); \ } \ void setPrimaryValue(const QVariant &value) override { \ setProperty(#x, value); \ - } + } \ + private: #define NUT_AUTO_INCREMENT(x) NUT_INFO(__nut_AUTO_INCREMENT, x, 0) @@ -277,31 +289,31 @@ inline T *get(const QSharedPointer row) { #endif -template -struct ForeignKeyData { - Nut::Row _table; - T _id; +//template +//struct ForeignKeyData { +// Nut::Row _table; +// T _id; - ForeignKeyData() : _table(nullptr) - {} +// ForeignKeyData() : _table(nullptr) +// {} - void setTable(Nut::Row t) { - _table = t; - _id = t->primaryValue().value(); - } - Nut::Row table() const { - return _table; - } - void setValue(const T& val) { - _table = nullptr; - _id = val; - } - T value() const { - if (_table) - return _table->primaryValue().value(); - return _id; - } -}; +// void setTable(Nut::Row t) { +// _table = t; +// _id = t->primaryValue().value(); +// } +// Nut::Row table() const { +// return _table; +// } +// void setValue(const T& val) { +// _table = nullptr; +// _id = val; +// } +// T value() const { +// if (_table) +// return _table->primaryValue().value(); +// return _id; +// } +//}; NUT_END_NAMESPACE diff --git a/test/common/comment.cpp b/test/common/comment.cpp index 87ec017..59b2e0d 100644 --- a/test/common/comment.cpp +++ b/test/common/comment.cpp @@ -1,6 +1,11 @@ #include "comment.h" +#include "post.h" +#include "user.h" Comment::Comment(QObject *parent) : Table(parent) { } + +NUT_FOREIGN_KEY_IMPLEMENT(Comment, Post, int, post, post, setPost) +NUT_FOREIGN_KEY_IMPLEMENT(Comment, User, int, author, author, setAuthor) diff --git a/test/common/score.cpp b/test/common/score.cpp index 08466a2..91881b5 100644 --- a/test/common/score.cpp +++ b/test/common/score.cpp @@ -1,6 +1,11 @@ #include "score.h" +#include "user.h" +#include "post.h" Score::Score(QObject *parent) : Nut::Table(parent) { } + +NUT_FOREIGN_KEY_IMPLEMENT(Score, Post, int, post, post, setPost) +NUT_FOREIGN_KEY_IMPLEMENT(Score, User, QUuid, author, author, setAuthor) diff --git a/test/tst_basic/tst_basic.cpp b/test/tst_basic/tst_basic.cpp index f728887..c512790 100644 --- a/test/tst_basic/tst_basic.cpp +++ b/test/tst_basic/tst_basic.cpp @@ -80,11 +80,11 @@ void BasicTest::createPost() comment->setAuthorId(user->id()); db.comments()->append(comment); } - for (int i = 0; i < 10; ++i) { - auto score = Nut::create(); - score->setScore(i % 5); - newPost->scores()->append(score); - } +// for (int i = 0; i < 10; ++i) { +// auto score = Nut::create(); +// score->setScore(i % 5); +// newPost->scores()->append(score); +// } db.saveChanges(); From e3800f44abc1145dd3bf12148c4b808570a5538f Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Sat, 22 Jun 2019 17:18:35 +0430 Subject: [PATCH 09/25] fix; invalid insert command generation --- src/generators/sqlgeneratorbase.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/generators/sqlgeneratorbase.cpp b/src/generators/sqlgeneratorbase.cpp index 38300a3..e1edd31 100644 --- a/src/generators/sqlgeneratorbase.cpp +++ b/src/generators/sqlgeneratorbase.cpp @@ -446,16 +446,17 @@ QString SqlGeneratorBase::insertRecord(Table *t, QString tableName) QStringList values; - foreach (QString f, t->changedProperties()) - if (f != key) - values.append(escapeValue(t->property(f.toLatin1().data()))); - - QString changedPropertiesText = QString(); QSet props = t->changedProperties(); - foreach (QString s, props) { + QString changedPropertiesText = QString(); + foreach (QString f, props) { + if (f == key) + continue; + + values.append(escapeValue(t->property(f.toLatin1().data()))); + if (changedPropertiesText != "") changedPropertiesText.append(", "); - changedPropertiesText.append(s); + changedPropertiesText.append(f); } sql = QString("INSERT INTO %1 (%2) VALUES (%3)") .arg(tableName, changedPropertiesText, values.join(", ")); From a7678b2a2acf81717dbcbed2d1770ed850ff9c21 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Sat, 22 Jun 2019 19:29:11 +0430 Subject: [PATCH 10/25] new serializer commit --- 3rdparty/serializer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/serializer b/3rdparty/serializer index f9d81fc..51500a4 160000 --- a/3rdparty/serializer +++ b/3rdparty/serializer @@ -1 +1 @@ -Subproject commit f9d81fcc6244271b3926891b0c86554e1e6b967e +Subproject commit 51500a497933444196942ee0db6628338a0422af From 9413c882dcc421ac21792a03830dfb1ae15ba8f1 Mon Sep 17 00:00:00 2001 From: "Hamed.Masafi" Date: Sun, 23 Jun 2019 18:37:06 +0430 Subject: [PATCH 11/25] do not remove if not count --- src/sqlmodel.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sqlmodel.cpp b/src/sqlmodel.cpp index 300b89e..8f219a8 100644 --- a/src/sqlmodel.cpp +++ b/src/sqlmodel.cpp @@ -101,9 +101,11 @@ QVariant SqlModel::data(const QModelIndex &index, int role) const void SqlModel::setRows(RowList rows) { d.detach(); - beginRemoveRows(QModelIndex(), 0, d->rows.count()); - d->rows.clear(); - endRemoveRows(); + if (d->rows.count()) { + beginRemoveRows(QModelIndex(), 0, d->rows.count()); + d->rows.clear(); + endRemoveRows(); + } beginInsertRows(QModelIndex(), 0, rows.count()); d->rows = rows; endInsertRows(); From 2c4495bfb48a7f0bd0b0617d3a4effa75e9f2878 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Wed, 26 Jun 2019 19:36:44 +0430 Subject: [PATCH 12/25] basic test passed --- 3rdparty/serializer | 2 +- src/defines.h | 13 +++++++++++++ test/common/comment.h | 4 ++-- test/common/score.h | 4 ++-- test/tst_basic/tst_basic.cpp | 12 ++++++------ 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/3rdparty/serializer b/3rdparty/serializer index f9d81fc..51500a4 160000 --- a/3rdparty/serializer +++ b/3rdparty/serializer @@ -1 +1 @@ -Subproject commit f9d81fcc6244271b3926891b0c86554e1e6b967e +Subproject commit 51500a497933444196942ee0db6628338a0422af diff --git a/src/defines.h b/src/defines.h index 50e3cbe..318f99b 100644 --- a/src/defines.h +++ b/src/defines.h @@ -83,10 +83,22 @@ public: \ } #define NUT_FOREIGN_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_FOREIGN_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_FOREIGN_KEY_DECLARE(type, keytype, name, read, write) \ NUT_INFO(__nut_FIELD, name##Id, 0) \ NUT_INFO(__nut_FOREIGN_KEY, name, type) \ Nut::Row m_##name; \ keytype m_##name##Id; \ + Q_PROPERTY(Nut::Row name READ read WRITE write) \ Q_PROPERTY(keytype name##Id READ read##Id WRITE write##Id) \ public: \ Nut::Row read() const; \ @@ -115,6 +127,7 @@ public: \ return m_##name##Id; \ } \ void class::write##Id(keytype name##Id){ \ + propertyChanged(QT_STRINGIFY2(name##Id)); \ m_##name##Id = name##Id; \ m_##name = nullptr; \ propertyChanged(QT_STRINGIFY2(name##Id)); \ diff --git a/test/common/comment.h b/test/common/comment.h index 83fd55b..e7628ab 100644 --- a/test/common/comment.h +++ b/test/common/comment.h @@ -21,8 +21,8 @@ class Comment : public Table NUT_DECLARE_FIELD(QDateTime, saveDate, saveDate, setSaveDate) NUT_DECLARE_FIELD(qreal, point, point, setPoint) - NUT_FOREIGN_KEY(Post, int, post, post, setPost) - NUT_FOREIGN_KEY(User, int, author, author, setAuthor) + NUT_FOREIGN_KEY_DECLARE(Post, int, post, post, setPost) + NUT_FOREIGN_KEY_DECLARE(User, int, author, author, setAuthor) public: Q_INVOKABLE explicit Comment(QObject *parentTableSet = nullptr); diff --git a/test/common/score.h b/test/common/score.h index a87fa08..f8c3579 100644 --- a/test/common/score.h +++ b/test/common/score.h @@ -15,8 +15,8 @@ class Score : public Nut::Table NUT_DECLARE_FIELD(int, score, score, setScore) - NUT_FOREIGN_KEY(Post, int, post, post, setPost) - NUT_FOREIGN_KEY(User, QUuid, author, author, setAuthor) + NUT_FOREIGN_KEY_DECLARE(Post, int, post, post, setPost) + NUT_FOREIGN_KEY_DECLARE(User, QUuid, author, author, setAuthor) public: Q_INVOKABLE Score(QObject *parent = Q_NULLPTR); diff --git a/test/tst_basic/tst_basic.cpp b/test/tst_basic/tst_basic.cpp index c512790..55d59c3 100644 --- a/test/tst_basic/tst_basic.cpp +++ b/test/tst_basic/tst_basic.cpp @@ -78,13 +78,13 @@ void BasicTest::createPost() comment->setMessage("comment #" + QString::number(i)); comment->setSaveDate(QDateTime::currentDateTime()); comment->setAuthorId(user->id()); - db.comments()->append(comment); + newPost->comments()->append(comment); + } + for (int i = 0; i < 10; ++i) { + auto score = Nut::create(); + score->setScore(i % 5); + newPost->scores()->append(score); } -// for (int i = 0; i < 10; ++i) { -// auto score = Nut::create(); -// score->setScore(i % 5); -// newPost->scores()->append(score); -// } db.saveChanges(); From 52b5da463cf54dece6dd11fccd29cf7c6de44013 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Sat, 29 Jun 2019 01:04:19 +0430 Subject: [PATCH 13/25] New commit of serializer --- 3rdparty/serializer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/serializer b/3rdparty/serializer index 51500a4..8c395f0 160000 --- a/3rdparty/serializer +++ b/3rdparty/serializer @@ -1 +1 @@ -Subproject commit 51500a497933444196942ee0db6628338a0422af +Subproject commit 8c395f0bfee271637f44e10f51178665a876c6ec From 012784baa07e8c0b2ef0d421da7ed723cd28c7eb Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Sat, 29 Jun 2019 11:40:42 +0430 Subject: [PATCH 14/25] new serializzer commit --- 3rdparty/serializer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/serializer b/3rdparty/serializer index 8c395f0..5b30b13 160000 --- a/3rdparty/serializer +++ b/3rdparty/serializer @@ -1 +1 @@ -Subproject commit 8c395f0bfee271637f44e10f51178665a876c6ec +Subproject commit 5b30b13af9b9537d4226c922d04106dc4ba0595b From 04bfecd66e7d538980861f7e1103d34024579da8 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Tue, 2 Jul 2019 11:00:49 +0430 Subject: [PATCH 15/25] fix between sql operator --- src/generators/sqlgeneratorbase.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/generators/sqlgeneratorbase.cpp b/src/generators/sqlgeneratorbase.cpp index e1edd31..53dd741 100644 --- a/src/generators/sqlgeneratorbase.cpp +++ b/src/generators/sqlgeneratorbase.cpp @@ -991,7 +991,12 @@ QString SqlGeneratorBase::createConditionalPhrase(const PhraseData *d) const else if (op == PhraseData::AddSeconds) ret = QString("DATEADD(second, %1, %2)") .arg(d->operand.toString(), createConditionalPhrase(d->left)); - else if (op == PhraseData::DatePartYear) + else if (op == PhraseData::Between) { + QVariantList list = d->operand.toList(); + ret = QString("%1 BETWEEN %2 AND %3") + .arg(createConditionalPhrase(d->left), escapeValue(list.at(0)), escapeValue(list.at(1))); + + } else if (op == PhraseData::DatePartYear) ret = QString("DATEPART(year, %1)") .arg(d->operand.toString()); else if (op == PhraseData::DatePartMonth) From 3b39018985dcba4c5a957ad81cf2fbe8054f4928 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Tue, 2 Jul 2019 12:23:44 +0430 Subject: [PATCH 16/25] join works on shared pointer mode --- src/defines.h | 4 ++-- src/query.h | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/defines.h b/src/defines.h index 318f99b..17d1032 100644 --- a/src/defines.h +++ b/src/defines.h @@ -87,9 +87,9 @@ public: \ NUT_DECLARE_FIELD(keytype, name##Id, read##Id, write##Id) \ NUT_INFO(__nut_FOREIGN_KEY, name, type) \ Nut::Row m_##name; \ -public: \ +public slots: \ Nut::Row read() const { return m_##name ; } \ - void write(Nut::Row name){ \ + Q_INVOKABLE void write(Nut::Row name){ \ m_##name = name; \ } diff --git a/src/query.h b/src/query.h index b535df3..9b76e35 100644 --- a/src/query.h +++ b/src/query.h @@ -317,9 +317,20 @@ Q_OUTOFLINE_TEMPLATE RowList Query::toList(int count) for (int i = 0; i < data.masters.count(); ++i) { int master = data.masters[i]; - table->setProperty(data.masterFields[i].toLocal8Bit().data(), +#ifdef NUT_SHARED_POINTER + QString mName = QString("set%1").arg(levels[master].lastRow->metaObject()->className()); + QString type = QString("Nut::Row<%1>").arg(levels[master].lastRow->metaObject()->className()); + bool ok = table->metaObject()->invokeMethod(table, + mName.toLocal8Bit().data(), + QGenericArgument(type.toLatin1().data(), levels[master].lastRow)); +#else + bool ok = table->setProperty(data.masterFields[i].toLocal8Bit().data(), QVariant::fromValue(levels[master].lastRow)); +#endif + if (!ok) + qWarning("Unable to set property %s::%s", + table->metaObject()->className(), data.masterFields[i].toLocal8Bit().data()); table->setParentTableSet( levels[master].lastRow->childTableSet( data.table->className())); @@ -330,7 +341,7 @@ Q_OUTOFLINE_TEMPLATE RowList Query::toList(int count) table->clear(); //set last created row - data.lastRow = table; + data.lastRow = /*QSharedPointer
*/(table); } //while } // while From 52937cbcb3fecbd5f7359c64a6d50f0776755b01 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Tue, 2 Jul 2019 22:00:29 +0430 Subject: [PATCH 17/25] do clear in shared pointer mode --- src/tablesetbase.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tablesetbase.cpp b/src/tablesetbase.cpp index 1cf0b0d..6546650 100644 --- a/src/tablesetbase.cpp +++ b/src/tablesetbase.cpp @@ -61,9 +61,10 @@ int TableSetBase::save(Database *db, bool cleanUp) || t->status() == Table::Modified || t->status() == Table::Deleted){ rowsAffected += t->save(db); - +#ifndef NUT_SHARED_POINTER if(cleanUp) t->deleteLater(); +#endif } } @@ -76,7 +77,7 @@ int TableSetBase::save(Database *db, bool cleanUp) void TableSetBase::clearChilds() { #ifndef NUT_SHARED_POINTER - foreach (Table *t, data->_childRows) + foreach (Table *t, data->childRows) t->deleteLater(); #endif data->childRows.clear(); From 73c5295f6da979b51e7d0e64f2ce3b7a1cf25541 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Wed, 3 Jul 2019 18:27:00 +0430 Subject: [PATCH 18/25] removed few comment lines --- src/sqlmodel.cpp | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/sqlmodel.cpp b/src/sqlmodel.cpp index 8f219a8..fa4b698 100644 --- a/src/sqlmodel.cpp +++ b/src/sqlmodel.cpp @@ -73,27 +73,10 @@ QVariant SqlModel::data(const QModelIndex &index, int role) const if (role == Qt::DisplayRole) { 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()) { -// case COL_ID: -// return index.row() + 1; -// case COL_Type: { -// return typeText(d->type); -// } -// case COL_TITLE: -// return d->title; -// case COL_File: -// return d->file; -// case COL_Function: -// return d->function; -// case COL_Line: -// return d->line; -// } } return QVariant(); } From f02e409ee2c9276b1632e48f3559af6add7e12b2 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Sat, 6 Jul 2019 18:38:40 +0430 Subject: [PATCH 19/25] wip: date time functions --- src/generators/mysqlgenerator.cpp | 37 ++++++++++++++++ src/generators/mysqlgenerator.h | 7 +-- src/generators/postgresqlgenerator.cpp | 26 ++++++++++++ src/generators/sqlgeneratorbase.cpp | 17 +++++++- src/generators/sqlgeneratorbase_p.h | 1 + src/generators/sqlitegenerator.cpp | 32 ++++++++++++++ src/generators/sqlitegenerator.h | 1 + src/tablesetbase.cpp | 4 +- test/test.pro | 3 +- test/tst_datetime/db.cpp | 9 ++++ test/tst_datetime/db.h | 21 +++++++++ test/tst_datetime/sampletable.cpp | 6 +++ test/tst_datetime/sampletable.h | 27 ++++++++++++ test/tst_datetime/tst_datetime.cpp | 59 ++++++++++++++++++++++++++ test/tst_datetime/tst_datetime.h | 37 ++++++++++++++++ test/tst_datetime/tst_datetime.pro | 20 +++++++++ 16 files changed, 300 insertions(+), 7 deletions(-) create mode 100644 test/tst_datetime/db.cpp create mode 100644 test/tst_datetime/db.h create mode 100644 test/tst_datetime/sampletable.cpp create mode 100644 test/tst_datetime/sampletable.h create mode 100644 test/tst_datetime/tst_datetime.cpp create mode 100644 test/tst_datetime/tst_datetime.h create mode 100644 test/tst_datetime/tst_datetime.pro diff --git a/src/generators/mysqlgenerator.cpp b/src/generators/mysqlgenerator.cpp index c33af38..ea50b15 100644 --- a/src/generators/mysqlgenerator.cpp +++ b/src/generators/mysqlgenerator.cpp @@ -290,4 +290,41 @@ bool MySqlGenerator::readInsideParentese(QString &text, QString &out) // return command; //} +QString MySqlGenerator::createConditionalPhrase(const PhraseData *d) const +{ + if (!d) + return QString(); + + PhraseData::Condition op = d->operatorCond; + //apply not (!) + if (d->isNot) { + if (op < 20) + op = static_cast((op + 10) % 20); + } + + if (d->type == PhraseData::WithVariant) { + if (op == PhraseData::AddYears) + return QString("DATE_ADD(%2, INTERVAL %1 YEAR)") + .arg(d->operand.toString(), createConditionalPhrase(d->left)); + else if (op == PhraseData::AddMonths) + return QString("DATE_ADD(%2, INTERVAL %1 MONTH)") + .arg(d->operand.toString(), createConditionalPhrase(d->left)); + else if (op == PhraseData::AddDays) + return QString("DATE_ADD(%2, INTERVAL %1 DAY)") + .arg(d->operand.toString(), createConditionalPhrase(d->left)); + else if (op == PhraseData::AddHours) + return QString("DATE_ADD(%2, INTERVAL %1 HOUR)") + .arg(d->operand.toString(), createConditionalPhrase(d->left)); + else if (op == PhraseData::AddMinutes) + return QString("DATE_ADD(%2, INTERVAL %1 MINUTE)") + .arg(d->operand.toString(), createConditionalPhrase(d->left)); + else if (op == PhraseData::AddSeconds) + return QString("DATE_ADD(%2, INTERVAL %1 SECOND)") + .arg(d->operand.toString(), createConditionalPhrase(d->left)); + + } + + return SqlGeneratorBase::createConditionalPhrase(d); +} + NUT_END_NAMESPACE diff --git a/src/generators/mysqlgenerator.h b/src/generators/mysqlgenerator.h index 847b653..1609c81 100644 --- a/src/generators/mysqlgenerator.h +++ b/src/generators/mysqlgenerator.h @@ -31,11 +31,12 @@ class MySqlGenerator : public SqlGeneratorBase public: explicit MySqlGenerator(Database *parent = 0); - QString fieldType(FieldModel *field); - QString escapeValue(const QVariant &v) const; - QVariant unescapeValue(const QMetaType::Type &type, const QVariant &dbValue); + QString fieldType(FieldModel *field) override; + QString escapeValue(const QVariant &v) const override; + QVariant unescapeValue(const QMetaType::Type &type, const QVariant &dbValue) override; // 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 createConditionalPhrase(const PhraseData *d) const override; private: bool readInsideParentese(QString &text, QString &out); }; diff --git a/src/generators/postgresqlgenerator.cpp b/src/generators/postgresqlgenerator.cpp index e1da1c2..83bc2c9 100644 --- a/src/generators/postgresqlgenerator.cpp +++ b/src/generators/postgresqlgenerator.cpp @@ -328,12 +328,38 @@ QString PostgreSqlGenerator::createConditionalPhrase(const PhraseData *d) const if (!d) return QString(); + PhraseData::Condition op = d->operatorCond; + //apply not (!) + if (d->isNot) { + if (op < 20) + op = static_cast((op + 10) % 20); + } + 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)); } + if (op == PhraseData::AddYears) + return QString("DATEADD(year, %1, %2)") + .arg(d->operand.toString(), createConditionalPhrase(d->left)); + else if (op == PhraseData::AddMonths) + return QString("DATEADD(month, %1, %2)") + .arg(d->operand.toString(), createConditionalPhrase(d->left)); + else if (op == PhraseData::AddDays) + return QString("DATEADD(day, %1, %2)") + .arg(d->operand.toString(), createConditionalPhrase(d->left)); + else if (op == PhraseData::AddHours) + return QString("DATEADD(hour, %1, %2)") + .arg(d->operand.toString(), createConditionalPhrase(d->left)); + else if (op == PhraseData::AddMinutes) + return QString("DATEADD(minute, %1, %2)") + .arg(d->operand.toString(), createConditionalPhrase(d->left)); + else if (op == PhraseData::AddSeconds) + return QString("DATEADD(second, %1, %2)") + .arg(d->operand.toString(), createConditionalPhrase(d->left)); + } return SqlGeneratorBase::createConditionalPhrase(d); diff --git a/src/generators/sqlgeneratorbase.cpp b/src/generators/sqlgeneratorbase.cpp index 53dd741..a5ff052 100644 --- a/src/generators/sqlgeneratorbase.cpp +++ b/src/generators/sqlgeneratorbase.cpp @@ -785,6 +785,19 @@ void SqlGeneratorBase::removeTableNames(QString &command) command = command.replace("[" + m->className() + "].", ""); } +QString SqlGeneratorBase::dateTimePartName(const PhraseData::Condition &op) const +{ + switch (op) { + case PhraseData::AddYears: return "YEAR"; + case PhraseData::AddMonths: return "MONTH"; + case PhraseData::AddDays: return "DAY"; + case PhraseData::AddHours: return "HOUR"; + case PhraseData::AddMinutes: return "MINUTE"; + case PhraseData::AddSeconds: return "SECOND"; + } + return QString(); +} + //QString SqlGeneratorBase::deleteCommand(QList &wheres, // QString tableName) //{ @@ -973,7 +986,7 @@ QString SqlGeneratorBase::createConditionalPhrase(const PhraseData *d) const break; case PhraseData::WithVariant: - if (op == PhraseData::AddYears) + /* if (op == PhraseData::AddYears) ret = QString("DATEADD(year, %1, %2)") .arg(d->operand.toString(), createConditionalPhrase(d->left)); else if (op == PhraseData::AddMonths) @@ -991,7 +1004,7 @@ QString SqlGeneratorBase::createConditionalPhrase(const PhraseData *d) const else if (op == PhraseData::AddSeconds) ret = QString("DATEADD(second, %1, %2)") .arg(d->operand.toString(), createConditionalPhrase(d->left)); - else if (op == PhraseData::Between) { + else */if (op == PhraseData::Between) { QVariantList list = d->operand.toList(); ret = QString("%1 BETWEEN %2 AND %3") .arg(createConditionalPhrase(d->left), escapeValue(list.at(0)), escapeValue(list.at(1))); diff --git a/src/generators/sqlgeneratorbase_p.h b/src/generators/sqlgeneratorbase_p.h index e6acf73..a25d3ee 100644 --- a/src/generators/sqlgeneratorbase_p.h +++ b/src/generators/sqlgeneratorbase_p.h @@ -162,6 +162,7 @@ protected: void replaceTableNames(QString &command); void removeTableNames(QString &command); + QString dateTimePartName(const PhraseData::Condition &op) const; }; NUT_END_NAMESPACE diff --git a/src/generators/sqlitegenerator.cpp b/src/generators/sqlitegenerator.cpp index e45168f..4ea91e7 100644 --- a/src/generators/sqlitegenerator.cpp +++ b/src/generators/sqlitegenerator.cpp @@ -205,4 +205,36 @@ QString SqliteGenerator::primaryKeyConstraint(const TableModel *table) const // return sql; } +QString SqliteGenerator::createConditionalPhrase(const PhraseData *d) const +{ + if (!d) + return QString(); + + PhraseData::Condition op = d->operatorCond; + //apply not (!) + if (d->isNot) { + if (op < 20) + op = static_cast((op + 10) % 20); + } + + if (d->type == PhraseData::WithVariant) { + QString part; + switch (op) { + case PhraseData::AddYears: + case PhraseData::AddMonths: + case PhraseData::AddDays: + case PhraseData::AddHours: + case PhraseData::AddMinutes: + case PhraseData::AddSeconds: + int i = d->operand.toInt(); + return QString("DATE(%1,'%2 %3')") + .arg(createConditionalPhrase(d->left), + (i < 0 ? "-" : "+") + QString::number(i), + dateTimePartName(op)); + } + } + + return SqlGeneratorBase::createConditionalPhrase(d); +} + NUT_END_NAMESPACE diff --git a/src/generators/sqlitegenerator.h b/src/generators/sqlitegenerator.h index 402ae41..5576bae 100644 --- a/src/generators/sqlitegenerator.h +++ b/src/generators/sqlitegenerator.h @@ -41,6 +41,7 @@ public: QString primaryKeyConstraint(const TableModel *table) const override; QStringList diff(TableModel *oldTable, TableModel *newTable) override; + QString createConditionalPhrase(const PhraseData *d) const override; }; NUT_END_NAMESPACE diff --git a/src/tablesetbase.cpp b/src/tablesetbase.cpp index 6546650..97bcefc 100644 --- a/src/tablesetbase.cpp +++ b/src/tablesetbase.cpp @@ -61,9 +61,11 @@ int TableSetBase::save(Database *db, bool cleanUp) || t->status() == Table::Modified || t->status() == Table::Deleted){ rowsAffected += t->save(db); -#ifndef NUT_SHARED_POINTER if(cleanUp) +#ifndef NUT_SHARED_POINTER t->deleteLater(); +#else + remove(t); #endif } } diff --git a/test/test.pro b/test/test.pro index 6fad6dd..1a7a38e 100644 --- a/test/test.pro +++ b/test/test.pro @@ -8,5 +8,6 @@ SUBDIRS += \ tst_quuid \ tst_generators \ tst_upgrades \ - tst_json + tst_json \ + tst_datetime diff --git a/test/tst_datetime/db.cpp b/test/tst_datetime/db.cpp new file mode 100644 index 0000000..e570806 --- /dev/null +++ b/test/tst_datetime/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_datetime/db.h b/test/tst_datetime/db.h new file mode 100644 index 0000000..401ac8c --- /dev/null +++ b/test/tst_datetime/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_datetime/sampletable.cpp b/test/tst_datetime/sampletable.cpp new file mode 100644 index 0000000..68a9c9d --- /dev/null +++ b/test/tst_datetime/sampletable.cpp @@ -0,0 +1,6 @@ +#include "sampletable.h" + +SampleTable::SampleTable(QObject *parent) : Nut::Table (parent) +{ + +} diff --git a/test/tst_datetime/sampletable.h b/test/tst_datetime/sampletable.h new file mode 100644 index 0000000..2587bac --- /dev/null +++ b/test/tst_datetime/sampletable.h @@ -0,0 +1,27 @@ +#ifndef SAMPLETABLE_H +#define SAMPLETABLE_H + +#include +#include +#include + +#include "table.h" + +class SampleTable : public Nut::Table +{ + Q_OBJECT + + NUT_PRIMARY_AUTO_INCREMENT(id) + NUT_DECLARE_FIELD(int, id, id, setId) + + NUT_DECLARE_FIELD(QDate, d, d, setD) + NUT_DECLARE_FIELD(QTime, t, t, setT) + NUT_DECLARE_FIELD(QDateTime, dt, dt, setDT) + +public: + Q_INVOKABLE SampleTable(QObject *parent = Q_NULLPTR); +}; + +Q_DECLARE_METATYPE(SampleTable*) + +#endif // SAMPLETABLE_H diff --git a/test/tst_datetime/tst_datetime.cpp b/test/tst_datetime/tst_datetime.cpp new file mode 100644 index 0000000..9b11845 --- /dev/null +++ b/test/tst_datetime/tst_datetime.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include + +#include "consts.h" + +#include "tst_datetime.h" +#include "query.h" +#include "tableset.h" +#include "tablemodel.h" +#include "databasemodel.h" + +#include "sampletable.h" + +DateTimeTest::DateTimeTest(QObject *parent) : QObject(parent) +{ + _baseDateTime = QDateTime::currentDateTime(); +} + +void DateTimeTest::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); + + QTEST_ASSERT(db.open()); + + db.sampleTables()->query()->remove(); +} + +void DateTimeTest::date() +{ + auto s = Nut::create(); + s->setD(_baseDateTime.addDays(10).date()); + db.sampleTables()->append(s); + db.saveChanges(); + + auto q = db.sampleTables()->query() + ->where(SampleTable::dField().addDays(9) < QDate::currentDate().addDays(10)); + + auto count = q->count(); + qDebug() << q->sqlCommand(); + QTEST_ASSERT(count); +} + +void DateTimeTest::cleanupTestCase() +{ + db.sampleTables()->query()->remove(); + db.close(); +} + +QTEST_MAIN(DateTimeTest) diff --git a/test/tst_datetime/tst_datetime.h b/test/tst_datetime/tst_datetime.h new file mode 100644 index 0000000..830a2a0 --- /dev/null +++ b/test/tst_datetime/tst_datetime.h @@ -0,0 +1,37 @@ +#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 DateTimeTest : public QObject +{ + Q_OBJECT + DB db; + + QDateTime _baseDateTime; + +public: + explicit DateTimeTest(QObject *parent = nullptr); + +signals: + +private slots: + void initTestCase(); + void date(); + void cleanupTestCase(); +}; + +#endif // MAINTEST_H diff --git a/test/tst_datetime/tst_datetime.pro b/test/tst_datetime/tst_datetime.pro new file mode 100644 index 0000000..7a9a327 --- /dev/null +++ b/test/tst_datetime/tst_datetime.pro @@ -0,0 +1,20 @@ +QT += testlib sql gui + +TARGET = tst_datetime +TEMPLATE = app + +CONFIG += warn_on c++11 + +include(../common/nut-lib.pri) + +SOURCES += \ + db.cpp \ + sampletable.cpp \ + tst_datetime.cpp + +HEADERS += \ + db.h \ + sampletable.h \ + tst_datetime.h + +include($$PWD/../../ci-test-init.pri) From 3cee4dae4f7a6a308f3ce3e450ae387937e81586 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Sun, 7 Jul 2019 12:20:49 +0430 Subject: [PATCH 20/25] sqlite passed tst_datetime --- src/generators/sqlgeneratorbase.cpp | 24 +++++-- src/generators/sqlitegenerator.cpp | 61 ++++++++++++++-- src/generators/sqlitegenerator.h | 3 + src/phrases/datephrase.h | 13 ++++ src/phrases/phrasedata.h | 8 +++ test/tst_datetime/tst_datetime.cpp | 104 +++++++++++++++++++++++++--- test/tst_datetime/tst_datetime.h | 4 +- 7 files changed, 195 insertions(+), 22 deletions(-) diff --git a/src/generators/sqlgeneratorbase.cpp b/src/generators/sqlgeneratorbase.cpp index a5ff052..74afe17 100644 --- a/src/generators/sqlgeneratorbase.cpp +++ b/src/generators/sqlgeneratorbase.cpp @@ -788,12 +788,24 @@ void SqlGeneratorBase::removeTableNames(QString &command) QString SqlGeneratorBase::dateTimePartName(const PhraseData::Condition &op) const { switch (op) { - case PhraseData::AddYears: return "YEAR"; - case PhraseData::AddMonths: return "MONTH"; - case PhraseData::AddDays: return "DAY"; - case PhraseData::AddHours: return "HOUR"; - case PhraseData::AddMinutes: return "MINUTE"; - case PhraseData::AddSeconds: return "SECOND"; + case PhraseData::AddYears: + case PhraseData::AddYearsDateTime: + return "YEAR"; + case PhraseData::AddMonths: + case PhraseData::AddMonthsDateTime: + return "MONTH"; + case PhraseData::AddDays: + case PhraseData::AddDaysDateTime: + return "DAY"; + case PhraseData::AddHours: + case PhraseData::AddHoursDateTime: + return "HOUR"; + case PhraseData::AddMinutes: + case PhraseData::AddMinutesDateTime: + return "MINUTE"; + case PhraseData::AddSeconds: + case PhraseData::AddSecondsDateTime: + return "SECOND"; } return QString(); } diff --git a/src/generators/sqlitegenerator.cpp b/src/generators/sqlitegenerator.cpp index 4ea91e7..4ed07a7 100644 --- a/src/generators/sqlitegenerator.cpp +++ b/src/generators/sqlitegenerator.cpp @@ -219,22 +219,73 @@ QString SqliteGenerator::createConditionalPhrase(const PhraseData *d) const if (d->type == PhraseData::WithVariant) { QString part; + switch (op) { case PhraseData::AddYears: case PhraseData::AddMonths: - case PhraseData::AddDays: - case PhraseData::AddHours: - case PhraseData::AddMinutes: - case PhraseData::AddSeconds: + case PhraseData::AddDays: { int i = d->operand.toInt(); return QString("DATE(%1,'%2 %3')") .arg(createConditionalPhrase(d->left), - (i < 0 ? "-" : "+") + QString::number(i), + (i < 0 ? "" : "+") + QString::number(i), dateTimePartName(op)); + break; + } + case PhraseData::AddHours: + case PhraseData::AddMinutes: + case PhraseData::AddSeconds: { + int i = d->operand.toInt(); + return QString("TIME(%1,'%2 %3')") + .arg(createConditionalPhrase(d->left), + (i < 0 ? "" : "+") + QString::number(i), + dateTimePartName(op)); + break; + } + case PhraseData::AddYearsDateTime: + case PhraseData::AddMonthsDateTime: + case PhraseData::AddDaysDateTime: + case PhraseData::AddHoursDateTime: + case PhraseData::AddMinutesDateTime: + case PhraseData::AddSecondsDateTime: { + int i = d->operand.toInt(); + return QString("DATETIME(%1,'%2 %3')") + .arg(createConditionalPhrase(d->left), + (i < 0 ? "" : "+") + QString::number(i), + dateTimePartName(op)); + break; + } } } return SqlGeneratorBase::createConditionalPhrase(d); } +QString SqliteGenerator::escapeValue(const QVariant &v) const +{ + 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") + "'"; + + return SqlGeneratorBase::escapeValue(v); +} + +QVariant SqliteGenerator::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(); + + return SqlGeneratorBase::unescapeValue(type, dbValue); +} + NUT_END_NAMESPACE diff --git a/src/generators/sqlitegenerator.h b/src/generators/sqlitegenerator.h index 5576bae..b0f2f0e 100644 --- a/src/generators/sqlitegenerator.h +++ b/src/generators/sqlitegenerator.h @@ -42,6 +42,9 @@ public: QStringList diff(TableModel *oldTable, TableModel *newTable) override; QString createConditionalPhrase(const PhraseData *d) const override; + + QString escapeValue(const QVariant &v) const override; + QVariant unescapeValue(const QMetaType::Type &type, const QVariant &dbValue) override; }; NUT_END_NAMESPACE diff --git a/src/phrases/datephrase.h b/src/phrases/datephrase.h index 5db2186..ab79d9c 100644 --- a/src/phrases/datephrase.h +++ b/src/phrases/datephrase.h @@ -102,32 +102,45 @@ public: ConditionalPhrase addYears(int val) { if (!is_valid_template()) return ConditionalPhrase(); + if (std::is_same::value) + return ConditionalPhrase(this, PhraseData::AddYearsDateTime, val); + return ConditionalPhrase(this, PhraseData::AddYears, val); } ConditionalPhrase addMonths(int val) { if (!is_valid_template()) return ConditionalPhrase(); + if (std::is_same::value) + return ConditionalPhrase(this, PhraseData::AddMonthsDateTime, val); return ConditionalPhrase(this, PhraseData::AddMonths, val); } ConditionalPhrase addDays(int val) { if (!is_valid_template()) return ConditionalPhrase(); + if (std::is_same::value) + return ConditionalPhrase(this, PhraseData::AddDaysDateTime, val); return ConditionalPhrase(this, PhraseData::AddDays, val); } ConditionalPhrase addHours(int val) { if (!is_valid_template()) return ConditionalPhrase(); + if (std::is_same::value) + return ConditionalPhrase(this, PhraseData::AddHoursDateTime, val); return ConditionalPhrase(this, PhraseData::AddHours, val); } ConditionalPhrase addMinutes(int val) { if (!is_valid_template()) return ConditionalPhrase(); + if (std::is_same::value) + return ConditionalPhrase(this, PhraseData::AddMinutesDateTime, val); return ConditionalPhrase(this, PhraseData::AddMinutes, val); } ConditionalPhrase addSeconds(int val) { if (!is_valid_template()) return ConditionalPhrase(); + if (std::is_same::value) + return ConditionalPhrase(this, PhraseData::AddSecondsDateTime, val); return ConditionalPhrase(this, PhraseData::AddSeconds, val); } diff --git a/src/phrases/phrasedata.h b/src/phrases/phrasedata.h index d1ae6f0..a773cb6 100644 --- a/src/phrases/phrasedata.h +++ b/src/phrases/phrasedata.h @@ -64,6 +64,14 @@ public: AddMinutes, AddSeconds, + // sqlite need to know works with qdate, qtime or qdatetime + AddYearsDateTime, + AddMonthsDateTime, + AddDaysDateTime, + AddHoursDateTime, + AddMinutesDateTime, + AddSecondsDateTime, + DatePartYear, DatePartMonth, DatePartDay, diff --git a/test/tst_datetime/tst_datetime.cpp b/test/tst_datetime/tst_datetime.cpp index 9b11845..eb5119f 100644 --- a/test/tst_datetime/tst_datetime.cpp +++ b/test/tst_datetime/tst_datetime.cpp @@ -35,19 +35,103 @@ void DateTimeTest::initTestCase() db.sampleTables()->query()->remove(); } -void DateTimeTest::date() +#define TEST_DATE(date, command, n) \ +do { \ + auto s = Nut::create(); \ + s->setD(date); \ + db.sampleTables()->append(s); \ + db.saveChanges(); \ + auto count = db.sampleTables()->query() \ + ->where(SampleTable::dField().command(n) == date.command(n)) \ + ->count(); \ + QTEST_ASSERT(count); \ +} while (false) + +#define TEST_TIME(time, command, n, num) \ +do { \ + auto s = Nut::create(); \ + s->setT(time); \ + db.sampleTables()->append(s); \ + db.saveChanges(); \ + auto count = db.sampleTables()->query() \ + ->where(SampleTable::tField().command(n) == time.addSecs(num)) \ + ->count(); \ + QTEST_ASSERT(count); \ +} while (false) + +#define TEST_DATE2(datetime, command, n) \ +do { \ + auto s = Nut::create(); \ + s->setDT(datetime); \ + db.sampleTables()->append(s); \ + db.saveChanges(); \ + auto count = db.sampleTables()->query() \ + ->where(SampleTable::dtField().command(n) == datetime.command(n)); \ + ->count(); \ + QTEST_ASSERT(count); \ +} while (false) + +#define TEST_TIME2(datetime, command, n, num) \ +do { \ + auto s = Nut::create(); \ + s->setDT(datetime); \ + db.sampleTables()->append(s); \ + db.saveChanges(); \ + auto count = db.sampleTables()->query() \ + ->where(SampleTable::dtField().command(n) == datetime.addSecs(num)); \ + ->count(); \ + QTEST_ASSERT(count); \ +} while (false) + +#define MINUTE(m) m * 60 +#define HOUR(h) MINUTE(h) * 60 + +void DateTimeTest::dateAdd() { - auto s = Nut::create(); - s->setD(_baseDateTime.addDays(10).date()); - db.sampleTables()->append(s); - db.saveChanges(); + QDate d = QDate::currentDate(); - auto q = db.sampleTables()->query() - ->where(SampleTable::dField().addDays(9) < QDate::currentDate().addDays(10)); + TEST_DATE(d, addYears, 10); + TEST_DATE(d, addMonths, 10); + TEST_DATE(d, addDays, 10); - auto count = q->count(); - qDebug() << q->sqlCommand(); - QTEST_ASSERT(count); + TEST_DATE(d, addYears, -10); + TEST_DATE(d, addMonths, -10); + TEST_DATE(d, addDays, -10); +} + +void DateTimeTest::timeAdd() +{ + QTime t = QTime::currentTime(); + + TEST_TIME(t, addHours, 10, HOUR(10)); + TEST_TIME(t, addMinutes, 10, MINUTE(10)); + TEST_TIME(t, addSeconds, 10, 10); + + TEST_TIME(t, addHours, -10, HOUR(-10)); + TEST_TIME(t, addMinutes, -10, MINUTE(-10)); + TEST_TIME(t, addSeconds, -10, -10); +} + +void DateTimeTest::dateTimeAdd() +{ + QDateTime dt = QDateTime::currentDateTime(); + + TEST_DATE2(dt, addYears, 10); + TEST_DATE2(dt, addMonths, 10); + TEST_DATE2(dt, addDays, 10); + + TEST_DATE2(dt, addYears, -10); + TEST_DATE2(dt, addMonths, -10); + TEST_DATE2(dt, addDays, -10); + + + TEST_TIME2(dt, addHours, 10, HOUR(10)); + TEST_TIME2(dt, addMinutes, 10, MINUTE(10)); + TEST_TIME2(dt, addSeconds, 10, 10); + + TEST_TIME2(dt, addHours, -10, HOUR(-10)); + TEST_TIME2(dt, addMinutes, -10, MINUTE(-10)); + TEST_TIME2(dt, addSeconds, -10, -10); } void DateTimeTest::cleanupTestCase() diff --git a/test/tst_datetime/tst_datetime.h b/test/tst_datetime/tst_datetime.h index 830a2a0..960a47f 100644 --- a/test/tst_datetime/tst_datetime.h +++ b/test/tst_datetime/tst_datetime.h @@ -30,7 +30,9 @@ signals: private slots: void initTestCase(); - void date(); + void dateAdd(); + void timeAdd(); + void dateTimeAdd(); void cleanupTestCase(); }; From e539a1da759ac7f51552d133998653ef3a166387 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Sun, 7 Jul 2019 19:18:20 +0430 Subject: [PATCH 21/25] date/time parts added for sqlite --- src/generators/sqlitegenerator.cpp | 33 +++++++++++++- test/tst_datetime/tst_datetime.cpp | 72 +++++++++++++++++++++++++++++- test/tst_datetime/tst_datetime.h | 3 ++ 3 files changed, 104 insertions(+), 4 deletions(-) diff --git a/src/generators/sqlitegenerator.cpp b/src/generators/sqlitegenerator.cpp index 4ed07a7..0555c47 100644 --- a/src/generators/sqlitegenerator.cpp +++ b/src/generators/sqlitegenerator.cpp @@ -218,8 +218,6 @@ QString SqliteGenerator::createConditionalPhrase(const PhraseData *d) const } if (d->type == PhraseData::WithVariant) { - QString part; - switch (op) { case PhraseData::AddYears: case PhraseData::AddMonths: @@ -256,6 +254,37 @@ QString SqliteGenerator::createConditionalPhrase(const PhraseData *d) const } } } + if (d->type == PhraseData::WithoutOperand) { + switch (op) { + case PhraseData::DatePartYear: + return QString("CAST(strftime('%Y', %1) AS INT)") + .arg(createConditionalPhrase(d->left)); + + case PhraseData::DatePartMonth: + return QString("CAST(strftime('%m', %1) AS INT)") + .arg(createConditionalPhrase(d->left)); + + case PhraseData::DatePartDay: + return QString("CAST(strftime('%d', %1) AS INT)") + .arg(createConditionalPhrase(d->left)); + + case PhraseData::DatePartHour: + return QString("CAST(strftime('%H', %1) AS INT)") + .arg(createConditionalPhrase(d->left)); + + case PhraseData::DatePartMinute: + return QString("CAST(strftime('%M', %1) AS INT)") + .arg(createConditionalPhrase(d->left)); + + case PhraseData::DatePartSecond: + return QString("CAST(strftime('%S', %1) AS INT)") + .arg(createConditionalPhrase(d->left)); + + // case PhraseData::DatePartMilisecond: + // return QString("CAST(strftime('%Y', %1) AS INT)") + // .arg(createConditionalPhrase(d->left)); + } + } return SqlGeneratorBase::createConditionalPhrase(d); } diff --git a/test/tst_datetime/tst_datetime.cpp b/test/tst_datetime/tst_datetime.cpp index eb5119f..5f1fd53 100644 --- a/test/tst_datetime/tst_datetime.cpp +++ b/test/tst_datetime/tst_datetime.cpp @@ -66,7 +66,7 @@ do { \ db.sampleTables()->append(s); \ db.saveChanges(); \ auto count = db.sampleTables()->query() \ - ->where(SampleTable::dtField().command(n) == datetime.command(n)); \ + ->where(SampleTable::dtField().command(n) == datetime.command(n)) \ ->count(); \ QTEST_ASSERT(count); \ } while (false) @@ -78,7 +78,7 @@ do { \ db.sampleTables()->append(s); \ db.saveChanges(); \ auto count = db.sampleTables()->query() \ - ->where(SampleTable::dtField().command(n) == datetime.addSecs(num)); \ + ->where(SampleTable::dtField().command(n) == datetime.addSecs(num)) \ ->count(); \ QTEST_ASSERT(count); \ } while (false) @@ -134,6 +134,74 @@ void DateTimeTest::dateTimeAdd() TEST_TIME2(dt, addSeconds, -10, -10); } +void DateTimeTest::datePart() +{ + db.sampleTables()->query()->remove(); + + QDate d = QDate::currentDate(); + auto s = Nut::create(); + s->setD(d); + db.sampleTables()->append(s); + db.saveChanges(); + + int count; + + count = db.sampleTables()->query()->where(SampleTable::dField().year() == d.year())->count(); + QTEST_ASSERT(count); + count = db.sampleTables()->query()->where(SampleTable::dField().month() == d.month())->count(); + QTEST_ASSERT(count); + count = db.sampleTables()->query()->where(SampleTable::dField().day() == d.day())->count(); + QTEST_ASSERT(count); + +} + +void DateTimeTest::timePart() +{ + db.sampleTables()->query()->remove(); + + QTime t = QTime::currentTime(); + auto s = Nut::create(); + s->setT(t); + db.sampleTables()->append(s); + db.saveChanges(); + + int count; + + count = db.sampleTables()->query()->where(SampleTable::tField().hour() == t.hour())->count(); + QTEST_ASSERT(count); + count = db.sampleTables()->query()->where(SampleTable::tField().minute() == t.minute())->count(); + QTEST_ASSERT(count); + count = db.sampleTables()->query()->where(SampleTable::tField().second() == t.second())->count(); + QTEST_ASSERT(count); +} + +void DateTimeTest::dateTimePart() +{ + db.sampleTables()->query()->remove(); + + QDateTime dt = QDateTime::currentDateTime(); + auto s = Nut::create(); + s->setDT(dt); + db.sampleTables()->append(s); + db.saveChanges(); + + int count; + + count = db.sampleTables()->query()->where(SampleTable::dtField().year() == dt.date().year())->count(); + QTEST_ASSERT(count); + count = db.sampleTables()->query()->where(SampleTable::dtField().month() == dt.date().month())->count(); + QTEST_ASSERT(count); + count = db.sampleTables()->query()->where(SampleTable::dtField().day() == dt.date().day())->count(); + QTEST_ASSERT(count); + + count = db.sampleTables()->query()->where(SampleTable::dtField().hour() == dt.time().hour())->count(); + QTEST_ASSERT(count); + count = db.sampleTables()->query()->where(SampleTable::dtField().minute() == dt.time().minute())->count(); + QTEST_ASSERT(count); + count = db.sampleTables()->query()->where(SampleTable::dtField().second() == dt.time().second())->count(); + QTEST_ASSERT(count); +} + void DateTimeTest::cleanupTestCase() { db.sampleTables()->query()->remove(); diff --git a/test/tst_datetime/tst_datetime.h b/test/tst_datetime/tst_datetime.h index 960a47f..9f309c2 100644 --- a/test/tst_datetime/tst_datetime.h +++ b/test/tst_datetime/tst_datetime.h @@ -33,6 +33,9 @@ private slots: void dateAdd(); void timeAdd(); void dateTimeAdd(); + void datePart(); + void timePart(); + void dateTimePart(); void cleanupTestCase(); }; From 1c196fe3ef82c7fc0aa072a9a52082c2e55ef744 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Sun, 7 Jul 2019 19:42:37 +0430 Subject: [PATCH 22/25] polish FieldPhrase --- src/phrases/datephrase.cpp | 125 +++++++++++++++++++++ src/phrases/datephrase.h | 218 +++++++++++++------------------------ 2 files changed, 203 insertions(+), 140 deletions(-) diff --git a/src/phrases/datephrase.cpp b/src/phrases/datephrase.cpp index a9c8d9b..0355377 100644 --- a/src/phrases/datephrase.cpp +++ b/src/phrases/datephrase.cpp @@ -20,3 +20,128 @@ #include "datephrase.h" +NUT_BEGIN_NAMESPACE + +FieldPhrase::FieldPhrase(const char *className, const char *s) : + AbstractFieldPhrase(className, s) +{} + +ConditionalPhrase FieldPhrase::addYears(int years) { + return ConditionalPhrase(this, PhraseData::AddYears, years); +} + +ConditionalPhrase FieldPhrase::addMonths(int months) { + return ConditionalPhrase(this, PhraseData::AddMonths, months); +} + +ConditionalPhrase FieldPhrase::addDays(int days) { + return ConditionalPhrase(this, PhraseData::AddDays, days); +} + +ConditionalPhrase FieldPhrase::year() { + return ConditionalPhrase(this, PhraseData::DatePartYear); +} + +ConditionalPhrase FieldPhrase::month() { + return ConditionalPhrase(this, PhraseData::DatePartMonth); +} + +ConditionalPhrase FieldPhrase::day() { + return ConditionalPhrase(this, PhraseData::DatePartDay); +} + +FieldPhrase::FieldPhrase(const char *className, const char *s) : + AbstractFieldPhrase(className, s) +{} + + +ConditionalPhrase FieldPhrase::addHours(int hours) { + return ConditionalPhrase(this, PhraseData::AddHours, hours); +} + +ConditionalPhrase FieldPhrase::addMinutes(int minutes) { + return ConditionalPhrase(this, PhraseData::AddMinutes, minutes); +} + +ConditionalPhrase FieldPhrase::addSeconds(int seconds) { + return ConditionalPhrase(this, PhraseData::AddSeconds, seconds); +} + +ConditionalPhrase FieldPhrase::hour() { + return ConditionalPhrase(this, PhraseData::DatePartHour); +} + +ConditionalPhrase FieldPhrase::minute() { + return ConditionalPhrase(this, PhraseData::DatePartMinute); +} + +ConditionalPhrase FieldPhrase::second() { + return ConditionalPhrase(this, PhraseData::DatePartSecond); +} + +ConditionalPhrase FieldPhrase::msec() { + return ConditionalPhrase(this, PhraseData::DatePartMilisecond); +} + +FieldPhrase::FieldPhrase(const char *className, const char *s) : + AbstractFieldPhrase(className, s) +{} + +ConditionalPhrase FieldPhrase::addYears(int years) { + return ConditionalPhrase(this, PhraseData::AddYearsDateTime, years); +} + +ConditionalPhrase FieldPhrase::addMonths(int months) { + return ConditionalPhrase(this, PhraseData::AddMonthsDateTime, months); +} + +ConditionalPhrase FieldPhrase::addDays(int days) { + return ConditionalPhrase(this, PhraseData::AddDaysDateTime, days); +} + +ConditionalPhrase FieldPhrase::addHours(int hours) { + return ConditionalPhrase(this, PhraseData::AddHoursDateTime, hours); +} + +ConditionalPhrase FieldPhrase::addMinutes(int minutes) { + return ConditionalPhrase(this, PhraseData::AddMinutesDateTime, minutes); +} + +ConditionalPhrase FieldPhrase::addSeconds(int seconds) { + return ConditionalPhrase(this, PhraseData::AddSecondsDateTime, seconds); +} + +ConditionalPhrase FieldPhrase::year() { + return ConditionalPhrase(this, PhraseData::DatePartYear); +} + +ConditionalPhrase FieldPhrase::month() { + return ConditionalPhrase(this, PhraseData::DatePartMonth); +} + +ConditionalPhrase FieldPhrase::day() { + return ConditionalPhrase(this, PhraseData::DatePartDay); +} + +ConditionalPhrase FieldPhrase::hour() { + return ConditionalPhrase(this, PhraseData::DatePartHour); +} + +ConditionalPhrase FieldPhrase::minute() { + return ConditionalPhrase(this, PhraseData::DatePartMinute); +} + +ConditionalPhrase FieldPhrase::second() { + return ConditionalPhrase(this, PhraseData::DatePartSecond); +} + +ConditionalPhrase FieldPhrase::msec() { + return ConditionalPhrase(this, PhraseData::DatePartMilisecond); +} + + +COMMON_OPERATORS_IMPL(QDate) +COMMON_OPERATORS_IMPL(QTime) +COMMON_OPERATORS_IMPL(QDateTime) + +NUT_END_NAMESPACE diff --git a/src/phrases/datephrase.h b/src/phrases/datephrase.h index ab79d9c..8d3dbd3 100644 --- a/src/phrases/datephrase.h +++ b/src/phrases/datephrase.h @@ -27,158 +27,96 @@ NUT_BEGIN_NAMESPACE +#define COMMON_OPERATORS_DECL(T) \ + AssignmentPhrase operator =(const T &other); \ + ConditionalPhrase operator <(const QVariant &other); \ + ConditionalPhrase operator <=(const QVariant &other); \ + ConditionalPhrase operator >(const QVariant &other); \ + ConditionalPhrase operator >=(const QVariant &other); \ + ConditionalPhrase between(const QVariant &min, const QVariant &max); -template -struct __is_date_helper - : public std::false_type { }; +#define COMMON_OPERATORS_IMPL(T) \ + AssignmentPhrase FieldPhrase::operator =(const T &other) { \ + return AssignmentPhrase(this, other); \ + } \ + ConditionalPhrase FieldPhrase::operator <(const QVariant &other) { \ + return ConditionalPhrase(this, PhraseData::Less, other); \ + } \ + ConditionalPhrase FieldPhrase::operator <=(const QVariant &other) { \ + return ConditionalPhrase(this, PhraseData::LessEqual, other); \ + } \ + ConditionalPhrase FieldPhrase::operator >(const QVariant &other) { \ + return ConditionalPhrase(this, PhraseData::Greater, other); \ + } \ + ConditionalPhrase FieldPhrase::operator >=(const QVariant &other) { \ + return ConditionalPhrase(this, PhraseData::GreaterEqual, other); \ + } \ + ConditionalPhrase FieldPhrase::between(const QVariant &min, const QVariant &max) \ + { \ + return ConditionalPhrase(this, PhraseData::Between, \ + QVariantList() << min << max); \ + } 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 +class NUT_EXPORT FieldPhrase : public AbstractFieldPhrase { public: - FieldPhrase(const char *className, const char *s) : - AbstractFieldPhrase(className, s) - {} + FieldPhrase(const char *className, const char *s); - AssignmentPhrase operator =(const T &other) { - return AssignmentPhrase(this, other); - } + COMMON_OPERATORS_DECL(QDate) - 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 addYears(int years); + ConditionalPhrase addMonths(int months); + ConditionalPhrase addDays(int days); - ConditionalPhrase between(const QVariant &min, const QVariant &max) - { - return ConditionalPhrase(this, PhraseData::Between, - QVariantList() << min << max); - } + ConditionalPhrase year(); + ConditionalPhrase month(); + ConditionalPhrase day(); +}; -// template::value, int>::type = 0> - ConditionalPhrase addYears(int val) { - if (!is_valid_template()) - return ConditionalPhrase(); - if (std::is_same::value) - return ConditionalPhrase(this, PhraseData::AddYearsDateTime, val); +template<> +class NUT_EXPORT FieldPhrase : public AbstractFieldPhrase +{ +public: + FieldPhrase(const char *className, const char *s); - return ConditionalPhrase(this, PhraseData::AddYears, val); - } - ConditionalPhrase addMonths(int val) { - if (!is_valid_template()) - return ConditionalPhrase(); - if (std::is_same::value) - return ConditionalPhrase(this, PhraseData::AddMonthsDateTime, val); - return ConditionalPhrase(this, PhraseData::AddMonths, val); - } - ConditionalPhrase addDays(int val) { - if (!is_valid_template()) - return ConditionalPhrase(); - if (std::is_same::value) - return ConditionalPhrase(this, PhraseData::AddDaysDateTime, val); - return ConditionalPhrase(this, PhraseData::AddDays, val); - } + COMMON_OPERATORS_DECL(QTime) - ConditionalPhrase addHours(int val) { - if (!is_valid_template()) - return ConditionalPhrase(); - if (std::is_same::value) - return ConditionalPhrase(this, PhraseData::AddHoursDateTime, val); - return ConditionalPhrase(this, PhraseData::AddHours, val); - } - ConditionalPhrase addMinutes(int val) { - if (!is_valid_template()) - return ConditionalPhrase(); - if (std::is_same::value) - return ConditionalPhrase(this, PhraseData::AddMinutesDateTime, val); - return ConditionalPhrase(this, PhraseData::AddMinutes, val); - } - ConditionalPhrase addSeconds(int val) { - if (!is_valid_template()) - return ConditionalPhrase(); - if (std::is_same::value) - return ConditionalPhrase(this, PhraseData::AddSecondsDateTime, val); - return ConditionalPhrase(this, PhraseData::AddSeconds, val); - } + ConditionalPhrase addHours(int hours); + ConditionalPhrase addMinutes(int minutes); + ConditionalPhrase addSeconds(int seconds); - 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); - } + ConditionalPhrase hour(); + ConditionalPhrase minute(); + ConditionalPhrase second(); + ConditionalPhrase msec(); +}; + +template<> +class NUT_EXPORT FieldPhrase : public AbstractFieldPhrase +{ +public: + FieldPhrase(const char *className, const char *s); + + COMMON_OPERATORS_DECL(QDateTime) + + ConditionalPhrase addYears(int year); + ConditionalPhrase addMonths(int months); + ConditionalPhrase addDays(int days); + + + ConditionalPhrase addHours(int hours); + ConditionalPhrase addMinutes(int minutes); + ConditionalPhrase addSeconds(int seconds); + + ConditionalPhrase year(); + ConditionalPhrase month(); + ConditionalPhrase day(); + + ConditionalPhrase hour(); + ConditionalPhrase minute(); + ConditionalPhrase second(); + ConditionalPhrase msec(); }; NUT_END_NAMESPACE From 1975ff00271ce4e75b49745fb6b92f8db41ce34a Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Mon, 8 Jul 2019 19:23:02 +0430 Subject: [PATCH 23/25] wip: shared pointer childs for tableset --- src/query.h | 14 ++++++++++---- src/tablesetbase.cpp | 15 +++++++++++++++ src/tablesetbase_p.h | 5 ++++- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/query.h b/src/query.h index 9b76e35..ca1a7f5 100644 --- a/src/query.h +++ b/src/query.h @@ -289,11 +289,13 @@ Q_OUTOFLINE_TEMPLATE RowList Query::toList(int count) //create table row Table *table; + Row shp; if (data.table->className() == d->className) { table = new T(); #ifdef NUT_SHARED_POINTER - auto shp = QSharedPointer(qobject_cast(table)); + shp = QSharedPointer(qobject_cast(table)); returnList.append(shp); + d->tableSet->add(shp); #else returnList.append(dynamic_cast(table)); #endif @@ -331,9 +333,13 @@ Q_OUTOFLINE_TEMPLATE RowList Query::toList(int count) if (!ok) qWarning("Unable to set property %s::%s", table->metaObject()->className(), data.masterFields[i].toLocal8Bit().data()); - table->setParentTableSet( - levels[master].lastRow->childTableSet( - data.table->className())); + + auto tableset = levels[master].lastRow->childTableSet( + data.table->className()); + table->setParentTableSet(tableset); +#ifdef NUT_SHARED_POINTER + tableset->add(qSharedPointerCast
(shp)); +#endif } table->setStatus(Table::FeatchedFromDB); diff --git a/src/tablesetbase.cpp b/src/tablesetbase.cpp index 97bcefc..8ee15a0 100644 --- a/src/tablesetbase.cpp +++ b/src/tablesetbase.cpp @@ -42,6 +42,9 @@ TableSetBase::~TableSetBase() { foreach (Table *t, data->tables) t->setParentTableSet(nullptr); + + foreach (Row
t, data->childs) + t->setParentTableSet(nullptr); } int TableSetBase::save(Database *db, bool cleanUp) @@ -101,6 +104,18 @@ void TableSetBase::remove(Table *t) data->childRows.removeOne(get(t)); } +void TableSetBase::add(Row
t) +{ + data.detach(); + data->childs.append(t); +} + +void TableSetBase::remove(Row
t) +{ + data.detach(); + data->childs.removeOne(t); +} + QString TableSetBase::childClassName() const { return data->childClassName; diff --git a/src/tablesetbase_p.h b/src/tablesetbase_p.h index cb8c6d2..5911f42 100644 --- a/src/tablesetbase_p.h +++ b/src/tablesetbase_p.h @@ -57,10 +57,13 @@ protected: // QString _childClassName; QExplicitlySharedDataPointer data; -private: +public://TODO: change this to private void add(Table* t); void remove(Table *t); + void add(Row
t); + void remove(Row
t); + friend class Table; friend class QueryBase; }; From 765ac9a5e3b2767ea1f711ba6015d030fe985216 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Mon, 8 Jul 2019 20:08:20 +0430 Subject: [PATCH 24/25] new serializer commit --- 3rdparty/serializer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/serializer b/3rdparty/serializer index 5b30b13..e0bdec4 160000 --- a/3rdparty/serializer +++ b/3rdparty/serializer @@ -1 +1 @@ -Subproject commit 5b30b13af9b9537d4226c922d04106dc4ba0595b +Subproject commit e0bdec4a7c93b4d4307fd89200000ff817a86c99 From 8e502d2d6a57844da58047ed2d1b972fd5d496e9 Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Tue, 9 Jul 2019 10:45:59 +0430 Subject: [PATCH 25/25] Update start.md --- doc/start.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/start.md b/doc/start.md index 8ce946d..74505c9 100644 --- a/doc/start.md +++ b/doc/start.md @@ -2,8 +2,8 @@ ```cpp auto posts = db.posts()->query() - where(Post::idField() == postId) - toList(); + ->where(Post::idField() == postId) + ->toList(); // now posts is a QList contain all posts in // database that has id equal to postId variable auto post = q->first();