diff --git a/src/DbStructureModel.cpp b/src/DbStructureModel.cpp index a2e6a9ca..68e3da93 100644 --- a/src/DbStructureModel.cpp +++ b/src/DbStructureModel.cpp @@ -14,7 +14,7 @@ DbStructureModel::DbStructureModel(DBBrowserDB& db, QObject* parent) { // Create root item and use its columns to store the header strings QStringList header; - header << tr("Name") << tr("Object") << tr("Type") << tr("Schema"); + header << tr("Name") << tr("Object") << tr("Type") << tr("Schema") << tr("Database"); rootItem = new QTreeWidgetItem(header); } @@ -60,7 +60,7 @@ Qt::ItemFlags DbStructureModel::flags(const QModelIndex &index) const Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled; // Only enable dragging for entire table objects - QString type = data(index.sibling(index.row(), 1), Qt::DisplayRole).toString(); + QString type = data(index.sibling(index.row(), ColumnObjectType), Qt::DisplayRole).toString(); if(type == "table" || type == "view" || type == "index" || type == "trigger") flags |= Qt::ItemIsDragEnabled; @@ -138,77 +138,22 @@ void DbStructureModel::reloadData() // In the root node there are two nodes: 'browsables' and 'all'. The first node contains a list of a all browsable objects, i.e. views and tables. // The seconds node contains four sub-nodes (tables, indices, views and triggers), each containing a list of objects of that type. // This way we only have to have and only have to update one model and can use it in all sorts of places, just by setting a different root node. - QMap typeToParentItem; - QTreeWidgetItem* itemBrowsables = new QTreeWidgetItem(rootItem); - itemBrowsables->setIcon(0, QIcon(QString(":/icons/view"))); - itemBrowsables->setText(0, tr("Browsables (%1)").arg(m_db.schemata["main"].values("table").count() + m_db.schemata["main"].values("view").count())); - typeToParentItem.insert("browsable", itemBrowsables); + itemBrowsables->setIcon(ColumnName, QIcon(QString(":/icons/view"))); + itemBrowsables->setText(ColumnName, tr("Browsables")); QTreeWidgetItem* itemAll = new QTreeWidgetItem(rootItem); - itemAll->setIcon(0, QIcon(QString(":/icons/view"))); - itemAll->setText(0, tr("All")); + itemAll->setIcon(ColumnName, QIcon(QString(":/icons/database"))); + itemAll->setText(ColumnName, tr("All")); + buildTree(itemAll, itemBrowsables, "main"); - QTreeWidgetItem* itemTables = new QTreeWidgetItem(itemAll); - itemTables->setIcon(0, QIcon(QString(":/icons/table"))); - itemTables->setText(0, tr("Tables (%1)").arg(m_db.schemata["main"].values("table").count())); - typeToParentItem.insert("table", itemTables); - - QTreeWidgetItem* itemIndices = new QTreeWidgetItem(itemAll); - itemIndices->setIcon(0, QIcon(QString(":/icons/index"))); - itemIndices->setText(0, tr("Indices (%1)").arg(m_db.schemata["main"].values("index").count())); - typeToParentItem.insert("index", itemIndices); - - QTreeWidgetItem* itemViews = new QTreeWidgetItem(itemAll); - itemViews->setIcon(0, QIcon(QString(":/icons/view"))); - itemViews->setText(0, tr("Views (%1)").arg(m_db.schemata["main"].values("view").count())); - typeToParentItem.insert("view", itemViews); - - QTreeWidgetItem* itemTriggers = new QTreeWidgetItem(itemAll); - itemTriggers->setIcon(0, QIcon(QString(":/icons/trigger"))); - itemTriggers->setText(0, tr("Triggers (%1)").arg(m_db.schemata["main"].values("trigger").count())); - typeToParentItem.insert("trigger", itemTriggers); - - // Get all database objects and sort them by their name - QMultiMap dbobjs; - for(auto it=m_db.schemata["main"].constBegin(); it != m_db.schemata["main"].constEnd(); ++it) - dbobjs.insert((*it)->name(), (*it)); - - // Add the actual table objects - for(auto it=dbobjs.constBegin();it!=dbobjs.constEnd();++it) + // Add the temporary database as a node if it isn't empty + if(!m_db.schemata["temp"].isEmpty()) { - // Object node - QTreeWidgetItem* item = addNode(typeToParentItem.value(sqlb::Object::typeToString((*it)->type())), *it); - - // If it is a table or view add the field nodes, add an extra node for the browsable section - if((*it)->type() == sqlb::Object::Types::Table || (*it)->type() == sqlb::Object::Types::View) - addNode(typeToParentItem.value("browsable"), *it); - - // Add field nodes if there are any - sqlb::FieldInfoList fieldList = (*it)->fieldInformation(); - if(!fieldList.empty()) - { - QStringList pk_columns; - if((*it)->type() == sqlb::Object::Types::Table) - { - sqlb::FieldVector pk = (*it).dynamicCast()->primaryKey(); - foreach(sqlb::FieldPtr pk_col, pk) - pk_columns.push_back(pk_col->name()); - - } - foreach(const sqlb::FieldInfo& field, fieldList) - { - QTreeWidgetItem *fldItem = new QTreeWidgetItem(item); - fldItem->setText(0, field.name); - fldItem->setText(1, "field"); - fldItem->setText(2, field.type); - fldItem->setText(3, field.sql); - if(pk_columns.contains(field.name)) - fldItem->setIcon(0, QIcon(":/icons/field_key")); - else - fldItem->setIcon(0, QIcon(":/icons/field")); - } - } + QTreeWidgetItem* itemTemp = new QTreeWidgetItem(itemAll); + itemTemp->setIcon(ColumnName, QIcon(QString(":/icons/database"))); + itemTemp->setText(ColumnName, tr("Temporary")); + buildTree(itemTemp, itemBrowsables, "temp"); } // Refresh the view @@ -229,19 +174,20 @@ QMimeData* DbStructureModel::mimeData(const QModelIndexList& indices) const foreach(QModelIndex index, indices) { // Only export data for valid indices and only for the SQL column, i.e. only once per row - if(index.isValid() && index.column() == 3) + if(index.isValid() && index.column() == ColumnSQL) { // Add the SQL code used to create the object d = d.append(data(index, Qt::DisplayRole).toString() + ";\n"); // If it is a table also add the content - if(data(index.sibling(index.row(), 1), Qt::DisplayRole).toString() == "table") + if(data(index.sibling(index.row(), ColumnObjectType), Qt::DisplayRole).toString() == "table") { SqliteTableModel tableModel(m_db); - tableModel.setTable(sqlb::ObjectIdentifier("main", data(index.sibling(index.row(), 0), Qt::DisplayRole).toString())); + tableModel.setTable(sqlb::ObjectIdentifier(data(index.sibling(index.row(), ColumnSchema), Qt::DisplayRole).toString(), + data(index.sibling(index.row(), ColumnName), Qt::DisplayRole).toString())); for(int i=0; i < tableModel.rowCount(); ++i) { - QString insertStatement = "INSERT INTO " + sqlb::escapeIdentifier(data(index.sibling(index.row(), 0), Qt::DisplayRole).toString()) + " VALUES("; + QString insertStatement = "INSERT INTO " + sqlb::escapeIdentifier(data(index.sibling(index.row(), ColumnName), Qt::DisplayRole).toString()) + " VALUES("; for(int j=1; j < tableModel.columnCount(); ++j) insertStatement += QString("'%1',").arg(tableModel.data(tableModel.index(i, j)).toString()); insertStatement.chop(1); @@ -285,15 +231,94 @@ bool DbStructureModel::dropMimeData(const QMimeData* data, Qt::DropAction action } } -QTreeWidgetItem* DbStructureModel::addNode(QTreeWidgetItem* parent, const sqlb::ObjectPtr& object) +void DbStructureModel::buildTree(QTreeWidgetItem* parent, QTreeWidgetItem* browsables, const QString& schema) +{ + // Build a map from object type to tree node to simplify finding the correct tree node later + QMap typeToParentItem; + + // Get object map for the given schema + objectMap objmap = m_db.schemata[schema]; + + // Prepare tree + QTreeWidgetItem* itemTables = new QTreeWidgetItem(parent); + itemTables->setIcon(ColumnName, QIcon(QString(":/icons/table"))); + itemTables->setText(ColumnName, tr("Tables (%1)").arg(objmap.values("table").count())); + typeToParentItem.insert("table", itemTables); + + QTreeWidgetItem* itemIndices = new QTreeWidgetItem(parent); + itemIndices->setIcon(ColumnName, QIcon(QString(":/icons/index"))); + itemIndices->setText(ColumnName, tr("Indices (%1)").arg(objmap.values("index").count())); + typeToParentItem.insert("index", itemIndices); + + QTreeWidgetItem* itemViews = new QTreeWidgetItem(parent); + itemViews->setIcon(ColumnName, QIcon(QString(":/icons/view"))); + itemViews->setText(ColumnName, tr("Views (%1)").arg(objmap.values("view").count())); + typeToParentItem.insert("view", itemViews); + + QTreeWidgetItem* itemTriggers = new QTreeWidgetItem(parent); + itemTriggers->setIcon(ColumnName, QIcon(QString(":/icons/trigger"))); + itemTriggers->setText(ColumnName, tr("Triggers (%1)").arg(objmap.values("trigger").count())); + typeToParentItem.insert("trigger", itemTriggers); + + // Get all database objects and sort them by their name + QMultiMap dbobjs; + for(auto it=objmap.constBegin(); it != objmap.constEnd(); ++it) + dbobjs.insert((*it)->name(), (*it)); + + // Add the database objects to the tree nodes + for(auto it=dbobjs.constBegin();it!=dbobjs.constEnd();++it) + { + // Object node + QTreeWidgetItem* item = addNode(typeToParentItem.value(sqlb::Object::typeToString((*it)->type())), *it, schema); + + // If it is a table or view add the field nodes, add an extra node for the browsable section + if((*it)->type() == sqlb::Object::Types::Table || (*it)->type() == sqlb::Object::Types::View) + { + // TODO We're currently only adding objects in the main schema to the list of browsable objects because browsing non-main objects + // isn't really supported in the main window yet. As soon as this is implemented the following if statement can be removed. + if(schema == "main") + addNode(browsables, *it, schema); + } + + // Add field nodes if there are any + sqlb::FieldInfoList fieldList = (*it)->fieldInformation(); + if(!fieldList.empty()) + { + QStringList pk_columns; + if((*it)->type() == sqlb::Object::Types::Table) + { + sqlb::FieldVector pk = (*it).dynamicCast()->primaryKey(); + foreach(sqlb::FieldPtr pk_col, pk) + pk_columns.push_back(pk_col->name()); + + } + foreach(const sqlb::FieldInfo& field, fieldList) + { + QTreeWidgetItem *fldItem = new QTreeWidgetItem(item); + fldItem->setText(ColumnName, field.name); + fldItem->setText(ColumnObjectType, "field"); + fldItem->setText(ColumnDataType, field.type); + fldItem->setText(ColumnSQL, field.sql); + fldItem->setText(ColumnSchema, schema); + if(pk_columns.contains(field.name)) + fldItem->setIcon(ColumnName, QIcon(":/icons/field_key")); + else + fldItem->setIcon(ColumnName, QIcon(":/icons/field")); + } + } + } +} + +QTreeWidgetItem* DbStructureModel::addNode(QTreeWidgetItem* parent, const sqlb::ObjectPtr& object, const QString& schema) { 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->name()); - item->setText(1, type); - item->setText(3, object->originalSql()); + item->setIcon(ColumnName, QIcon(QString(":/icons/%1").arg(type))); + item->setText(ColumnName, object->name()); + item->setText(ColumnObjectType, type); + item->setText(ColumnSQL, object->originalSql()); + item->setText(ColumnSchema, schema); return item; } diff --git a/src/DbStructureModel.h b/src/DbStructureModel.h index 784ccdb0..f1bb764a 100644 --- a/src/DbStructureModel.h +++ b/src/DbStructureModel.h @@ -29,11 +29,21 @@ public: QMimeData* mimeData(const QModelIndexList& indices) const; bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent); + enum Columns + { + ColumnName, + ColumnObjectType, + ColumnDataType, + ColumnSQL, + ColumnSchema, + }; + private: QTreeWidgetItem* rootItem; DBBrowserDB& m_db; - QTreeWidgetItem* addNode(QTreeWidgetItem* parent, const sqlb::ObjectPtr& object); + void buildTree(QTreeWidgetItem* parent, QTreeWidgetItem* browsables, const QString& schema); + QTreeWidgetItem* addNode(QTreeWidgetItem* parent, const sqlb::ObjectPtr& object, const QString& schema); }; #endif diff --git a/src/EditIndexDialog.cpp b/src/EditIndexDialog.cpp index 88971cae..b3ea640d 100644 --- a/src/EditIndexDialog.cpp +++ b/src/EditIndexDialog.cpp @@ -5,11 +5,11 @@ #include #include -EditIndexDialog::EditIndexDialog(DBBrowserDB& db, const QString& indexName, bool createIndex, QWidget* parent) +EditIndexDialog::EditIndexDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier& indexName, bool createIndex, QWidget* parent) : QDialog(parent), pdb(db), curIndex(indexName), - index(indexName), + index(indexName.name()), newIndex(createIndex), ui(new Ui::EditIndexDialog), m_sRestorePointName(pdb.generateSavepointName("editindex")) @@ -18,13 +18,31 @@ EditIndexDialog::EditIndexDialog(DBBrowserDB& db, const QString& indexName, bool ui->setupUi(this); // Get list of tables, sort it alphabetically and fill the combobox - objectMap dbobjs; - QList tables = pdb.schemata["main"].values("table"); - for(auto it=tables.constBegin();it!=tables.constEnd();++it) - dbobjs.insert((*it)->name(), (*it)); + QMap dbobjs; // Map from display name to full object identifier + if(newIndex) // If this is a new index, offer all tables of all database schemata + { + for(auto it=pdb.schemata.constBegin();it!=pdb.schemata.constEnd();++it) + { + QList tables = it->values("table"); + for(auto jt=tables.constBegin();jt!=tables.constEnd();++jt) + { + // Only show the schema name for non-main schemata + sqlb::ObjectIdentifier obj(it.key(), (*jt)->name()); + dbobjs.insert(obj.toDisplayString(), obj); + } + } + } else { // If this is an existing index, only offer tables of the current database schema + QList tables = pdb.schemata[curIndex.schema()].values("table"); + for(auto it=tables.constBegin();it!=tables.constEnd();++it) + { + // Only show the schema name for non-main schemata + sqlb::ObjectIdentifier obj(curIndex.schema(), (*it)->name()); + dbobjs.insert(obj.toDisplayString(), obj); + } + } ui->comboTableName->blockSignals(true); for(auto it=dbobjs.constBegin();it!=dbobjs.constEnd();++it) - ui->comboTableName->addItem(QIcon(QString(":icons/table")), (*it)->name()); + ui->comboTableName->addItem(QIcon(QString(":icons/table")), it.key(), it.value().toVariant()); ui->comboTableName->blockSignals(false); QHeaderView *tableHeaderView = ui->tableIndexColumns->horizontalHeader(); @@ -34,7 +52,7 @@ EditIndexDialog::EditIndexDialog(DBBrowserDB& db, const QString& indexName, bool if(!newIndex) { // Load the current layout and fill in the dialog fields - index = *(pdb.getObjectByName(sqlb::ObjectIdentifier("main", curIndex)).dynamicCast()); + index = *(pdb.getObjectByName(curIndex).dynamicCast()); ui->editIndexName->blockSignals(true); ui->editIndexName->setText(index.name()); @@ -76,7 +94,7 @@ void EditIndexDialog::tableChanged(const QString& new_table, bool initialLoad) // Set the table name and clear all index columns if(!initialLoad) { - index.setTable(new_table); + index.setTable(sqlb::ObjectIdentifier(ui->comboTableName->currentData()).name()); index.clearColumns(); } @@ -94,7 +112,7 @@ void EditIndexDialog::tableChanged(const QString& new_table, bool initialLoad) void EditIndexDialog::updateColumnLists() { // Fill the table column list - sqlb::FieldInfoList tableFields = pdb.getObjectByName(sqlb::ObjectIdentifier("main", index.table())).dynamicCast()->fieldInformation(); + sqlb::FieldInfoList tableFields = pdb.getObjectByName(sqlb::ObjectIdentifier(ui->comboTableName->currentData())).dynamicCast()->fieldInformation(); ui->tableTableColumns->setRowCount(tableFields.size()); int tableRows = 0; for(int i=0;iapplicationName(), tr("Deleting the old index failed:\n%1").arg(pdb.lastError())); return; } } - // Create the new index - if(pdb.executeSQL(index.sql())) + // Create the new index in the schema of the selected table + if(pdb.executeSQL(index.sql(sqlb::ObjectIdentifier(ui->comboTableName->currentData()).schema()))) QDialog::accept(); else QMessageBox::warning(this, QApplication::applicationName(), tr("Creating the index failed:\n%1").arg(pdb.lastError())); @@ -261,7 +279,7 @@ void EditIndexDialog::reject() void EditIndexDialog::updateSqlText() { - ui->sqlTextEdit->setText(index.sql()); + ui->sqlTextEdit->setText(index.sql(sqlb::ObjectIdentifier(ui->comboTableName->currentData()).schema())); } void EditIndexDialog::moveColumnUp() diff --git a/src/EditIndexDialog.h b/src/EditIndexDialog.h index ba967b90..ad6d28b3 100644 --- a/src/EditIndexDialog.h +++ b/src/EditIndexDialog.h @@ -17,7 +17,7 @@ class EditIndexDialog : public QDialog Q_OBJECT public: - explicit EditIndexDialog(DBBrowserDB& db, const QString& indexName, bool createIndex, QWidget* parent = 0); + explicit EditIndexDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier& indexName, bool createIndex, QWidget* parent = 0); ~EditIndexDialog(); private slots: @@ -34,7 +34,7 @@ private slots: private: DBBrowserDB& pdb; - QString curIndex; + sqlb::ObjectIdentifier curIndex; sqlb::Index index; bool newIndex; Ui::EditIndexDialog* ui; diff --git a/src/EditTableDialog.cpp b/src/EditTableDialog.cpp index 71f77994..4e26d4c0 100644 --- a/src/EditTableDialog.cpp +++ b/src/EditTableDialog.cpp @@ -11,12 +11,12 @@ #include #include -EditTableDialog::EditTableDialog(DBBrowserDB& db, const QString& tableName, bool createTable, QWidget* parent) +EditTableDialog::EditTableDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier& tableName, bool createTable, QWidget* parent) : QDialog(parent), ui(new Ui::EditTableDialog), pdb(db), curTable(tableName), - m_table(tableName), + m_table(tableName.name()), m_bNewTable(createTable), m_sRestorePointName(pdb.generateSavepointName("edittable")) { @@ -33,7 +33,7 @@ EditTableDialog::EditTableDialog(DBBrowserDB& db, const QString& tableName, bool if(m_bNewTable == false) { // Existing table, so load and set the current layout - m_table = *(pdb.getObjectByName(sqlb::ObjectIdentifier("main", curTable)).dynamicCast()); + m_table = *(pdb.getObjectByName(curTable).dynamicCast()); ui->labelEditWarning->setVisible(!m_table.fullyParsed()); // Set without rowid and temporary checkboxex. No need to trigger any events here as we're only loading a table exactly as it is stored by SQLite, so no need @@ -52,7 +52,7 @@ EditTableDialog::EditTableDialog(DBBrowserDB& db, const QString& tableName, bool pdb.setSavepoint(m_sRestorePointName); // Update UI - ui->editTableName->setText(curTable); + ui->editTableName->setText(curTable.name()); updateColumnWidth(); checkInput(); @@ -161,9 +161,9 @@ void EditTableDialog::accept() // Editing of old table // Rename table if necessary - if(ui->editTableName->text() != curTable) + if(ui->editTableName->text() != curTable.name()) { - if(!pdb.renameTable("main", curTable, ui->editTableName->text())) + if(!pdb.renameTable(curTable.schema(), curTable.name(), ui->editTableName->text())) { QMessageBox::warning(this, QApplication::applicationName(), pdb.lastError()); return; @@ -210,7 +210,7 @@ void EditTableDialog::checkInput() if (oldTableName == fk->table()) { fk->setTable(normTableName); if(!fksEnabled) - pdb.renameColumn(sqlb::ObjectIdentifier("main", curTable), m_table, f->name(), f, 0); + pdb.renameColumn(curTable, m_table, f->name(), f, 0); } } } @@ -239,7 +239,7 @@ void EditTableDialog::updateTypes() m_table.fields().at(index)->setType(type); if(!m_bNewTable) - pdb.renameColumn(sqlb::ObjectIdentifier("main", curTable), m_table, column, m_table.fields().at(index)); + pdb.renameColumn(curTable, m_table, column, m_table.fields().at(index)); checkInput(); } } @@ -279,7 +279,7 @@ void EditTableDialog::itemChanged(QTreeWidgetItem *item, int column) if(!m_bNewTable) { sqlb::FieldVector pk = m_table.primaryKey(); - foreach(const sqlb::ObjectPtr& fkobj, pdb.schemata["main"].values("table")) + foreach(const sqlb::ObjectPtr& fkobj, pdb.schemata[curTable.schema()].values("table")) { QList fks = fkobj.dynamicCast()->constraints(sqlb::FieldVector(), sqlb::Constraint::ForeignKeyConstraintType); foreach(sqlb::ConstraintPtr fkptr, fks) @@ -352,10 +352,9 @@ void EditTableDialog::itemChanged(QTreeWidgetItem *item, int column) // we need to check for this case and cancel here. Maybe we can think of some way to modify the INSERT INTO ... SELECT statement // to at least replace all troublesome NULL values by the default value SqliteTableModel m(pdb, this); - m.setQuery(QString("SELECT COUNT(%1) FROM %2.%3 WHERE %4 IS NULL;") - .arg(sqlb::escapeIdentifier(pdb.getObjectByName(sqlb::ObjectIdentifier("main", curTable)).dynamicCast()->rowidColumn())) - .arg(sqlb::escapeIdentifier("main")) - .arg(sqlb::escapeIdentifier(curTable)) + m.setQuery(QString("SELECT COUNT(%1) FROM %2 WHERE %3 IS NULL;") + .arg(sqlb::escapeIdentifier(pdb.getObjectByName(curTable).dynamicCast()->rowidColumn())) + .arg(curTable.toString()) .arg(sqlb::escapeIdentifier(field->name()))); if(m.data(m.index(0, 0)).toInt() > 0) { @@ -381,7 +380,7 @@ void EditTableDialog::itemChanged(QTreeWidgetItem *item, int column) { SqliteTableModel m(pdb, this); m.setQuery(QString("SELECT COUNT(*) FROM %1 WHERE %2 <> CAST(%3 AS INTEGER);") - .arg(sqlb::escapeIdentifier(curTable)) + .arg(curTable.toString()) .arg(sqlb::escapeIdentifier(field->name())) .arg(sqlb::escapeIdentifier(field->name()))); if(m.data(m.index(0, 0)).toInt() > 0) @@ -424,9 +423,9 @@ void EditTableDialog::itemChanged(QTreeWidgetItem *item, int column) { // Because our renameColumn() function fails when setting a column to unique when it already contains the same values SqliteTableModel m(pdb, this); - m.setQuery(QString("SELECT COUNT(%2) FROM %1;").arg(sqlb::escapeIdentifier(curTable)).arg(sqlb::escapeIdentifier(field->name()))); + m.setQuery(QString("SELECT COUNT(%2) FROM %1;").arg(curTable.toString()).arg(sqlb::escapeIdentifier(field->name()))); int rowcount = m.data(m.index(0, 0)).toInt(); - m.setQuery(QString("SELECT COUNT(DISTINCT %2) FROM %1;").arg(sqlb::escapeIdentifier(curTable)).arg(sqlb::escapeIdentifier(field->name()))); + m.setQuery(QString("SELECT COUNT(DISTINCT %2) FROM %1;").arg(curTable.toString()).arg(sqlb::escapeIdentifier(field->name()))); int uniquecount = m.data(m.index(0, 0)).toInt(); if(rowcount != uniquecount) { @@ -490,7 +489,7 @@ void EditTableDialog::itemChanged(QTreeWidgetItem *item, int column) if(callRenameColumn) { - if(!pdb.renameColumn(sqlb::ObjectIdentifier("main", curTable), m_table, oldFieldName, field)) + if(!pdb.renameColumn(curTable, m_table, oldFieldName, field)) QMessageBox::warning(this, qApp->applicationName(), tr("Modifying this column failed. Error returned from database:\n%1").arg(pdb.lastError())); } } @@ -549,7 +548,7 @@ void EditTableDialog::addField() // Actually add the new column to the table if we're editing an existing table if(!m_bNewTable) - pdb.addColumn(sqlb::ObjectIdentifier("main", curTable), f); + pdb.addColumn(curTable, f); checkInput(); } @@ -577,12 +576,12 @@ void EditTableDialog::removeField() QString msg = tr("Are you sure you want to delete the field '%1'?\nAll data currently stored in this field will be lost.").arg(ui->treeWidget->currentItem()->text(0)); if(QMessageBox::warning(this, QApplication::applicationName(), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) { - if(!pdb.renameColumn(sqlb::ObjectIdentifier("main", curTable), m_table, ui->treeWidget->currentItem()->text(0), sqlb::FieldPtr())) + if(!pdb.renameColumn(curTable, m_table, ui->treeWidget->currentItem()->text(0), sqlb::FieldPtr())) { QMessageBox::warning(0, QApplication::applicationName(), pdb.lastError()); } else { //relayout - m_table = *(pdb.getObjectByName(sqlb::ObjectIdentifier("main", curTable)).dynamicCast()); + m_table = *(pdb.getObjectByName(curTable).dynamicCast()); populateFields(); } } @@ -655,7 +654,7 @@ void EditTableDialog::moveCurrentField(bool down) // Move the actual column if(!pdb.renameColumn( - sqlb::ObjectIdentifier("main", curTable), + curTable, m_table, ui->treeWidget->currentItem()->text(0), m_table.fields().at(ui->treeWidget->indexOfTopLevelItem(ui->treeWidget->currentItem())), @@ -665,7 +664,7 @@ void EditTableDialog::moveCurrentField(bool down) QMessageBox::warning(0, QApplication::applicationName(), pdb.lastError()); } else { // Reload table SQL - m_table = *(pdb.getObjectByName(sqlb::ObjectIdentifier("main", curTable)).dynamicCast()); + m_table = *(pdb.getObjectByName(curTable).dynamicCast()); populateFields(); // Select old item at new position @@ -711,7 +710,7 @@ void EditTableDialog::setWithoutRowid(bool without_rowid) // Update table if we're editing an existing table if(!m_bNewTable) { - if(!pdb.renameColumn(sqlb::ObjectIdentifier("main", curTable), m_table, QString(), sqlb::FieldPtr(), 0)) + if(!pdb.renameColumn(curTable, m_table, QString(), sqlb::FieldPtr(), 0)) { QMessageBox::warning(this, QApplication::applicationName(), tr("Setting the rowid column for the table failed. Error message:\n%1").arg(pdb.lastError())); @@ -730,7 +729,7 @@ void EditTableDialog::setTemporary(bool is_temp) // Update table if we're editing an existing table if(!m_bNewTable) { - if(!pdb.renameColumn(sqlb::ObjectIdentifier("main", curTable), m_table, QString(), sqlb::FieldPtr(), 0)) + if(!pdb.renameColumn(curTable, m_table, QString(), sqlb::FieldPtr(), 0)) { QMessageBox::warning(this, QApplication::applicationName(), tr("Setting the temporary flag for the table failed. Error message:\n%1").arg(pdb.lastError())); diff --git a/src/EditTableDialog.h b/src/EditTableDialog.h index 64ebeaeb..1f4b08f3 100644 --- a/src/EditTableDialog.h +++ b/src/EditTableDialog.h @@ -18,7 +18,7 @@ class EditTableDialog : public QDialog Q_OBJECT public: - explicit EditTableDialog(DBBrowserDB& pdb, const QString& tableName, bool createTable, QWidget* parent = 0); + explicit EditTableDialog(DBBrowserDB& pdb, const sqlb::ObjectIdentifier& tableName, bool createTable, QWidget* parent = 0); ~EditTableDialog(); protected: @@ -61,7 +61,7 @@ private: Ui::EditTableDialog* ui; DBBrowserDB& pdb; ForeignKeyEditorDelegate* m_fkEditorDelegate; - QString curTable; + sqlb::ObjectIdentifier curTable; sqlb::Table m_table; QStringList types; QStringList fields; diff --git a/src/ExportDataDialog.cpp b/src/ExportDataDialog.cpp index 6545c23c..7276e950 100644 --- a/src/ExportDataDialog.cpp +++ b/src/ExportDataDialog.cpp @@ -12,7 +12,7 @@ #include #include -ExportDataDialog::ExportDataDialog(DBBrowserDB& db, ExportFormats format, QWidget* parent, const QString& query, const QString& selection) +ExportDataDialog::ExportDataDialog(DBBrowserDB& db, ExportFormats format, QWidget* parent, const QString& query, const sqlb::ObjectIdentifier& selection) : QDialog(parent), ui(new Ui::ExportDataDialog), pdb(db), @@ -42,9 +42,17 @@ ExportDataDialog::ExportDataDialog(DBBrowserDB& db, ExportFormats format, QWidge if(query.isEmpty()) { // Get list of tables to export - objectMap objects = pdb.getBrowsableObjects("main"); - foreach(const sqlb::ObjectPtr& obj, objects) - ui->listTables->addItem(new QListWidgetItem(QIcon(QString(":icons/%1").arg(sqlb::Object::typeToString(obj->type()))), obj->name())); + for(auto it=pdb.schemata.constBegin();it!=pdb.schemata.constEnd();++it) + { + QList tables = it->values("table") + it->values("view"); + for(auto jt=tables.constBegin();jt!=tables.constEnd();++jt) + { + sqlb::ObjectIdentifier obj(it.key(), (*jt)->name()); + QListWidgetItem* item = new QListWidgetItem(QIcon(QString(":icons/%1").arg(sqlb::Object::typeToString((*jt)->type()))), obj.toDisplayString()); + item->setData(Qt::UserRole, obj.toVariant()); + ui->listTables->addItem(item); + } + } // Sort list of tables and select the table specified in the selection parameter or alternatively the first one ui->listTables->model()->sort(0); @@ -52,9 +60,14 @@ ExportDataDialog::ExportDataDialog(DBBrowserDB& db, ExportFormats format, QWidge { ui->listTables->setCurrentItem(ui->listTables->item(0)); } else { - QList items = ui->listTables->findItems(selection, Qt::MatchExactly); - if(!items.isEmpty()) - ui->listTables->setCurrentItem(items.first()); + for(int i=0;ilistTables->count();i++) + { + if(sqlb::ObjectIdentifier(ui->listTables->item(i)->data(Qt::UserRole)) == selection) + { + ui->listTables->setCurrentRow(i); + break; + } + } } } else { // Hide table combo box @@ -320,8 +333,7 @@ void ExportDataDialog::accept() { // if we are called from execute sql tab, query is already set // and we only export 1 select - QString sQuery = QString("SELECT * FROM %1;").arg(sqlb::escapeIdentifier(selectedItems.at(i)->text())); - + QString sQuery = QString("SELECT * FROM %1;").arg(sqlb::ObjectIdentifier(selectedItems.at(i)->data(Qt::UserRole)).toString()); exportQuery(sQuery, filenames.at(i)); } } diff --git a/src/ExportDataDialog.h b/src/ExportDataDialog.h index 65ccafe7..4971a826 100644 --- a/src/ExportDataDialog.h +++ b/src/ExportDataDialog.h @@ -3,6 +3,8 @@ #include +#include "sqlitetypes.h" + class DBBrowserDB; namespace Ui { @@ -20,7 +22,8 @@ public: ExportFormatJson, }; - explicit ExportDataDialog(DBBrowserDB& db, ExportFormats format, QWidget* parent = 0, const QString& query = "", const QString& selection = ""); + explicit ExportDataDialog(DBBrowserDB& db, ExportFormats format, QWidget* parent = 0, + const QString& query = "", const sqlb::ObjectIdentifier& selection = sqlb::ObjectIdentifier()); ~ExportDataDialog(); private slots: diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 7c0fb46a..ece791a7 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -95,13 +95,15 @@ void MainWindow::init() // Set up DB structure tab dbStructureModel = new DbStructureModel(db, this); ui->dbTreeWidget->setModel(dbStructureModel); - ui->dbTreeWidget->setColumnHidden(1, true); - ui->dbTreeWidget->setColumnWidth(0, 300); + ui->dbTreeWidget->setColumnWidth(DbStructureModel::ColumnName, 300); + ui->dbTreeWidget->setColumnHidden(DbStructureModel::ColumnObjectType, true); + ui->dbTreeWidget->setColumnHidden(DbStructureModel::ColumnSchema, true); // Set up DB schema dock ui->treeSchemaDock->setModel(dbStructureModel); - ui->treeSchemaDock->setColumnHidden(1, true); - ui->treeSchemaDock->setColumnWidth(0, 300); + ui->treeSchemaDock->setColumnWidth(DbStructureModel::ColumnName, 300); + ui->treeSchemaDock->setColumnHidden(DbStructureModel::ColumnObjectType, true); + ui->treeSchemaDock->setColumnHidden(DbStructureModel::ColumnSchema, true); // Set up the table combo box in the Browse Data tab ui->comboBrowseTable->setModel(dbStructureModel); @@ -746,7 +748,7 @@ void MainWindow::createTable() return; } - EditTableDialog dialog(db, "", true, this); + EditTableDialog dialog(db, sqlb::ObjectIdentifier(), true, this); if(dialog.exec()) { populateTable(); @@ -760,7 +762,7 @@ void MainWindow::createIndex() return; } - EditIndexDialog dialog(db, "", true, this); + EditIndexDialog dialog(db, sqlb::ObjectIdentifier(), true, this); if(dialog.exec()) populateTable(); } @@ -774,16 +776,17 @@ void MainWindow::compact() void MainWindow::deleteObject() { // Get name and type of object to delete - QString table = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), 0), Qt::EditRole).toString(); - QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), 1), Qt::EditRole).toString(); + sqlb::ObjectIdentifier name(ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnSchema), Qt::EditRole).toString(), + ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnName), Qt::EditRole).toString()); + QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnObjectType), Qt::EditRole).toString(); // Ask user if he really wants to delete that table - if(QMessageBox::warning(this, QApplication::applicationName(), tr("Are you sure you want to delete the %1 '%2'?\nAll data associated with the %1 will be lost.").arg(type).arg(table), + if(QMessageBox::warning(this, QApplication::applicationName(), tr("Are you sure you want to delete the %1 '%2'?\nAll data associated with the %1 will be lost.").arg(type).arg(name.name()), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) { // Delete the table - QString statement = QString("DROP %1 %2;").arg(type.toUpper()).arg(sqlb::escapeIdentifier(table)); - if(!db.executeSQL( statement)) + QString statement = QString("DROP %1 %2;").arg(type.toUpper()).arg(name.toString()); + if(!db.executeSQL(statement)) { QString error = tr("Error: could not delete the %1. Message from database engine:\n%2").arg(type).arg(db.lastError()); QMessageBox::warning(this, QApplication::applicationName(), error); @@ -800,8 +803,9 @@ void MainWindow::editObject() return; // Get name and type of the object to edit - QString name = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), 0), Qt::EditRole).toString(); - QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), 1), Qt::EditRole).toString(); + sqlb::ObjectIdentifier name(ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnSchema), Qt::EditRole).toString(), + ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnName), Qt::EditRole).toString()); + QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnObjectType), Qt::EditRole).toString(); if(type == "table") { @@ -1158,14 +1162,19 @@ void MainWindow::importTableFromCSV() void MainWindow::exportTableToCSV() { // Get the current table name if we are in the Browse Data tab - QString current_table; - if(ui->mainTab->currentIndex() == StructureTab) { - QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), 1)).toString(); + sqlb::ObjectIdentifier current_table; + if(ui->mainTab->currentIndex() == StructureTab) + { + QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnObjectType)).toString(); if(type == "table" || type == "view") - current_table = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), 0)).toString(); + { + QString schema = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnSchema)).toString(); + QString name = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnName)).toString(); + current_table = sqlb::ObjectIdentifier(schema, name); + } + } else if(ui->mainTab->currentIndex() == BrowseTab) { + current_table = sqlb::ObjectIdentifier("main", ui->comboBrowseTable->currentText()); } - else if(ui->mainTab->currentIndex() == BrowseTab) - current_table = ui->comboBrowseTable->currentText(); // Open dialog ExportDataDialog dialog(db, ExportDataDialog::ExportFormatCsv, this, "", current_table); @@ -1175,14 +1184,19 @@ void MainWindow::exportTableToCSV() void MainWindow::exportTableToJson() { // Get the current table name if we are in the Browse Data tab - QString current_table; - if(ui->mainTab->currentIndex() == StructureTab) { - QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), 1)).toString(); + sqlb::ObjectIdentifier current_table; + if(ui->mainTab->currentIndex() == StructureTab) + { + QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnObjectType)).toString(); if(type == "table" || type == "view") - current_table = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), 0)).toString(); + { + QString schema = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnSchema)).toString(); + QString name = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnName)).toString(); + current_table = sqlb::ObjectIdentifier(schema, name); + } + } else if(ui->mainTab->currentIndex() == BrowseTab) { + current_table = sqlb::ObjectIdentifier("main", ui->comboBrowseTable->currentText()); } - else if(ui->mainTab->currentIndex() == BrowseTab) - current_table = ui->comboBrowseTable->currentText(); // Open dialog ExportDataDialog dialog(db, ExportDataDialog::ExportFormatJson, this, "", current_table); diff --git a/src/sqlitetypes.cpp b/src/sqlitetypes.cpp index 4344fe81..8b1fce14 100644 --- a/src/sqlitetypes.cpp +++ b/src/sqlitetypes.cpp @@ -1148,7 +1148,7 @@ QString Index::sql(const QString& schema, bool ifNotExists) const .arg(m_unique ? QString("UNIQUE ") : QString("")) .arg(ifNotExists ? QString(" IF NOT EXISTS") : QString("")) .arg(ObjectIdentifier(schema, m_name).toString(true)) - .arg(ObjectIdentifier(schema, m_table).toString(true)); + .arg(sqlb::escapeIdentifier(m_table)); // Add column list sql += columnSqlList().join(",\n"); diff --git a/src/sqlitetypes.h b/src/sqlitetypes.h index c8954b5b..aa4da038 100644 --- a/src/sqlitetypes.h +++ b/src/sqlitetypes.h @@ -7,6 +7,7 @@ #include #include #include +#include namespace sqlb { @@ -36,6 +37,18 @@ public: { } + ObjectIdentifier(QVariant variant) + { + QStringList str = variant.toStringList(); + m_schema = str.first(); + m_name = str.last(); + } + + bool operator==(const ObjectIdentifier& rhs) const + { + return (rhs.m_schema == m_schema && rhs.m_name == m_name); + } + const QString& schema() const { return m_schema; } const QString& name() const { return m_name; } void setSchema(const QString& schema) { m_schema = schema; } @@ -49,6 +62,7 @@ public: bool isEmpty() const { return m_name.isEmpty(); } + // This returns a string which can be used in SQL statements QString toString(bool shortName = false) const { if(shortName && m_schema == "main") @@ -57,6 +71,22 @@ public: return QString("%1.%2").arg(sqlb::escapeIdentifier(m_schema)).arg(sqlb::escapeIdentifier(m_name)); } + // This returns a string which can be used in the user interface + QString toDisplayString() const + { + if(m_schema == "main") + return m_name; + else + return QString("%1.%2").arg(m_schema).arg(m_name); + } + + QVariant toVariant() const + { + QStringList result; + result << m_schema << m_name; + return QVariant(result); + } + private: QString m_schema; QString m_name;