From d12bb621566a490bc0009b4ddb40f534d797e5dd Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Fri, 4 Jan 2013 16:38:59 +0100 Subject: [PATCH] Change design of DBBrowserDB a bit and support more actions with views Change the design of DBBrowserDB to store all DB objects in one single map instead of having one map for table, one for views, one for triggers etc. This requires some changes in the entire program. Show more details of views in the DB structure tab, i.e. the names of the fields included in the view. Allow browsing views in the browse tab and allow exporting views as CSV. --- src/CreateIndexForm.cpp | 14 ++-- src/CreateIndexForm.h | 4 +- src/MainWindow.cpp | 111 ++++++++++++++----------------- src/sqlitedb.cpp | 141 +++++++++++++++++++++++----------------- src/sqlitedb.h | 33 +++------- 5 files changed, 149 insertions(+), 154 deletions(-) diff --git a/src/CreateIndexForm.cpp b/src/CreateIndexForm.cpp index 66afa381..ef98f4ad 100644 --- a/src/CreateIndexForm.cpp +++ b/src/CreateIndexForm.cpp @@ -34,7 +34,7 @@ void createIndexForm::languageChange() void createIndexForm::tableSelected( const QString & entry ) { - tableMap::Iterator it; + objectMap::Iterator it; for ( it = mtablemap.begin(); it != mtablemap.end(); ++it ) { QString tname = it.value().getname() ; @@ -86,20 +86,20 @@ void createIndexForm::confirmCreate() } } -void createIndexForm::populateTable(const tableMap& rmap) +void createIndexForm::populateTable(const QList& rmap) { - tableMap::ConstIterator it; + QList::ConstIterator it; for ( it = rmap.begin(); it != rmap.end(); ++it ) { - comboTables->addItem( it.value().getname() ); + comboTables->addItem( (*it).getname() ); //populate the fields with first table name - if (it==mtablemap.begin()){ + /*if (it==mtablemap.begin()){ fieldMap::Iterator fit; - fieldMap fmap = it.value().fldmap; + fieldMap fmap = (*it).value().fldmap; for ( fit = fmap.begin(); fit != fmap.end(); ++fit ) { comboFields->addItem( fit.value().getname() ); } - } + }*/ } } diff --git a/src/CreateIndexForm.h b/src/CreateIndexForm.h index 57ce5823..9d637575 100644 --- a/src/CreateIndexForm.h +++ b/src/CreateIndexForm.h @@ -241,13 +241,13 @@ public: createIndexForm(QWidget* parent = 0, Qt::WindowFlags fl = Qt::Window); ~createIndexForm(); - tableMap mtablemap; + objectMap mtablemap; QString createStatement; public slots: virtual void tableSelected( const QString & entry ); virtual void confirmCreate(); - virtual void populateTable( const tableMap& rmap ); + virtual void populateTable(const QList &rmap ); protected slots: virtual void languageChange(); diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index af6321d0..c3b22685 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -173,56 +173,34 @@ void MainWindow::populateStructure() return; } db.updateSchema(); - QStringList tblnames = db.getTableNames(); + QStringList tblnames = db.getBrowsableObjectNames(); sqliteHighlighter->setTableNames(tblnames); - tableMap::ConstIterator it; + objectMap::ConstIterator it; - for ( it = db.tbmap.begin(); it != db.tbmap.end(); ++it ) { - - //* Table node + for ( it = db.objMap.begin(); it != db.objMap.end(); ++it ) + { + // Object node QTreeWidgetItem *tableItem = new QTreeWidgetItem(); - tableItem->setText(0, it.value().getname()); - tableItem->setText(1, "table"); - tableItem->setText(3, it.value().getsql()); - tableItem->setIcon(0, QIcon(":/icons/table")); + tableItem->setIcon(0, QIcon(QString(":/icons/%1").arg((*it).gettype()))); + tableItem->setText(0, (*it).getname()); + tableItem->setText(1, (*it).gettype()); + tableItem->setText(3, (*it).getsql()); ui->dbTreeWidget->addTopLevelItem(tableItem); - //* Field Nodes - fieldMap::ConstIterator fit; - for ( fit = it.value().fldmap.begin(); fit != it.value().fldmap.end(); ++fit ) { - QTreeWidgetItem *fldItem = new QTreeWidgetItem(tableItem); - fldItem->setText( 0, fit.value().getname() ); - fldItem->setText( 1, "field" ); - fldItem->setText( 2, fit.value().gettype() ); - fldItem->setIcon(0, QIcon(":/icons/field")); + // If it is a table add the field Nodes + if((*it).gettype() == "table" || (*it).gettype() == "view") + { + fieldMap::ConstIterator fit; + for ( fit = (*it).fldmap.begin(); fit != (*it).fldmap.end(); ++fit ) { + QTreeWidgetItem *fldItem = new QTreeWidgetItem(tableItem); + fldItem->setText( 0, fit.value().getname() ); + fldItem->setText( 1, "field" ); + fldItem->setText( 2, fit.value().gettype() ); + fldItem->setIcon(0, QIcon(":/icons/field")); + } + // TODO make an options/setting autoexpand + ui->dbTreeWidget->setItemExpanded(tableItem, true); } - // TODO make an options/setting autoexpand - ui->dbTreeWidget->setItemExpanded(tableItem, true); - } - objectMap::ConstIterator it2; - for ( it2 = db.idxmap.begin(); it2 != db.idxmap.end(); ++it2 ) { - QTreeWidgetItem *item = new QTreeWidgetItem(); - item->setText( 0, it2.value().getname() ); - item->setText( 1, "index" ); - item->setText( 3, it2.value().getsql() ); - item->setIcon(0, QIcon(":/icons/index")); - ui->dbTreeWidget->addTopLevelItem(item); - } - for ( it2 = db.viewmap.begin(); it2 != db.viewmap.end(); ++it2 ) { - QTreeWidgetItem *item = new QTreeWidgetItem(); - item->setText( 0, it2.value().getname() ); - item->setText( 1, "view" ); - item->setText( 3, it2.value().getsql() ); - item->setIcon(0, QIcon(":/icons/view")); - ui->dbTreeWidget->addTopLevelItem(item); - } - for ( it2 = db.trgmap.begin(); it2 != db.trgmap.end(); ++it2 ) { - QTreeWidgetItem *item = new QTreeWidgetItem(); - item->setText( 0, it2.value().getname() ); - item->setText( 1, "trigger" ); - item->setText( 3, it2.value().getsql() ); - item->setIcon(0, QIcon(":/icons/trigger")); - ui->dbTreeWidget->addTopLevelItem(item); } } @@ -245,6 +223,12 @@ void MainWindow::populateTable( const QString & tablename, bool keepColumnWidths return; } + // Activate the add and delete record buttons and editing only if a table has been selected + bool is_table = db.getObjectByName(tablename).gettype() == "table"; + ui->buttonNewRecord->setEnabled(is_table); + ui->buttonDeleteRecord->setEnabled(is_table); + ui->dataTable->setEditTriggers(is_table ? QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed | QAbstractItemView::EditKeyPressed : QAbstractItemView::NoEditTriggers); + if (mustreset){ updateTableView(0, keepColumnWidths); if (findWin) findWin->resetFields(db.getTableFields(db.curBrowseTableName)); @@ -266,9 +250,12 @@ void MainWindow::resetBrowser() { QString sCurrentTable = ui->comboBrowseTable->currentText(); ui->comboBrowseTable->clear(); - QStringList tab = db.getTableNames(); - if(!tab.isEmpty()) { - ui->comboBrowseTable->addItems(tab); + objectMap tab = db.getBrowsableObjects(); + for(objectMap::ConstIterator i=tab.begin();i!=tab.end();++i) + { + ui->comboBrowseTable->addItem(QIcon(QString(":icons/%1").arg(i.value().gettype())), i.value().getname()); + + //ui->comboBrowseTable->addItems(tab); } setRecordsetLabel(); int pos = ui->comboBrowseTable->findText(sCurrentTable); @@ -577,7 +564,7 @@ void MainWindow::createIndex() return; } createIndexForm dialog(this); - dialog.populateTable(db.tbmap); + dialog.populateTable(db.objMap.values("table")); if(dialog.exec()) { if (!db.executeSQL(dialog.createStatement)){ @@ -763,6 +750,10 @@ void MainWindow::doubleClickTable( int row, int col ) return; } + // Don't allow editing of other objects than tables + if(db.getObjectByName(ui->comboBrowseTable->currentText()).gettype() != "table") + return; + int realRow = row; editText(realRow , col); @@ -869,7 +860,7 @@ void MainWindow::importTableFromCSV() void MainWindow::exportTableToCSV() { exportTableCSVForm dialog(this); - dialog.populateOptions(db.getTableNames()); + dialog.populateOptions(db.getBrowsableObjectNames()); if(dialog.exec()) { //load our table @@ -1081,26 +1072,18 @@ void MainWindow::on_tree_context_menu(const QPoint &qPoint){ } //** Tree selection changed void MainWindow::on_tree_selection_changed(){ - if (!ui->dbTreeWidget->selectionModel()->hasSelection()){ - ui->editDeleteTableAction->setEnabled(false); - ui->editModifyTableAction->setEnabled(false); - ui->editAddFieldActionPopup->setEnabled(false); - ui->editModifyFieldActionPopup->setEnabled(false); - ui->editDeleteFieldActionPopup->setEnabled(false); - return; - } + // Just assume first that something's selected that can not be edited at all + ui->editDeleteTableAction->setEnabled(false); + ui->editModifyTableAction->setEnabled(false); + ui->editAddFieldActionPopup->setEnabled(false); + ui->editModifyFieldActionPopup->setEnabled(false); + ui->editDeleteFieldActionPopup->setEnabled(false); if(ui->dbTreeWidget->currentItem()->text(1) == "table"){ ui->editDeleteTableAction->setEnabled(true); ui->editModifyTableAction->setEnabled(true); ui->editAddFieldActionPopup->setEnabled(true); - ui->editModifyFieldActionPopup->setEnabled(false); - ui->editDeleteFieldActionPopup->setEnabled(false); - - }else if(ui->dbTreeWidget->currentItem()->text(1) == "field"){ - ui->editAddFieldActionPopup->setEnabled(false); - ui->editDeleteTableAction->setEnabled(false); - ui->editModifyTableAction->setEnabled(false); + }else if(ui->dbTreeWidget->currentItem()->text(1) == "field" && ui->dbTreeWidget->currentItem()->parent()->text(1) == "table"){ ui->editModifyFieldActionPopup->setEnabled(true); ui->editDeleteFieldActionPopup->setEnabled(true); } diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index adae46be..d5952477 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -8,7 +8,7 @@ #include "SQLLogDock.h" #include -void DBBrowserTable::addField(int order, const QString& wfield,const QString& wtype) +void DBBrowserObject::addField(int order, const QString& wfield,const QString& wtype) { fldmap[order] = DBBrowserField(wfield,wtype); } @@ -217,10 +217,7 @@ void DBBrowserDB::close (){ sqlite3_close(_db); } _db = 0; - idxmap.clear(); - trgmap.clear(); - viewmap.clear(); - tbmap.clear(); + objMap.clear(); idmap.clear(); browseRecs.clear(); browseFields.clear(); @@ -543,26 +540,42 @@ resultMap DBBrowserDB::getFindResults( const QString & wstatement) } -QStringList DBBrowserDB::getTableNames() +QStringList DBBrowserDB::getBrowsableObjectNames() { - tableMap::ConstIterator it; + objectMap::ConstIterator it; QStringList res; - for ( it = tbmap.begin(); it != tbmap.end(); ++it ) { - res.append( it.value().getname() ); + for(it=objMap.begin();it!=objMap.end();++it) + { + if(it.key() == "table" || it.key() == "view") + res.append(it.value().getname()); } return res; } +objectMap DBBrowserDB::getBrowsableObjects() +{ + objectMap::ConstIterator it; + objectMap res; + + for(it=objMap.begin();it!=objMap.end();++it) + { + if(it.key() == "table" || it.key() == "view") + res.insert(it.key(), it.value()); + } + + return res; +} + QStringList DBBrowserDB::getIndexNames() { - objectMap::Iterator it; - objectMap tmap = idxmap; + QList tmap = objMap.values("index"); + QList::ConstIterator it; QStringList res; for ( it = tmap.begin(); it != tmap.end(); ++it ) { - res.append( it.value().getname() ); + res.append( (*it).getname() ); } return res; @@ -570,14 +583,16 @@ QStringList DBBrowserDB::getIndexNames() QStringList DBBrowserDB::getTableFields(const QString & tablename) { - tableMap::ConstIterator it; + objectMap::ConstIterator it; QStringList res; - for ( it = tbmap.begin(); it != tbmap.end(); ++it ) { - if (tablename.compare(it.value().getname())==0 ){ + for ( it = objMap.begin(); it != objMap.end(); ++it ) + { + if((*it).getname() == tablename) + { fieldMap::ConstIterator fit; - for ( fit = it.value().fldmap.begin(); fit != it.value().fldmap.end(); ++fit ) { + for ( fit = (*it).fldmap.begin(); fit != (*it).fldmap.end(); ++fit ) { res.append( fit.value().getname() ); } } @@ -587,14 +602,16 @@ QStringList DBBrowserDB::getTableFields(const QString & tablename) QStringList DBBrowserDB::getTableTypes(const QString & tablename) { - tableMap::ConstIterator it; + objectMap::ConstIterator it; QStringList res; - for ( it = tbmap.begin(); it != tbmap.end(); ++it ) { - if (tablename.compare(it.value().getname())==0 ){ + for ( it = objMap.begin(); it != objMap.end(); ++it ) + { + if((*it).getname() == tablename) + { fieldMap::ConstIterator fit; - for ( fit = it.value().fldmap.begin(); fit != it.value().fldmap.end(); ++fit ) { + for ( fit = (*it).fldmap.begin(); fit != (*it).fldmap.end(); ++fit ) { res.append( fit.value().gettype() ); } } @@ -602,6 +619,19 @@ QStringList DBBrowserDB::getTableTypes(const QString & tablename) return res; } +DBBrowserObject DBBrowserDB::getObjectByName(const QString& name) +{ + objectMap::ConstIterator it; + QStringList res; + + for ( it = objMap.begin(); it != objMap.end(); ++it ) + { + if((*it).getname() == name) + return *it; + } + return DBBrowserObject(); +} + int DBBrowserDB::getRecordCount() { return browseRecs.count(); @@ -629,10 +659,7 @@ void DBBrowserDB::updateSchema( ) const char *tail; int err=0; - idxmap.clear(); - tbmap.clear(); - viewmap.clear(); - trgmap.clear(); + objMap.clear(); lastErrorMessage = QString("no error"); QString statement = "SELECT type, name, sql FROM sqlite_master;"; @@ -647,14 +674,8 @@ void DBBrowserDB::updateSchema( ) val2 = QString((const char *) sqlite3_column_text(vm, 1)); val3 = QString((const char *) sqlite3_column_text(vm, 2)); - if(val1 == "table") - tbmap[val2] = DBBrowserTable(GetDecodedQString(val2), GetDecodedQString(val3)); - else if(val1 == "index") - idxmap[val2] = DBBrowserObject(GetDecodedQString(val2), GetDecodedQString(val3)); - else if(val1 == "view") - viewmap[val2] = DBBrowserObject(GetDecodedQString(val2), GetDecodedQString(val3)); - else if(val1 == "trigger") - trgmap[val2] = DBBrowserObject(GetDecodedQString(val2), GetDecodedQString(val3)); + if(val1 == "table" || val1 == "index" || val1 == "view" || val1 == "trigger") + objMap.insert(val1, DBBrowserObject(GetDecodedQString(val2), GetDecodedQString(val3), GetDecodedQString(val1))); else qDebug("unknown object type %s", val1.toStdString().c_str()); } @@ -664,35 +685,39 @@ void DBBrowserDB::updateSchema( ) } qDebug(sqlite3_errmsg(_db)); - //now get the field list for each table in tbmap - tableMap::Iterator it; - for ( it = tbmap.begin(); it != tbmap.end(); ++it ) { - statement = "PRAGMA TABLE_INFO("; - statement.append( it.value().getname()); - statement.append(");"); - logSQL(statement, kLogMsg_App); - err=sqlite3_prepare(_db,statement.toUtf8(),statement.length(), - &vm, &tail); - if (err == SQLITE_OK){ - it.value(). fldmap.clear(); - int e = 0; - while ( sqlite3_step(vm) == SQLITE_ROW ){ - if (sqlite3_column_count(vm)==6) { - QString val1, val2; - int ispk= 0; - val1 = QString((const char *) sqlite3_column_text(vm, 1)); - val2 = QString((const char *) sqlite3_column_text(vm, 2)); - ispk = sqlite3_column_int(vm, 5); - if (ispk==1){ - val2.append(QString(" PRIMARY KEY")); + //now get the field list for each table + objectMap::Iterator it; + for ( it = objMap.begin(); it != objMap.end(); ++it ) + { + if((*it).gettype() == "table" || (*it).gettype() == "view") + { + statement = "PRAGMA TABLE_INFO("; + statement.append( (*it).getname()); + statement.append(");"); + logSQL(statement, kLogMsg_App); + err=sqlite3_prepare(_db,statement.toUtf8(),statement.length(), + &vm, &tail); + if (err == SQLITE_OK){ + (*it).fldmap.clear(); + int e = 0; + while ( sqlite3_step(vm) == SQLITE_ROW ){ + if (sqlite3_column_count(vm)==6) { + QString val1, val2; + int ispk= 0; + val1 = QString((const char *) sqlite3_column_text(vm, 1)); + val2 = QString((const char *) sqlite3_column_text(vm, 2)); + ispk = sqlite3_column_int(vm, 5); + if (ispk==1){ + val2.append(QString(" PRIMARY KEY")); + } + (*it).addField(e,GetDecodedQString(val1),GetDecodedQString(val2)); + e++; } - it.value().addField(e,GetDecodedQString(val1),GetDecodedQString(val2)); - e++; } + sqlite3_finalize(vm); + } else{ + lastErrorMessage = QString ("could not get types"); } - sqlite3_finalize(vm); - } else{ - lastErrorMessage = QString ("could not get types"); } } } diff --git a/src/sqlitedb.h b/src/sqlitedb.h index a575b8a9..306ee9c6 100644 --- a/src/sqlitedb.h +++ b/src/sqlitedb.h @@ -3,6 +3,7 @@ #include #include +#include #include "sqlite3.h" class SQLLogDock; @@ -33,8 +34,7 @@ static QString g_sApplicationNameShort = QString("sqlitebrowser"); static QString g_applicationIconName = QString(":/oldimages/icon16"); typedef QMap fieldMap; -typedef QMap tableMap; -typedef QMap objectMap; +typedef QMultiMap objectMap; typedef QMap rowIdMap; typedef QList rowList; @@ -58,32 +58,20 @@ class DBBrowserObject { public: DBBrowserObject() : name( "" ) { } - DBBrowserObject( const QString& wname,const QString& wsql ) - : name( wname), sql( wsql ) - { } - QString getname() const { return name; } - QString getsql() const { return sql; } -private: - QString name; - QString sql; -}; - -class DBBrowserTable -{ -public: - DBBrowserTable() : name( "" ) { } - DBBrowserTable( const QString& wname,const QString& wsql ) - : name( wname), sql( wsql ) + DBBrowserObject( const QString& wname,const QString& wsql, const QString& wtype ) + : name( wname), sql( wsql ), type(wtype) { } void addField(int order, const QString& wfield,const QString& wtype); QString getname() const { return name; } QString getsql() const { return sql; } + QString gettype() const { return type; } fieldMap fldmap; private: QString name; QString sql; + QString type; }; @@ -114,7 +102,9 @@ public: QStringList getTableFields(const QString & tablename); QStringList getTableTypes(const QString & tablename); - QStringList getTableNames(); + QStringList getBrowsableObjectNames(); + objectMap getBrowsableObjects(); + DBBrowserObject getObjectByName(const QString& name); QStringList getIndexNames(); resultMap getFindResults( const QString & wstatement); int getRecordCount(); @@ -135,10 +125,7 @@ public: QStringList decodeCSV(const QString & csvfilename, char sep, char quote, int maxrecords, int * numfields); - tableMap tbmap; - objectMap idxmap; - objectMap viewmap; - objectMap trgmap; + objectMap objMap; rowIdMap idmap; rowList browseRecs;