diff --git a/README.md b/README.md index d44fb0d..315ab32 100644 --- a/README.md +++ b/README.md @@ -61,4 +61,10 @@ 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/nut.qbs b/nut.qbs index d7e3dc1..f78c213 100644 --- a/nut.qbs +++ b/nut.qbs @@ -1,39 +1,53 @@ import qbs -Product { - type: "staticlibrary" - name: "nut" - Depends { name: 'cpp' } - Depends { name: "Qt.core" } - Depends { name: "Qt.sql" } +Project { +// StaticLibrary { +// name: "nut_static" +// files: [ +// "src/*.h", +// "src/*.cpp", +// "src/generators/*.h", +// "src/generators/*.cpp" +// ] +// Depends { name: 'cpp' } +// Depends { name: "Qt.core" } +// Depends { name: "Qt.sql" } +// Group { qbs.install: true; fileTagsFilter: product.type;} - Export { - Depends { name: "cpp" } +// Export { +// Depends { name: "cpp" } +// Depends { name: "Qt.core" } +// Depends { name: "Qt.sql" } +// cpp.includePaths: [ +// product.sourceDirectory + "/src", +// product.sourceDirectory + "/include" +// ] +// } +// } + + DynamicLibrary { + name: "nut" + files: [ + "src/*.h", + "src/*.cpp", + "src/generators/*.h", + "src/generators/*.cpp" + ] + + Depends { name: 'cpp' } Depends { name: "Qt.core" } Depends { name: "Qt.sql" } - cpp.includePaths: [ - product.sourceDirectory + "/src", - product.sourceDirectory + "/include" - ] - } - files: [ - "src/tableset.cpp", - "src/query.cpp", - "src/databasemodel.cpp", - "src/tablesetbase.cpp", - "src/changelogtable.cpp", - "src/querybase.cpp", - "src/tablemodel.cpp", - "src/wherephrase.cpp", - "src/table.cpp", - "src/database.cpp", - "src/generators/sqlgeneratorbase.cpp", - "src/generators/postgresqlgenerator.cpp", - "src/generators/mysqlgenerator.cpp", - "src/generators/sqlitegenerator.cpp", - "src/generators/sqlservergenerator.cpp", - "src/types/dbgeography.cpp", - "src/serializableobject.cpp" - ] + Group { qbs.install: true; fileTagsFilter: product.type;} + + Export { + Depends { name: "cpp" } + Depends { name: "Qt.core" } + Depends { name: "Qt.sql" } + cpp.includePaths: [ + product.sourceDirectory + "/src", + product.sourceDirectory + "/include" + ] + } + } } diff --git a/src/database.cpp b/src/database.cpp index 40de725..cc69a2a 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -203,14 +203,25 @@ bool DatabasePrivate::getCurrectScheema() QString name; QString value; - if (!checkClassInfo(q->metaObject()->classInfo(i), - type, name, value)) { + if (!nutClassInfoString(q->metaObject()->classInfo(i), + type, name, value)) { + qDebug() << "No valid table in" << q->metaObject()->classInfo(i).value(); continue; } - - if (type == __nut_TABLE) + if (type == __nut_TABLE) { + //name: table class name + //value: table variable name (table name in db) tables.insert(name, value); + int typeId = QMetaType::type(name.toLocal8Bit() + "*"); + + if (!typeId) + qFatal("The class %s is not registered with qt meta object", qPrintable(name)); + + TableModel *sch = new TableModel(typeId, value); + currentModel.append(sch); + } + if (type == __nut_DB_VERSION) currentModel.setVersion(name); @@ -249,21 +260,21 @@ bool DatabasePrivate::getCurrectScheema() return true; } -bool DatabasePrivate::checkClassInfo(const QMetaClassInfo &classInfo, QString &type, QString &name, QString &value) -{ - if (!QString(classInfo.name()).startsWith(__nut_NAME_PERFIX)) { - return false; - } else { - QStringList parts = QString(classInfo.value()).split("\n"); - if (parts.count() != 3) - return false; +//bool DatabasePrivate::checkClassInfo(const QMetaClassInfo &classInfo, QString &type, QString &name, QString &value) +//{ +// if (!QString(classInfo.name()).startsWith(__nut_NAME_PERFIX)) { +// return false; +// } else { +// QStringList parts = QString(classInfo.value()).split("\n"); +// if (parts.count() != 3) +// return false; - type = parts[0]; - name = parts[1]; - value = parts[2]; - return true; - } -} +// type = parts[0]; +// name = parts[1]; +// value = parts[2]; +// return true; +// } +//} DatabaseModel DatabasePrivate::getLastScheema() { diff --git a/src/database_p.h b/src/database_p.h index 3299e4a..cbe2379 100644 --- a/src/database_p.h +++ b/src/database_p.h @@ -45,8 +45,8 @@ public: DatabaseModel getLastScheema(); bool getCurrectScheema(); - bool checkClassInfo(const QMetaClassInfo &classInfo, - QString &type, QString &name, QString &value); +// bool checkClassInfo(const QMetaClassInfo &classInfo, +// QString &type, QString &name, QString &value); QSqlDatabase db; QString hostName; diff --git a/src/databasemodel.cpp b/src/databasemodel.cpp index 5aad118..2a579f8 100644 --- a/src/databasemodel.cpp +++ b/src/databasemodel.cpp @@ -233,7 +233,7 @@ void DatabaseModel::deleteAllModels() while (i.hasNext()) { i.next(); // cout << i.key() << ": " << i.value() << endl; - delete i.value(); +// qDeleteAll(i.value()); } // qDeleteAll(_models.values()); _models.clear(); diff --git a/src/defines.h b/src/defines.h index a779ccd..348ff6e 100644 --- a/src/defines.h +++ b/src/defines.h @@ -26,6 +26,11 @@ #include "defines_p.h" #include "qglobal.h" +#include +#include +#include +#include + #ifdef NUT_COMPILE_STATIC # define NUT_EXPORT #else @@ -36,21 +41,96 @@ Q_CLASSINFO(__nut_NAME_PERFIX type #name #value, \ type "\n" #name "\n" #value) +#define NUT_INFO_STRING(type, name, value) \ + Q_CLASSINFO(__nut_NAME_PERFIX type #name #value, \ + type "\n" #name "\n" value) + +inline bool nutClassInfo(const QMetaClassInfo &classInfo, + QString &type, QString &name, QVariant &value) +{ + if (!QString(classInfo.name()).startsWith(__nut_NAME_PERFIX)) { + return false; + } else { + QStringList parts = QString(classInfo.value()).split("\n"); + if (parts.count() != 3) + return false; + + type = parts[0]; + name = parts[1]; + value = qVariantFromValue(parts[2]); + return true; + } +} + +inline bool nutClassInfoString(const QMetaClassInfo &classInfo, + QString &type, QString &name, QString &value) +{ + if (!QString(classInfo.name()).startsWith(__nut_NAME_PERFIX)) { + return false; + } else { + QStringList parts = QString(classInfo.value()).split("\n"); + if (parts.count() != 3) + return false; + + type = parts[0]; + name = parts[1]; + value = parts[2]; + return true; + } +} + +inline bool nutClassInfoBool(const QMetaClassInfo &classInfo, + QString &type, QString &name, bool &value) +{ + if (!QString(classInfo.name()).startsWith(__nut_NAME_PERFIX)) { + return false; + } else { + QStringList parts = QString(classInfo.value()).split("\n"); + if (parts.count() != 3) + return false; + + QString buffer = parts[2].toLower(); + if (buffer != "true" && buffer != "false") + return false; + + type = parts[0]; + name = parts[1]; + value = (buffer == "true"); + return true; + } +} + +inline bool nutClassInfoInt(const QMetaClassInfo &classInfo, + QString &type, QString &name, bool &value) +{ + if (!QString(classInfo.name()).startsWith(__nut_NAME_PERFIX)) { + return false; + } else { + QStringList parts = QString(classInfo.value()).split("\n"); + if (parts.count() != 3) + return false; + bool ok; + type = parts[0]; + name = parts[1]; + value = parts[2].toInt(&ok); + return ok; + } +} + + // Database #define NUT_DB_VERSION(version) \ NUT_INFO(__nut_DB_VERSION, version, 0) #define NUT_DECLARE_TABLE(type, name) \ NUT_INFO(__nut_TABLE, type, name) \ - Q_PROPERTY(type* name READ name) \ - Q_PROPERTY(NUT_WRAP_NAMESPACE(TableSet) name##Table READ name##Table)\ - type* m_##name; \ - NUT_WRAP_NAMESPACE(TableSet) *m_##name##Table; \ -public: \ - static const type _##name; \ - type* name() const{ return m_##name; } \ - NUT_WRAP_NAMESPACE(TableSet) *name##Table() const \ - { return m_##name##Table; } + Q_PROPERTY(NUT_WRAP_NAMESPACE(TableSet) name READ name) \ + NUT_WRAP_NAMESPACE(TableSet) *m_##name; \ + public: \ + static const type *_##name; \ + NUT_WRAP_NAMESPACE(TableSet) *name() const \ + { return m_##name; } \ + private: //Table #define NUT_DECLARE_FIELD(type, name, read, write) \ @@ -58,7 +138,7 @@ public: \ 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); \ @@ -99,11 +179,9 @@ public: \ return m_##n; \ } - #define NUT_PRIMARY_KEY(x) NUT_INFO(__nut_PRIMARY_KEY, x, 0) #define NUT_AUTO_INCREMENT(x) NUT_INFO(__nut_AUTO_INCREMENT, x, 0) -#define NUT_PRIMARY_AUTO_INCREMENT(x) NUT_PRIMARY_KEY(x) \ - NUT_AUTO_INCREMENT(x) +#define NUT_PRIMARY_AUTO_INCREMENT(x) NUT_INFO(__nut_PRIMARY_KEY_AI, x, 0) #define NUT_DISPLAY_NAME(field, name) NUT_INFO(__nut_DISPLAY, field, name) #define NUT_UNIQUE(x) NUT_INFO(__nut_UNIQUE, x, 0) #define NUT_LEN(field, len) NUT_INFO(__nut_LEN, field, len) diff --git a/src/defines_p.h b/src/defines_p.h index 045d001..b37f715 100644 --- a/src/defines_p.h +++ b/src/defines_p.h @@ -27,10 +27,11 @@ #define __FOREIGN_KEYS "foreign_keys" #define __nut_FIELD "field" +#define __nut_NAME_PERFIX "nut_info::" #define __nut_DB_VERSION "database_version" -#define __nut_NAME_PERFIX "nut_db_key::" #define __nut_PRIMARY_KEY "primary_key" #define __nut_AUTO_INCREMENT "auto_increment" +#define __nut_PRIMARY_KEY_AI "primary_key_ai" #define __nut_UNIQUE "unique" #define __nut_TABLE "table" #define __nut_TABLE_NAME "table_name" diff --git a/src/generators/postgresqlgenerator.cpp b/src/generators/postgresqlgenerator.cpp index 6bab95e..a791922 100644 --- a/src/generators/postgresqlgenerator.cpp +++ b/src/generators/postgresqlgenerator.cpp @@ -63,7 +63,7 @@ QString PostgreSqlGenerator::fieldType(FieldModel *field) if(field->isAutoIncrement) dbType = "BIGSERIAL"; else - dbType = "BIGINTEGER"; + dbType = "BIGINT"; break; case QVariant::Double: diff --git a/src/generators/sqlgeneratorbase.cpp b/src/generators/sqlgeneratorbase.cpp index 51812cf..4a404af 100644 --- a/src/generators/sqlgeneratorbase.cpp +++ b/src/generators/sqlgeneratorbase.cpp @@ -395,13 +395,13 @@ QString SqlGeneratorBase::join(const QStringList &list, QStringList *order) QString SqlGeneratorBase::insertRecord(Table *t, QString tableName) { QString sql = QString(); - QString key = t->primaryKey(); + QString key = t->isPrimaryKeyAutoIncrement() ? t->primaryKey() : QString(); + QStringList values; foreach (QString f, t->changedProperties()) if (f != key) - values.append("'" + t->property(f.toLatin1().data()).toString() - + "'"); + values.append(escapeValue(t->property(f.toLatin1().data()))); QString changedPropertiesText = QString(); QSet props = t->changedProperties(); @@ -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, @@ -785,7 +806,7 @@ QString SqlGeneratorBase::escapeValue(const QVariant &v) const break; case QVariant::Uuid: - return v.toUuid().toString(); + return "'" + v.toUuid().toString() + "'"; break; case QVariant::Char: 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/generators/sqlitegenerator.cpp b/src/generators/sqlitegenerator.cpp index 6e8d774..fa18978 100644 --- a/src/generators/sqlitegenerator.cpp +++ b/src/generators/sqlitegenerator.cpp @@ -64,6 +64,9 @@ QString SqliteGenerator::fieldType(FieldModel *field) else dbType = "text"; break; + case QVariant::Uuid: + dbType = "text"; + break; default: dbType = QString(); } diff --git a/src/phrase.cpp b/src/phrase.cpp index 274cdcc..65b2691 100644 --- a/src/phrase.cpp +++ b/src/phrase.cpp @@ -24,8 +24,6 @@ NUT_BEGIN_NAMESPACE -#define LOG(s) qDebug() << __func__ << s - PhraseData::PhraseData() : className(""), fieldName(""), type(Field), operatorCond(NotAssign), @@ -65,7 +63,6 @@ PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o, QVariant r) PhraseData *PhraseData::operator =(PhraseData *other) { - LOG(""); other->parents++; return other; } @@ -76,20 +73,6 @@ PhraseData &PhraseData::operator =(PhraseData &other) return other; } -//PhraseData::PhraseData(const PhraseData &other) : -// left(other.left), right(other.right), operand(other.operand), -// operatorCond(other.operatorCond), className(other.className), -// fieldName(other.fieldName), type(other.type), isNot(other.isNot), -// parents(other.parents + 1) -//{ } - -//PhraseData::PhraseData(const PhraseData *other) : -// left(other->left), right(other->right), operand(other->operand), -// operatorCond(other->operatorCond), className(other->className), -// fieldName(other->fieldName), type(other->type), isNot(other->isNot), -// parents(other->parents + 1) -//{ } - QString PhraseData::toString() const { return QString("[%1].%2").arg(className).arg(fieldName); @@ -97,27 +80,10 @@ QString PhraseData::toString() const PhraseData::~PhraseData() { -// if (type == WithOther) { -// delete left; -// delete right; -// } -// if (type == WithVariant) { -// if (left) -// delete left; -// } - -// if (right && !--right->parents) -// delete right; - -// if (left && !--left->parents) -// delete left; - - LOG(""); } void PhraseData::cleanUp() { -// cleanUp(this); } void PhraseData::cleanUp(PhraseData *d) @@ -154,12 +120,6 @@ AbstractFieldPhrase::AbstractFieldPhrase(AbstractFieldPhrase &&other) AbstractFieldPhrase::~AbstractFieldPhrase() { - if (data) { - LOG(data->toString()) << data->parents; - } else { - LOG(""); - } - if (data) { --data->parents; if (data->parents <= 0) @@ -232,14 +192,12 @@ PhraseList::PhraseList() : isValid(false) PhraseList::PhraseList(const PhraseList &other) : isValid(true) { - LOG(""); data = qMove(other.data); const_cast(other).data.clear(); } PhraseList::PhraseList(PhraseList &&other) { - LOG(""); data = other.data; } @@ -277,7 +235,6 @@ PhraseList::PhraseList(PhraseList *left, const AbstractFieldPhrase *right) PhraseList::~PhraseList() { - LOG(""); } PhraseList &PhraseList::operator =(const PhraseList &other) @@ -484,7 +441,6 @@ ConditionalPhrase::ConditionalPhrase(ConditionalPhrase *l, ConditionalPhrase::~ConditionalPhrase() { - LOG(""); if (data) { data->cleanUp(); if (!--data->parents) @@ -521,7 +477,7 @@ ConditionalPhrase ConditionalPhrase::operator ==(const QVariant &other) // const_cast(other)); //} -#define DECLARE_CONDITIONALPHRASE_OPERATORS(op, cond) \ +#define DECLARE_CONDITIONALPHRASE_OPERATORS_IMPL(op, cond) \ ConditionalPhrase operator op(const ConditionalPhrase &l, \ const ConditionalPhrase &r) \ { \ @@ -570,9 +526,9 @@ ConditionalPhrase operator op(ConditionalPhrase &&l, ConditionalPhrase &&r) \ return p; \ } -DECLARE_CONDITIONALPHRASE_OPERATORS(==, PhraseData::Equal) -DECLARE_CONDITIONALPHRASE_OPERATORS(||, PhraseData::Or) -DECLARE_CONDITIONALPHRASE_OPERATORS(&&, PhraseData::And) +DECLARE_CONDITIONALPHRASE_OPERATORS_IMPL(==, PhraseData::Equal) +DECLARE_CONDITIONALPHRASE_OPERATORS_IMPL(||, PhraseData::Or) +DECLARE_CONDITIONALPHRASE_OPERATORS_IMPL(&&, PhraseData::And) ConditionalPhrase ConditionalPhrase::operator !() { 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.cpp b/src/table.cpp index e3deae5..afb4033 100644 --- a/src/table.cpp +++ b/src/table.cpp @@ -74,8 +74,9 @@ void Table::propertyChanged(QString propName) if (!myModel) qFatal ("model for this class not found"); - if (propName == primaryKey()) - return; + foreach (FieldModel *f, myModel->fields()) + if(f->isPrimaryKey && propName == f->name && f->isAutoIncrement) + return; _changedProperties.insert(propName); if (_status == FeatchedFromDB) 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/tablemodel.cpp b/src/tablemodel.cpp index bc542a0..2908f26 100644 --- a/src/tablemodel.cpp +++ b/src/tablemodel.cpp @@ -156,22 +156,22 @@ bool TableModel::operator !=(const TableModel &t) const return !(*this == t); } -bool TableModel::checkClassInfo(const QMetaClassInfo &classInfo, - QString &type, QString &name, QString &value) -{ - if (!QString(classInfo.name()).startsWith(__nut_NAME_PERFIX)) { - return false; - } else { - QStringList parts = QString(classInfo.value()).split("\n"); - if (parts.count() != 3) - return false; +//bool TableModel::checkClassInfo(const QMetaClassInfo &classInfo, +// QString &type, QString &name, QString &value) +//{ +// if (!QString(classInfo.name()).startsWith(__nut_NAME_PERFIX)) { +// return false; +// } else { +// QStringList parts = QString(classInfo.value()).split("\n"); +// if (parts.count() != 3) +// return false; - type = parts[0]; - name = parts[1]; - value = parts[2]; - return true; - } -} +// type = parts[0]; +// name = parts[1]; +// value = parts[2]; +// return true; +// } +//} TableModel::TableModel(int typeId, QString tableName) { @@ -196,7 +196,7 @@ TableModel::TableModel(int typeId, QString tableName) QString name; QString value; - if (!checkClassInfo(tableMetaObject->classInfo(j), + if (!nutClassInfoString(tableMetaObject->classInfo(j), type, name, value)) { continue; } @@ -227,7 +227,7 @@ TableModel::TableModel(int typeId, QString tableName) QString name; QString value; - if (!checkClassInfo(tableMetaObject->classInfo(j), + if (!nutClassInfoString(tableMetaObject->classInfo(j), type, name, value)) { continue; } @@ -248,23 +248,27 @@ TableModel::TableModel(int typeId, QString tableName) FieldModel *f = field(name); - if(!f) + if (!f) continue; - if(type == __nut_LEN) + if (type == __nut_LEN) f->length = value.toInt(); - else if(type == __nut_NOT_NULL) + else if (type == __nut_NOT_NULL) f->notNull = true; - else if(type == __nut_DEFAULT_VALUE) + else if (type == __nut_DEFAULT_VALUE) f->defaultValue = value; - else if(type == __nut_PRIMARY_KEY) + else if (type == __nut_PRIMARY_KEY) f->isPrimaryKey = true; - else if(type == __nut_AUTO_INCREMENT) + else if (type == __nut_AUTO_INCREMENT) f->isAutoIncrement = true; - else if(type == __nut_UNIQUE) + else if (type == __nut_UNIQUE) f->isUnique = true; - else if(type == __nut_DISPLAY) + else if (type == __nut_DISPLAY) f->displayName = value.mid(1, value.length() - 2); + else if (type == __nut_PRIMARY_KEY_AI) { + f->isPrimaryKey = true; + f->isAutoIncrement = true; + } } if(!findByTypeId(typeId) && !tableName.isNull()) diff --git a/src/tablemodel.h b/src/tablemodel.h index 67fcc5e..345374f 100644 --- a/src/tablemodel.h +++ b/src/tablemodel.h @@ -138,8 +138,8 @@ private: QList _fields; QList _foreignKeys; static QSet_allModels; - bool checkClassInfo(const QMetaClassInfo &classInfo, - QString &type, QString &name, QString &value); +// bool checkClassInfo(const QMetaClassInfo &classInfo, +// QString &type, QString &name, QString &value); }; NUT_END_NAMESPACE diff --git a/src/tableset.h b/src/tableset.h index 8034a09..a6bfd2c 100644 --- a/src/tableset.h +++ b/src/tableset.h @@ -21,10 +21,10 @@ #ifndef TABLESET_H #define TABLESET_H -#include -#include +#include #include #include +#include #include #include "tablesetbase_p.h" @@ -52,6 +52,7 @@ public: int length() const; T *at(int i) const; const T &operator[](int i) const; + Query *query(); Query *query(bool autoDelete); }; 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 74f5d14..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(); - - db.commentTable()->query()->remove(); - db.postTable()->query()->remove(); - QTEST_ASSERT(ok); + + db.comments()->query()->remove(); + db.posts()->query()->remove(); + db.users()->query()->remove(); + db.scores()->query()->remove(); } void MainTest::dataScheema() @@ -61,9 +65,10 @@ void MainTest::dataScheema() void MainTest::createUser() { user = new User; + user->setId(QUuid::createUuid()); user->setUsername("admin"); user->setPassword("123456"); - db.userTable()->append(user); + db.users()->append(user); db.saveChanges(); } @@ -73,11 +78,13 @@ void MainTest::createPost() Post *newPost = new Post; newPost->setTitle("post title"); newPost->setSaveDate(QDateTime::currentDateTime()); + newPost->setPublic(false); - db.postTable()->append(newPost); + 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,30 +107,32 @@ 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.postTable()->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()); - db.commentTable()->append(comment); + //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() { - auto c = db.postTable()->query() + auto c = db.posts()->query() ->where(Post::idField() == postId) ->update(Post::titleField() = "New title"); @@ -132,19 +141,22 @@ void MainTest::updatePostOnTheFly() void MainTest::selectPublicts() { - auto q = db.postTable()->query() + auto q = db.posts()->query() ->where(Post::isPublicField()) - ->toList(); + ->count(); - auto q2 = db.postTable()->query() + auto q2 = db.posts()->query() ->where(!Post::isPublicField()) - ->toList(); + ->count(); + + QTEST_ASSERT(q == 1); + QTEST_ASSERT(q2 == 1); } void MainTest::selectPosts() { - auto q = db.postTable()->query() - ->join()//Comment::authorIdField() == Post::idField()) + auto q = db.posts()->query() + ->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"); @@ -166,7 +179,7 @@ void MainTest::selectPosts() void MainTest::selectScoreAverage() { - auto a = db.scoreTable()->query() + auto a = db.scores()->query() ->join() ->setWhere(Post::idField() == 1) ->average(Score::scoreField()); @@ -175,7 +188,7 @@ void MainTest::selectScoreAverage() void MainTest::selectFirst() { - auto posts = db.postTable()->query() + auto posts = db.posts()->query() ->first(); QTEST_ASSERT(posts != Q_NULLPTR); @@ -183,7 +196,7 @@ void MainTest::selectFirst() void MainTest::selectPostsWithoutTitle() { - auto q = db.postTable()->query(); + auto q = db.posts()->query(); q->setWhere(Post::titleField().isNull()); auto count = q->count(); QTEST_ASSERT(count == 0); @@ -191,9 +204,9 @@ void MainTest::selectPostsWithoutTitle() void MainTest::selectPostIds() { - auto q = db.postTable()->query(); + auto q = db.posts()->query(); auto ids = q->select(Post::idField()); -qDebug() << q->sqlCommand(); +qDebug() << ids.count(); QTEST_ASSERT(ids.count() == 2); } @@ -207,11 +220,11 @@ void MainTest::testDate() newPost->setTitle("post title"); newPost->setSaveDate(d); - db.postTable()->append(newPost); + db.posts()->append(newPost); db.saveChanges(true); - auto q = db.postTable()->query() + auto q = db.posts()->query() ->setWhere(Post::idField() == newPost->id()) ->first(); @@ -221,7 +234,7 @@ void MainTest::testDate() void MainTest::join() { TIC(); - auto q = db.commentTable()->query() + auto q = db.comments()->query() ->join() ->join(); @@ -236,24 +249,24 @@ void MainTest::join() void MainTest::selectWithInvalidRelation() { - auto q = db.postTable()->query(); + auto q = db.posts()->query(); q->join("Invalid_Class_Name"); q->toList(); } void MainTest::modifyPost() { - auto q = db.postTable()->query(); + auto q = db.posts()->query(); q->setWhere(Post::idField() == postId); Post *post = q->first(); - QTEST_ASSERT(post != 0); + QTEST_ASSERT(post != nullptr); post->setTitle("new name"); db.saveChanges(); - q = db.postTable()->query() + q = db.posts()->query() ->setWhere(Post::idField() == postId); post = q->first(); @@ -263,10 +276,10 @@ void MainTest::modifyPost() void MainTest::emptyDatabase() { - auto commentsCount = db.commentTable()->query()->remove(); - auto postsCount = db.postTable()->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/basic/maintest.h b/test/basic/maintest.h index 67ec06b..c5e9699 100644 --- a/test/basic/maintest.h +++ b/test/basic/maintest.h @@ -16,7 +16,7 @@ class MainTest : public QObject User *user; public: - explicit MainTest(QObject *parent = 0); + explicit MainTest(QObject *parent = nullptr); signals: diff --git a/test/benckmark/maintest.h b/test/benckmark/maintest.h index e5d172f..b98b7ce 100644 --- a/test/benckmark/maintest.h +++ b/test/benckmark/maintest.h @@ -14,7 +14,7 @@ class MainTest : public QObject Post *post; Query *q; public: - explicit MainTest(QObject *parent = 0); + explicit MainTest(QObject *parent = nullptr); signals: diff --git a/test/commands/maintest.h b/test/commands/maintest.h index b8753b3..0f19910 100644 --- a/test/commands/maintest.h +++ b/test/commands/maintest.h @@ -14,7 +14,7 @@ class MainTest : public QObject Post *post; Query *q; public: - explicit MainTest(QObject *parent = 0); + explicit MainTest(QObject *parent = nullptr); signals: diff --git a/test/common/comment.h b/test/common/comment.h index 0fd30cc..808b521 100644 --- a/test/common/comment.h +++ b/test/common/comment.h @@ -3,6 +3,7 @@ #include #include +#include #include "table.h" #ifdef NUT_NAMESPACE @@ -15,17 +16,17 @@ class Comment : public Table { Q_OBJECT - NUT_PRIMARY_AUTO_INCREMENT(id) - NUT_DECLARE_FIELD(int, id, id, setId) + 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) NUT_DECLARE_FIELD(qreal, point, point, setPoint) NUT_FOREGION_KEY(Post, int, post, post, setPost) - NUT_FOREGION_KEY(User, int, author, author, setAuthor) + 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 40ca178..9c0c228 100644 --- a/test/common/consts.h +++ b/test/common/consts.h @@ -1,6 +1,12 @@ #ifndef CONSTS_H #define CONSTS_H +#define PRINT(x) qDebug() << #x "=" << x; +#define TIC() QElapsedTimer timer; timer.start() +#define TOC() qDebug() << QString("Elapsed time: %1ms for %2") \ + .arg(timer.elapsed() / 1000.) \ + .arg(__func__) + //#define DRIVER "QPSQL" //#define HOST "127.0.0.1" //#define DATABASE "nutdb2" @@ -9,7 +15,7 @@ #define DRIVER "QSQLITE" #define HOST "127.0.0.1" -#define DATABASE "nutdb" +#define DATABASE "nutdb1" #define USERNAME "root" #define PASSWORD "onlyonlyi" @@ -19,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 0b98721..c16f6ec 100644 --- a/test/common/user.h +++ b/test/common/user.h @@ -17,8 +17,8 @@ class User : public Nut::Table { Q_OBJECT - NUT_PRIMARY_AUTO_INCREMENT(id) - NUT_DECLARE_FIELD(int, id, id, setId) + NUT_PRIMARY_KEY(id) + NUT_DECLARE_FIELD(QUuid, id, id, setId) NUT_NOT_NULL(username) NUT_LEN(username, 50) @@ -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/common/weblogdatabase.cpp b/test/common/weblogdatabase.cpp index d020e2b..cf459b9 100644 --- a/test/common/weblogdatabase.cpp +++ b/test/common/weblogdatabase.cpp @@ -8,9 +8,9 @@ #include "weblogdatabase.h" WeblogDatabase::WeblogDatabase() : Database(), - m_postTable(new TableSet(this)), - m_commentTable(new TableSet(this)), - m_userTable(new TableSet(this)), - m_scoreTable(new TableSet(this)) + m_posts(new TableSet(this)), + m_comments(new TableSet(this)), + m_users(new TableSet(this)), + m_scores(new TableSet(this)) { } diff --git a/test/common/weblogdatabase.h b/test/common/weblogdatabase.h index af72442..fb807a8 100644 --- a/test/common/weblogdatabase.h +++ b/test/common/weblogdatabase.h @@ -17,10 +17,10 @@ class WeblogDatabase : public Database NUT_DB_VERSION(1) - NUT_DECLARE_TABLE(Post, post) - NUT_DECLARE_TABLE(Comment, comment) - NUT_DECLARE_TABLE(User, user) - NUT_DECLARE_TABLE(Score, score) + NUT_DECLARE_TABLE(Post, posts) + NUT_DECLARE_TABLE(Comment, comments) + NUT_DECLARE_TABLE(User, users) + NUT_DECLARE_TABLE(Score, scores) public: WeblogDatabase(); diff --git a/test/phrases/maintest.cpp b/test/phrases/maintest.cpp index 8eaecb3..3e94a69 100644 --- a/test/phrases/maintest.cpp +++ b/test/phrases/maintest.cpp @@ -21,7 +21,7 @@ void MainTest::no1() FieldPhrase name("main", "name"); FieldPhrase last_name("main", "last_name"); FieldPhrase date("main", "date"); - auto w = id == 4 && name == "salam"; + auto w = (id == 4 && name == "hi"); } QTEST_MAIN(MainTest) diff --git a/test/phrases/maintest.h b/test/phrases/maintest.h index 5c7304e..78d83bc 100644 --- a/test/phrases/maintest.h +++ b/test/phrases/maintest.h @@ -11,7 +11,7 @@ class MainTest : public QObject Q_OBJECT public: - explicit MainTest(QObject *parent = 0); + explicit MainTest(QObject *parent = nullptr); signals: 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/test.cpp b/test/quuid/test.cpp new file mode 100644 index 0000000..69ddded --- /dev/null +++ b/test/quuid/test.cpp @@ -0,0 +1,6 @@ +#include "test.h" + +Test::Test(QObject *parentTableSet) +{ + +} diff --git a/test/quuid/test.h b/test/quuid/test.h new file mode 100644 index 0000000..edff96c --- /dev/null +++ b/test/quuid/test.h @@ -0,0 +1,25 @@ +#ifndef TEST_H +#define TEST_H + +#include + +#include "table.h" + +class Test : public Nut::Table +{ + Q_OBJECT + + NUT_PRIMARY_KEY(id) + NUT_DECLARE_FIELD(QUuid, id, id, setId) + + NUT_NOT_NULL(username) + NUT_LEN(username, 50) + NUT_DECLARE_FIELD(QString, username, username, setUsername) + +public: + Q_INVOKABLE Test(QObject *parentTableSet = 0); +}; + +Q_DECLARE_METATYPE(Test*) + +#endif // TEST_H diff --git a/test/quuid/testdatabase.cpp b/test/quuid/testdatabase.cpp new file mode 100644 index 0000000..336119e --- /dev/null +++ b/test/quuid/testdatabase.cpp @@ -0,0 +1,8 @@ +#include "testdatabase.h" +#include "test.h" + +TestDatabase::TestDatabase(QObject *parent) + : Database(parent), m_tests(new Nut::TableSet(this)) +{ + +} diff --git a/test/quuid/testdatabase.h b/test/quuid/testdatabase.h new file mode 100644 index 0000000..1e257e6 --- /dev/null +++ b/test/quuid/testdatabase.h @@ -0,0 +1,18 @@ +#ifndef TESTDATABASE_H +#define TESTDATABASE_H + +#include + +class Test; +class TestDatabase : public Nut::Database +{ + Q_OBJECT + + NUT_DB_VERSION(1) + + NUT_DECLARE_TABLE(Test, tests) +public: + TestDatabase(QObject *parent = nullptr); +}; + +#endif // TESTDATABASE_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