wip: detect and apply foreign keys
This commit is contained in:
parent
9c0df27eea
commit
fa21efde1b
|
|
@ -141,7 +141,7 @@ bool DatabasePrivate::updateDatabase()
|
|||
qDebug("Databse is changed");
|
||||
|
||||
QStringList sql = sqlGenertor->diff(last, current);
|
||||
|
||||
qDebug()<<"database Sql =\n"<<sql;
|
||||
db.transaction();
|
||||
foreach (QString s, sql) {
|
||||
db.exec(s);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#define __NAME "name"
|
||||
#define __TYPE "type"
|
||||
#define __FIELDS "fields"
|
||||
#define __FOREIGN_KEYS "foreign_keys"
|
||||
#define __nut_FIELD "field"
|
||||
|
||||
#define __nut_DB_VERSION "database_version"
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ QString SqlGeneratorBase::masterDatabaseName(QString databaseName)
|
|||
|
||||
QString SqlGeneratorBase::createTable(TableModel *table)
|
||||
{
|
||||
Q_UNUSED(table);
|
||||
Q_UNUSED(table)
|
||||
return "";
|
||||
}
|
||||
|
||||
|
|
@ -114,6 +114,13 @@ QString SqlGeneratorBase::fieldDeclare(FieldModel *field)
|
|||
return field->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<QString> fieldNames;
|
||||
QList<QString> fieldNames;
|
||||
QList<QString> 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<QString> 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<RelationModel*> 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())
|
||||
|
|
|
|||
|
|
@ -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<RelationModel*> 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);
|
||||
|
|
|
|||
|
|
@ -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<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("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
|
||||
|
|
|
|||
|
|
@ -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<WherePhrase> &wheres,
|
||||
// QList<WherePhrase> &orders,
|
||||
// QList<RelationModel *> joins, int skip, int take);
|
||||
};
|
||||
|
||||
NUT_END_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ QList<FieldModel *> TableModel::fields() const
|
|||
|
||||
QList<RelationModel *> 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<QVariant::Type>(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
|
||||
|
|
|
|||
|
|
@ -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<FieldModel*> _fields;
|
||||
QList<RelationModel*> _foregionKeys;
|
||||
QList<RelationModel*> _foreignKeys;
|
||||
static QSet<TableModel*>_allModels;
|
||||
bool checkClassInfo(const QMetaClassInfo &classInfo,
|
||||
QString &type, QString &name, QString &value);
|
||||
|
|
|
|||
Loading…
Reference in New Issue