commit
ac6e55b60d
|
|
@ -7,6 +7,3 @@ before_install:
|
|||
script:
|
||||
- qmake nut.pro
|
||||
- make
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ public:
|
|||
DatabaseModel getLastScheema();
|
||||
bool getCurrectScheema();
|
||||
|
||||
bool checkClassInfo(const QMetaClassInfo &classInfo,
|
||||
QString &type, QString &name, QString &value);
|
||||
QSqlDatabase db;
|
||||
|
||||
QString hostName;
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 "";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
|||
296
src/query.h
296
src/query.h
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
13
src/table.h
13
src/table.h
|
|
@ -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 *);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -230,4 +230,5 @@ WherePhrase WherePhrase::operator>=(const QVariant &other)
|
|||
return WherePhrase(this, PhraseData::GreaterEqual, other);
|
||||
}
|
||||
|
||||
|
||||
NUT_END_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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*)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
#include "score.h"
|
||||
|
||||
Score::Score(QObject *parent) : Nut::Table(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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))
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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*)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue