Merge pull request #8 from HamedMasafi/wip/join

Wip/join
This commit is contained in:
Hamed Masafi 2018-01-15 17:31:08 +03:30 committed by GitHub
commit ac6e55b60d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 838 additions and 300 deletions

View File

@ -7,6 +7,3 @@ before_install:
script:
- qmake nut.pro
- make
notifications:
email: false

View File

@ -38,7 +38,7 @@ class ChangeLogTable : public Table
NUT_DECLARE_FIELD(QString, version, version, setVersion)
public:
ChangeLogTable(QObject *tableSet = Q_NULLPTR);
ChangeLogTable(QObject *parentTableSet = Q_NULLPTR);
};
NUT_END_NAMESPACE

View File

@ -188,16 +188,20 @@ bool DatabasePrivate::getCurrectScheema()
changeLogs = new TableSet<ChangeLogTable>(q);
for (int i = 0; i < q->metaObject()->classInfoCount(); i++) {
QMetaClassInfo ci = q->metaObject()->classInfo(i);
QString ciName = QString(ci.name())
.replace(__nut_NAME_PERFIX, "")
.replace("\"", "");
QString type;
QString name;
QString value;
if (ciName.startsWith(__nut_TABLE))
tables.insert(ciName.split(" ").at(1), ci.value());
if (!checkClassInfo(q->metaObject()->classInfo(i),
type, name, value)) {
continue;
}
if (ciName == __nut_DB_VERSION) {
currentModel.setVersion(QString(ci.value()));
if (type == __nut_TABLE)
tables.insert(name, value);
if (type == __nut_DB_VERSION)
currentModel.setVersion(name);
/* TODO: remove
QStringList version
@ -213,7 +217,6 @@ bool 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");*/
}
}
for (int i = 1; i < q->metaObject()->propertyCount(); i++) {
@ -227,14 +230,30 @@ bool DatabasePrivate::getCurrectScheema()
}
}
foreach (TableModel *sch, currentModel)
foreach (RelationModel *fk, sch->foregionKeys())
fk->table = currentModel.tableByClassName(fk->className);
foreach (TableModel *table, currentModel)
foreach (RelationModel *fk, table->foregionKeys())
fk->masterTable = currentModel.tableByClassName(fk->masterClassName);
allTableMaps.insert(q->metaObject()->className(), currentModel);
return true;
}
bool DatabasePrivate::checkClassInfo(const QMetaClassInfo &classInfo, QString &type, QString &name, QString &value)
{
if (!QString(classInfo.name()).startsWith(__nut_NAME_PERFIX)) {
return false;
} else {
QStringList parts = QString(classInfo.value()).split("\n");
if (parts.count() != 3)
return false;
type = parts[0];
name = parts[1];
value = parts[2];
return true;
}
}
DatabaseModel DatabasePrivate::getLastScheema()
{
ChangeLogTable *u = changeLogs->query()

View File

@ -45,6 +45,8 @@ public:
DatabaseModel getLastScheema();
bool getCurrectScheema();
bool checkClassInfo(const QMetaClassInfo &classInfo,
QString &type, QString &name, QString &value);
QSqlDatabase db;
QString hostName;

View File

@ -30,17 +30,20 @@ QMap<QString, DatabaseModel*> DatabaseModel::_models;
#define NODE_VERSION "version"
#define NODE_TABLES "tables"
DatabaseModel::DatabaseModel(const QString &name) : QList<TableModel*>(), _databaseClassName(name), _version(QString::null)
DatabaseModel::DatabaseModel(const QString &name) :
QList<TableModel*>(), _databaseClassName(name), _version(QString::null)
{
_models.insert(name, this);
}
DatabaseModel::DatabaseModel(const DatabaseModel &other) : QList<TableModel*>(other), _version(QString::null)
DatabaseModel::DatabaseModel(const DatabaseModel &other) :
QList<TableModel*>(other), _version(QString::null)
{
}
DatabaseModel::DatabaseModel(const QJsonObject &json) : QList<TableModel*>()
DatabaseModel::DatabaseModel(const QJsonObject &json) :
QList<TableModel*>()
{
setVersion(json.value(NODE_VERSION).toString());
@ -70,16 +73,15 @@ TableModel *DatabaseModel::tableByName(QString tableName) const
TableModel *DatabaseModel::tableByClassName(QString className) const
{
QStringList l;
for(int i = 0; i < size(); i++){
TableModel *s = at(i);
l.append(s->className());
if(s->className() == className)
return s;
}
// qWarning("Table with class name '%s' not found in model",
// qUtf8Printable(className));
// Q_UNREACHABLE();
return 0;
}
@ -145,7 +147,7 @@ RelationModel *DatabaseModel::relationByClassNames(const QString &masterClassNam
return 0;
foreach (RelationModel *rel, childTable->foregionKeys())
if(rel->className == masterClassName)
if(rel->masterClassName == masterClassName)
return rel;
return 0;
@ -159,7 +161,7 @@ RelationModel *DatabaseModel::relationByTableNames(const QString &masterTableNam
return 0;
foreach (RelationModel *rel, childTable->foregionKeys())
if(rel->table->name() == masterTableName)
if(rel->masterTable->name() == masterTableName)
return rel;
return 0;
@ -204,6 +206,15 @@ bool DatabaseModel::remove(const QString &tableName)
return false;
}
void DatabaseModel::fixRelations()
{
/*TODO: fixme
foreach (TableModel *table, currentModel)
foreach (RelationModel *fk, table->foregionKeys())
fk->masterTable = currentModel.tableByClassName(fk->masterClassName);
*/
}
DatabaseModel *DatabaseModel::modelByName(const QString &name)
{
if (_models.contains(name))

View File

@ -65,6 +65,9 @@ public:
bool remove(const QString &tableName);
//TODO: may be private (called from DatabasePrivate::getCurrectScheema only)
void fixRelations();
static DatabaseModel *modelByName(const QString &name);
};

View File

@ -32,13 +32,15 @@
# define NUT_EXPORT Q_DECL_EXPORT
#endif
#define NUT_INFO(type, name, value) \
Q_CLASSINFO(__nut_NAME_PERFIX type #name #value, type "\n" #name "\n" #value)
// Database
//TODO: remove minor version
#define NUT_DB_VERSION(version) \
Q_CLASSINFO(QT_STRINGIFY(__nut_NAME_PERFIX __nut_DB_VERSION), #version)
NUT_INFO(__nut_DB_VERSION, version, 0)
#define NUT_DECLARE_TABLE(type, name) \
Q_CLASSINFO(QT_STRINGIFY(__nut_NAME_PERFIX __nut_TABLE " " #type), #name) \
NUT_INFO(__nut_TABLE, type, name) \
Q_PROPERTY(type* name READ name) \
Q_PROPERTY(NUT_WRAP_NAMESPACE(TableSet<type>) name##s READ name##s) \
type* m_##name; \
@ -51,7 +53,7 @@ public: \
//Table
#define NUT_DECLARE_FIELD(type, name, read, write) \
Q_PROPERTY(type name READ read WRITE write) \
Q_CLASSINFO(QT_STRINGIFY(__nut_NAME_PERFIX #name " " __nut_FIELD), #name) \
NUT_INFO(__nut_FIELD, name, 0) \
type m_##name; \
public: \
static NUT_WRAP_NAMESPACE(FieldPhrase<type>) name ## Field(){ \
@ -69,7 +71,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(QT_STRINGIFY(__nut_NAME_PERFIX #name "Id " __nut_FOREGION_KEY), #type) \
NUT_INFO(__nut_FOREGION_KEY, name, type) \
type *m_##name; \
public: \
type *read() const { return m_##name ; } \
@ -94,27 +96,14 @@ public: \
}
#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_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_UNIQUE(x) Q_CLASSINFO(QT_STRINGIFY(__nut_NAME_PERFIX #x " " __nut_UNIQUE), #x)
#define NUT_LEN(field, len) Q_CLASSINFO(QT_STRINGIFY(__nut_NAME_PERFIX #field " " __nut_LEN), #len)
#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")
#define NUT_UNIQUE(x) NUT_INFO(__nut_UNIQUE, x, 0)
#define NUT_LEN(field, len) NUT_INFO(__nut_LEN, field, len)
#define NUT_DEFAULT_VALUE(x, n) NUT_INFO(__nut_DEFAULT_VALUE, x, n)
#define NUT_NOT_NULL(x) NUT_INFO(__nut_NOT_NULL, x, 1)
#define NUT_INDEX(name, field, order)
#ifndef NUT_NO_KEYWORDS
# define FROM(x) (x->query())
# define WHERE(x) ->setWhere(x)
# define JOIN(x) ->join<x>()
# define ORDERBY(x) ->orderBy(#x);
# define ORDERBY_DESC(x) ->orderBy(!#x);
# define SELECT() ->toList()
# define COUNT() ->count()
# define DELETE() ->remove()
# define FIRST() ->first()
#endif // NUT_NO_KEYWORDS
#endif // SYNTAX_DEFINES_H

View File

@ -134,4 +134,24 @@ QString MySqlGenerator::phrase(const PhraseData *d) const
return SqlGeneratorBase::phrase(d);
}
QString MySqlGenerator::selectCommand(SqlGeneratorBase::AgregateType t,
QString agregateArg,
QString tableName,
QList<WherePhrase> &wheres,
QList<WherePhrase> &orders,
QList<RelationModel*> joins,
int skip, int take)
{
QString command = SqlGeneratorBase::selectCommand(t, agregateArg,
tableName,
wheres, orders,
joins, skip, take);
if (take != -1 && skip != -1)
command.append(QString(" LIMIT %1 OFFSET %2")
.arg(take)
.arg(skip));
return command;
}
NUT_END_NAMESPACE

View File

@ -35,6 +35,7 @@ public:
QString escapeValue(const QVariant &v) const;
QVariant readValue(const QVariant::Type &type, const QVariant &dbValue);
QString phrase(const PhraseData *d) const;
QString selectCommand(AgregateType t, QString agregateArg, QString tableName, QList<WherePhrase> &wheres, QList<WherePhrase> &orders, QList<RelationModel *> joins, int skip, int take);
};
NUT_END_NAMESPACE

View File

@ -19,9 +19,11 @@
**************************************************************************/
#include <QDate>
#include <QDebug>
#include <QDateTime>
#include <QPointF>
#include <QTime>
#include <QUuid>
#include <QVariant>
#include "sqlgeneratorbase_p.h"
@ -44,6 +46,11 @@ NUT_BEGIN_NAMESPACE
* REFERENCES `account` (`id`)
* ON DELETE CASCADE
* ON UPDATE CASCADE;
*
* SELECT
* FROM dbo.GiftTypes
* INNER JOIN dbo.GiftCards ON dbo.GiftTypes.GiftTypeID = dbo.GiftCards.GiftTypeID
* INNER JOIN dbo.Entities ON dbo.GiftCards.GiftCardID = dbo.Entities.GiftCardID
*/
SqlGeneratorBase::SqlGeneratorBase(Database *parent)
: QObject((QObject *)parent)
@ -64,7 +71,8 @@ QString SqlGeneratorBase::masterDatabaseName(QString databaseName)
QString SqlGeneratorBase::createTable(TableModel *table)
{
Q_UNUSED(table);
return "";
}
QString SqlGeneratorBase::saveRecord(Table *t, QString tableName)
@ -88,6 +96,20 @@ QString SqlGeneratorBase::saveRecord(Table *t, QString tableName)
return "";
}
QString SqlGeneratorBase::recordsPhrase(TableModel *table)
{
if (!table)
return "";
QString ret = "";
foreach (FieldModel *f, table->fields()) {
if (!ret.isEmpty())
ret.append(", ");
ret.append(QString("%1.%2 AS [%1.%2]").arg(table->name()).arg(f->name));
}
return ret;
}
QString SqlGeneratorBase::fieldDeclare(FieldModel *field)
{
return field->name + " " + fieldType(field) + (field->notNull ? " NOT NULL" : "");
@ -178,27 +200,93 @@ QString SqlGeneratorBase::diff(TableModel *oldTable, TableModel *newTable)
return sql;
}
QString SqlGeneratorBase::join(const QStringList &list)
QString SqlGeneratorBase::join(const QString &mainTable,
const QList<RelationModel*> list,
QStringList *order)
{
QString ret = mainTable;
QList<RelationModel*>::const_iterator i;
for (i = list.begin(); i != list.end(); ++i) {
if ((*i)->masterTable->name() == mainTable) {
ret.append(QString(" INNER JOIN %3 ON %1.%2 = %3.%4")
.arg((*i)->masterTable->name())
.arg((*i)->masterTable->primaryKey())
.arg((*i)->slaveTable->name())
.arg((*i)->localColumn));
if (order != Q_NULLPTR)
order->append((*i)->slaveTable->name() + "." + (*i)->slaveTable->primaryKey());
} else {
ret.append(QString(" INNER JOIN %3 ON %1.%2 = %3.%4")
.arg(mainTable)
.arg((*i)->localColumn)
.arg((*i)->masterTable->name())
.arg((*i)->masterTable->primaryKey()));
if (order != Q_NULLPTR)
order->append((*i)->masterTable->name() + "." + (*i)->masterTable->primaryKey());
}
}
return ret;
}
QString SqlGeneratorBase::join(const QStringList &list, QStringList *order)
{
//TODO: reorder list first!
//TODO: make this ungly code better and bugless :-)
/*
* Known issues:
* Support onle near joins, far supports with medium table finding not support yet
*/
if (!list.count())
return "";
if (list.count() == 1)
return list.first();
return "[" + list.first() + "]";
DatabaseModel model = _database->model();
QStringList clone = list;
QString mainTable = clone.takeFirst();
QString ret = mainTable;
QString ret = "[" + mainTable + "]";
do {
QString t = model.tableByClassName(clone.first())->name();
RelationModel *rel = model.relationByTableNames(mainTable, t);
if (!clone.count())
break;
QString table = clone.first();// model.tableByClassName(clone.first())->name();
RelationModel *rel = model.relationByClassNames(mainTable, clone.first());
if (rel) {
clone.takeFirst();
ret.append(", " + _database->tableName(clone.takeFirst()));
//mainTable is master of table
ret.append(QString(" INNER JOIN [%1] ON %4.%2 = %1.%3")
.arg(table)
.arg(rel->masterTable->primaryKey())
.arg(rel->localColumn)
.arg(mainTable));
if (order != Q_NULLPTR)
order->append(mainTable + "." + rel->masterTable->primaryKey());
} else{
rel = model.relationByClassNames(clone.first(), mainTable);
if (rel) {
// table is master of mainTable
ret.append(QString(" INNER JOIN [%1] ON %4.%2 = %1.%3")
.arg(table)
.arg(rel->localColumn)
.arg(rel->masterTable->primaryKey())
.arg(mainTable));
if (order != Q_NULLPTR)
order->append(mainTable + "." + rel->localColumn);
} else {
qInfo("Relation for %s and %s not exists",
qPrintable(table), qPrintable(mainTable));
}
}
clone.takeFirst();
} while (clone.count());
return ret;
@ -340,23 +428,40 @@ QString SqlGeneratorBase::deleteRecords(QString tableName, QString where)
QString SqlGeneratorBase::selectCommand(SqlGeneratorBase::AgregateType t,
QString agregateArg,
QString tableName,
QList<WherePhrase> &wheres,
QList<WherePhrase> &orders,
QString tableName,
QString joinClassName, int skip, int take)
QList<RelationModel*> joins,
int skip, int take)
{
Q_UNUSED(take);
Q_UNUSED(skip);
QStringList joinedOrders;
QString select = agregateText(t, agregateArg);
//TODO: temporatory disabled
if (t == SelectAll) {
QSet<TableModel*> tables;
tables.insert(_database->model().tableByName(tableName));
foreach (RelationModel *rel, joins)
tables << rel->masterTable << rel->slaveTable;
select = "";
foreach (TableModel *t, tables) {
if (!select.isEmpty())
select.append(", ");
select.append(recordsPhrase(t));
}
}
QString from = join(tableName, joins, &joinedOrders);
QString where = createWhere(wheres);
QString order = "";
QString from = fromTableText(tableName, joinClassName, order);
QString orderText = joinedOrders.join(", ");
foreach (WherePhrase p, orders) {
if (order != "")
order.append(", ");
order.append(phraseOrder(p.data()));
if (orderText != "")
orderText.append(", ");
orderText.append(phraseOrder(p.data()));
}
QString sql = "SELECT " + select + " FROM " + from;
@ -364,8 +469,8 @@ QString SqlGeneratorBase::selectCommand(SqlGeneratorBase::AgregateType t,
if (where != "")
sql.append(" WHERE " + where);
if (order != "")
sql.append(" ORDER BY " + order);
if (orderText != "")
sql.append(" ORDER BY " + orderText);
for (int i = 0; i < _database->model().count(); i++)
sql = sql.replace(_database->model().at(i)->className() + ".",
@ -373,7 +478,7 @@ QString SqlGeneratorBase::selectCommand(SqlGeneratorBase::AgregateType t,
replaceTableNames(sql);
return sql;
return sql + " ";
}
QString SqlGeneratorBase::createWhere(QList<WherePhrase> &wheres)
@ -393,7 +498,8 @@ QString SqlGeneratorBase::createWhere(QList<WherePhrase> &wheres)
void SqlGeneratorBase::replaceTableNames(QString &command)
{
foreach (TableModel *m, TableModel::allModels())
command = command.replace("[" + m->className() + "].", "`" + m->name() + "`.");
command = command
.replace("[" + m->className() + "]", "`" + m->name() + "`");
}
void SqlGeneratorBase::removeTableNames(QString &command)
@ -441,14 +547,6 @@ QString SqlGeneratorBase::updateCommand(WherePhrase &phrase,
return sql;
}
QString SqlGeneratorBase::joinTables(QStringList tables)
{
Q_UNUSED(tables);
//TODO: implement me
// _database->model().relationByClassNames()
return "";
}
QString SqlGeneratorBase::escapeValue(const QVariant &v) const
{
switch (v.type()) {
@ -464,18 +562,22 @@ QString SqlGeneratorBase::escapeValue(const QVariant &v) const
return v.toString();
break;
case QVariant::Uuid:
return v.toUuid().toString();
break;
case QVariant::Char:
case QVariant::String:
return "'" + v.toString() + "'";
case QVariant::DateTime:
return "'" + v.toDateTime().toString() + "'";
return "'" + v.toDateTime().toString(Qt::ISODate) + "'";
case QVariant::Date:
return "'" + v.toDate().toString() + "'";
return "'" + v.toDate().toString(Qt::ISODate) + "'";
case QVariant::Time:
return "'" + v.toTime().toString() + "'";
return "'" + v.toTime().toString(Qt::ISODate) + "'";
case QVariant::StringList:
case QVariant::List:
@ -496,6 +598,7 @@ QString SqlGeneratorBase::escapeValue(const QVariant &v) const
return "<FAIL>";
default:
Q_UNREACHABLE();
return "";
}
}

View File

@ -33,6 +33,7 @@ struct FieldModel;
class DatabaseModel;
class TableModel;
class Database;
class RelationModel;
class SqlGeneratorBase : public QObject
{
// Q_OBJECT
@ -68,30 +69,31 @@ public:
virtual QString diff(FieldModel *oldField, FieldModel *newField);
virtual QString diff(TableModel *oldTable, TableModel *newTable);
virtual QString join(const QStringList &list);
virtual QString join(const QString &mainTable,
const QList<RelationModel*> list,
QStringList *order = Q_NULLPTR);
virtual QString join(const QStringList &list, QStringList *order = Q_NULLPTR);
virtual QString saveRecord(Table *t, QString tableName);
virtual QString recordsPhrase(TableModel *table);
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 selectCommand(AgregateType t,
QString agregateArg,
QString agregateArg, QString tableName,
QList<WherePhrase> &wheres,
QList<WherePhrase> &orders,
QString tableName,
QString joinClassName,
QList<RelationModel*> joins,
int skip = -1, int take = -1);
virtual QString deleteCommand(QList<WherePhrase> &wheres, QString tableName);
virtual QString updateCommand(WherePhrase &phrase, QList<WherePhrase> &wheres, QString tableName);
virtual QString joinTables(QStringList tables);
virtual QString escapeValue(const QVariant &v) const;
virtual QVariant readValue(const QVariant::Type &type, const QVariant &dbValue);
virtual QString phrase(const PhraseData *d) const;

View File

@ -54,10 +54,9 @@ QString SqliteGenerator::fieldType(FieldModel *field)
dbType = "real";
break;
case QVariant::Int:
// if(field->isPrimaryKey)
// dbType = "INTEGER PRIMARY KEY";
// else
dbType = "integer";
dbType = "integer";
// if (field->isAutoIncrement)
// dbType.append(" PRIMARY KEY AUTOINCREMENT");
break;
case QVariant::String:
if(field->length)
@ -72,4 +71,24 @@ QString SqliteGenerator::fieldType(FieldModel *field)
return dbType;
}
QString SqliteGenerator::selectCommand(SqlGeneratorBase::AgregateType t,
QString agregateArg,
QString tableName,
QList<WherePhrase> &wheres,
QList<WherePhrase> &orders,
QList<RelationModel*> joins,
int skip, int take)
{
QString command = SqlGeneratorBase::selectCommand(t, agregateArg,
tableName,
wheres, orders,
joins, skip, take);
if (take != -1 && skip != -1)
command.append(QString(" LIMIT %1 OFFSET %2")
.arg(take)
.arg(skip));
return command;
}
NUT_END_NAMESPACE

View File

@ -32,6 +32,12 @@ public:
SqliteGenerator(Database *parent = 0);
QString fieldType(FieldModel *field);
QString selectCommand(AgregateType t, QString agregateArg,
QString tableName,
QList<WherePhrase> &wheres,
QList<WherePhrase> &orders,
QList<RelationModel *> joins, int skip, int take);
};
NUT_END_NAMESPACE

View File

@ -133,12 +133,17 @@ QString SqlServerGenerator::escapeValue(const QVariant &v) const
return SqlGeneratorBase::escapeValue(v);
}
QString SqlServerGenerator::selectCommand(
SqlGeneratorBase::AgregateType t, QString agregateArg,
QList<WherePhrase> &wheres, QList<WherePhrase> &orders, QString tableName,
QString joinClassName, int skip, int take)
QString SqlServerGenerator::selectCommand(SqlGeneratorBase::AgregateType t,
QString agregateArg,
QString tableName,
QList<WherePhrase> &wheres,
QList<WherePhrase> &orders,
QList<RelationModel*> joins, int skip, int take)
{
QString command = SqlGeneratorBase::selectCommand(t, agregateArg, wheres, orders, tableName, joinClassName, skip, take);
QString command = SqlGeneratorBase::selectCommand(t, agregateArg,
tableName,
wheres, orders,
joins, skip, take);
if (take != -1 && skip != -1)
command.append(QString("OFFSET %1 ROWS FETCH NEXT %2 ROWS ONLY")

View File

@ -39,9 +39,10 @@ public:
QString escapeValue(const QVariant &v) const;
QString selectCommand(AgregateType t, QString agregateArg,
QString tableName,
QList<WherePhrase> &wheres,
QList<WherePhrase> &orders, QString tableName,
QString joinClassName, int skip, int take);
QList<WherePhrase> &orders,
QList<RelationModel *> joins, int skip, int take);
};
NUT_END_NAMESPACE

View File

@ -23,7 +23,7 @@
NUT_BEGIN_NAMESPACE
QueryPrivate::QueryPrivate(QueryBase *parent) : q_ptr(parent),
joinClassName(QString::null), skip(-1), take(-1)
skip(-1), take(-1)
{
}

View File

@ -26,6 +26,9 @@
#include <QtCore/QScopedPointer>
#include <QtCore/QRegularExpression>
#include <QtCore/QMetaObject>
#include <QtSql/QSqlResult>
#include <QElapsedTimer>
#include <QSqlError>
#include "query_p.h"
#include "database.h"
@ -39,7 +42,7 @@
NUT_BEGIN_NAMESPACE
template <class T>
class NUT_EXPORT Query : public QueryBase
class NUT_EXPORT Query : public QueryBase
{
QueryPrivate *d_ptr;
Q_DECLARE_PRIVATE(Query)
@ -63,7 +66,8 @@ public:
return this;
}
// Query<T> *orderBy(QString fieldName, QString type);
// Query<T> *orderBy(QString fieldName, QString type);
Query<T> *skip(int n);
Query<T> *take(int n);
Query<T> *orderBy(WherePhrase phrase);
@ -104,9 +108,10 @@ Q_OUTOFLINE_TEMPLATE Query<T>::Query(Database *database, TableSetBase *tableSet,
d->database = database;
d->tableSet = tableSet;
d->className = T::staticMetaObject.className();
d->tableName
= // TableModel::findByClassName(T::staticMetaObject.className())->name();
d->database->model()
= // TableModel::findByClassName(T::staticMetaObject.className())->name();
d->database->model()
.tableByClassName(T::staticMetaObject.className())
->name();
}
@ -121,105 +126,177 @@ Q_OUTOFLINE_TEMPLATE Query<T>::~Query()
template <class T>
Q_OUTOFLINE_TEMPLATE QList<T *> Query<T>::toList(int count)
{
Q_UNUSED(count);
Q_D(Query);
QList<T*> result;
QList<T*> returnList;
d->select = "*";
QElapsedTimer t;
t.start();
d->joins.prepend(d->tableName);
qDebug() << "JOINS="<< d->database->sqlGenertor()->join(d->joins);
// QSqlQuery q =
// d->database->exec(d->database->sqlGenertor()->selectCommand(d->wheres,
// d->orders, d->tableName, d->joinClassName));
d->sql = d->database->sqlGenertor()->selectCommand(
SqlGeneratorBase::SelectAll, "", d->wheres, d->orderPhrases,
d->tableName, d->joinClassName, d->skip, d->take);
SqlGeneratorBase::SelectAll, "",
d->tableName,
d->wheres, d->orderPhrases, d->relations,
d->skip, d->take);
QSqlQuery q = d->database->exec(d->sql);
// QString pk = TableModel::findByName(d->tableName)->primaryKey();
QString pk = d->database->model().tableByName(d->tableName)->primaryKey();
QVariant lastPkValue = QVariant();
int childTypeId = 0;
T *lastRow = 0;
TableSetBase *childTableSet = Q_NULLPTR;
// FIXME: getting table error
// QStringList masterFields =
// TableModel::findByName(d->tableName)->fieldsNames();
QStringList masterFields
= d->database->model().tableByName(d->tableName)->fieldsNames();
QStringList childFields;
if (!d->joinClassName.isNull()) {
TableModel *joinTableModel
= TableModel::findByClassName(d->joinClassName);
if (joinTableModel) {
// childFields =
// d->database->model().modelByClass(d->joinClassName)->fieldsNames();
childFields
= TableModel::findByClassName(d->joinClassName)->fieldsNames();
QString joinTableName = d->database->tableName(d->joinClassName);
childTypeId = d->database->model().tableByName(joinTableName)->typeId();
// childTypeId =
// TableModel::findByName(joinTableName)->typeId();
}
if (q.lastError().isValid()) {
qDebug() << q.lastError().text();
return returnList;
}
while (q.next()) {
if (lastPkValue != q.value(pk)) {
T *t = new T();
foreach (QString field, masterFields)
t->setProperty(field.toLatin1().data(), q.value(field));
// for (int i = 0; i < t->metaObject()->propertyCount();
// i++) {
// const QMetaProperty p =
// t->metaObject()->property(i);
QSet<TableModel*> relatedTables;
relatedTables << d->database->model().tableByName(d->tableName);
foreach (RelationModel *rel, d->relations)
relatedTables << rel->slaveTable << rel->masterTable;
// p.write(t,
// d->database->sqlGenertor()->readValue(p.type(),
// q.value(p.name())));
// }
t->setTableSet(d->tableSet);
t->setStatus(Table::FeatchedFromDB);
t->setParent(this);
t->clear();
struct LevelData{
QList<int> masters;
QList<int> slaves;
QList<QString> masterFields;
QString keyFiledname;
QVariant lastKeyValue;
TableModel *table;
Table *lastRow;
};
QVector<LevelData> levels;
QSet<QString> importedTables;
auto add_table = [&](int i, TableModel* table) {
if (importedTables.contains(table->name()))
return;
importedTables.insert(table->name());
result.append(t);
lastRow = t;
LevelData data;
data.table = table;
data.keyFiledname = data.table->name() + "." + data.table->primaryKey();
data.lastKeyValue = QVariant();
if (childTypeId) {
QSet<TableSetBase *> tableSets = t->tableSets;
foreach (TableSetBase *ts, tableSets)
if (ts->childClassName() == d->joinClassName)
childTableSet = ts;
QHash<QString, QString> masters;
foreach (RelationModel *rel, d->relations)
if (rel->slaveTable->name() == table->name())
masters.insert(rel->masterTable->name(), rel->localProperty);
for (int j = 0; j < levels.count(); ++j) {
LevelData &dt = levels[j];
qDebug() <<"[check]"<<table->name() << dt.table->name();
QHashIterator<QString, QString> it(masters);
while (it.hasNext()) {
it.next();
if (dt.table->name() == it.key()) {
data.masters.append(j);
data.masterFields.append(it.value());
dt.slaves.append(i);
}
}
}
if (childTypeId) {
const QMetaObject *childMetaObject
= QMetaType::metaObjectForType(childTypeId);
Table *childTable
= qobject_cast<Table *>(childMetaObject->newInstance());
foreach (QString field, childFields)
childTable->setProperty(field.toLatin1().data(),
q.value(field));
// TODO: set database for table
childTable->setParent(this);
childTable->setParentTable(lastRow);
childTable->setStatus(Table::FeatchedFromDB);
childTable->setTableSet(childTableSet);
childTable->clear();
childTableSet->add(childTable);
}
lastPkValue = q.value(pk);
if (!--count)
break;
qDebug() << data.table->name() <<"added";
levels.append(data);
};
for (int i = 0; i < d->relations.count(); ++i) {
RelationModel *rel = d->relations[i];
add_table(i, rel->masterTable);
add_table(i, rel->slaveTable);
}
if (!importedTables.count()) {
LevelData data;
data.table = d->database->model().tableByName(d->tableName);
data.keyFiledname = d->tableName + "." + data.table->primaryKey();
data.lastKeyValue = QVariant();
levels.append(data);
}
QVector<bool> checked;
checked.reserve(levels.count());
for (int i = 0; i < levels.count(); ++i)
checked.append(false);
qDebug() << "Elapsed time:" << QString("%1ms").arg(t.elapsed() / 1000.);
while (q.next()) {
checked.fill(false);
int p = levels.count();
qDebug() << "p is"<<p;
int n = -1;
int lastP = p;
while (p) {
// Q_ASSERT(p != lastP);
// if (p == lastP)
// qFatal("NULL Loop detected");
n = (++n) % levels.count();
if (checked[n])
continue;
LevelData &data = levels[n];
// check if key value is changed
if (data.lastKeyValue == q.value(data.keyFiledname)) {
--p;
continue;
}
// check if master if current table has processed
foreach (int m, data.masters)
if (!checked[m])
continue;
checked[n] = true;
--p;
data.lastKeyValue = q.value(data.keyFiledname);
//create table row
Table *table;
if (data.table->className() == d->className) {
table = new T();
table->setParentTableSet(d->tableSet);
returnList.append(dynamic_cast<T*>(table));
} else {
const QMetaObject *childMetaObject
= QMetaType::metaObjectForType(data.table->typeId());
table = qobject_cast<Table *>(childMetaObject->newInstance());
}
qDebug() << "table created" << table;
QStringList childFields = data.table->fieldsNames();
foreach (QString field, childFields)
table->setProperty(field.toLatin1().data(),
q.value(data.table->name() + "." + field));
for (int i = 0; i < data.masters.count(); ++i) {
int master = data.masters[i];
table->setProperty(data.masterFields[i].toLocal8Bit().data(),
QVariant::fromValue(levels[master].lastRow));
table->setParentTableSet(levels[master].lastRow->childTableSet(data.table->className()));
TableSetBase *ts = levels[master].lastRow->childTableSet(data.table->className());
qDebug() << table << "added to"
<< levels[master].lastRow
<< ts->childClassName()
<< data.masterFields[i];
}
table->setStatus(Table::FeatchedFromDB);
table->setParent(this);
table->clear();
//set last created row
data.lastRow = table;
lastP = p;
} //while
} // while
if (m_autoDelete)
deleteLater();
return result;
qDebug() << "Elapsed time:" << QString("%1ms").arg(t.elapsed() / 1000.);
return returnList;
}
template <typename T>
@ -228,9 +305,12 @@ Q_OUTOFLINE_TEMPLATE QList<F> Query<T>::select(const FieldPhrase<F> f)
{
Q_D(Query);
QList<F> ret;
d->joins.prepend(d->tableName);
d->sql = d->database->sqlGenertor()->selectCommand(
SqlGeneratorBase::SignleField, f.data()->text, d->wheres,
d->orderPhrases, d->tableName, d->joinClassName, d->skip, d->take);
SqlGeneratorBase::SignleField, f.data()->text,
d->tableName, d->wheres,
d->orderPhrases, d->relations, d->skip, d->take);
QSqlQuery q = d->database->exec(d->sql);
@ -262,9 +342,14 @@ Q_OUTOFLINE_TEMPLATE int Query<T>::count()
{
Q_D(Query);
d->joins.prepend(d->tableName);
d->select = "COUNT(*)";
d->sql = d->database->sqlGenertor()->selectCommand(SqlGeneratorBase::Count,
QStringLiteral("*"), d->wheres, d->orderPhrases, d->tableName, d->joinClassName);
QStringLiteral("*"),
d->tableName,
d->wheres,
d->orderPhrases,
d->relations);
QSqlQuery q = d->database->exec(d->sql);
if (q.next())
@ -277,9 +362,11 @@ Q_OUTOFLINE_TEMPLATE QVariant Query<T>::max(FieldPhrase<int> &f)
{
Q_D(Query);
d->joins.prepend(d->tableName);
d->sql = d->database->sqlGenertor()->selectCommand(
SqlGeneratorBase::Max, f.data()->text, d->wheres, d->orderPhrases,
d->tableName, d->joinClassName);
SqlGeneratorBase::Max, f.data()->text, d->tableName,
d->wheres, d->orderPhrases,
d->relations);
QSqlQuery q = d->database->exec(d->sql);
if (q.next())
@ -292,9 +379,11 @@ Q_OUTOFLINE_TEMPLATE QVariant Query<T>::min(FieldPhrase<int> &f)
{
Q_D(Query);
d->joins.prepend(d->tableName);
d->sql = d->database->sqlGenertor()->selectCommand(
SqlGeneratorBase::Min, f.data()->text, d->wheres, d->orderPhrases,
d->tableName, d->joinClassName);
SqlGeneratorBase::Min, f.data()->text, d->tableName,
d->wheres, d->orderPhrases,
d->relations);
QSqlQuery q = d->database->exec(d->sql);
if (q.next())
@ -307,9 +396,11 @@ Q_OUTOFLINE_TEMPLATE QVariant Query<T>::average(FieldPhrase<int> &f)
{
Q_D(Query);
d->joins.prepend(d->tableName);
d->sql = d->database->sqlGenertor()->selectCommand(
SqlGeneratorBase::Average, f.data()->text, d->wheres, d->orderPhrases,
d->tableName, d->joinClassName);
SqlGeneratorBase::Average, f.data()->text, d->tableName,
d->wheres, d->orderPhrases,
d->relations);
QSqlQuery q = d->database->exec(d->sql);
if (q.next())
@ -321,7 +412,18 @@ template <class T>
Q_OUTOFLINE_TEMPLATE Query<T> *Query<T>::join(const QString &className)
{
Q_D(Query);
d->joinClassName = className;
RelationModel *rel = d->database->model().relationByClassNames(d->className, className);
if (!rel)
rel = d->database->model().relationByClassNames(className, d->className);
if (!rel) {
qInfo("No relation between %s and %s",
qPrintable(d->className), qPrintable(className));
return this;
}
d->relations.append(rel);
d->joins.append(className);
return this;
}
@ -378,7 +480,6 @@ template <class T>
Q_OUTOFLINE_TEMPLATE Query<T> *Query<T>::include(TableSetBase *t)
{
Q_D(Query);
d->joinClassName = t->childClassName();
return this;
}
@ -386,7 +487,6 @@ template <class T>
Q_OUTOFLINE_TEMPLATE Query<T> *Query<T>::include(Table *t)
{
Q_D(Query);
d->joinClassName = t->metaObject()->className();
return this;
}

View File

@ -30,8 +30,8 @@ NUT_BEGIN_NAMESPACE
class Database;
class TableSetBase;
//template<class T>
class QueryBase;
class RelationModel;
class QueryPrivate{
QueryBase *q_ptr;
Q_DECLARE_PUBLIC(QueryBase)
@ -41,12 +41,13 @@ public:
~QueryPrivate();
QString sql;
QString className;
QString tableName;
QString select;
Database *database;
TableSetBase *tableSet;
QString joinClassName;
QStringList joins;
QList<RelationModel*> relations;
QList<WherePhrase> wheres;
QList<WherePhrase> orderPhrases;
QHash<QString, QString> orders;

View File

@ -1,5 +1,9 @@
#include "querybase_p.h"
#include "table.h"
#include "tablesetbase_p.h"
NUT_BEGIN_NAMESPACE
QueryBase::QueryBase(QObject *parent) : QObject(parent)
@ -7,4 +11,9 @@ QueryBase::QueryBase(QObject *parent) : QObject(parent)
}
void QueryBase::addTableToSet(TableSetBase *set, Table *table)
{
set->add(table);
}
NUT_END_NAMESPACE

View File

@ -28,13 +28,16 @@
NUT_BEGIN_NAMESPACE
//TODO: remove this class
class Table;
class TableSetBase;
class QueryBase : public QObject
{
Q_OBJECT
public:
explicit QueryBase(QObject *parent = 0);
signals:
protected:
void addTableToSet(TableSetBase *set, Table *table);
public slots:
};

View File

@ -22,6 +22,7 @@
#include <QVariant>
#include "table.h"
#include "database.h"
#include "databasemodel.h"
#include "generators/sqlgeneratorbase_p.h"
NUT_BEGIN_NAMESPACE
@ -33,7 +34,7 @@ Table::Table(QObject *parent) : QObject(parent)
void Table::add(TableSetBase *t)
{
this->tableSets.insert(t);
this->childTableSets.insert(t);
}
@ -67,7 +68,10 @@ QString Table::primaryKey() const
bool Table::isPrimaryKeyAutoIncrement() const
{
return TableModel::findByClassName(metaObject()->className())->field(primaryKey())->isAutoIncrement;
auto m = TableModel::findByClassName(metaObject()->className());
auto pk = m->primaryKey();
auto f = m->field(pk);
return f->isAutoIncrement;
}
@ -105,7 +109,7 @@ bool Table::setParentTable(Table *master)
TableModel *myModel = TableModel::findByClassName(metaObject()->className());
foreach (RelationModel *r, myModel->foregionKeys())
if(r->className == masterClassName)
if(r->masterClassName == masterClassName)
{
setProperty(QString(r->localColumn).toLatin1().data(), master->primaryValue());
_changedProperties.insert(r->localColumn);
@ -115,15 +119,23 @@ bool Table::setParentTable(Table *master)
return false;
}
TableSetBase *Table::tableSet() const
TableSetBase *Table::parentTableSet() const
{
return _tableSet;
return _parentTableSet;
}
void Table::setTableSet(TableSetBase *parent)
void Table::setParentTableSet(TableSetBase *parent)
{
_tableSet = parent;
_tableSet->add(this);
_parentTableSet = parent;
_parentTableSet->add(this);
}
TableSetBase *Table::childTableSet(const QString &name) const
{
foreach (TableSetBase *t, childTableSets)
if (t->childClassName() == name)
return t;
return Q_NULLPTR;
}
int Table::save(Database *db)
@ -133,7 +145,7 @@ int Table::save(Database *db)
if(status() == Added && isPrimaryKeyAutoIncrement())
setProperty(primaryKey().toLatin1().data(), q.lastInsertId());
foreach(TableSetBase *ts, tableSets)
foreach(TableSetBase *ts, childTableSets)
ts->save(db);
setStatus(FeatchedFromDB);

View File

@ -38,7 +38,7 @@ class NUT_EXPORT Table : public QObject
Q_OBJECT
public:
explicit Table(QObject *tableSet = 0);
explicit Table(QObject *parentTableSet = 0);
enum Status{
NewCreated,
@ -56,8 +56,10 @@ public:
Status status() const;
void setStatus(const Status &status);
TableSetBase *tableSet() const;
void setTableSet(TableSetBase *tableSet);
TableSetBase *parentTableSet() const;
void setParentTableSet(TableSetBase *parentTableSet);
TableSetBase *childTableSet(const QString &name) const;
QSet<QString> changedProperties() const;
@ -72,9 +74,10 @@ protected:
private:
Status _status;
QSet<QString> _changedProperties;
TableSetBase *_tableSet;
//TODO: is this removable?
TableSetBase *_parentTableSet;
QSet<TableSetBase*> tableSets;
QSet<TableSetBase*> childTableSets;
void clear();
void add(TableSetBase *);

View File

@ -144,6 +144,23 @@ bool TableModel::operator !=(const TableModel &t) const
return !(*this == t);
}
bool TableModel::checkClassInfo(const QMetaClassInfo &classInfo,
QString &type, QString &name, QString &value)
{
if (!QString(classInfo.name()).startsWith(__nut_NAME_PERFIX)) {
return false;
} else {
QStringList parts = QString(classInfo.value()).split("\n");
if (parts.count() != 3)
return false;
type = parts[0];
name = parts[1];
value = parts[2];
return true;
}
}
TableModel::TableModel(int typeId, QString tableName)
{
//TODO: check that
@ -163,20 +180,19 @@ 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("\"", "");
QString type;
QString name;
QString value;
name = name.remove(__nut_NAME_PERFIX);
if (!checkClassInfo(tableMetaObject->classInfo(j),
type, name, value)) {
continue;
}
if(name.contains(" ")){
QStringList parts = name.split(" ");
QString propName = parts.at(1);
if(propName == __nut_FIELD){
FieldModel *f = new FieldModel;
f->name = parts.at(0);
_fields.append(f);
}
if(type == __nut_FIELD){
FieldModel *f = new FieldModel;
f->name = name;
_fields.append(f);
}
}
// Browse all fields
@ -195,47 +211,48 @@ TableModel::TableModel(int typeId, QString tableName)
// Browse class infos
for(int j = 0; j < tableMetaObject->classInfoCount(); j++){
QString name = tableMetaObject->classInfo(j).name();
QString value = tableMetaObject->classInfo(j).value();
QString type;
QString name;
QString value;
name = name.replace("\"", "").remove(__nut_NAME_PERFIX);
value = value.replace("\"", "");
if (!checkClassInfo(tableMetaObject->classInfo(j),
type, name, value)) {
continue;
}
if(name.contains(" ")){
QStringList parts = name.split(" ");
QString propName = parts.at(1);
if(type == __nut_FOREGION_KEY){
RelationModel *fk = new RelationModel;
fk->slaveTable = this;
fk->localColumn = name + "Id";
fk->localProperty = name;
fk->foregionColumn = value;
fk->masterClassName = value;
_foregionKeys.append(fk);
}
if(propName == __nut_FOREGION_KEY){
RelationModel *fk = new RelationModel;
fk->localColumn = parts.at(0);
fk->foregionColumn = value;
fk->className = value;
_foregionKeys.append(fk);
}
if(propName == __nut_FIELD){
}
FieldModel *f = field(parts.at(0));
if(!f)
continue;
if(propName == __nut_LEN)
f->length = value.toInt();
else if(propName == __nut_NOT_NULL)
f->notNull = true;
else if(propName == __nut_DEFAULT_VALUE)
f->defaultValue = value;
else if(propName == __nut_PRIMARY_KEY)
f->isPrimaryKey = true;
else if(propName == __nut_AUTO_INCREMENT)
f->isAutoIncrement = true;
else if(propName == __nut_UNIQUE)
f->isUnique = true;
if(type == __nut_FIELD){
}
FieldModel *f = field(name);
if(!f)
continue;
if(type == __nut_LEN)
f->length = value.toInt();
else if(type == __nut_NOT_NULL)
f->notNull = true;
else if(type == __nut_DEFAULT_VALUE)
f->defaultValue = value;
else if(type == __nut_PRIMARY_KEY)
f->isPrimaryKey = true;
else if(type == __nut_AUTO_INCREMENT)
f->isAutoIncrement = true;
else if(type == __nut_UNIQUE)
f->isUnique = true;
}
if(!findByTypeId(typeId) && !tableName.isNull())
@ -355,7 +372,7 @@ QJsonObject TableModel::toJson() const
RelationModel *TableModel::foregionKey(QString otherTable) const
{
foreach (RelationModel *fk, _foregionKeys)
if(fk->className == otherTable)
if(fk->masterClassName == otherTable)
return fk;
return 0;

View File

@ -65,10 +65,15 @@ struct FieldModel{
};
struct RelationModel{
QString className;
//slave
QString localColumn;
TableModel *table;
QString localProperty;
TableModel *slaveTable;
//master
QString foregionColumn;
TableModel *masterTable;
QString masterClassName;
};
class TableModel
{
@ -117,6 +122,8 @@ private:
QList<FieldModel*> _fields;
QList<RelationModel*> _foregionKeys;
static QSet<TableModel*>_allModels;
bool checkClassInfo(const QMetaClassInfo &classInfo,
QString &type, QString &name, QString &value);
};
NUT_END_NAMESPACE

View File

@ -109,7 +109,7 @@ Q_OUTOFLINE_TEMPLATE void TableSet<T>::append(T *t)
_tables.insert(t);
_tablesList.append(t);
// rows.append(t);
t->setTableSet(this);
t->setParentTableSet(this);
if(t->status() != Table::FeatchedFromDB)
t->setStatus(Table::Added);
}

View File

@ -40,7 +40,6 @@ public:
virtual int save(Database *db, bool cleanUp = false);
void clearChilds();
void add(Table* t);
QString childClassName() const;
Database *database() const;
@ -53,6 +52,12 @@ protected:
Database *_database;
Table *_table;
QString _childClassName;
private:
void add(Table* t);
friend class Table;
friend class QueryBase;
};
NUT_END_NAMESPACE

View File

@ -230,4 +230,5 @@ WherePhrase WherePhrase::operator>=(const QVariant &other)
return WherePhrase(this, PhraseData::GreaterEqual, other);
}
NUT_END_NAMESPACE

View File

@ -145,6 +145,8 @@ class FieldPhrase : public WherePhrase
public:
FieldPhrase(const char *className, const char *s);
WherePhrase operator=(const FieldPhrase<T> &other);
WherePhrase operator=(const WherePhrase &other);
WherePhrase operator=(const QVariant &other);
WherePhrase operator+(const QVariant &other);
@ -179,6 +181,13 @@ operator=(const QVariant &other)
return WherePhrase(this, PhraseData::Set, other);
}
template <typename T>
Q_OUTOFLINE_TEMPLATE WherePhrase
FieldPhrase<T>::operator=(const FieldPhrase<T> &other)
{
return WherePhrase(this, PhraseData::Equal, &other);
}
template <typename T>
Q_OUTOFLINE_TEMPLATE WherePhrase FieldPhrase<T>::
operator=(const WherePhrase &other)

View File

@ -14,6 +14,7 @@
#include "post.h"
#include "comment.h"
#define PRINT(x) qDebug() << #x "=" << x;
MainTest::MainTest(QObject *parent) : QObject(parent)
{
}
@ -46,7 +47,16 @@ void MainTest::dataScheema()
// qDebug() << model.toJson();
// qDebug() << db.model().toJson();
// QTEST_ASSERT(model == db.model());
// QTEST_ASSERT(model == db.model());
}
void MainTest::createUser()
{
user = new User;
user->setUsername("admin");
user->setPassword("123456");
db.users()->append(user);
db.saveChanges();
}
void MainTest::createPost()
@ -61,6 +71,7 @@ void MainTest::createPost()
Comment *comment = new Comment;
comment->setMessage("comment #" + QString::number(i));
comment->setSaveDate(QDateTime::currentDateTime());
comment->setAuthorId(user->id());
newPost->comments()->append(comment);
}
db.saveChanges();
@ -84,6 +95,7 @@ void MainTest::createPost2()
Comment *comment = new Comment;
comment->setMessage("comment #" + QString::number(i));
comment->setSaveDate(QDateTime::currentDateTime());
comment->setAuthor(user);
comment->setPostId(newPost->id());
db.comments()->append(comment);
}
@ -96,18 +108,16 @@ void MainTest::createPost2()
void MainTest::selectPosts()
{
auto q = db.posts()->query()
// q->join(Post::commentsTable());
// q->join(Post::commentsTable());
->join<User>()
->join<Comment>()
->join<Comment>()//Comment::authorIdField() == Post::idField())
->orderBy(!Post::saveDateField() & Post::bodyField())
->setWhere(Post::idField() == postId);
auto posts = q->toList();
qDebug() << "SQL="<<q->sqlCommand();
post = posts.at(0);
post->setBody("");
PRINT(posts.length());
PRINT(posts.at(0)->comments()->length());
QTEST_ASSERT(posts.length() == 1);
QTEST_ASSERT(posts.at(0)->comments()->length() == 3);
QTEST_ASSERT(posts.at(0)->title() == "post title");
@ -118,12 +128,21 @@ void MainTest::selectPosts()
db.cleanUp();
}
void MainTest::selectFirst()
{
auto q = db.posts()->query();
auto posts = q->first();
qDebug() << q->sqlCommand();
QTEST_ASSERT(posts != Q_NULLPTR);
}
void MainTest::selectPostsWithoutTitle()
{
auto q = db.posts()->query();
q->setWhere(Post::titleField().isNull());
auto count = q->count();
qDebug() << q->sqlCommand();
QTEST_ASSERT(count == 0);
}
@ -158,14 +177,16 @@ void MainTest::testDate()
void MainTest::join()
{
auto q = db.comments()->query()
// ->join(Comment::author())
// ->join(Comment::post())
->join<Post>()
->setWhere(Comment::saveDateField() < QDateTime::currentDateTime().addDays(-1))
->orderBy(Comment::saveDateField());
->join<User>()
->join<Post>();
q->toList();
// Comment *comment = q->first();
auto comments = q->toList();
// Comment *comment = q->toList().first();
qDebug() << q->sqlCommand();
QTEST_ASSERT(comments.length());
QTEST_ASSERT(comments[0]->author());
QTEST_ASSERT(comments[0]->author()->username() == "admin");
}
@ -176,13 +197,6 @@ void MainTest::selectWithInvalidRelation()
q->toList();
}
void MainTest::select10NewstPosts()
{
auto q = db.posts()->query();
q->orderBy(!Post::saveDateField());
q->toList(10);
}
void MainTest::modifyPost()
{
auto q = db.posts()->query();
@ -199,6 +213,7 @@ void MainTest::modifyPost()
->setWhere(Post::idField() == postId);
post = q->first();
PRINT(post->title());
QTEST_ASSERT(post->title() == "new name");
}
@ -206,7 +221,6 @@ void MainTest::emptyDatabase()
{
auto commentsCount = db.comments()->query()->remove();
auto postsCount = db.posts()->query()->remove();
QTEST_ASSERT(postsCount == 3);
QTEST_ASSERT(commentsCount == 6);
}

View File

@ -6,12 +6,14 @@
#include "weblogdatabase.h"
class Post;
class User;
class MainTest : public QObject
{
Q_OBJECT
WeblogDatabase db;
int postId;
Post *post;
User *user;
public:
explicit MainTest(QObject *parent = 0);
@ -22,13 +24,15 @@ private slots:
void initTestCase();
void dataScheema();
void createUser();
void createPost();
void createPost2();
void join();
void selectPosts();
void selectFirst();
void selectPostsWithoutTitle();
void selectPostIds();
void testDate();
void join();
void selectWithInvalidRelation();
void select10NewstPosts();
void modifyPost();

View File

@ -13,7 +13,8 @@ SOURCES += \
../common/comment.cpp \
../common/post.cpp \
../common/user.cpp \
../common/weblogdatabase.cpp
../common/weblogdatabase.cpp \
../common/score.cpp
HEADERS += \
maintest.h \
@ -21,4 +22,5 @@ HEADERS += \
../common/comment.h \
../common/post.h \
../common/user.h \
../common/weblogdatabase.h
../common/weblogdatabase.h \
../common/score.h

View File

@ -1,6 +1,7 @@
#include "comment.h"
Comment::Comment(QObject *parent) : Table(parent)
Comment::Comment(QObject *parent) : Table(parent),
m_author(Q_NULLPTR), m_post(Q_NULLPTR)
{
}

View File

@ -25,7 +25,7 @@ class Comment : public Table
NUT_FOREGION_KEY(User, int, author, author, setAuthor)
public:
Q_INVOKABLE explicit Comment(QObject *tableSet = 0);
Q_INVOKABLE explicit Comment(QObject *parentTableSet = 0);
};
Q_DECLARE_METATYPE(Comment*)

View File

@ -1,9 +1,12 @@
#include "post.h"
#include "comment.h"
#include "score.h"
#include "tableset.h"
Post::Post(QObject *parent) : Table(parent),
m_comments(new TableSet<Comment>(this)), m_id(0), m_title("")
m_id(0), m_title(""),
m_comments(new TableSet<Comment>(this)),
m_scores(new TableSet<Score>(this))
{
}

View File

@ -11,6 +11,7 @@ using namespace NUT_NAMESPACE;
#endif
class Comment;
class Score;
class Post : public Table
{
Q_OBJECT
@ -27,9 +28,10 @@ class Post : public Table
NUT_DECLARE_FIELD(QString, body, body, setBody)
NUT_DECLARE_CHILD_TABLE(Comment, comments)
NUT_DECLARE_CHILD_TABLE(Score, scores)
public:
Q_INVOKABLE Post(QObject *tableSet = 0);
Q_INVOKABLE Post(QObject *parentTableSet = 0);
signals:

6
test/common/score.cpp Normal file
View File

@ -0,0 +1,6 @@
#include "score.h"
Score::Score(QObject *parent) : Nut::Table(parent)
{
}

24
test/common/score.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef SCORE_H
#define SCORE_H
#include "table.h"
class User;
class Post;
class Score : public Nut::Table
{
Q_OBJECT
NUT_PRIMARY_AUTO_INCREMENT(id)
NUT_DECLARE_FIELD(int, id, id, setId)
NUT_DECLARE_FIELD(int, score, score, setScore)
NUT_FOREGION_KEY(Post, int, post, post, setPost)
NUT_FOREGION_KEY(User, int, user, user, setUser)
public:
Q_INVOKABLE Score(QObject *parent = Q_NULLPTR);
};
#endif // SCORE_H

View File

@ -1,9 +1,11 @@
#include "comment.h"
#include "score.h"
#include "user.h"
#include "comment.h"
User::User(QObject *tableSet) : Table(tableSet),
m_comments(new TableSet<Comment>(this))
m_comments(new TableSet<Comment>(this)),
m_scores(new TableSet<Score>(this))
{
}

View File

@ -12,12 +12,13 @@ using namespace NUT_NAMESPACE;
#endif
class Comment;
class Score;
class User : public Nut::Table
{
Q_OBJECT
NUT_PRIMARY_AUTO_INCREMENT(id)
NUT_DECLARE_FIELD(QUuid, id, id, setId)
NUT_DECLARE_FIELD(int, id, id, setId)
NUT_NOT_NULL(username)
NUT_LEN(username, 50)
@ -28,9 +29,10 @@ class User : public Nut::Table
NUT_DECLARE_FIELD(QString, password, password, setPassword)
NUT_DECLARE_CHILD_TABLE(Comment, comments)
NUT_DECLARE_CHILD_TABLE(Score, scores)
public:
Q_INVOKABLE User(QObject *tableSet = 0);
Q_INVOKABLE User(QObject *parentTableSet = 0);
};
Q_DECLARE_METATYPE(User*)

View File

@ -4,11 +4,13 @@
#include "post.h"
#include "comment.h"
#include "user.h"
#include "score.h"
#include "weblogdatabase.h"
WeblogDatabase::WeblogDatabase() : Database(),
m_posts(new TableSet<Post>(this)),
m_comments(new TableSet<Comment>(this)),
m_users(new TableSet<User>(this))
m_users(new TableSet<User>(this)),
m_scores(new TableSet<Score>(this))
{
}

View File

@ -10,6 +10,7 @@ using namespace NUT_NAMESPACE;
class Post;
class Comment;
class User;
class Score;
class WeblogDatabase : public Database
{
Q_OBJECT
@ -19,6 +20,7 @@ class WeblogDatabase : public Database
NUT_DECLARE_TABLE(Post, post)
NUT_DECLARE_TABLE(Comment, comment)
NUT_DECLARE_TABLE(User, user)
NUT_DECLARE_TABLE(Score, score)
public:
WeblogDatabase();

77
test/join/jointest.cpp Normal file
View File

@ -0,0 +1,77 @@
#include <QtTest>
#include <QJsonDocument>
#include <QSqlError>
#include "consts.h"
#include "jointest.h"
#include "query.h"
#include "tableset.h"
#include "tablemodel.h"
#include "databasemodel.h"
#include "user.h"
#include "post.h"
#include "comment.h"
#include "score.h"
#define PRINT(x) qDebug() << #x "=" << x;
JoinTest::JoinTest(QObject *parent) : QObject(parent)
{
}
void JoinTest::initTestCase()
{
qDebug() << "User type id:" << qRegisterMetaType<User*>();
qDebug() << "Post type id:" << qRegisterMetaType<Post*>();
qDebug() << "Comment type id:" << qRegisterMetaType<Comment*>();
qDebug() << "Score type id:" << qRegisterMetaType<Score*>();
qDebug() << "DB type id:" << qRegisterMetaType<WeblogDatabase*>();
db.setDriver(DRIVER);
db.setHostName(HOST);
db.setDatabaseName("nut_tst_join.db");
db.setUserName(USERNAME);
db.setPassword(PASSWORD);
bool ok = db.open();
// db.comments()->query()->remove();
// db.posts()->query()->remove();
QTEST_ASSERT(ok);
}
void JoinTest::join()
{
auto q = db.comments()->query()
->join<User>()
->join<Post>();
// Comment *comment = q->first();
auto comments = q->toList();
// Comment *comment = q->toList().first();
// qDebug() << q->sqlCommand();
PRINT(comments.length());
// QTEST_ASSERT(comments.length());
// QTEST_ASSERT(comments[0]->author());
// QTEST_ASSERT(comments[0]->author()->username() == "admin");
}
void JoinTest::join2()
{
auto q = db.users()->query()
->join<Comment>()
->join<Score>();
// Comment *comment = q->first();
auto comments = q->toList();
// Comment *comment = q->toList().first();
// qDebug() << q->sqlCommand();
// QTEST_ASSERT(comments.length());
// QTEST_ASSERT(comments[0]->author());
// QTEST_ASSERT(comments[0]->author()->username() == "admin");
}
QTEST_MAIN(JoinTest)

26
test/join/jointest.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef JOINTEST_H
#define JOINTEST_H
#include <QtCore/QObject>
#include <QtCore/qglobal.h>
#include "weblogdatabase.h"
class JoinTest : public QObject
{
Q_OBJECT
WeblogDatabase db;
public:
explicit JoinTest(QObject *parent = 0);
signals:
private slots:
void initTestCase();
void join();
void join2();
};
#endif // JOINTEST_H

26
test/join/tst_join.pro Normal file
View File

@ -0,0 +1,26 @@
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 += \
jointest.cpp \
../common/comment.cpp \
../common/post.cpp \
../common/user.cpp \
../common/weblogdatabase.cpp \
../common/score.cpp
HEADERS += \
jointest.h \
../common/consts.h \
../common/comment.h \
../common/post.h \
../common/user.h \
../common/weblogdatabase.h \
../common/score.h