From 38144bbcadc68aec424583774d336922d1690cb1 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Mon, 23 Jan 2017 20:59:12 +0100 Subject: [PATCH] Finish main part of the recent refactoring effort This finally gets rid of the DBBrowserObject class entirely and moves all its functionality to the newer classes in the sqlb namespace. I'm still not entirely happy with this but at least things should be a little more consistent now. --- src/DbStructureModel.cpp | 37 +++++++-------------- src/DbStructureModel.h | 4 +-- src/EditIndexDialog.cpp | 8 ++--- src/EditTableDialog.cpp | 6 ++-- src/ExportDataDialog.cpp | 4 +-- src/ExportSqlDialog.cpp | 2 +- src/ForeignKeyEditorDelegate.cpp | 6 ++-- src/ImportCsvDialog.cpp | 4 +-- src/MainWindow.cpp | 4 +-- src/VacuumDialog.cpp | 8 ++--- src/sqlitedb.cpp | 56 +++++++++++++++++--------------- src/sqlitedb.h | 23 +------------ src/sqlitetypes.cpp | 12 +++++++ src/sqlitetypes.h | 3 +- 14 files changed, 79 insertions(+), 98 deletions(-) diff --git a/src/DbStructureModel.cpp b/src/DbStructureModel.cpp index bbe9dc5f..e77921bb 100644 --- a/src/DbStructureModel.cpp +++ b/src/DbStructureModel.cpp @@ -170,40 +170,32 @@ void DbStructureModel::reloadData() typeToParentItem.insert("trigger", itemTriggers); // Get all database objects and sort them by their name - QMultiMap dbobjs; + QMultiMap dbobjs; for(auto it=m_db.objMap.constBegin(); it != m_db.objMap.constEnd(); ++it) - dbobjs.insert((*it).getname(), (*it)); + dbobjs.insert((*it)->name(), (*it)); // Add the actual table objects for(auto it=dbobjs.constBegin();it!=dbobjs.constEnd();++it) { // Object node - QString type; - switch((*it).gettype()) - { - case sqlb::Object::Types::Table: type = "table"; break; - case sqlb::Object::Types::Index: type = "index"; break; - case sqlb::Object::Types::Trigger: type = "trigger"; break; - case sqlb::Object::Types::View: type = "view"; break; - } - QTreeWidgetItem* item = addNode(typeToParentItem.value(type), *it); + QTreeWidgetItem* item = addNode(typeToParentItem.value(sqlb::Object::typeToString((*it)->type())), *it); // If it is a table or view add the field nodes - if((*it).gettype() == sqlb::Object::Types::Table || (*it).gettype() == sqlb::Object::Types::View) + if((*it)->type() == sqlb::Object::Types::Table || (*it)->type() == sqlb::Object::Types::View) { // Add extra node for browsable section addNode(typeToParentItem.value("browsable"), *it); // Add field nodes QStringList pk_columns; - if(it->gettype() == sqlb::Object::Types::Table) + if((*it)->type() == sqlb::Object::Types::Table) { - sqlb::FieldVector pk = it->object.dynamicCast()->primaryKey(); + sqlb::FieldVector pk = (*it).dynamicCast()->primaryKey(); foreach(sqlb::FieldPtr pk_col, pk) pk_columns.push_back(pk_col->name()); } - sqlb::FieldInfoList fieldList = it->object->fieldInformation(); + sqlb::FieldInfoList fieldList = (*it)->fieldInformation(); foreach(const sqlb::FieldInfo& field, fieldList) { QTreeWidgetItem *fldItem = new QTreeWidgetItem(item); @@ -293,22 +285,15 @@ bool DbStructureModel::dropMimeData(const QMimeData* data, Qt::DropAction action } } -QTreeWidgetItem* DbStructureModel::addNode(QTreeWidgetItem* parent, const DBBrowserObject& object) +QTreeWidgetItem* DbStructureModel::addNode(QTreeWidgetItem* parent, const sqlb::ObjectPtr& object) { - QString type; - switch(object.gettype()) - { - case sqlb::Object::Types::Table: type = "table"; break; - case sqlb::Object::Types::Index: type = "index"; break; - case sqlb::Object::Types::Trigger: type = "trigger"; break; - case sqlb::Object::Types::View: type = "view"; break; - } + QString type = sqlb::Object::typeToString(object->type()); QTreeWidgetItem *item = new QTreeWidgetItem(parent); item->setIcon(0, QIcon(QString(":/icons/%1").arg(type))); - item->setText(0, object.getname()); + item->setText(0, object->name()); item->setText(1, type); - item->setText(3, object.getsql()); + item->setText(3, object->originalSql()); return item; } diff --git a/src/DbStructureModel.h b/src/DbStructureModel.h index 45aab828..784ccdb0 100644 --- a/src/DbStructureModel.h +++ b/src/DbStructureModel.h @@ -4,8 +4,8 @@ #include class DBBrowserDB; -class DBBrowserObject; class QTreeWidgetItem; +namespace sqlb { class Object; typedef QSharedPointer ObjectPtr; } class DbStructureModel : public QAbstractItemModel { @@ -33,7 +33,7 @@ private: QTreeWidgetItem* rootItem; DBBrowserDB& m_db; - QTreeWidgetItem* addNode(QTreeWidgetItem* parent, const DBBrowserObject& object); + QTreeWidgetItem* addNode(QTreeWidgetItem* parent, const sqlb::ObjectPtr& object); }; #endif diff --git a/src/EditIndexDialog.cpp b/src/EditIndexDialog.cpp index 96d1e9d4..f63c48e1 100644 --- a/src/EditIndexDialog.cpp +++ b/src/EditIndexDialog.cpp @@ -17,13 +17,13 @@ EditIndexDialog::EditIndexDialog(DBBrowserDB& db, const QString& indexName, bool ui->setupUi(this); // Get list of tables, sort it alphabetically and fill the combobox - QMultiMap dbobjs; - QList tables = pdb.objMap.values("table"); + objectMap dbobjs; + QList tables = pdb.objMap.values("table"); for(auto it=tables.constBegin();it!=tables.constEnd();++it) - dbobjs.insert((*it).getname(), (*it)); + dbobjs.insert((*it)->name(), (*it)); ui->comboTableName->blockSignals(true); for(auto it=dbobjs.constBegin();it!=dbobjs.constEnd();++it) - ui->comboTableName->addItem(QIcon(QString(":icons/table")), (*it).getname()); + ui->comboTableName->addItem(QIcon(QString(":icons/table")), (*it)->name()); ui->comboTableName->blockSignals(false); QHeaderView *tableHeaderView = ui->tableIndexColumns->horizontalHeader(); diff --git a/src/EditTableDialog.cpp b/src/EditTableDialog.cpp index 70a8dd57..f7d725ce 100644 --- a/src/EditTableDialog.cpp +++ b/src/EditTableDialog.cpp @@ -272,9 +272,9 @@ void EditTableDialog::itemChanged(QTreeWidgetItem *item, int column) if(!m_bNewTable) { sqlb::FieldVector pk = m_table.primaryKey(); - foreach(const DBBrowserObject& fkobj, pdb.objMap.values("table")) + foreach(const sqlb::ObjectPtr& fkobj, pdb.objMap.values("table")) { - QList fks = fkobj.object.dynamicCast()->constraints(sqlb::FieldVector(), sqlb::Constraint::ForeignKeyConstraintType); + QList fks = fkobj.dynamicCast()->constraints(sqlb::FieldVector(), sqlb::Constraint::ForeignKeyConstraintType); foreach(sqlb::ConstraintPtr fkptr, fks) { QSharedPointer fk = fkptr.dynamicCast(); @@ -284,7 +284,7 @@ void EditTableDialog::itemChanged(QTreeWidgetItem *item, int column) { QMessageBox::warning(this, qApp->applicationName(), tr("This column is referenced in a foreign key in table %1 and thus " "its name cannot be changed.") - .arg(fkobj.getname())); + .arg(fkobj->name())); // Reset the name to the old value but avoid calling this method again for that automatic change ui->treeWidget->blockSignals(true); item->setText(column, oldFieldName); diff --git a/src/ExportDataDialog.cpp b/src/ExportDataDialog.cpp index fd982165..736df164 100644 --- a/src/ExportDataDialog.cpp +++ b/src/ExportDataDialog.cpp @@ -40,8 +40,8 @@ ExportDataDialog::ExportDataDialog(DBBrowserDB& db, ExportFormats format, QWidge { // Get list of tables to export objectMap objects = pdb.getBrowsableObjects(); - foreach(const DBBrowserObject& obj, objects) - ui->listTables->addItem(new QListWidgetItem(QIcon(QString(":icons/%1").arg(obj.gettype())), obj.getname())); + foreach(const sqlb::ObjectPtr& obj, objects) + ui->listTables->addItem(new QListWidgetItem(QIcon(QString(":icons/%1").arg(sqlb::Object::typeToString(obj->type()))), obj->name())); // Sort list of tables and select the table specified in the selection parameter or alternatively the first one ui->listTables->model()->sort(0); diff --git a/src/ExportSqlDialog.cpp b/src/ExportSqlDialog.cpp index 1de45fc9..f33e2903 100644 --- a/src/ExportSqlDialog.cpp +++ b/src/ExportSqlDialog.cpp @@ -27,7 +27,7 @@ ExportSqlDialog::ExportSqlDialog(DBBrowserDB* db, QWidget* parent, const QString // Get list of tables to export objectMap objects = pdb->getBrowsableObjects(); for(auto it=objects.constBegin();it!=objects.constEnd();++it) { - ui->listTables->addItem(new QListWidgetItem(QIcon(QString(":icons/%1").arg(it.value().gettype())), it.value().getname())); + ui->listTables->addItem(new QListWidgetItem(QIcon(QString(":icons/%1").arg((*it)->type())), (*it)->name())); } // Sort list of tables and select the table specified in the diff --git a/src/ForeignKeyEditorDelegate.cpp b/src/ForeignKeyEditorDelegate.cpp index 805b87c2..973c00d3 100644 --- a/src/ForeignKeyEditorDelegate.cpp +++ b/src/ForeignKeyEditorDelegate.cpp @@ -81,9 +81,9 @@ ForeignKeyEditorDelegate::ForeignKeyEditorDelegate(const DBBrowserDB& db, sqlb:: { const auto objects = m_db.getBrowsableObjects(); for (auto& obj : objects) { - if (obj.gettype() == sqlb::Object::Types::Table) { - QString tableName = obj.object->name(); - m_tablesIds.insert(tableName, obj.object.dynamicCast()->fieldNames()); + if (obj->type() == sqlb::Object::Types::Table) { + QString tableName = obj->name(); + m_tablesIds.insert(tableName, obj.dynamicCast()->fieldNames()); } } } diff --git a/src/ImportCsvDialog.cpp b/src/ImportCsvDialog.cpp index da54c7ac..823b61a9 100644 --- a/src/ImportCsvDialog.cpp +++ b/src/ImportCsvDialog.cpp @@ -186,9 +186,9 @@ void ImportCsvDialog::accept() objectMap objects = pdb->getBrowsableObjects(); for(auto it=objects.constBegin();it!=objects.constEnd();++it) { - if(it.value().gettype() == sqlb::Object::Types::Table && it.value().getname() == ui->editName->text()) + if((*it)->type() == sqlb::Object::Types::Table && (*it)->name() == ui->editName->text()) { - if((size_t)it.value().object.dynamicCast()->fields().size() != csv.columns()) + if((size_t)(*it).dynamicCast()->fields().size() != csv.columns()) { QMessageBox::warning(this, QApplication::applicationName(), tr("There is already a table of that name and an import into an existing table is only possible if the number of columns match.")); diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index c8118468..959a550c 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -365,9 +365,9 @@ void MainWindow::populateStructure() SqlUiLexer::TablesAndColumnsMap tablesToColumnsMap; for(auto it=tab.constBegin();it!=tab.constEnd();++it) { - QString objectname = it.value().getname(); + QString objectname = (*it)->name(); - sqlb::FieldInfoList fi = it->object->fieldInformation(); + sqlb::FieldInfoList fi = (*it)->fieldInformation(); foreach(const sqlb::FieldInfo& f, fi) tablesToColumnsMap[objectname].append(f.name); } diff --git a/src/VacuumDialog.cpp b/src/VacuumDialog.cpp index 766bea3d..9e6e0042 100644 --- a/src/VacuumDialog.cpp +++ b/src/VacuumDialog.cpp @@ -16,13 +16,13 @@ VacuumDialog::VacuumDialog(DBBrowserDB* _db, QWidget* parent) : ui->labelSavepointWarning->setVisible(db->getDirty()); // Populate list of objects to compact - QList objects = db->objMap.values("table"); + QList objects = db->objMap.values("table"); objects.append(db->objMap.values("index")); - for(QList::const_iterator i=objects.constBegin();i!=objects.constEnd();++i) + for(QList::const_iterator i=objects.constBegin();i!=objects.constEnd();++i) { QTreeWidgetItem* item = new QTreeWidgetItem(ui->treeSelectedObjects); - item->setText(0, (*i).getname()); - item->setIcon(0, QIcon(QString(":icons/%1").arg((*i).gettype()))); + item->setText(0, (*i)->name()); + item->setIcon(0, QIcon(QString(":icons/%1").arg(sqlb::Object::typeToString((*i)->type())))); ui->treeSelectedObjects->addTopLevelItem(item); } diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index 2ecfbcd3..6c2dc1b4 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -458,20 +458,20 @@ bool DBBrowserDB::dump(const QString& filename, QApplication::setOverrideCursor(Qt::WaitCursor); size_t numRecordsTotal = 0, numRecordsCurrent = 0; - QList tables = objMap.values("table"); - QMutableListIterator it(tables); + QList tables = objMap.values("table"); + QMutableListIterator it(tables); while(it.hasNext()) { it.next(); // Remove the sqlite_stat1 table if there is one - if(it.value().getname() == "sqlite_stat1" || it.value().getname() == "sqlite_sequence") + if(it.value()->name() == "sqlite_stat1" || it.value()->name() == "sqlite_sequence") { it.remove(); } else { // Otherwise get the number of records in this table SqliteTableModel tableModel(*this); - tableModel.setTable(it.value().getname()); + tableModel.setTable(it.value()->name()); numRecordsTotal += tableModel.totalRowCount(); } } @@ -491,21 +491,21 @@ bool DBBrowserDB::dump(const QString& filename, // Loop through all tables first as they are required to generate views, indices etc. later for(auto it=tables.constBegin();it!=tables.constEnd();++it) { - if (tablesToDump.indexOf(it->getname()) == -1) + if (tablesToDump.indexOf((*it)->name()) == -1) continue; // Write the SQL string used to create this table to the output file if(exportSchema) - stream << it->getsql() << ";\n"; + stream << (*it)->originalSql() << ";\n"; // If the user doesn't want the data to be exported skip the rest of the loop block here if(!exportData) continue; // get columns - QStringList cols(it->object.dynamicCast()->fieldNames()); + QStringList cols((*it).dynamicCast()->fieldNames()); - QString sQuery = QString("SELECT * FROM %1;").arg(sqlb::escapeIdentifier(it->getname())); + QString sQuery = QString("SELECT * FROM %1;").arg(sqlb::escapeIdentifier((*it)->name())); QByteArray utf8Query = sQuery.toUtf8(); sqlite3_stmt *stmt; QString lineSep(QString(")%1\n").arg(insertNewSyntx?',':';')); @@ -522,7 +522,7 @@ bool DBBrowserDB::dump(const QString& filename, if (!insertNewSyntx || !counter) { - stream << "INSERT INTO " << sqlb::escapeIdentifier(it->getname()); + stream << "INSERT INTO " << sqlb::escapeIdentifier((*it)->name()); if (insertColNames) stream << " (" << cols.join(",") << ")"; stream << " VALUES ("; @@ -594,12 +594,12 @@ bool DBBrowserDB::dump(const QString& filename, for(auto it=objMap.constBegin();it!=objMap.constEnd();++it) { // Make sure it's not a table again - if(it.value().gettype() == sqlb::Object::Types::Table) + if(it.value()->type() == sqlb::Object::Types::Table) continue; // Write the SQL string used to create this object to the output file - if(!it->getsql().isEmpty()) - stream << it->getsql() << ";\n"; + if(!(*it)->originalSql().isEmpty()) + stream << (*it)->originalSql() << ";\n"; } } @@ -1076,13 +1076,13 @@ bool DBBrowserDB::renameColumn(const QString& tablename, const sqlb::Table& tabl for(auto it=objMap.constBegin();it!=objMap.constEnd();++it) { // If this object references the table and it's not the table itself save it's SQL string - if((*it).getTableName() == tablename && (*it).gettype() != sqlb::Object::Types::Table) + if((*it)->baseTable() == tablename && (*it)->type() != sqlb::Object::Types::Table) { // If this is an index, update the fields first. This highly increases the chance that the SQL statement won't throw an // error later on when we try to recreate it. - if((*it).gettype() == sqlb::Object::Types::Index) + if((*it)->type() == sqlb::Object::Types::Index) { - sqlb::IndexPtr idx = (*it).object.dynamicCast(); + sqlb::IndexPtr idx = (*it).dynamicCast(); for(int i=0;icolumns().size();i++) { if(idx->column(i)->name() == name) @@ -1092,7 +1092,7 @@ bool DBBrowserDB::renameColumn(const QString& tablename, const sqlb::Table& tabl } else { // If it's a view or a trigger we don't have any chance to corrections yet. Just store the statement as is and // hope for the best. - otherObjectsSql << (*it).getsql().trimmed() + ";"; + otherObjectsSql << (*it)->originalSql().trimmed() + ";"; } } } @@ -1183,8 +1183,8 @@ const sqlb::ObjectPtr DBBrowserDB::getObjectByName(const QString& name) const { for(auto it=objMap.constBegin();it!=objMap.constEnd();++it) { - if((*it).getname() == name) - return it->object; + if((*it)->name() == name) + return *it; } return sqlb::ObjectPtr(nullptr); } @@ -1252,35 +1252,39 @@ void DBBrowserDB::updateSchema( ) else continue; - DBBrowserObject obj(val_name, val_sql, type, val_tblname); if(!val_sql.isEmpty()) { - obj.object = sqlb::Object::parseSQL(type, val_sql); + sqlb::ObjectPtr object = sqlb::Object::parseSQL(type, val_sql); if(val_temp == "1") - obj.object->setTemporary(true); + object->setTemporary(true); + + // If parsing wasn't successful set the object name manually, so that at least the name is going to be correct + if(!object->fullyParsed()) + object->setName(val_name); // For virtual tables and views query the column list using the SQLite pragma because for both we can't yet rely on our grammar parser - if((type == sqlb::Object::Types::Table && obj.object.dynamicCast()->isVirtual()) || type == sqlb::Object::Types::View) + if((type == sqlb::Object::Types::Table && object.dynamicCast()->isVirtual()) || type == sqlb::Object::Types::View) { auto columns = queryColumnInformation(val_name); if(type == sqlb::Object::Types::Table) { - sqlb::TablePtr tab = obj.object.dynamicCast(); + sqlb::TablePtr tab = object.dynamicCast(); foreach(const auto& column, columns) tab->addField(sqlb::FieldPtr(new sqlb::Field(column.first, column.second))); } else { - sqlb::ViewPtr view = obj.object.dynamicCast(); + sqlb::ViewPtr view = object.dynamicCast(); foreach(const auto& column, columns) view->addField(sqlb::FieldPtr(new sqlb::Field(column.first, column.second))); } } else if(type == sqlb::Object::Types::Trigger) { // For triggers set the name of the table the trigger operates on here because we don't have a parser for trigger statements yet. - sqlb::TriggerPtr trg = obj.object.dynamicCast(); + sqlb::TriggerPtr trg = object.dynamicCast(); trg->setTable(val_tblname); } + + objMap.insert(val_type, object); } - objMap.insert(val_type, obj); } sqlite3_finalize(vm); }else{ diff --git a/src/sqlitedb.h b/src/sqlitedb.h index 4e446252..e68c36f6 100644 --- a/src/sqlitedb.h +++ b/src/sqlitedb.h @@ -16,28 +16,7 @@ enum kLogMsg_App }; -typedef QMultiMap objectMap; - -class DBBrowserObject -{ -public: - DBBrowserObject() : name( "" ) { } - DBBrowserObject(const QString& wname, const QString& wsql, sqlb::Object::Types wtype, const QString& tbl_name) - : name( wname), sql( wsql ), type(wtype), table_name(tbl_name) - { } - - QString getname() const { return name; } - QString getsql() const { return sql; } - sqlb::Object::Types gettype() const { return type; } - QString getTableName() const { return table_name; } - - sqlb::ObjectPtr object; -private: - QString name; - QString sql; - sqlb::Object::Types type; - QString table_name; // The name of the table this object references, interesting for views, triggers and indices -}; +typedef QMultiMap objectMap; class DBBrowserDB : public QObject { diff --git a/src/sqlitetypes.cpp b/src/sqlitetypes.cpp index 27b7f48a..c6a2dd5b 100644 --- a/src/sqlitetypes.cpp +++ b/src/sqlitetypes.cpp @@ -91,6 +91,18 @@ ObjectPtr Object::parseSQL(Object::Types type, const QString& sSQL) return result; } +QString Object::typeToString(Types type) +{ + switch(type) + { + case Types::Table: return "table"; + case Types::Index: return "index"; + case Types::View: return "view"; + case Types::Trigger: return "trigger"; + } + return QString(); +} + bool ForeignKeyClause::isSet() const { return m_override.size() || m_table.size(); diff --git a/src/sqlitetypes.h b/src/sqlitetypes.h index ea0a047f..1b3c659c 100644 --- a/src/sqlitetypes.h +++ b/src/sqlitetypes.h @@ -20,7 +20,7 @@ class Trigger; class Field; class Constraint; class IndexedColumn; -struct FieldInfo; +class FieldInfo; typedef QSharedPointer ObjectPtr; typedef QSharedPointer TablePtr; typedef QSharedPointer IndexPtr; @@ -58,6 +58,7 @@ public: virtual ~Object() {} virtual Types type() const = 0; + static QString typeToString(Types type); void setName(const QString& name) { m_name = name; } const QString& name() const { return m_name; }