diff --git a/README.md b/README.md index d44fb0d..773e766 100644 --- a/README.md +++ b/README.md @@ -61,4 +61,8 @@ if(post) { } ``` +### Donate +Butcoin address: 1Dn1WHKkaxanXe4cTGDk4cFRRABxLUpEVj +![Wallet addresst](btc-qr.png) + For more information read [Wiki](wiki). diff --git a/btc-qr.png b/btc-qr.png new file mode 100644 index 0000000..a183b66 Binary files /dev/null and b/btc-qr.png differ diff --git a/src/database.cpp b/src/database.cpp index e7b52b7..cc69a2a 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -214,7 +214,10 @@ bool DatabasePrivate::getCurrectScheema() tables.insert(name, value); int typeId = QMetaType::type(name.toLocal8Bit() + "*"); - qDebug() << type << name << value << typeId; + + if (!typeId) + qFatal("The class %s is not registered with qt meta object", qPrintable(name)); + TableModel *sch = new TableModel(typeId, value); currentModel.append(sch); } diff --git a/src/databasemodel.cpp b/src/databasemodel.cpp index 89d5ede..2a579f8 100644 --- a/src/databasemodel.cpp +++ b/src/databasemodel.cpp @@ -229,13 +229,13 @@ DatabaseModel *DatabaseModel::modelByName(const QString &name) void DatabaseModel::deleteAllModels() { -// QMapIterator i(_models); -// while (i.hasNext()) { -// i.next(); -//// cout << i.key() << ": " << i.value() << endl; -// delete i.value(); -// } - qDeleteAll(_models.values()); + QMapIterator i(_models); + while (i.hasNext()) { + i.next(); +// cout << i.key() << ": " << i.value() << endl; +// qDeleteAll(i.value()); + } +// qDeleteAll(_models.values()); _models.clear(); } diff --git a/src/defines.h b/src/defines.h index bf1bb7b..348ff6e 100644 --- a/src/defines.h +++ b/src/defines.h @@ -138,7 +138,7 @@ inline bool nutClassInfoInt(const QMetaClassInfo &classInfo, NUT_INFO(__nut_FIELD, name, 0) \ type m_##name; \ public: \ - static NUT_WRAP_NAMESPACE(FieldPhrase) name ## Field(){ \ + static NUT_WRAP_NAMESPACE(FieldPhrase)& name ## Field(){ \ static NUT_WRAP_NAMESPACE(FieldPhrase) f = \ NUT_WRAP_NAMESPACE(FieldPhrase) \ (staticMetaObject.className(), #name); \ diff --git a/src/generators/sqlgeneratorbase.cpp b/src/generators/sqlgeneratorbase.cpp index 3a8ae1a..4a404af 100644 --- a/src/generators/sqlgeneratorbase.cpp +++ b/src/generators/sqlgeneratorbase.cpp @@ -534,7 +534,7 @@ QString SqlGeneratorBase::selectCommand(const QString &tableName, Q_UNUSED(take); QString selectText; - if (!fields.isValid) { + if (fields.data.count() == 0) { QSet tables; tables.insert(_database->model().tableByName(tableName)); foreach (RelationModel *rel, joins) @@ -648,6 +648,27 @@ QString SqlGeneratorBase::updateCommand(const QString &tableName, return sql; } +QString SqlGeneratorBase::insertCommand(const QString &tableName, const AssignmentPhraseList &assigments) +{ + + QString fieldNames; + QString values; + foreach (PhraseData *d, assigments.data) { + if (fieldNames != "") + fieldNames.append(", "); + + if (values != "") + values.append(", "); + + fieldNames.append(d->left->fieldName); + values.append(escapeValue(d->operand)); + } + return QString("INSERT INTO %1 (%2) VALUES (%3);") + .arg(tableName) + .arg(fieldNames) + .arg(values); +} + //QString SqlGeneratorBase::selectCommand(SqlGeneratorBase::AgregateType t, // QString agregateArg, // QString tableName, diff --git a/src/generators/sqlgeneratorbase_p.h b/src/generators/sqlgeneratorbase_p.h index 4781ae2..35a545d 100644 --- a/src/generators/sqlgeneratorbase_p.h +++ b/src/generators/sqlgeneratorbase_p.h @@ -109,6 +109,9 @@ public: virtual QString updateCommand(const QString &tableName, const AssignmentPhraseList &assigments, const ConditionalPhrase &where); + + virtual QString insertCommand(const QString &tableName, + const AssignmentPhraseList &assigments); // virtual QString selectCommand(AgregateType t, // QString agregateArg, QString tableName, // QList &wheres, diff --git a/src/query.h b/src/query.h index 8ab8c54..77e2c36 100644 --- a/src/query.h +++ b/src/query.h @@ -83,12 +83,14 @@ public: QVariant min(const FieldPhrase &f); QVariant average(const FieldPhrase &f); + QVariant insert(AssignmentPhraseList p); + //data mailpulation int update(const AssignmentPhraseList &ph); // int insert(const AssignmentPhraseList &ph); int remove(); - QSqlQueryModel *toModal(); + QSqlQueryModel *toModel(); //debug purpose QString sqlCommand() const; @@ -121,7 +123,6 @@ template Q_OUTOFLINE_TEMPLATE Query::~Query() { Q_D(Query); - qDebug() << "~Query";// << d->sql; delete d; } @@ -223,7 +224,8 @@ Q_OUTOFLINE_TEMPLATE QList Query::toList(int count) // if (p == lastP) // qFatal("NULL Loop detected"); - n = (++n) % levels.count(); + ++n; + n = n % levels.count(); if (checked[n]) continue; LevelData &data = levels[n]; @@ -231,13 +233,16 @@ Q_OUTOFLINE_TEMPLATE QList Query::toList(int count) // check if key value is changed if (data.lastKeyValue == q.value(data.keyFiledname)) { --p; + qDebug() << "key os not changed for" << data.keyFiledname; continue; } // check if master if current table has processed foreach (int m, data.masters) - if (!checked[m]) + if (!checked[m]) { + qDebug() << "row is checked"; continue; + } checked[n] = true; --p; @@ -257,6 +262,8 @@ Q_OUTOFLINE_TEMPLATE QList Query::toList(int count) if (!table) qFatal("Could not create instance of %s", qPrintable(data.table->name())); + + qDebug() << data.table->name() << "created"; } QStringList childFields = data.table->fieldsNames(); @@ -327,7 +334,7 @@ Q_OUTOFLINE_TEMPLATE T *Query::first() if (list.count()) return list.first(); else - return 0; + return nullptr; } template @@ -404,6 +411,17 @@ Q_OUTOFLINE_TEMPLATE QVariant Query::average(const FieldPhrase &f) return 0; } +template +Q_OUTOFLINE_TEMPLATE QVariant Query::insert(AssignmentPhraseList p) +{ + Q_D(Query); + d->sql = d->database->sqlGenertor() + ->insertCommand(d->tableName, p); + QSqlQuery q = d->database->exec(d->sql); + + return q.lastInsertId(); +} + template Q_OUTOFLINE_TEMPLATE Query *Query::join(const QString &className) { @@ -524,7 +542,7 @@ Q_OUTOFLINE_TEMPLATE int Query::remove() } template -Q_OUTOFLINE_TEMPLATE QSqlQueryModel *Query::toModal() +Q_OUTOFLINE_TEMPLATE QSqlQueryModel *Query::toModel() { Q_D(Query); @@ -533,21 +551,31 @@ Q_OUTOFLINE_TEMPLATE QSqlQueryModel *Query::toModal() d->fieldPhrase, d->wherePhrase, d->orderPhrase, d->relations, d->skip, d->take); - +qDebug() << d->sql; DatabaseModel dbModel = d->database->model(); QSqlQueryModel *model = new QSqlQueryModel; model->setQuery(d->sql, d->database->database()); int fieldIndex = 0; - foreach (const PhraseData *pd, d->fieldPhrase.data) { - QString displayName = dbModel.tableByClassName(pd->className) - ->field(pd->fieldName)->displayName; - qDebug() << "Display name for"<className<fieldName - <<"="<setHeaderData(fieldIndex++, - Qt::Horizontal, - displayName); + if (d->fieldPhrase.data.count()) { + foreach (const PhraseData *pd, d->fieldPhrase.data) { + QString displayName = dbModel.tableByClassName(pd->className) + ->field(pd->fieldName)->displayName; + + qDebug() << "Display name for"<className<fieldName + <<"="<setHeaderData(fieldIndex++, + Qt::Horizontal, + displayName); + } + } else { + TableModel *tbl = d->database->model().tableByName(d->tableName); + foreach (FieldModel *f, tbl->fields()) { + model->setHeaderData(fieldIndex++, + Qt::Horizontal, + f->displayName); + } } return model; diff --git a/src/table.h b/src/table.h index 544de25..858a405 100644 --- a/src/table.h +++ b/src/table.h @@ -39,7 +39,7 @@ class NUT_EXPORT Table : public QObject Q_OBJECT public: - explicit Table(QObject *parentTableSet = 0); + explicit Table(QObject *parentTableSet = nullptr); enum Status{ NewCreated, diff --git a/src/tablesetbase.cpp b/src/tablesetbase.cpp index 1891885..f5bde89 100644 --- a/src/tablesetbase.cpp +++ b/src/tablesetbase.cpp @@ -25,12 +25,14 @@ NUT_BEGIN_NAMESPACE -TableSetBase::TableSetBase(Database *parent) : QObject(parent), _database(parent), _table(0) +TableSetBase::TableSetBase(Database *parent) : QObject(parent), _database(parent), _table(0), + _tableName(QString()) { parent->add(this); } -TableSetBase::TableSetBase(Table *parent) : QObject(parent), _database(0), _table(parent) +TableSetBase::TableSetBase(Table *parent) : QObject(parent), _database(0), _table(parent), + _tableName(QString()) { parent->add(this); } diff --git a/test/basic/maintest.cpp b/test/basic/maintest.cpp index 9cf028a..5cc87d8 100644 --- a/test/basic/maintest.cpp +++ b/test/basic/maintest.cpp @@ -22,17 +22,20 @@ .arg(timer.elapsed() / 1000.) \ .arg(__func__) +#define REGISTER(x) qDebug() << #x << "type id:" << qRegisterMetaType() + MainTest::MainTest(QObject *parent) : QObject(parent) { } void MainTest::initTestCase() { - qDebug() << "User type id:" << qRegisterMetaType(); - qDebug() << "Post type id:" << qRegisterMetaType(); - qDebug() << "Score type id:" << qRegisterMetaType(); - qDebug() << "Comment type id:" << qRegisterMetaType(); - qDebug() << "DB type id:" << qRegisterMetaType(); + //register all entities with Qt-MetaType mechanism + REGISTER(User); + REGISTER(Post); + REGISTER(Score); + REGISTER(Comment); + REGISTER(WeblogDatabase); db.setDriver(DRIVER); db.setHostName(HOST); @@ -41,11 +44,12 @@ void MainTest::initTestCase() db.setPassword(PASSWORD); bool ok = db.open(); + QTEST_ASSERT(ok); db.comments()->query()->remove(); db.posts()->query()->remove(); - - QTEST_ASSERT(ok); + db.users()->query()->remove(); + db.scores()->query()->remove(); } void MainTest::dataScheema() @@ -61,6 +65,7 @@ void MainTest::dataScheema() void MainTest::createUser() { user = new User; + user->setId(QUuid::createUuid()); user->setUsername("admin"); user->setPassword("123456"); db.users()->append(user); @@ -73,11 +78,13 @@ void MainTest::createPost() Post *newPost = new Post; newPost->setTitle("post title"); newPost->setSaveDate(QDateTime::currentDateTime()); + newPost->setPublic(false); db.posts()->append(newPost); for(int i = 0 ; i < 3; i++){ Comment *comment = new Comment; + comment->setId(QUuid::createUuid()); comment->setMessage("comment #" + QString::number(i)); comment->setSaveDate(QDateTime::currentDateTime()); comment->setAuthorId(user->id()); @@ -100,25 +107,27 @@ void MainTest::createPost() void MainTest::createPost2() { - Post *newPost = new Post; - newPost->setTitle("post title"); - newPost->setSaveDate(QDateTime::currentDateTime()); + //create post on the fly + QVariant postIdVar = db.posts()->query()->insert( + (Post::titleField() = "This is a sample") + & (Post::isPublicField() = true)); - db.posts()->append(newPost); - db.saveChanges(); + QTEST_ASSERT(postIdVar.type() == QVariant::LongLong); + int postId = postIdVar.toInt(); for(int i = 0 ; i < 3; i++){ Comment *comment = new Comment; + comment->setId(QUuid::createUuid()); comment->setMessage("comment #" + QString::number(i + 2)); comment->setSaveDate(QDateTime::currentDateTime()); comment->setAuthor(user); - comment->setPostId(newPost->id()); + //join child to master by id + comment->setPostId(postId); db.comments()->append(comment); } db.saveChanges(); - QTEST_ASSERT(newPost->id() != 0); - qDebug() << "New post2 inserted with id:" << newPost->id(); + QTEST_ASSERT(postId != 0); } void MainTest::updatePostOnTheFly() @@ -134,17 +143,20 @@ void MainTest::selectPublicts() { auto q = db.posts()->query() ->where(Post::isPublicField()) - ->toList(); + ->count(); auto q2 = db.posts()->query() ->where(!Post::isPublicField()) - ->toList(); + ->count(); + + QTEST_ASSERT(q == 1); + QTEST_ASSERT(q2 == 1); } void MainTest::selectPosts() { auto q = db.posts()->query() - ->join()//Comment::authorIdField() == Post::idField()) + ->join() ->orderBy(!Post::saveDateField() | Post::bodyField()) ->setWhere(Post::idField() == postId); @@ -155,6 +167,7 @@ void MainTest::selectPosts() PRINT(posts.length()); PRINT(posts.at(0)->comments()->length()); QTEST_ASSERT(posts.length() == 1); + qDebug() << posts.at(0)->comments()->length(); QTEST_ASSERT(posts.at(0)->comments()->length() == 3); QTEST_ASSERT(posts.at(0)->title() == "post title"); @@ -193,7 +206,7 @@ void MainTest::selectPostIds() { auto q = db.posts()->query(); auto ids = q->select(Post::idField()); -qDebug() << q->sqlCommand(); +qDebug() << ids.count(); QTEST_ASSERT(ids.count() == 2); } @@ -248,7 +261,7 @@ void MainTest::modifyPost() Post *post = q->first(); - QTEST_ASSERT(post != 0); + QTEST_ASSERT(post != nullptr); post->setTitle("new name"); db.saveChanges(); @@ -263,10 +276,10 @@ void MainTest::modifyPost() void MainTest::emptyDatabase() { - auto commentsCount = db.comments()->query()->remove(); - auto postsCount = db.posts()->query()->remove(); - QTEST_ASSERT(postsCount == 3); - QTEST_ASSERT(commentsCount == 6); +// auto commentsCount = db.comments()->query()->remove(); +// auto postsCount = db.posts()->query()->remove(); +// QTEST_ASSERT(postsCount == 3); +// QTEST_ASSERT(commentsCount == 6); } void MainTest::cleanupTestCase() @@ -274,8 +287,10 @@ void MainTest::cleanupTestCase() post->deleteLater(); user->deleteLater(); + //release models before exiting qDeleteAll(TableModel::allModels()); - DatabaseModel::deleteAllModels(); + + PRINT_FORM(db); } QTEST_MAIN(MainTest) diff --git a/test/common/comment.h b/test/common/comment.h index 0d0f9d2..808b521 100644 --- a/test/common/comment.h +++ b/test/common/comment.h @@ -16,7 +16,7 @@ class Comment : public Table { Q_OBJECT - NUT_PRIMARY_AUTO_INCREMENT(id) + NUT_PRIMARY_KEY(id) NUT_DECLARE_FIELD(QUuid, id, id, setId) NUT_DECLARE_FIELD(QString, message, message, setMessage) NUT_DECLARE_FIELD(QDateTime, saveDate, saveDate, setSaveDate) @@ -26,7 +26,7 @@ class Comment : public Table NUT_FOREGION_KEY(User, QUuid, author, author, setAuthor) public: - Q_INVOKABLE explicit Comment(QObject *parentTableSet = 0); + Q_INVOKABLE explicit Comment(QObject *parentTableSet = nullptr); }; Q_DECLARE_METATYPE(Comment*) diff --git a/test/common/consts.h b/test/common/consts.h index 8078cde..9c0c228 100644 --- a/test/common/consts.h +++ b/test/common/consts.h @@ -25,4 +25,25 @@ //#define USERNAME "sa" //#define PASSWORD "qwe123!@#" +#ifdef Q_OS_LINUX +# define OS "Linux" +#elif defined(Q_OS_WIN) +# define OS "Windows" +#elif defined(Q_OS_OSX) +# define OS "macOS" +#else +# define OS "Unknown" +#endif + +#define PRINT_FORM(db) \ + qDebug() << "\n\n****************************" \ + << "\nAll tests passed," \ + << "please fill in bellow form and email it to me at" \ + << "hamed.masafi@gmail.com" \ + << "\n\tDriver:" << db.driver() \ + << "\n\tOS: " OS " (version: ________)" \ + << "\n\tQt version: " QT_VERSION_STR \ + << "\n\tTest:" << metaObject()->className() \ + << "\n****************************\n"; + #endif // CONSTS_H diff --git a/test/common/user.h b/test/common/user.h index 2fff65f..c16f6ec 100644 --- a/test/common/user.h +++ b/test/common/user.h @@ -32,7 +32,7 @@ class User : public Nut::Table NUT_DECLARE_CHILD_TABLE(Score, scores) public: - Q_INVOKABLE User(QObject *parentTableSet = 0); + Q_INVOKABLE User(QObject *parentTableSet = nullptr); }; Q_DECLARE_METATYPE(User*) diff --git a/test/quuid/maintest.cpp b/test/quuid/maintest.cpp new file mode 100644 index 0000000..dc43140 --- /dev/null +++ b/test/quuid/maintest.cpp @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include + +#include "consts.h" + +#include "maintest.h" +#include "query.h" +#include "tableset.h" +#include "tablemodel.h" + +#include "test.h" + +MainTest::MainTest(QObject *parent) : QObject(parent) +{ +} + +void MainTest::initTestCase() +{ + qDebug() << "Test type id:" << qRegisterMetaType(); + qDebug() << "DB type id:" << qRegisterMetaType(); + + db.setDriver(DRIVER); + db.setHostName(HOST); + db.setDatabaseName("nut_tst_quuid"); + db.setUserName(USERNAME); + db.setPassword(PASSWORD); + + bool ok = db.open(); + + db.tests()->query()->remove(); + + QTEST_ASSERT(ok); +} + +void MainTest::add() +{ + TIC(); + QUuid uuid = QUuid::createUuid(); + Test t; + t.setId(uuid); + t.setUsername("test username"); + db.tests()->append(&t); + db.saveChanges(); + TOC(); + + Test *t2 = db.tests()->query() + ->where(Test::idField() == uuid) + ->first(); + + TOC(); + QTEST_ASSERT(t2->id() == uuid); +} + +void MainTest::cleanupTestCase() +{ + qDeleteAll(Nut::TableModel::allModels()); +// Nut::DatabaseModel::deleteAllModels(); +} + +QTEST_MAIN(MainTest) diff --git a/test/quuid/maintest.h b/test/quuid/maintest.h new file mode 100644 index 0000000..32fad84 --- /dev/null +++ b/test/quuid/maintest.h @@ -0,0 +1,26 @@ +#ifndef MAINTEST_H +#define MAINTEST_H + +#include +#include + +#include "testdatabase.h" +class Test; +class MainTest : public QObject +{ + Q_OBJECT + TestDatabase db; + +public: + explicit MainTest(QObject *parent = 0); + +signals: + +private slots: + void initTestCase(); + void add(); + + void cleanupTestCase(); +}; + +#endif // MAINTEST_H diff --git a/test/quuid/tst_quuid.pro b/test/quuid/tst_quuid.pro new file mode 100644 index 0000000..0a702dc --- /dev/null +++ b/test/quuid/tst_quuid.pro @@ -0,0 +1,20 @@ +QT += qml quick testlib sql +QT -= gui + +TARGET = tst_nut +TEMPLATE = app + +CONFIG += warn_on qmltestcase c++11 +INCLUDEPATH += $$PWD/../../src $$PWD/../common +include(../../nut.pri) +IMPORTPATH += $$OUT_PWD/../src/imports +SOURCES += \ + maintest.cpp \ + testdatabase.cpp \ + test.cpp + +HEADERS += \ + maintest.h \ + ../common/consts.h \ + testdatabase.h \ + test.h