From f51b44d8d7d4b8ee932bba67230b33f0650e3a0c Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Thu, 7 Jun 2018 16:08:08 +0200 Subject: [PATCH] Add support for deleting rows in views which are unlocked for editing If a view has been unlocked for editing by specifiying a pseudo primary key, with this commit you can now delete records from the view if an appropriate trigger exists. See issue #141. --- src/MainWindow.cpp | 8 +++---- src/MainWindow.h | 2 +- src/sqlitedb.cpp | 50 ++++++++++++++++++++++++++-------------- src/sqlitedb.h | 4 +++- src/sqlitetablemodel.cpp | 8 ++----- 5 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 27d1774f..4dc5d71c 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -1616,15 +1616,15 @@ void MainWindow::activateFields(bool enable) remoteDock->enableButtons(); } -void MainWindow::enableEditing(bool enable_edit, bool enable_insertdelete) +void MainWindow::enableEditing(bool enable_edit, bool enable_insert) { // Don't enable anything if this is a read only database bool edit = enable_edit && !db.readOnly(); - bool insertdelete = enable_insertdelete && !db.readOnly(); + bool insert = enable_insert && !db.readOnly(); // Apply settings - ui->buttonNewRecord->setEnabled(insertdelete); - ui->buttonDeleteRecord->setEnabled(insertdelete); + ui->buttonNewRecord->setEnabled(insert); + ui->buttonDeleteRecord->setEnabled(edit); ui->dataTable->setEditTriggers(edit ? QAbstractItemView::SelectedClicked | QAbstractItemView::AnyKeyPressed | QAbstractItemView::EditKeyPressed : QAbstractItemView::NoEditTriggers); } diff --git a/src/MainWindow.h b/src/MainWindow.h index fc0568a9..da2bf8f1 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -176,7 +176,7 @@ private: void setCurrentFile(const QString& fileName); void addToRecentFilesMenu(const QString& filename); void activateFields(bool enable = true); - void enableEditing(bool enable_edit, bool enable_insertdelete); + void enableEditing(bool enable_edit, bool enable_insert); void loadExtensionsFromSettings(); void saveAsView(QString query); void duplicateRecord(int currentRow); diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index 114dcdcb..82a66eb6 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -977,17 +977,25 @@ QString DBBrowserDB::addRecord(const sqlb::ObjectIdentifier& tablename) } } -bool DBBrowserDB::deleteRecords(const sqlb::ObjectIdentifier& table, const QStringList& rowids) +bool DBBrowserDB::deleteRecords(const sqlb::ObjectIdentifier& table, const QStringList& rowids, const QString& pseudo_pk) { if (!isOpen()) return false; + // Get primary key of the object to edit. + QString pk = primaryKeyForEditing(table, pseudo_pk); + if(pk.isNull()) + { + lastErrorMessage = tr("Cannot delete this object"); + return false; + } + QStringList quoted_rowids; for(QString rowid : rowids) quoted_rowids.append("'" + rowid.replace("'", "''") + "'"); QString statement = QString("DELETE FROM %1 WHERE %2 IN (%3);") .arg(table.toString()) - .arg(sqlb::escapeIdentifier(getObjectByName(table).dynamicCast()->rowidColumn())) + .arg(pk) .arg(quoted_rowids.join(", ")); if(executeSQL(statement)) { @@ -1002,22 +1010,12 @@ bool DBBrowserDB::updateRecord(const sqlb::ObjectIdentifier& table, const QStrin { if (!isOpen()) return false; - // Get primary key of the object to edit. For views we support 'pseudo' primary keys which must be specified manually. - // If no pseudo pk is specified we'll take the rowid column of the table. If this isn't a table, however, we'll just assume - // it's a view that hasn't been configured for editing and thus abort here. - QString pk; - if(pseudo_pk.isEmpty()) + // Get primary key of the object to edit. + QString pk = primaryKeyForEditing(table, pseudo_pk); + if(pk.isNull()) { - sqlb::TablePtr tbl = getObjectByName(table).dynamicCast(); - if(tbl) - { - pk = tbl->rowidColumn(); - } else { - lastErrorMessage = tr("Cannot set data on this object"); - return false; - } - } else { - pk = pseudo_pk; + lastErrorMessage = tr("Cannot set data on this object"); + return false; } QString sql = QString("UPDATE %1 SET %2=? WHERE %3='%4';") @@ -1064,6 +1062,24 @@ bool DBBrowserDB::updateRecord(const sqlb::ObjectIdentifier& table, const QStrin } } +QString DBBrowserDB::primaryKeyForEditing(const sqlb::ObjectIdentifier& table, const QString& pseudo_pk) const +{ + // This function returns the primary key of the object to edit. For views we support 'pseudo' primary keys which must be specified manually. + // If no pseudo pk is specified we'll take the rowid column of the table instead. If this neither a table nor was a pseudo-PK specified, + // it is most likely a view that hasn't been configured for editing yet. In this case we return a null string to abort. + + if(pseudo_pk.isEmpty()) + { + sqlb::TablePtr tbl = getObjectByName(table).dynamicCast(); + if(tbl) + return tbl->rowidColumn(); + } else { + return pseudo_pk; + } + + return QString(); +} + bool DBBrowserDB::createTable(const sqlb::ObjectIdentifier& name, const sqlb::FieldVector& structure) { // Build SQL statement diff --git a/src/sqlitedb.h b/src/sqlitedb.h index 03116152..b1f34ca6 100644 --- a/src/sqlitedb.h +++ b/src/sqlitedb.h @@ -71,7 +71,7 @@ public: * @return An sqlite conform INSERT INTO statement with empty values. (NULL,'',0) */ QString emptyInsertStmt(const QString& schemaName, const sqlb::Table& t, const QString& pk_value = QString()) const; - bool deleteRecords(const sqlb::ObjectIdentifier& table, const QStringList& rowids); + bool deleteRecords(const sqlb::ObjectIdentifier& table, const QStringList& rowids, const QString& pseudo_pk = QString()); bool updateRecord(const sqlb::ObjectIdentifier& table, const QString& column, const QString& rowid, const QByteArray& value, bool itsBlob, const QString& pseudo_pk = QString()); bool createTable(const sqlb::ObjectIdentifier& name, const sqlb::FieldVector& structure); @@ -131,6 +131,8 @@ private: bool isEncrypted; bool isReadOnly; + QString primaryKeyForEditing(const sqlb::ObjectIdentifier& table, const QString& pseudo_pk) const; + void collationNeeded(void* pData, sqlite3* db, int eTextRep, const char* sCollationName); bool tryEncryptionSettings(const QString& filename, bool* encrypted, CipherDialog*& cipherSettings); diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index cee74be6..f4acf250 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -489,8 +489,6 @@ bool SqliteTableModel::removeRows(int row, int count, const QModelIndex& parent) QMutexLocker lock(&m_mutexDataCache); - bool ok = true; - QStringList rowids; for(int i=count-1;i>=0;i--) { @@ -498,10 +496,8 @@ bool SqliteTableModel::removeRows(int row, int count, const QModelIndex& parent) m_data.removeAt(row + i); --m_rowCountAdjustment; } - if(!m_db.deleteRecords(m_sTable, rowids)) - { - ok = false; - } + + bool ok = m_db.deleteRecords(m_sTable, rowids, m_pseudoPk); endRemoveRows(); return ok;