From 0a971815c4fc4b71d6aafef7e79d8ce1d7e5d1ad Mon Sep 17 00:00:00 2001 From: Hamed Masafi Date: Sun, 5 Jun 2016 16:52:26 +0430 Subject: [PATCH] order phrase completed --- doc/nut.index | 5 + doc/nut.qhp | 14 ++ doc/nut.qhp.sha1 | 1 + include.tar.gz | Bin 0 -> 27816 bytes include/header_copier | 0 nut.pri | 3 +- nut.qdocconf | 24 +++ src/database.cpp | 63 +++++-- src/database.h | 4 + src/database_p.h | 3 + src/defines.h | 22 ++- src/query.cpp | 46 ++++- src/query.h | 233 ++++++++++------------- src/query_p.h | 49 +++++ src/sqlgeneratorbase.cpp | 306 +++++++++++++++++++++++++++---- src/sqlgeneratorbase_p.h | 27 ++- src/sqlservergenerator.cpp | 8 + src/sqlservergenerator.h | 2 + src/tablemodel.cpp | 5 +- src/wherephrase.cpp | 356 ++++++++++++++---------------------- src/wherephrase.h | 45 ++--- test/basic/maintest.cpp | 56 +++++- test/basic/maintest.h | 3 + test/basic/tst_basic.pro | 1 + test/benckmark/maintest.cpp | 2 - test/common/consts.h | 14 +- 26 files changed, 841 insertions(+), 451 deletions(-) create mode 100644 doc/nut.index create mode 100644 doc/nut.qhp create mode 100644 doc/nut.qhp.sha1 create mode 100644 include.tar.gz mode change 100755 => 100644 include/header_copier create mode 100644 nut.qdocconf create mode 100644 src/query_p.h diff --git a/doc/nut.index b/doc/nut.index new file mode 100644 index 0000000..deb4f4e --- /dev/null +++ b/doc/nut.index @@ -0,0 +1,5 @@ + + + + + diff --git a/doc/nut.qhp b/doc/nut.qhp new file mode 100644 index 0000000..5d4c609 --- /dev/null +++ b/doc/nut.qhp @@ -0,0 +1,14 @@ + + + + + + +
+
+
+ + + + + diff --git a/doc/nut.qhp.sha1 b/doc/nut.qhp.sha1 new file mode 100644 index 0000000..d04ba34 --- /dev/null +++ b/doc/nut.qhp.sha1 @@ -0,0 +1 @@ +da39a3ee5e6b4b0d3255bfef95601890afd80709 \ No newline at end of file diff --git a/include.tar.gz b/include.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..b9d29848edc742c209d8dae9b2b6f79517b93f2a GIT binary patch literal 27816 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 literal 0 HcmV?d00001 diff --git a/include/header_copier b/include/header_copier old mode 100755 new mode 100644 diff --git a/nut.pri b/nut.pri index 82e7dbe..bd2f947 100644 --- a/nut.pri +++ b/nut.pri @@ -20,7 +20,8 @@ HEADERS += \ $$PWD/src/sqlitegenerator.h \ $$PWD/src/tablemodel.h \ $$PWD/src/sqlservergenerator.h \ - $$PWD/src/wherephrase.h + $$PWD/src/wherephrase.h \ + $$PWD/src/query_p.h SOURCES += \ $$PWD/src/database.cpp \ diff --git a/nut.qdocconf b/nut.qdocconf new file mode 100644 index 0000000..6134af9 --- /dev/null +++ b/nut.qdocconf @@ -0,0 +1,24 @@ +project = Nut +description = Advanced, Powerful and easy to use ORM for Qt5 +version = 0.1 + +outputdir = doc + +source += src/query.cpp +headerdirs += src +sourcedirs += src +exampledirs = . + + + +qhp.projects = Nut + +qhp.qtestclass.file = nut.qhp +qhp.qtestclass.namespace = org.kaj.nut.0.1 +qhp.qtestclass.virtualFolder = nut +qhp.qtestclass.indexTitle = nut +qhp.qtestclass.indexRoot = + +qhp.qtestclass.filterAttributes = nut 0.1 qtrefdoc +qhp.qtestclass.customFilters.Qt.name = qtestclass 0.1 +qhp.qtestclass.customFilters.Qt.filterAttributes = qtestclass 0.1 \ No newline at end of file diff --git a/src/database.cpp b/src/database.cpp index 4139022..75cc7f3 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -65,10 +65,11 @@ bool DatabasePrivate::open() qWarning(db.lastError().text().toLocal8Bit().data()); if(db.lastError().text().contains("database \"" + databaseName + "\" does not exist") - || db.lastError().text().contains("Cannot open database")){ + || db.lastError().text().contains("Cannot open database") + || db.lastError().text().contains("Unknown database '" + databaseName + "'")){ db.setDatabaseName(sqlGenertor->masterDatabaseName(databaseName)); ok = db.open(); - qInfo("Creating database"); + qDebug("Creating database"); if(ok){ db.exec("CREATE DATABASE " + databaseName); db.close(); @@ -89,18 +90,20 @@ bool DatabasePrivate::open() bool DatabasePrivate::updateDatabase() { + Q_Q(Database); + DatabaseModel last = getLastScheema(); DatabaseModel current = currentModel; if(last == current){ - qInfo("Databse is up-to-date"); + qDebug("Databse is up-to-date"); return true; } if(!last.count()) - qInfo("Databse is new"); + qDebug("Databse is new"); else - qInfo("Databse is changed"); + qDebug("Databse is changed"); QStringList sql = sqlGenertor->diff(last, current); db.transaction(); @@ -108,13 +111,26 @@ bool DatabasePrivate::updateDatabase() qDebug() << "going to exec " << s; db.exec(s); - if(!db.lastError().type() == QSqlError::NoError) + if(db.lastError().type() != QSqlError::NoError) qWarning(db.lastError().text().toLatin1().data()); } bool ok = db.commit(); if(ok){ storeScheemaInDB(); + + q->databaseUpdated(last.versionMajor(), last.versionMinor(), current.versionMajor(), current.versionMinor()); + QString versionText = QString::number(current.versionMajor()) + "_" + QString::number(current.versionMinor()); + + for(int i = 0; i < q->metaObject()->methodCount(); i++){ + QMetaMethod m = q->metaObject()->method(i); + if(m.name() == "update" + versionText){ + m.invoke(q, Qt::DirectConnection, + Q_ARG(int, current.versionMajor()), + Q_ARG(int, current.versionMinor())); + break; + } + } }else{ qWarning("Unable update database"); qWarning(db.lastError().text().toLatin1().data()); @@ -135,12 +151,12 @@ QVariantMap DatabasePrivate::getCurrectScheema() for(int i = 0; i < q->metaObject()->classInfoCount(); i++){ QMetaClassInfo ci = q->metaObject()->classInfo(i); - QString ciName = QString(ci.name()).replace(__nut_NAME_PERFIX, ""); + QString ciName = QString(ci.name()).replace(__nut_NAME_PERFIX, "").replace("\"", ""); if(ciName.startsWith(__nut_TABLE)) - tables.insert(QString(ci.name()).replace(__nut_NAME_PERFIX, "").split(" ").at(1), ci.value()); + tables.insert(ciName.split(" ").at(1), ci.value()); if(ciName == __nut_DB_VERSION){ - QStringList version = QString(ci.value()).split('.'); + QStringList version = QString(ci.value()).replace("\"", "").split('.'); bool ok = false; if(version.length() == 1){ currentModel.setVersionMajor(version.at(0).toInt(&ok)); @@ -150,14 +166,14 @@ QVariantMap DatabasePrivate::getCurrectScheema() } if(!ok) - qFatal("NUT_DB_VERSION macro accept version in format 'x' or 'x.y' only, and x[,y] must be integer values\n"); + qFatal("NUT_DB_VERSION macro accept version in format 'x' or 'x[.y]' only, and x,y must be integer values\n"); } } QVariantMap databaseVariant; for(int i = 1; i < q->metaObject()->propertyCount(); i++){ QMetaProperty tableProperty = q->metaObject()->property(i); - uint typeId = QMetaType::type(tableProperty.typeName()); + int typeId = QMetaType::type(tableProperty.typeName()); if(tables.values().contains(tableProperty.name()) && typeId >= QVariant::UserType){ TableModel *sch = new TableModel(typeId, tableProperty.name()); @@ -220,6 +236,12 @@ void DatabasePrivate::createChangeLogs() db.exec(diff); } + +/*! + * \class Database + * \brief Database class + */ + Database::Database(QObject *parent) : QObject(parent), d_ptr(new DatabasePrivate(this)) { Q_D(Database); @@ -268,6 +290,10 @@ QString Database::driver() const return d->driver; } +/*! + * \brief Database::model + * \return The model of this database + */ DatabaseModel Database::model() const { Q_D(const Database); @@ -328,6 +354,14 @@ SqlGeneratorBase *Database::sqlGenertor() const return d->sqlGenertor; } +void Database::databaseUpdated(int oldMajor, int oldMinor, int newMajor, int newMinor) +{ + Q_UNUSED(oldMajor); + Q_UNUSED(oldMinor); + Q_UNUSED(newMajor); + Q_UNUSED(newMinor); +} + bool Database::open() { Q_D(Database); @@ -357,9 +391,16 @@ bool Database::open() } } +void Database::close() +{ + Q_D(Database); + d->db.close(); +} + QSqlQuery Database::exec(QString sql) { Q_D(Database); + qDebug() <db.exec(sql); if(d->db.lastError().type() != QSqlError::NoError) qWarning(d->db.lastError().text().toLatin1().data()); diff --git a/src/database.h b/src/database.h index 91896da..18de998 100644 --- a/src/database.h +++ b/src/database.h @@ -45,6 +45,7 @@ public: Database(QObject *parent = 0); bool open(); + void close(); QSqlQuery exec(QString sql); @@ -65,6 +66,9 @@ public: SqlGeneratorBase *sqlGenertor() const; +protected: + virtual void databaseUpdated(int oldMajor, int oldMinor, int newMajor, int newMinor); + public slots: void setDatabaseName(QString databaseName); void setHostName(QString hostName); diff --git a/src/database_p.h b/src/database_p.h index 2382105..6ce02b0 100644 --- a/src/database_p.h +++ b/src/database_p.h @@ -27,6 +27,7 @@ #include +QT_BEGIN_NAMESPACE class DatabasePrivate { @@ -63,4 +64,6 @@ public: TableSet *changeLogs; }; +QT_END_NAMESPACE + #endif // DATABASE_P_H diff --git a/src/defines.h b/src/defines.h index 390e54d..d7bd16b 100644 --- a/src/defines.h +++ b/src/defines.h @@ -33,10 +33,10 @@ #endif // Database -#define NUT_DB_VERSION(major, minor) Q_CLASSINFO(__nut_NAME_PERFIX __nut_DB_VERSION, #major "." #minor) +#define NUT_DB_VERSION(major, minor) Q_CLASSINFO(QT_STRINGIFY(__nut_NAME_PERFIX __nut_DB_VERSION), QT_STRINGIFY(#major "." #minor)) #define NUT_DECLARE_TABLE(type, name) \ - Q_CLASSINFO(__nut_NAME_PERFIX __nut_TABLE " " #type, #name) \ + Q_CLASSINFO(QT_STRINGIFY(__nut_NAME_PERFIX __nut_TABLE " " #type), #name) \ Q_PROPERTY(type* name READ name) \ Q_PROPERTY(TableSet name##s READ name##s) \ type* m_##name; \ @@ -49,7 +49,7 @@ public: \ //Table #define NUT_DECLARE_FIELD(type, name, read, write) \ Q_PROPERTY(type name READ read WRITE write) \ - Q_CLASSINFO(__nut_NAME_PERFIX #name " " __nut_FIELD, #name) \ + Q_CLASSINFO(QT_STRINGIFY(__nut_NAME_PERFIX #name " " __nut_FIELD), #name) \ type m_##name; \ public: \ static FieldPhrase name##Field(){ \ @@ -67,7 +67,7 @@ public: \ #define NUT_FOREGION_KEY(type, keytype, name, read, write) \ Q_PROPERTY(type* name READ read WRITE write) \ NUT_DECLARE_FIELD(keytype, name##Id, read##Id, write##Id) \ - Q_CLASSINFO(__nut_NAME_PERFIX #name "Id " __nut_FOREGION_KEY, #type) \ + Q_CLASSINFO(QT_STRINGIFY(__nut_NAME_PERFIX #name "Id " __nut_FOREGION_KEY), #type) \ type *m_##name; \ public: \ type *read() const { return m_##name ; } \ @@ -89,14 +89,14 @@ public: \ #define NUT_INDEX(name, field, order) -#define NUT_PRIMARY_KEY(x) Q_CLASSINFO(__nut_NAME_PERFIX #x " " __nut_PRIMARY_KEY, #x) -#define NUT_AUTO_INCREMENT(x) Q_CLASSINFO(__nut_NAME_PERFIX #x " " __nut_AUTO_INCREMENT, #x) +#define NUT_PRIMARY_KEY(x) Q_CLASSINFO(QT_STRINGIFY(__nut_NAME_PERFIX #x " " __nut_PRIMARY_KEY), #x) +#define NUT_AUTO_INCREMENT(x) Q_CLASSINFO(QT_STRINGIFY(__nut_NAME_PERFIX #x " " __nut_AUTO_INCREMENT), #x) #define NUT_PRIMARY_AUTO_INCREMENT(x) NUT_PRIMARY_KEY(x) \ NUT_AUTO_INCREMENT(x) -#define NUT_UNIQUE(x) Q_CLASSINFO(__nut_NAME_PERFIX #x " " __nut_UNIQUE, #x) -#define NUT_LEN(x, n) Q_CLASSINFO(__nut_NAME_PERFIX #x " " __nut_LEN, #n) -#define NUT_DEFAULT_VALUE(x, n) Q_CLASSINFO(__nut_NAME_PERFIX #x " " __nut_DEFAULT_VALUE, #n) -#define NUT_NOT_NULL(x) Q_CLASSINFO(__nut_NAME_PERFIX #x " " __nut_NOT_NULL, "1") +#define NUT_UNIQUE(x) Q_CLASSINFO(QT_STRINGIFY(__nut_NAME_PERFIX #x " " __nut_UNIQUE), #x) +#define NUT_LEN(x, n) Q_CLASSINFO(QT_STRINGIFY(__nut_NAME_PERFIX #x " " __nut_LEN), #n) +#define NUT_DEFAULT_VALUE(x, n) Q_CLASSINFO(QT_STRINGIFY(__nut_NAME_PERFIX #x " " __nut_DEFAULT_VALUE), #n) +#define NUT_NOT_NULL(x) Q_CLASSINFO(QT_STRINGIFY(__nut_NAME_PERFIX #x " " __nut_NOT_NULL), "1") #ifndef NUT_NO_KEYWORDS # define FROM(x) /*QScopedPointer*/(x->createQuery()) @@ -111,4 +111,6 @@ public: \ # define FIRST() ->first() #endif // NUT_NO_KEYWORDS + + #endif // SYNTAX_DEFINES_H diff --git a/src/query.cpp b/src/query.cpp index de01254..4412232 100644 --- a/src/query.cpp +++ b/src/query.cpp @@ -1,4 +1,4 @@ -/************************************************************************** +/*!************************************************************************ ** ** This file is part of Nut project. ** https://github.com/HamedMasafi/Nut @@ -20,4 +20,48 @@ #include "query.h" +QT_BEGIN_NAMESPACE +QueryPrivate::QueryPrivate(QueryBase *parent) : q_ptr(parent), + joinClassName(QString::null) +{ + +} + +/*! + * \class Query + * \brief This class hold a query. A query can be used for getting database rows, editing or deleting without row fetching. + */ + +/*! + * \brief toList + * \param count Total rows must be returned + * \return This function return class itself + * This function return rows + */ + +/*! + * \brief setWhere + * \param where Where phrase + * \return This function return class itself + */ + +/*! + * \brief orderBy + * \param phrase Order phrase + * \return This function return class itself + * orderBy set a new order for this query. Order can be a hrase like that: + * \code + * query->orderBy(Post::idField()); + * \endcode + * If you need more than one order field seprate them by & operator, example: + * \code + * query->orderBy(Post::idField() & Post::bodyField()); + * \endcode + * Order can be also DESC, for that put exclamation mark near field + * \code + * query->orderBy(!Post::idField & Post::bodyField()); + * \endcode + */ + +QT_END_NAMESPACE diff --git a/src/query.h b/src/query.h index 99c32d3..029f675 100644 --- a/src/query.h +++ b/src/query.h @@ -26,6 +26,7 @@ #include #include +#include "query_p.h" #include "database.h" #include "databasemodel.h" #include "tablesetbase_p.h" @@ -38,74 +39,87 @@ QT_BEGIN_NAMESPACE template class NUT_EXPORT Query : public QueryBase { - QString _tableName; - QString _select; -// QString _where; - Database *_database; - TableSetBase *_tableSet; - QString _joinClassName; - QList _wheres; + QueryPrivate *d_ptr; + Q_DECLARE_PRIVATE(Query) + public: Query(Database *database, TableSetBase *tableSet); - - Query(TableSet *tset){ - _database = tset->database(); - _tableName = _database->tableName(T::staticMetaObject.className()); - } + ~Query(); QList toList(int count = -1); - T *first(); - int count(); int remove(); + T *first(); + + int count(); + + QVariant max(FieldPhrase &f); + QVariant min(FieldPhrase &f); + QVariant average(FieldPhrase &f){ + //TODO: ... + return QVariant(); + } Query *join(const QString &tableName); Query *setWhere(WherePhrase where); + + Query *join(Table *c){ + join(c->metaObject()->className()); + return this; + } + // Query *setWhere(const QString &where); Query *orderBy(QString fieldName, QString type); - -private: - static QHash _compiledCommands; - QString compileCommand(QString command); - QString queryText(); - QHash _orders; + Query *orderBy(WherePhrase phrase); }; -//template -//inline Query createQuery(TableSet *tset) -//{ -// return Query(tset); -//} - -template -QHash Query::_compiledCommands; - template Q_OUTOFLINE_TEMPLATE Query::Query(Database *database, TableSetBase *tableSet) : QueryBase(database), - _database(database), _tableSet(tableSet), _joinClassName(QString::null) + d_ptr(new QueryPrivate(this)) { - _tableName = _database->tableName(T::staticMetaObject.className()); + Q_D(Query); + + d->database = database; + d->tableSet = tableSet; + d->tableName = d->database->tableName(T::staticMetaObject.className()); +} + +template +Q_OUTOFLINE_TEMPLATE Query::~Query() +{ + qDebug() << "Query::~Query()"; + Q_D(Query); + delete d; } template Q_OUTOFLINE_TEMPLATE QList Query::toList(int count) { + Q_D(Query); QList result; - _select = "*"; - qDebug()<exec(_database->sqlGenertor()->selectCommand(_wheres, _orders, _tableName, _joinClassName)); + d->select = "*"; - QString pk =_database->model().model(_tableName)->primaryKey(); +// QSqlQuery q = d->database->exec(d->database->sqlGenertor()->selectCommand(d->wheres, d->orders, d->tableName, d->joinClassName)); + QSqlQuery q = d->database->exec(d->database->sqlGenertor()->selectCommand( + SqlGeneratorBase::SelectALl, + "", + d->wheres, + d->orderPhrases, + d->tableName, + d->joinClassName)); + + QString pk =d->database->model().model(d->tableName)->primaryKey(); QVariant lastPkValue = QVariant(); int childTypeId = 0; T *lastRow = 0; TableSetBase *childTableSet; - QStringList masterFields = _database->model().model(_tableName)->fieldsNames(); + QStringList masterFields = d->database->model().model(d->tableName)->fieldsNames(); QStringList childFields; - if(!_joinClassName.isNull()){ - childFields = _database->model().modelByClass(_joinClassName)->fieldsNames(); - QString joinTableName = _database->tableName(_joinClassName); - childTypeId = _database->model().model(joinTableName)->typeId(); - } + if(!d->joinClassName.isNull()) + if(d->database->model().modelByClass(d->joinClassName)){ + childFields = d->database->model().modelByClass(d->joinClassName)->fieldsNames(); + QString joinTableName = d->database->tableName(d->joinClassName); + childTypeId = d->database->model().model(joinTableName)->typeId(); + } while (q.next()) { if(lastPkValue != q.value(pk)){ @@ -114,7 +128,7 @@ Q_OUTOFLINE_TEMPLATE QList Query::toList(int count) foreach (QString field, masterFields) t->setProperty(field.toLatin1().data(), q.value(field)); - t->setTableSet(_tableSet); + t->setTableSet(d->tableSet); t->setStatus(Table::FeatchedFromDB); t->setParent(this); @@ -124,7 +138,7 @@ Q_OUTOFLINE_TEMPLATE QList Query::toList(int count) if(childTypeId){ QSet tableSets = t->tableSets; foreach (TableSetBase *ts, tableSets) - if(ts->childClassName() == _joinClassName) + if(ts->childClassName() == d->joinClassName) childTableSet = ts; } } @@ -166,8 +180,33 @@ Q_OUTOFLINE_TEMPLATE T *Query::first() template Q_OUTOFLINE_TEMPLATE int Query::count() { - _select = "COUNT(*)"; - QSqlQuery q = _database->exec(queryText()); + Q_D(Query); + + d->select = "COUNT(*)"; + QSqlQuery q = d->database->exec(d->database->sqlGenertor()->selectCommand("COUNT(*)", d->wheres, d->orders, d->tableName, d->joinClassName)); + + if(q.next()) + return q.value(0).toInt(); + return 0; +} + +template +Q_OUTOFLINE_TEMPLATE QVariant Query::max(FieldPhrase &f){ + Q_D(Query); + + QSqlQuery q = d->database->exec(d->database->sqlGenertor()->selectCommand("MAX(" + f.data()->text + ")", d->wheres, d->orders, d->tableName, d->joinClassName)); + + if(q.next()) + return q.value(0).toInt(); + return 0; +} + +template +Q_OUTOFLINE_TEMPLATE QVariant Query::min(FieldPhrase &f){ + Q_D(Query); + + QSqlQuery q = d->database->exec(d->database->sqlGenertor()->selectCommand("MIN(" + f.data()->text + ")", d->wheres, d->orders, d->tableName, d->joinClassName)); + if(q.next()) return q.value(0).toInt(); return 0; @@ -176,119 +215,45 @@ Q_OUTOFLINE_TEMPLATE int Query::count() template Q_OUTOFLINE_TEMPLATE int Query::remove() { - QString sql = _database->sqlGenertor()->deleteCommand(_wheres, _tableName); -// _database->sqlGenertor()->deleteRecords(_tableName, queryText()); + Q_D(Query); + + QString sql = d->database->sqlGenertor()->deleteCommand(d->wheres, d->tableName); +// d->_database->sqlGenertor()->deleteRecords(_tableName, queryText()); // sql = compileCommand(sql); - QSqlQuery q = _database->exec(sql); + QSqlQuery q = d->database->exec(sql); return q.numRowsAffected(); } template Q_OUTOFLINE_TEMPLATE Query *Query::join(const QString &tableName) { - _joinClassName = tableName; + Q_D(Query); + d->joinClassName = tableName; return this; } template Q_OUTOFLINE_TEMPLATE Query *Query::setWhere(WherePhrase where) { - _wheres.append(where); + Q_D(Query); + d->wheres.append(where); return this; } -//template -//Q_OUTOFLINE_TEMPLATE Query *Query::setWhere(const QString &where) -//{ -// _where = where; -// return this; -//} - template Q_OUTOFLINE_TEMPLATE Query *Query::orderBy(QString fieldName, QString type) { - _orders.insert(fieldName, type); + Q_D(Query); + d->orders.insert(fieldName, type); return this; } template -Q_OUTOFLINE_TEMPLATE QString Query::compileCommand(QString command) +Q_OUTOFLINE_TEMPLATE Query *Query::orderBy(WherePhrase phrase) { - if(!_compiledCommands.contains(command)){ - QString q = command - .replace("::", ".") - .replace("()", "") - .replace("==", "=") - .replace("!=", "<>"); - - QRegularExpression r("(\\w+)\\.(\\w+)"); - QRegularExpressionMatchIterator i = r.globalMatch(command); - while (i.hasNext()) { - QRegularExpressionMatch match = i.next(); - QString tableName = match.captured(1); - QString fieldName = match.captured(2); - tableName = _database->tableName(tableName); - q = command.replace(match.captured(), tableName + "." + fieldName); - } - _compiledCommands.insert(command, q); - } - - return _compiledCommands[command]; -} - -template -Q_OUTOFLINE_TEMPLATE QString Query::queryText() -{ - QStringList orderby; - QString q = "";//compileCommand(_where); - foreach (WherePhrase p, _wheres) { - if(q != "") - q.append(" AND "); - q.append(p.command(_database->sqlGenertor())); - } - - QString t = _tableName; - if(!_joinClassName.isNull()){ - QString joinTableName = _database->tableName(_joinClassName); - RelationModel *rel = _database->model().relationByTableNames(_tableName, joinTableName); - if(rel){ - QString pk = _database->model().model(_tableName)->primaryKey(); - t = QString("%1 INNER JOIN %2 ON (%1.%3 = %2.%4)") - .arg(_tableName) - .arg(joinTableName) - .arg(pk) - .arg(rel->localColumn); - orderby.append(_tableName + "." + pk); - }else{ - qWarning(QString("Relation between table %1 and class %2 (%3) not exists!") - .arg(_tableName) - .arg(_joinClassName) - .arg(joinTableName.isNull() ? "NULL" : joinTableName) - .toLatin1().data()); - _joinClassName = QString::null; - } - } - - QString orderText = ""; - if(_orders.count()) - foreach (QString o, _orders.keys()) - orderby.append(o + " " + _orders.value(o)); - - if(orderby.count()) - orderText = " ORDER BY " + orderby.join(", "); - - QString command = QString("SELECT %1 FROM %2 %3%4") - .arg(_select) - .arg(t) - .arg(q.isEmpty() ? "" : "WHERE " + q) - .arg(orderText); - - for(int i = 0; i < _database->model().count(); i++) - command = command.replace(_database->model().at(i)->className() + "." , _database->model().at(i)->name() + "."); - - qDebug() << command - << _database->sqlGenertor()->selectCommand(_wheres, _orders, _tableName, _joinClassName); - return command; + Q_D(Query); + d->orderPhrases.append(phrase); + return this; } QT_END_NAMESPACE diff --git a/src/query_p.h b/src/query_p.h new file mode 100644 index 0000000..3c01804 --- /dev/null +++ b/src/query_p.h @@ -0,0 +1,49 @@ +/************************************************************************** +** +** This file is part of Nut project. +** https://github.com/HamedMasafi/Nut +** +** Nut is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Nut is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with Nut. If not, see . +** +**************************************************************************/ + +#ifndef QUERY_P_H +#define QUERY_P_H + +#include "wherephrase.h" + +#include +#include + +class Database; +class TableSetBase; +//template +class QueryBase; +class QueryPrivate{ + QueryBase *q_ptr; + Q_DECLARE_PUBLIC(QueryBase) + +public: + QueryPrivate(QueryBase *parent); + QString tableName; + QString select; + Database *database; + TableSetBase *tableSet; + QString joinClassName; + QList wheres; + QHash orders; + QList orderPhrases; +}; + +#endif // QUERY_P_H diff --git a/src/sqlgeneratorbase.cpp b/src/sqlgeneratorbase.cpp index ef3ac62..e790775 100644 --- a/src/sqlgeneratorbase.cpp +++ b/src/sqlgeneratorbase.cpp @@ -28,6 +28,7 @@ #include "sqlgeneratorbase_p.h" #include "table.h" #include "tablemodel.h" +#include "wherephrase.h" QT_BEGIN_NAMESPACE @@ -174,9 +175,16 @@ QString SqlGeneratorBase::insertRecord(Table *t, QString tableName) if(f != key) values.append("'" + t->property(f.toLatin1().data()).toString() + "'"); + QString changedPropertiesText = ""; + QSet props = t->changedProperties(); + foreach (QString s, props) { + if(changedPropertiesText != "") + changedPropertiesText.append(", "); + changedPropertiesText.append(s); + } sql = QString("INSERT INTO %1 (%2) VALUES (%3)") .arg(tableName) - .arg(t->changedProperties().toList().join(", ")) + .arg(changedPropertiesText) .arg(values.join(", ")); return sql; @@ -208,6 +216,61 @@ QString SqlGeneratorBase::deleteRecord(Table *t, QString tableName) .arg(t->primaryValue().toString()); } +QString SqlGeneratorBase::agregateText(const AgregateType &t, const QString &arg) const +{ + switch (t) { + case SelectALl: + return "*"; + break; + + case Min: + return "MIN(" + arg + ")"; + break; + + case Max: + return "MAX(" + arg + ")"; + break; + + case Average: + return "AVERAGE(" + arg + ")"; + break; + + case Count: + return "COUNT(" + arg + ")"; + break; + + default: + return QString::null; + } +} + +QString SqlGeneratorBase::fromTableText(const QString &tableName, QString &joinClassName, QString &orderBy) const +{ + QString tableNameText = tableName; + if(!joinClassName.isNull()){ + QString joinTableName = _database->tableName(joinClassName); + RelationModel *rel = _database->model().relationByTableNames(tableName, joinTableName); + if(rel){ + QString pk = _database->model().model(tableName)->primaryKey(); + tableNameText = QString("%1 INNER JOIN %2 ON (%1.%3 = %2.%4)") + .arg(tableName) + .arg(joinTableName) + .arg(pk) + .arg(rel->localColumn); + orderBy = tableName + "." + pk; + }else{ + qWarning(QString("Relation between table %1 and class %2 (%3) not exists!") + .arg(tableName) + .arg(joinClassName) + .arg(joinTableName.isNull() ? "NULL" : joinTableName) + .toLatin1().data()); + joinClassName = QString::null; + } + } + + return tableNameText; +} + QString SqlGeneratorBase::deleteRecords(QString tableName, QString where) { QString sql = ""; @@ -218,60 +281,70 @@ QString SqlGeneratorBase::deleteRecords(QString tableName, QString where) return sql; } -QString SqlGeneratorBase::escapeFieldValue(QVariant &field) const +QString SqlGeneratorBase::selectCommand(SqlGeneratorBase::AgregateType t, QString agregateArg, QList &wheres, QList &orders, QString tableName, QString joinClassName) { - switch (field.type()) { - case QVariant::Int: - case QVariant::Double: - return field.toString(); - break; + QString select = agregateText(t, agregateArg); + QString where = createWhere(wheres); + QString order = ""; + QString from = fromTableText(tableName, joinClassName, order); - case QVariant::String: - return "'" + field.toString() + "'"; - - case QVariant::DateTime: - return "'" + field.toDateTime().toString(Qt::ISODate) + "'"; - - case QVariant::Date: - return "'" + field.toDate().toString(Qt::ISODate) + "'"; - - case QVariant::Time: - return "'" + field.toTime().toString(Qt::ISODate) + "'"; - - case QVariant::StringList: - case QVariant::List: - return "['" + field.toStringList().join("', '") + "']"; - - case QVariant::Invalid: - qFatal("Invalud field value"); - return ""; - - default: - return ""; + foreach(WherePhrase p, orders){ + if(order != "") + order.append(", "); + order.append(phraseOrder(p.data())); } + + QString sql = "SELECT " + select + " FROM " + from; + + if(where != "") + sql.append(" WHERE " + where); + + if(order != "") + sql.append(" ORDER BY " + order); + + for(int i = 0; i < _database->model().count(); i++) + sql = sql.replace(_database->model().at(i)->className() + "." , _database->model().at(i)->name() + "."); + + qDebug() << "new sql" << sql; + return sql; +} + +QString SqlGeneratorBase::selectCommand(QList &wheres, QHash &orders, QString tableName, QString joinClassName) +{ + return selectCommand("*", wheres, orders, tableName, joinClassName); } QString SqlGeneratorBase::createWhere(QList &wheres) { QString whereText = ""; - foreach (WherePhrase p, wheres) { +// for (int i = 0; i < wheres.count(); i++) { +// if(whereText != "") +// whereText.append(" AND "); +// whereText.append(phrase(wheres[i].data())); +// } + foreach (WherePhrase w, wheres) { if(whereText != "") whereText.append(" AND "); - whereText.append(p.command(this)); + + whereText.append(phrase(w.data())); } - if(whereText != "") - whereText.prepend(" WHERE "); +qDebug() << "WHWRE="<< whereText; +// if(whereText != "") +// whereText.prepend(" WHERE "); return whereText; } -QString SqlGeneratorBase::selectCommand(QList &wheres, QHash &orders, QString tableName, QString joinClassName) +QString SqlGeneratorBase::selectCommand(QString selectPhrase, QList &wheres, QHash &orders, QString tableName, QString joinClassName) { QString orderText = ""; QStringList orderby; QString whereText = createWhere(wheres); + if(whereText != "") + whereText.prepend(" WHERE "); + QString tableNameText = tableName; if(!joinClassName.isNull()){ QString joinTableName = _database->tableName(joinClassName); @@ -302,27 +375,184 @@ QString SqlGeneratorBase::selectCommand(QList &wheres, QHashmodel().count(); i++) command = command.replace(_database->model().at(i)->className() + "." , _database->model().at(i)->name() + "."); + + qDebug() << command; return command; } QString SqlGeneratorBase::deleteCommand(QList &wheres, QString tableName) { - QString command = "DELETE FROM " - + tableName - + createWhere(wheres); + QString command = "DELETE FROM " + tableName; + QString where = createWhere(wheres); + + if(where != "") + command.append(" WHERE " + where); for(int i = 0; i < _database->model().count(); i++) command = command.replace(_database->model().at(i)->className() + "." , _database->model().at(i)->name() + "."); return command; } +QString SqlGeneratorBase::escapeValue(const QVariant &v)const +{ + switch (v.type()) { + case QVariant::Int: + case QVariant::UInt: + case QVariant::ULongLong: + case QVariant::LongLong: + case QVariant::Double: + return v.toString(); + break; + + case QVariant::Char: + case QVariant::String: + return "'" + v.toString() + "'"; + + case QVariant::DateTime: + return "'" + v.toDateTime().toString() + "'"; + + case QVariant::Date: + return "'" + v.toDate().toString() + "'"; + + case QVariant::Time: + return "'" + v.toTime().toString() + "'"; + + case QVariant::StringList: + case QVariant::List: + return "['" + v.toStringList().join("', '") + "']"; + + case QVariant::Invalid: + qFatal("Invalud field value"); + return ""; + } + return ""; +} + +QString SqlGeneratorBase::phraseOrder(const PhraseData *d) const +{ + + QString ret = ""; + + switch(d->type){ + case PhraseData::Field: + if(d->operatorCond == PhraseData::Not) + ret = d->text + " DESC"; + else + ret = d->text; + break; + + case PhraseData::WithOther: + if(d->operatorCond != PhraseData::Append) + qFatal("Order phease can only have & operator"); + + ret = phraseOrder(d->left) + ", " + phraseOrder(d->right); + break; + + case PhraseData::WithoutOperand: + case PhraseData::WithVariant: + break; + } + + return ret; +} + +QString SqlGeneratorBase::phrase(const PhraseData *d) const +{ + QString ret = ""; + + qDebug() << "type"<type; + switch(d->type){ + case PhraseData::Field: + ret = d->text; + break; + + case PhraseData::WithVariant: + ret = phrase(d->left) + " " + operatorString(d->operatorCond) + " " + escapeValue(d->operand); + break; + + case PhraseData::WithOther: + ret = phrase(d->left) + " " + operatorString(d->operatorCond) + " " + phrase(d->right); + break; + + case PhraseData::WithoutOperand: + ret = phrase(d->left) + " " + operatorString(d->operatorCond); + break; + + default: + ret = ""; + } + + if(d->operatorCond == PhraseData::And || d->operatorCond == PhraseData::Or) + ret = "(" + ret + ")"; + + return ret; +} + +QString SqlGeneratorBase::operatorString(const PhraseData::Condition &cond) const +{ + switch (cond){ + case PhraseData::Equal: + return "="; + case PhraseData::NotEqual: + return "<>"; + case PhraseData::Less: + return "<"; + case PhraseData::Greater: + return ">"; + case PhraseData::LessEqual: + return "<="; + case PhraseData::GreaterEqual: + return ">="; + case PhraseData::Null: + return "IS NULL"; + + case PhraseData::NotNull: + return "IS NOT NULL"; + + case PhraseData::In: + return "IN"; + + case PhraseData::NotIn: + return "NOT IN"; + + case PhraseData::And: + return "AND"; + case PhraseData::Or: + return "OR"; + + case PhraseData::Like: + return "LIKE"; + case PhraseData::NotLike: + return "NOT LIKE"; + + case PhraseData::Add: + return "+"; + case PhraseData::Minus: + return "-"; + case PhraseData::Multiple: + return "*"; + case PhraseData::Divide: + return "/"; + + case PhraseData::Set: + return "="; + + case PhraseData::Append: + return ","; + } + + return QString(""); +} + QT_END_NAMESPACE diff --git a/src/sqlgeneratorbase_p.h b/src/sqlgeneratorbase_p.h index 0390dc6..06102b2 100644 --- a/src/sqlgeneratorbase_p.h +++ b/src/sqlgeneratorbase_p.h @@ -24,6 +24,7 @@ #include #include #include +#include "wherephrase.h" QT_BEGIN_NAMESPACE @@ -31,8 +32,9 @@ class Table; struct FieldModel; class DatabaseModel; class TableModel; -class WherePhrase; class Database; +//struct PhraseData; +//class WherePhrase; class SqlGeneratorBase : public QObject { // Q_OBJECT @@ -45,6 +47,13 @@ public: Update, Delete }; + enum AgregateType{ + SelectALl, + Count, + Min, + Max, + Average + }; SqlGeneratorBase(Database *parent); virtual ~SqlGeneratorBase(); @@ -58,23 +67,35 @@ public: virtual QString diff(FieldModel *oldField, FieldModel *newField); virtual QString diff(TableModel *oldTable, TableModel *newTable); - virtual QString saveRecord(Table *t, QString tableName); virtual QString insertRecord(Table *t, QString tableName); virtual QString updateRecord(Table *t, QString tableName); virtual QString deleteRecord(Table *t, QString tableName); + virtual QString deleteRecords(QString tableName, QString where); - virtual QString escapeFieldValue(QVariant &field) const; + virtual QString selectCommand(AgregateType t, QString agregateArg, + QList &wheres, QList &orders, + QString tableName, QString joinClassName); virtual QString selectCommand(QList &wheres, QHash &orders, QString tableName, QString joinClassName); + virtual QString selectCommand(QString selectPhrase, + QList &wheres, QHash &orders, + QString tableName, QString joinClassName); virtual QString deleteCommand(QList &wheres, QString tableName); + virtual QString escapeValue(const QVariant &v) const; + virtual QString phrase(const PhraseData *d) const; + virtual QString operatorString(const PhraseData::Condition &cond) const; + private: + QString agregateText(const AgregateType &t, const QString &arg = QString::null) const; + QString fromTableText(const QString &tableName, QString &joinClassName, QString &orderBy) const; QString createWhere(QList &wheres); + QString phraseOrder(const PhraseData *d) const; }; QT_END_NAMESPACE diff --git a/src/sqlservergenerator.cpp b/src/sqlservergenerator.cpp index ef35b11..77bc8d1 100644 --- a/src/sqlservergenerator.cpp +++ b/src/sqlservergenerator.cpp @@ -102,4 +102,12 @@ QString SqlServerGenerator::diff(FieldModel *oldField, FieldModel *newField) return sql; } +QString SqlServerGenerator::escapeValue(const QVariant &v) const +{ + if(v.type() == QVariant::String || v.type() == QVariant::Char) + return "N'" + v.toString() + "'"; + else + return SqlGeneratorBase::escapeValue(v); +} + QT_END_NAMESPACE diff --git a/src/sqlservergenerator.h b/src/sqlservergenerator.h index c71f21b..71a6825 100644 --- a/src/sqlservergenerator.h +++ b/src/sqlservergenerator.h @@ -35,6 +35,8 @@ public: QString fieldType(FieldModel *field); QString diff(FieldModel *oldField, FieldModel *newField); + + QString escapeValue(const QVariant &v) const; }; QT_END_NAMESPACE diff --git a/src/tablemodel.cpp b/src/tablemodel.cpp index de22f6e..d92093d 100644 --- a/src/tablemodel.cpp +++ b/src/tablemodel.cpp @@ -148,6 +148,7 @@ TableModel::TableModel(int typeId, QString tableName) // get fields names for(int j = 0; j < tableMetaObject->classInfoCount(); j++){ QString name = tableMetaObject->classInfo(j).name(); + name = name.replace("\"", ""); name = name.remove(__nut_NAME_PERFIX); @@ -181,8 +182,8 @@ TableModel::TableModel(int typeId, QString tableName) QString name = tableMetaObject->classInfo(j).name(); QString value = tableMetaObject->classInfo(j).value(); - name = name.remove(__nut_NAME_PERFIX); - + name = name.replace("\"", "").remove(__nut_NAME_PERFIX); + value = value.replace("\"", ""); if(name.contains(" ")){ QStringList parts = name.split(" "); diff --git a/src/wherephrase.cpp b/src/wherephrase.cpp index 0a442d1..ef08143 100644 --- a/src/wherephrase.cpp +++ b/src/wherephrase.cpp @@ -28,327 +28,249 @@ QT_BEGIN_NAMESPACE PhraseData::PhraseData(const char *className, const char *s){ text = QString(className) + "." + s; type = Field; + qDebug() << "(" << this << ")" << "Data type 0"; } PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o) : left(l){ operatorCond = o; type = WithoutOperand; + qDebug() << "(" << this << ")" << "Data type 1"; } PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o, const PhraseData *r) : left(l), right(r){ operatorCond = o; type = WithOther; + qDebug() << "(" << this << ")" << "Data type 2"; } PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o, QVariant r) : left(l), operand(r){ operatorCond = o; type = WithVariant; + qDebug() << "(" << this << ")" << "Data type 1"; } PhraseData::~PhraseData(){ - // if(type == WithOther){ - // delete left; - // delete right; - // } - // if(type == WithVariant){ - //// qDebug() << operator - // delete left; -} - -QString PhraseData::operatorString() const -{ - switch (operatorCond){ - case PhraseData::Equal: - return "="; - case PhraseData::NotEqual: - return "<>"; - case PhraseData::Less: - return "<"; - case PhraseData::Greater: - return ">"; - case PhraseData::LessEqual: - return "<="; - case PhraseData::GreaterEqual: - return ">="; - case PhraseData::Null: - return "IS NULL"; - - case PhraseData::NotNull: - return "IS NOT NULL"; - - case PhraseData::In: - return "IN"; - - case PhraseData::NotIn: - return "NOT IN"; - - case PhraseData::And: - return "AND"; - case PhraseData::Or: - return "OR"; - - case PhraseData::Like: - return "LIKE"; - case PhraseData::NotLike: - return "NOT LIKE"; - - case PhraseData::Add: - return "+"; - case PhraseData::Minus: - return "-"; - case PhraseData::Multiple: - return "*"; - case PhraseData::Divide: - return "/"; - - case PhraseData::Set: - return "="; - - case PhraseData::Append: - return ","; + qDebug() << "(" << this << ")" << "Data Deleting..." << type; + if(type == WithOther){ + qDebug() << " - Other" << left << right; + delete left; + delete right; } - - return QString(""); -} - -QString PhraseData::escapeVariant() const -{ - switch (operand.type()) { - case QVariant::Int: - case QVariant::Double: - return operand.toString(); - break; - - case QVariant::String: - return "'" + operand.toString() + "'"; - - case QVariant::DateTime: - return "'" + operand.toDateTime().toString() + "'"; - - case QVariant::Date: - return "'" + operand.toDate().toString() + "'"; - - case QVariant::Time: - return "'" + operand.toTime().toString() + "'"; - - case QVariant::StringList: - case QVariant::List: - return "['" + operand.toStringList().join("', '") + "']"; - - case QVariant::Invalid: - return ""; - - default: - return ""; + if(type == WithVariant){ + qDebug() << " - Variant" << left; + if(left) + delete left; } } -QString PhraseData::command(SqlGeneratorBase *generator) const +PhraseData *WherePhrase::data() const { - QString ret = ""; + return _data; +} - switch(type){ - case Field: - ret = text; - break; +WherePhrase::WherePhrase(const char *className, const char *s) +{ + qDebug() << "(" << this << ")" << "class ctor" << className << s; + _data = new PhraseData(className, s); +} - case WithVariant: - ret = left->command(generator) + " " + operatorString() + " " + escapeVariant(); - break; +WherePhrase::WherePhrase(const WherePhrase &l) +{ + _data = l._data; + // l._data = 0; + qDebug() << "(" << this << ")" << "Copy ctor, from" << _data << (&l); + _dataPointer = QSharedPointer(l._dataPointer); +} - case WithOther: - ret = left->command(generator) + " " + operatorString() + " " + right->command(generator); - break; +WherePhrase::WherePhrase(WherePhrase *l) +{ + _data = l->_data; - case WithoutOperand: - ret = left->command(generator) + " " + operatorString(); - break; - } + qDebug() << "(" << this << ")" << "From pointer" << _data; +// _dataPointer = QSharedPointer(_data); + l->_data = 0; + l->_dataPointer.reset(0); +} - if(operatorCond == PhraseData::And || operatorCond == PhraseData::Or) - ret = "(" + ret + ")"; - - return ret; +WherePhrase::WherePhrase(WherePhrase *l, PhraseData::Condition o) +{ + _data = new PhraseData(l->_data, o); +// _dataPointer = QSharedPointer(_data); + l->_data = 0; + qDebug() << "(" << this << ")" << "From cond, " << _data << o; + l->_dataPointer.reset(0); } -WherePhrase::WherePhrase(const char *className, const char *s) : willDeleteData(false) +WherePhrase::WherePhrase(WherePhrase *l, PhraseData::Condition o, WherePhrase *r) { - data = new PhraseData(className, s); - text = QString(className) + "." + s; + _data = new PhraseData(l->_data, o, r->_data); +// _dataPointer = QSharedPointer(_data); + l->_data = 0; + r->_data = 0; + + qDebug() << "(" << this << ")" << "From two pointer" << _data; + l->_dataPointer.reset(0); + r->_dataPointer.reset(0); } -WherePhrase::WherePhrase(PhraseData *l) : willDeleteData(false) +WherePhrase::WherePhrase(WherePhrase *l, PhraseData::Condition o, QVariant r) { - data = l; -} + _data = new PhraseData(l->_data, o, r); +// _dataPointer = QSharedPointer(_data); + l->_data = 0; -WherePhrase::WherePhrase(PhraseData *l, PhraseData::Condition o) : willDeleteData(false) -{ - data = new PhraseData(l, o); -} - - -WherePhrase::WherePhrase(PhraseData *l, PhraseData::Condition o, PhraseData *r) : willDeleteData(false) -{ - data = new PhraseData(l, o, r); -} - -WherePhrase::WherePhrase(PhraseData *l, PhraseData::Condition o, QVariant r) : willDeleteData(false) -{ - data = new PhraseData(l, o, r); + qDebug() << "(" << this << ")" << "From variant," << _data << l << r; + l->_dataPointer.reset(0); } WherePhrase::~WherePhrase() { -// if(willDeleteData) -// delete data; + qDebug() << "(" << this << ")" << "Dtor" << _data << _dataPointer.data(); + // if(_data){ + // delete _data; + // qDebug() << "deleted"; + // } } - -QString WherePhrase::command(SqlGeneratorBase *generator) +WherePhrase WherePhrase::operator ==(const WherePhrase &other) { - willDeleteData = true; - return data->command(generator); + return WherePhrase(this, PhraseData::Equal, (WherePhrase*)&other); } -void WherePhrase::deleteData(PhraseData *d) +WherePhrase WherePhrase::operator !=(const WherePhrase &other) { - deleteData(d); - if(d->type == PhraseData::WithOther){ - delete d->left; - delete d->right; - } - if(d->type == PhraseData::WithVariant) - delete d->left; + return WherePhrase(this, PhraseData::NotEqual, (WherePhrase*)&other); } -WherePhrase WherePhrase::operator ==(const WherePhrase &other){ - return WherePhrase(this->data, PhraseData::Equal, other.data); +WherePhrase WherePhrase::operator <(const WherePhrase &other) +{ + return WherePhrase(this, PhraseData::Less, (WherePhrase*)&other); } -WherePhrase WherePhrase::operator !=(const WherePhrase &other){ - return WherePhrase(this->data, PhraseData::NotEqual, other.data); +WherePhrase WherePhrase::operator >(const WherePhrase &other) +{ + return WherePhrase(this, PhraseData::Greater, (WherePhrase*)&other); } -WherePhrase WherePhrase::operator <(const WherePhrase &other){ - return WherePhrase(this->data, PhraseData::Less, other.data); +WherePhrase WherePhrase::operator <=(const WherePhrase &other) +{ + return WherePhrase(this, PhraseData::LessEqual, (WherePhrase*)&other); } -WherePhrase WherePhrase::operator >(const WherePhrase &other){ - return WherePhrase(this->data, PhraseData::Greater, other.data); -} - -WherePhrase WherePhrase::operator <=(const WherePhrase &other){ - return WherePhrase(this->data, PhraseData::LessEqual, other.data); -} - -WherePhrase WherePhrase::operator >=(const WherePhrase &other){ - return WherePhrase(this->data, PhraseData::GreaterEqual, other.data); +WherePhrase WherePhrase::operator >=(const WherePhrase &other) +{ + return WherePhrase(this, PhraseData::GreaterEqual, (WherePhrase*)&other); } WherePhrase WherePhrase::operator =(const WherePhrase &other) { - return WherePhrase(this->data, PhraseData::Set, other.data); + return WherePhrase(this, PhraseData::Set, (WherePhrase*)&other); } -WherePhrase WherePhrase::operator +(const WherePhrase &other){ - return WherePhrase(this->data, PhraseData::Add, other.data); +WherePhrase WherePhrase::operator +(const WherePhrase &other) +{ + return WherePhrase(this, PhraseData::Add, (WherePhrase*)&other); } -WherePhrase WherePhrase::operator -(const WherePhrase &other){ - return WherePhrase(this->data, PhraseData::Minus, other.data); +WherePhrase WherePhrase::operator -(const WherePhrase &other) +{ + return WherePhrase(this, PhraseData::Minus, (WherePhrase*)&other); } -WherePhrase WherePhrase::operator *(const WherePhrase &other){ - return WherePhrase(this->data, PhraseData::Multiple, other.data); +WherePhrase WherePhrase::operator *(const WherePhrase &other) +{ + return WherePhrase(this, PhraseData::Multiple, (WherePhrase*)&other); } -WherePhrase WherePhrase::operator /(const WherePhrase &other){ - return WherePhrase(this->data, PhraseData::Divide, other.data); +WherePhrase WherePhrase::operator /(const WherePhrase &other) +{ + return WherePhrase(this, PhraseData::Divide, (WherePhrase*)&other); } -WherePhrase WherePhrase::operator &&(const WherePhrase &other){ - return WherePhrase(this->data, PhraseData::And, other.data); +WherePhrase WherePhrase::operator &&(const WherePhrase &other) +{ + return WherePhrase(this, PhraseData::And, (WherePhrase*)&other); } -WherePhrase WherePhrase::operator ||(const WherePhrase &other){ - return WherePhrase(this->data, PhraseData::Or, other.data); +WherePhrase WherePhrase::operator ||(const WherePhrase &other) +{ + return WherePhrase(this, PhraseData::Or, (WherePhrase*)&other); } WherePhrase WherePhrase::operator &(const WherePhrase &other) { - return WherePhrase(this->data, PhraseData::Append, other.data); + qDebug() << "append" << this << (&other); + return WherePhrase(this, PhraseData::Append, (WherePhrase*)&other); } -WherePhrase FieldPhrase::operator !(){ - if(data->operatorCond < 20) - data->operatorCond = (PhraseData::Condition)((data->operatorCond + 10) % 20); +WherePhrase FieldPhrase::operator !() +{ + if(_data->operatorCond < 20) + _data->operatorCond = (PhraseData::Condition)((_data->operatorCond + 10) % 20); else qFatal("Operator ! can not aplied to non condition statements"); - return WherePhrase(data); + return this;//WherePhrase(this, PhraseData::Not); } -WherePhrase WherePhrase::operator ==(const QVariant &other){ - return WherePhrase(this->data, PhraseData::Equal, other); -} - -WherePhrase WherePhrase::operator !=(const QVariant &other){ - return WherePhrase(this->data, PhraseData::NotEqual, other); -} - -WherePhrase WherePhrase::operator <(const QVariant &other){ - return WherePhrase(this->data, PhraseData::Less, other); -} - -WherePhrase WherePhrase::operator >(const QVariant &other){ - qDebug() << "var"; - return WherePhrase(this->data, PhraseData::Greater, other); -} - -WherePhrase WherePhrase::operator <=(const QVariant &other){ - return WherePhrase(this->data, PhraseData::LessEqual, other); -} - -WherePhrase WherePhrase::operator >=(const QVariant &other){ - return WherePhrase(this->data, PhraseData::GreaterEqual, other); -} - -WherePhrase FieldPhrase::operator =(const QVariant &other) +WherePhrase WherePhrase::operator ==(const QVariant &other) { - return WherePhrase(this->data, PhraseData::Set, other); + return WherePhrase(this, PhraseData::Equal, other); +} + +WherePhrase WherePhrase::operator !=(const QVariant &other) +{ + return WherePhrase(this, PhraseData::NotEqual, other); +} + +WherePhrase WherePhrase::operator <(const QVariant &other) +{ + return WherePhrase(this, PhraseData::Less, other); +} + +WherePhrase WherePhrase::operator >(const QVariant &other) +{ + return WherePhrase(this, PhraseData::Greater, other); +} + +WherePhrase WherePhrase::operator <=(const QVariant &other) +{ + return WherePhrase(this, PhraseData::LessEqual, other); +} + +WherePhrase WherePhrase::operator >=(const QVariant &other) +{ + return WherePhrase(this, PhraseData::GreaterEqual, other); } FieldPhrase::FieldPhrase(const char *className, const char *s) : WherePhrase(className, s) { - data = new PhraseData(className, s); - text = QString(className) + "." + s; + qDebug() << "(" << this << ")" << "FieldPhrase ctor" << className << s; } -WherePhrase FieldPhrase::operator &(const QVariant &other) +WherePhrase FieldPhrase::operator =(const QVariant &other) { - Q_UNUSED(other); - qFatal("The operator & can not applied for two fields"); + return WherePhrase(this, PhraseData::Set, other); } WherePhrase FieldPhrase::isNull(){ - return WherePhrase(this->data, PhraseData::Null); + return WherePhrase(this, PhraseData::Null); } WherePhrase FieldPhrase::in(QVariantList list) { - return WherePhrase(this->data, PhraseData::In, list); + return WherePhrase(this, PhraseData::In, list); } WherePhrase FieldPhrase::in(QStringList list) { - return WherePhrase(this->data, PhraseData::In, list); + return WherePhrase(this, PhraseData::In, list); } WherePhrase FieldPhrase::like(QString pattern) { - return WherePhrase(this->data, PhraseData::Like, pattern); + return WherePhrase(this, PhraseData::Like, pattern); } QT_END_NAMESPACE diff --git a/src/wherephrase.h b/src/wherephrase.h index e60a4e1..d767c9f 100644 --- a/src/wherephrase.h +++ b/src/wherephrase.h @@ -27,21 +27,25 @@ #include #include #include +#include QT_BEGIN_NAMESPACE class SqlGeneratorBase; -struct PhraseData{ +class PhraseData{ +public: enum Condition { - Equal = 0, + NotAssign = 0, + Equal, Less, LessEqual, Null, In, Like, - NotEqual = 10, + Not = 10, + NotEqual, GreaterEqual, Greater, NotNull, @@ -51,6 +55,7 @@ struct PhraseData{ And = 20, Or, + Append, Set, @@ -81,34 +86,24 @@ struct PhraseData{ PhraseData(PhraseData *l, Condition o, QVariant r); ~PhraseData(); - - QString operatorString() const; - QString escapeVariant() const; - QString command(SqlGeneratorBase *generator) const; }; class WherePhrase{ protected: - PhraseData *data; - bool willDeleteData; + PhraseData *_data; + QSharedPointer _dataPointer; public: - - QString text; - WherePhrase(const char *className, const char* s); - WherePhrase(PhraseData *l); - WherePhrase(PhraseData *l, PhraseData::Condition o); - WherePhrase(PhraseData *l, PhraseData::Condition o, PhraseData *r); - WherePhrase(PhraseData *l, PhraseData::Condition o, QVariant r); + WherePhrase(const WherePhrase &l); + WherePhrase(WherePhrase *l); + WherePhrase(WherePhrase *l, PhraseData::Condition o); + WherePhrase(WherePhrase *l, PhraseData::Condition o, WherePhrase *r); + WherePhrase(WherePhrase *l, PhraseData::Condition o, QVariant r); ~WherePhrase(); - QString command(SqlGeneratorBase *generator); - - void deleteData(PhraseData *d); - WherePhrase operator ==(const WherePhrase &other); WherePhrase operator !=(const WherePhrase &other); WherePhrase operator <(const WherePhrase &other); @@ -137,14 +132,13 @@ public: WherePhrase operator >=(const QVariant &other); + PhraseData *data() const; }; class FieldPhrase: public WherePhrase{ public: FieldPhrase(const char *className, const char* s); - WherePhrase operator &(const QVariant &other); - WherePhrase operator =(const QVariant &other); WherePhrase operator !(); @@ -154,6 +148,13 @@ public: WherePhrase like(QString pattern); }; + +//TODO: make FieldPhrase template class +//template +//class FieldPhrase: public WherePhrase{ + +//}; + QT_END_NAMESPACE #endif // PHRASE_H diff --git a/test/basic/maintest.cpp b/test/basic/maintest.cpp index 4d66ea2..281147c 100644 --- a/test/basic/maintest.cpp +++ b/test/basic/maintest.cpp @@ -33,6 +33,11 @@ void MainTest::initTestCase() bool ok = db.open(); QTEST_ASSERT(ok); + + FROM(db.comments()) + DELETE(); + FROM(db.posts()) + DELETE(); } void MainTest::dataScheema() @@ -67,13 +72,36 @@ void MainTest::createPost() qDebug() << "New post inserted with id:" << newPost->id(); } +void MainTest::createPost2() +{ + Post *newPost = new Post; + newPost->setTitle("post title"); + newPost->setSaveDate(QDateTime::currentDateTime()); + + db.posts()->append(newPost); + db.saveChanges(); + + for(int i = 0 ; i < 3; i++){ + Comment *comment = new Comment; + comment->setMessage("comment #" + QString::number(i)); + comment->setSaveDate(QDateTime::currentDateTime()); + comment->setPostId(newPost->id()); + db.comments()->append(comment); + } + db.saveChanges(); + + QTEST_ASSERT(newPost->id() != 0); + qDebug() << "New post2 inserted with id:" << newPost->id(); +} + void MainTest::selectPosts() { // auto q = FROM(db.posts()) // JOIN(Comment) // WHERE(Post::idField() == postId); auto q = db.posts()->createQuery(); - q->join("Comment"); + q->join(Post::commentsTable()); + q->orderBy(!Post::saveDateField() & Post::bodyField()); q->setWhere(Post::idField() == postId); auto posts = q->toList(); @@ -91,6 +119,15 @@ void MainTest::selectPosts() db.cleanUp(); } +void MainTest::selectPostsWithoutTitle() +{ + auto q = db.posts()->createQuery(); + q->setWhere(Post::titleField().isNull()); + auto count = q->count(); + qDebug() << "selectPostsWithoutTitle, count=" << count; + QTEST_ASSERT(count == 0); +} + void MainTest::testDate() { QDateTime d = QDateTime::currentDateTime(); @@ -116,15 +153,22 @@ void MainTest::testDate() void MainTest::selectWithInvalidRelation() { - auto q = FROM(db.posts()) - JOIN(Invalid_Class_Name) - SELECT(); + auto q = db.posts()->createQuery(); + q->join("Invalid_Class_Name"); + q->toList(); +} + +void MainTest::select10NewstPosts() +{ + auto q = db.posts()->createQuery(); + q->orderBy(!Post::saveDateField()); + q->toList(10); } void MainTest::modifyPost() { - auto q = FROM(db.posts()) - WHERE(Post::idField() == postId); + auto q = db.posts()->createQuery(); + q->setWhere(Post::idField() == postId); Post *post = q->first(); diff --git a/test/basic/maintest.h b/test/basic/maintest.h index 4b69640..edcee0e 100644 --- a/test/basic/maintest.h +++ b/test/basic/maintest.h @@ -22,9 +22,12 @@ private slots: void dataScheema(); void createPost(); + void createPost2(); void selectPosts(); + void selectPostsWithoutTitle(); void testDate(); void selectWithInvalidRelation(); + void select10NewstPosts(); void modifyPost(); void deletePost(); }; diff --git a/test/basic/tst_basic.pro b/test/basic/tst_basic.pro index ff00896..b072765 100644 --- a/test/basic/tst_basic.pro +++ b/test/basic/tst_basic.pro @@ -16,6 +16,7 @@ SOURCES += \ HEADERS += \ maintest.h \ + ../common/consts.h \ ../common/comment.h \ ../common/post.h \ ../common/weblogdatabase.h diff --git a/test/benckmark/maintest.cpp b/test/benckmark/maintest.cpp index c456dca..f8b8877 100644 --- a/test/benckmark/maintest.cpp +++ b/test/benckmark/maintest.cpp @@ -33,8 +33,6 @@ void MainTest::initTestCase() bool ok = db.open(); QTEST_ASSERT(ok); - - QTEST_ASSERT(ok); } void MainTest::insert1kPost() diff --git a/test/common/consts.h b/test/common/consts.h index 08c7658..41c8154 100644 --- a/test/common/consts.h +++ b/test/common/consts.h @@ -1,11 +1,17 @@ #ifndef CONSTS_H #define CONSTS_H -#define DRIVER "QPSQL" +//#define DRIVER "QPSQL" +//#define HOST "127.0.0.1" +//#define DATABASE "nutdb3" +//#define USERNAME "postgres" +//#define PASSWORD "856856" + +#define DRIVER "QMYSQL" #define HOST "127.0.0.1" -#define DATABASE "nutdb3" -#define USERNAME "postgres" -#define PASSWORD "856856" +#define DATABASE "nutdb" +#define USERNAME "root" +#define PASSWORD "onlyonlyi" // db.setDriver("QODBC"); // db.setHostName("127.0.0.1");