diff --git a/src/database.cpp b/src/database.cpp index 749aa83..d8ac915 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -141,7 +141,7 @@ bool DatabasePrivate::updateDatabase() qDebug("Databse is changed"); QStringList sql = sqlGenertor->diff(last, current); - +qDebug()<<"database Sql =\n"<name + " " + fieldType(field) + (field->notNull ? " NOT NULL" : ""); } +QString SqlGeneratorBase::relationDeclare(const RelationModel *relation) +{ + return QString("FOREIGN KEY (FK_%1) REFERENCES %2(%1)") + .arg(relation->localColumn) + .arg(relation->slaveTable->name()); +} + QStringList SqlGeneratorBase::diff(DatabaseModel lastModel, DatabaseModel newModel) { @@ -125,9 +132,11 @@ QStringList SqlGeneratorBase::diff(DatabaseModel lastModel, TableModel *oldTable = lastModel.tableByName(table->name()); TableModel *newTable = newModel.tableByName(table->name()); QString sql = diff(oldTable, newTable); - if (!sql.isEmpty()) ret << sql; +// QString sqlRel = diffRelation(oldTable, newTable); +// if (!sqlRel.isEmpty()) +// ret << sqlRel; } return ret; @@ -161,14 +170,24 @@ QString SqlGeneratorBase::diff(TableModel *oldTable, TableModel *newTable) if (!newTable) return "DROP TABLE " + oldTable->name(); - QSet fieldNames; + QList fieldNames; + QList relations; - if (oldTable) + if (oldTable) { foreach (FieldModel *f, oldTable->fields()) - fieldNames.insert(f->name); + if (!fieldNames.contains(f->name)) + fieldNames.append(f->name); + foreach (RelationModel *r, oldTable->foregionKeys()) + if (!relations.contains(r->localColumn)) + relations.append(r->localColumn); + } foreach (FieldModel *f, newTable->fields()) - fieldNames.insert(f->name); + if (!fieldNames.contains(f->name)) + fieldNames.append(f->name); + foreach (RelationModel *r, newTable->foregionKeys()) + if (!relations.contains(r->localColumn)) + relations.append(r->localColumn); QStringList columnSql; foreach (QString fieldName, fieldNames) { @@ -183,22 +202,104 @@ QString SqlGeneratorBase::diff(TableModel *oldTable, TableModel *newTable) columnSql << fieldDeclare(newField); } } +// foreach (QString fieldName, relations) { +// RelationModel *newRelation = newTable->foregionKeyByField(fieldName); +// if (oldTable) { +// RelationModel *oldRelation = oldTable->foregionKeyByField(fieldName); + +// QString buffer = diff(oldRelation, newRelation); +// if (!buffer.isNull()) +// columnSql << buffer; +// } else { +// columnSql << relationDeclare(newRelation); +// } +// } QString sql; if (oldTable) { - sql = QString("ALTER TABLE %1 \n%2").arg(newTable->name()).arg( - columnSql.join(",\n")); + sql = QString("ALTER TABLE %1 \n%2") + .arg(newTable->name()) + .arg(columnSql.join(",\n")); } else { if (!newTable->primaryKey().isNull()) columnSql << QString("CONSTRAINT pk_%1 PRIMARY KEY (%2)") .arg(newTable->name()) .arg(newTable->primaryKey()); - sql = QString("CREATE TABLE %1 \n(%2)").arg(newTable->name()).arg( - columnSql.join(",\n")); + sql = QString("CREATE TABLE %1 \n(%2)") + .arg(newTable->name()) + .arg(columnSql.join(",\n")); } return sql; } +QString SqlGeneratorBase::diffRelation(TableModel *oldTable, TableModel *newTable) +{ + if (!newTable) + return ""; + + QList relations; + + if (oldTable) + foreach (RelationModel *r, oldTable->foregionKeys()) + if (!relations.contains(r->localColumn)) + relations.append(r->localColumn); + + foreach (RelationModel *r, newTable->foregionKeys()) + if (!relations.contains(r->localColumn)) + relations.append(r->localColumn); + + QStringList columnSql; + foreach (QString fieldName, relations) { + RelationModel *newRelation = newTable->foregionKeyByField(fieldName); + RelationModel *oldRelation = 0; + if (oldTable) + oldRelation = oldTable->foregionKeyByField(fieldName); + + QString buffer = diff(oldRelation, newRelation); + if (!buffer.isNull()) + columnSql << buffer; + } + + if (columnSql.count()) + return "ALTER TABLE " + newTable->name() + "\n" + + columnSql.join(",\n"); + else + return ""; + +} + +QString SqlGeneratorBase::diff(RelationModel *oldRel, RelationModel *newRel) +{ + /* + CONSTRAINT FK_PersonOrder FOREIGN KEY (PersonID) + REFERENCES Persons(PersonID) + + ADD CONSTRAINT FK_%1 FOREIGN KEY (%1) REFERENCES %2(%3) + + return QString("ADD CONSTRAINT FK_%1 FOREIGN KEY (%1) " + "REFERENCES %2(%3)") + .arg(newRelation->localColumn) + .arg(newRelation->masterTable->name()) + .arg(newRelation->foreignColumn); + */ + if (!oldRel) + return QString("ADD CONSTRAINT FK_%1 FOREIGN KEY (%1) " + "REFERENCES %2(%3)") + .arg(newRel->localColumn) + .arg(newRel->masterTable->name()) + .arg(newRel->foreignColumn); + + if (!newRel) + return QString("ADD CONSTRAINT FK_%1 FOREIGN KEY (%1) " + "REFERENCES %2(%3)") + .arg(oldRel->localColumn) + .arg(oldRel->masterTable->name()) + .arg(oldRel->foreignColumn); + +// if (*oldRel == *newRel) + return ""; +} + QString SqlGeneratorBase::join(const QString &mainTable, const QList list, QStringList *order) @@ -235,7 +336,7 @@ QString SqlGeneratorBase::join(const QStringList &list, QStringList *order) //TODO: make this ungly code better and bugless :-) /* * Known issues: - * Support onle near joins, far supports with medium table finding not support yet + * Support only near joins, far supports with medium table finding not support yet */ if (!list.count()) diff --git a/src/generators/sqlgeneratorbase_p.h b/src/generators/sqlgeneratorbase_p.h index ab9223c..650caba 100644 --- a/src/generators/sqlgeneratorbase_p.h +++ b/src/generators/sqlgeneratorbase_p.h @@ -65,10 +65,13 @@ public: virtual QString fieldType(FieldModel *field) = 0; virtual QString fieldDeclare(FieldModel *field); + virtual QString relationDeclare(const RelationModel *relation); virtual QStringList diff(DatabaseModel lastModel, DatabaseModel newModel); virtual QString diff(FieldModel *oldField, FieldModel *newField); virtual QString diff(TableModel *oldTable, TableModel *newTable); + virtual QString diffRelation(TableModel *oldTable, TableModel *newTable); + virtual QString diff(RelationModel *oldRel, RelationModel *newRel); virtual QString join(const QString &mainTable, const QList list, @@ -120,7 +123,7 @@ public: virtual QString phrase(const PhraseData *d) const; virtual QString operatorString(const PhraseData::Condition &cond) const; virtual void appendSkipTake(QString &sql, int skip = -1, int take = -1); -private: +protected: QString createConditionalPhrase(const PhraseData *d) const; QString createFieldPhrase(const PhraseList &ph); QString createOrderPhrase(const PhraseList &ph); diff --git a/src/generators/sqlservergenerator.cpp b/src/generators/sqlservergenerator.cpp index 13f43fd..549312b 100644 --- a/src/generators/sqlservergenerator.cpp +++ b/src/generators/sqlservergenerator.cpp @@ -91,6 +91,7 @@ QString SqlServerGenerator::fieldType(FieldModel *field) break; default: + Q_UNREACHABLE(); dbType = ""; } @@ -133,23 +134,11 @@ QString SqlServerGenerator::escapeValue(const QVariant &v) const return SqlGeneratorBase::escapeValue(v); } -//QString SqlServerGenerator::selectCommand(SqlGeneratorBase::AgregateType t, -// QString agregateArg, -// QString tableName, -// QList &wheres, -// QList &orders, -// QList 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("OFFSET %1 ROWS FETCH NEXT %2 ROWS ONLY") -// .arg(skip) -// .arg(take)); -// return command; -//} +void SqlServerGenerator::appendSkipTake(QString &sql, int skip, int take) +{ + if (take != -1 && skip != -1) + sql.append(QString(" OFFSET %1 ROWS FETCH NEXT %2 ROWS ONLY") + .arg(skip).arg(take)); +} NUT_END_NAMESPACE diff --git a/src/generators/sqlservergenerator.h b/src/generators/sqlservergenerator.h index db47268..28f6dca 100644 --- a/src/generators/sqlservergenerator.h +++ b/src/generators/sqlservergenerator.h @@ -37,12 +37,8 @@ public: QString diff(FieldModel *oldField, FieldModel *newField); QString escapeValue(const QVariant &v) const; + void appendSkipTake(QString &sql, int skip, int take); -// QString selectCommand(AgregateType t, QString agregateArg, -// QString tableName, -// QList &wheres, -// QList &orders, -// QList joins, int skip, int take); }; NUT_END_NAMESPACE diff --git a/src/tablemodel.cpp b/src/tablemodel.cpp index 4f428bc..882edd7 100644 --- a/src/tablemodel.cpp +++ b/src/tablemodel.cpp @@ -90,7 +90,7 @@ QList TableModel::fields() const QList TableModel::foregionKeys() const { - return _foregionKeys; + return _foreignKeys; } QStringList TableModel::fieldsNames() const @@ -237,9 +237,9 @@ TableModel::TableModel(int typeId, QString tableName) fk->slaveTable = this; fk->localColumn = name + "Id"; fk->localProperty = name; - fk->foregionColumn = value; + fk->foreignColumn = value; fk->masterClassName = value; - _foregionKeys.append(fk); + _foreignKeys.append(fk); } if(type == __nut_FIELD){ @@ -292,6 +292,7 @@ TableModel::TableModel(QJsonObject json, QString tableName) _name = tableName; QJsonObject fields = json.value(__FIELDS).toObject(); + QJsonObject relations = json.value(__FOREIGN_KEYS).toObject(); foreach (QString key, fields.keys()) { QJsonObject fieldObject = fields.value(key).toObject(); FieldModel *f = new FieldModel; @@ -309,11 +310,16 @@ TableModel::TableModel(QJsonObject json, QString tableName) _fields.append(f); } - if(json.keys().contains(__nut_AUTO_INCREMENT)) - field(json.value(__nut_AUTO_INCREMENT).toString())->isAutoIncrement = true; + foreach (QString key, relations.keys()) { + QJsonObject relObject = fields.value(key).toObject(); + _foreignKeys.append(new RelationModel(relObject)); + } - if(json.keys().contains(__nut_PRIMARY_KEY)) - field(json.value(__nut_PRIMARY_KEY).toString())->isAutoIncrement = true; +// if(json.keys().contains(__nut_AUTO_INCREMENT)) +// field(json.value(__nut_AUTO_INCREMENT).toString())->isAutoIncrement = true; + +// if(json.keys().contains(__nut_PRIMARY_KEY)) +// field(json.value(__nut_PRIMARY_KEY).toString())->isAutoIncrement = true; _allModels.insert(this); } @@ -322,6 +328,7 @@ QJsonObject TableModel::toJson() const { QJsonObject obj; QJsonObject fieldsObj; + QJsonObject foreignKeysObj; foreach (FieldModel *f, _fields) { QJsonObject fieldObj; @@ -337,28 +344,41 @@ QJsonObject TableModel::toJson() const if(!f->defaultValue.isNull()) fieldObj.insert(__nut_DEFAULT_VALUE, f->defaultValue); - fieldsObj.insert(f->name, fieldObj); - if(f->isAutoIncrement) - obj.insert(__nut_PRIMARY_KEY, f->name); + obj.insert(__nut_AUTO_INCREMENT, f->name); if(f->isPrimaryKey) - obj.insert(__nut_AUTO_INCREMENT, f->name); + obj.insert(__nut_PRIMARY_KEY, f->name); + + fieldsObj.insert(f->name, fieldObj); } + foreach (RelationModel *rel, _foreignKeys) + foreignKeysObj.insert(rel->localColumn, rel->toJson()); + obj.insert(__FIELDS, fieldsObj); + obj.insert(__FOREIGN_KEYS, foreignKeysObj); return obj; } -RelationModel *TableModel::foregionKey(QString otherTable) const +RelationModel *TableModel::foregionKey(const QString &otherTable) const { - foreach (RelationModel *fk, _foregionKeys) + foreach (RelationModel *fk, _foreignKeys) if(fk->masterClassName == otherTable) return fk; return 0; } +RelationModel *TableModel::foregionKeyByField(const QString &fieldName) const +{ + foreach (RelationModel *fk, _foreignKeys) + if(fk->localColumn == fieldName) + return fk; + + return 0; +} + QString TableModel::toString() const { QStringList sl; @@ -379,4 +399,59 @@ QString TableModel::primaryKey() const return QString::null; } +FieldModel::FieldModel(const QJsonObject &json) +{ + name = json.value(__NAME).toString(); + type = static_cast(json.value(__TYPE).toInt()); + length = json.value(__nut_LEN).toInt(); + notNull = json.value(__nut_NOT_NULL).toBool(); + isAutoIncrement = json.value(__nut_AUTO_INCREMENT).toBool(); + isPrimaryKey = json.value(__nut_PRIMARY_KEY).toBool(); + defaultValue = json.value(__nut_DEFAULT_VALUE).toString(); +} + +QJsonObject FieldModel::toJson() const +{ + QJsonObject fieldObj; + fieldObj.insert(__NAME, name); + fieldObj.insert(__TYPE, QString(QVariant::typeToName(type))); + fieldObj.insert(__nut_LEN, length); + fieldObj.insert(__nut_NOT_NULL, notNull); + fieldObj.insert(__nut_AUTO_INCREMENT, isAutoIncrement); + fieldObj.insert(__nut_PRIMARY_KEY, isPrimaryKey); + fieldObj.insert(__nut_DEFAULT_VALUE, defaultValue); + return fieldObj; +} + +RelationModel::RelationModel(const QJsonObject &obj) +{ + localColumn = obj.value("localColumn").toString(); + localProperty = obj.value("localProperty").toString(); + masterClassName = obj.value("masterClassName").toString(); + foreignColumn = obj.value("foreignColumn").toString(); +} + +QJsonObject RelationModel::toJson() const +{ + QJsonObject o; + o.insert("localColumn", localColumn); + o.insert("localProperty", localProperty); + o.insert("masterClassName", masterClassName); + o.insert("foreignColumn", foreignColumn); + return o; +} + +bool operator ==(const RelationModel &l, const RelationModel &r) +{ + return r.foreignColumn == l.foreignColumn + && r.localColumn == l.localColumn + && r.localProperty == l.localColumn + && r.masterClassName == l.masterClassName; +} + +bool operator !=(const RelationModel &l, const RelationModel &r) +{ + return !(l == r); +} + NUT_END_NAMESPACE diff --git a/src/tablemodel.h b/src/tablemodel.h index e089ed2..4fe2f66 100644 --- a/src/tablemodel.h +++ b/src/tablemodel.h @@ -37,6 +37,8 @@ struct FieldModel{ } + FieldModel(const QJsonObject &json); + QString name; //TODO: QMetaType::Type?? QVariant::Type type; @@ -63,19 +65,30 @@ struct FieldModel{ bool operator !=(const FieldModel &f) const{ return !(*this == f); } + + QJsonObject toJson() const; }; struct RelationModel{ + RelationModel() : localColumn(""), localProperty(""), slaveTable(0), + foreignColumn(""), masterTable(0), masterClassName("") + {} + RelationModel(const QJsonObject &obj); + //slave QString localColumn; QString localProperty; TableModel *slaveTable; //master - QString foregionColumn; + QString foreignColumn; TableModel *masterTable; QString masterClassName; + + QJsonObject toJson() const; }; +bool operator ==(const RelationModel &l, const RelationModel &r); +bool operator !=(const RelationModel &l, const RelationModel &r); class TableModel { public: @@ -91,7 +104,8 @@ public: FieldModel *field(int n) const; FieldModel *field(QString name) const; - RelationModel *foregionKey(QString otherTable) const; + RelationModel *foregionKey(const QString &otherTable) const; + RelationModel *foregionKeyByField(const QString &fieldName) const; QString toString() const; @@ -122,7 +136,7 @@ private: QString _className; int _typeId; QList _fields; - QList _foregionKeys; + QList _foreignKeys; static QSet_allModels; bool checkClassInfo(const QMetaClassInfo &classInfo, QString &type, QString &name, QString &value);