Improved support from removing rows in crud tabs.
It can handle now complex selections and reports back errors encountered when removing the rows fails.
This commit is contained in:
parent
950fea873c
commit
62c6ad5bfb
10 changed files with 365 additions and 116 deletions
|
|
@ -13,7 +13,9 @@
|
|||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include "Pgsql_oids.h"
|
||||
#include "Pgsql_PgException.h"
|
||||
#include "Pgsql_Params.h"
|
||||
#include "Pgsql_Transaction.h"
|
||||
#include <string>
|
||||
#include "ScopeGuard.h"
|
||||
|
||||
|
|
@ -94,7 +96,6 @@ CrudModel::Value CrudModel::getData(const QModelIndex &index) const
|
|||
int col = index.column();
|
||||
|
||||
auto row_mapping = m_rowMapping[grid_row];
|
||||
|
||||
const int last_row = rowCount() - 1;
|
||||
|
||||
//Oid o = m_roData->type(col);
|
||||
|
|
@ -225,7 +226,7 @@ void CrudModel::connectionStateChanged(ASyncDBConnection::State state)
|
|||
|
||||
Qt::ItemFlags CrudModel::flags(const QModelIndex &) const
|
||||
{
|
||||
Qt::ItemFlags flags = Qt::ItemIsSelectable + Qt::ItemIsEnabled;
|
||||
Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
||||
if (m_primaryKey) {
|
||||
flags |= Qt::ItemIsEditable;
|
||||
}
|
||||
|
|
@ -508,16 +509,12 @@ void CrudModel::appendNewRow()
|
|||
endInsertRows();
|
||||
}
|
||||
|
||||
void CrudModel::removeRows()
|
||||
{
|
||||
// determine selection
|
||||
// remove selected rows
|
||||
|
||||
}
|
||||
|
||||
bool CrudModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||
std::tuple<bool, QString> CrudModel::removeRows(const std::set<IntegerRange<int>> &row_ranges)
|
||||
{
|
||||
if (m_rowMapping.empty()) return false;
|
||||
if (row_ranges.empty()) return { true, "" };
|
||||
if (row_ranges.rbegin()->end() > m_rowMapping.size()) return { false, "Range error" };
|
||||
// When removing rows there is no direct mapping anymore between the rows in the grid
|
||||
// and the rows in m_roData
|
||||
|
||||
|
|
@ -527,42 +524,69 @@ bool CrudModel::removeRows(int row, int count, const QModelIndex &parent)
|
|||
|
||||
// 1. Get PKEY and remove that row from table
|
||||
|
||||
Pgsql::Connection db_update_conn;
|
||||
auto dbconfig = m_database->config();
|
||||
bool res = db_update_conn.connect(dbconfig.getKeywords(), dbconfig.getValues(), false);
|
||||
if (!res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// First delete rows in table
|
||||
QString delete_statement = createDeleteStatement();
|
||||
db_update_conn.query("BEGIN;");
|
||||
for (int current_row = row; current_row < row + count; ++current_row) {
|
||||
auto&& mapping = m_rowMapping[static_cast<size_t>(current_row)];
|
||||
auto params = getPKeyParamsForRow(mapping.rowKey);
|
||||
if (!params.empty()) {
|
||||
// Execute DELETE
|
||||
auto result = db_update_conn.queryParam(delete_statement, params);
|
||||
try {
|
||||
Pgsql::Connection db_update_conn;
|
||||
auto dbconfig = m_database->config();
|
||||
bool res = db_update_conn.connect(dbconfig.getKeywords(), dbconfig.getValues(), false);
|
||||
if (!res) {
|
||||
return { false, "Cannot connect to the database" };
|
||||
}
|
||||
}
|
||||
db_update_conn.query("COMMIT;");
|
||||
// Then from model
|
||||
{
|
||||
beginRemoveRows(parent, row, row);
|
||||
SCOPE_EXIT { endRemoveRows(); };
|
||||
for (int current_row = row; current_row < row + count; ++current_row) {
|
||||
auto&& mapping = m_rowMapping[static_cast<size_t>(current_row)];
|
||||
|
||||
// if the row is in modified it can be removed from modified
|
||||
if (mapping.modified) {
|
||||
m_modifiedRowList.erase(mapping.rowKey);
|
||||
// First delete rows in table
|
||||
QString delete_statement = createDeleteStatement();
|
||||
{
|
||||
db_update_conn.query("BEGIN;");
|
||||
auto tx = Pgsql::Transaction::startTransaction(db_update_conn);
|
||||
for (auto range : row_ranges) {
|
||||
for (int current_row = range.start(); current_row < range.end(); ++current_row) {
|
||||
auto&& mapping = m_rowMapping[static_cast<size_t>(current_row)];
|
||||
auto params = getPKeyParamsForRow(mapping.rowKey);
|
||||
if (!params.empty()) {
|
||||
// Execute DELETE
|
||||
db_update_conn.queryParam(delete_statement, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// \todo can it be pending? should be removed if it is.
|
||||
|
||||
tx.commit();
|
||||
// If something goes wrong after this commit we should reload contents of model
|
||||
}
|
||||
// remove the rows from m_rowMapping
|
||||
auto first = m_rowMapping.begin() + row;
|
||||
m_rowMapping.erase(first, first + count);
|
||||
// Then from model
|
||||
{
|
||||
|
||||
int rows_deleted = 0;
|
||||
for (auto range : row_ranges) {
|
||||
range.setStart(range.start() - rows_deleted);
|
||||
beginRemoveRows(QModelIndex(), range.start(), range.end() - 1);
|
||||
SCOPE_EXIT { endRemoveRows(); };
|
||||
for (int current_row = range.start(); current_row < range.end(); ++current_row) {
|
||||
auto&& mapping = m_rowMapping[static_cast<size_t>(current_row)];
|
||||
|
||||
// if the row is in modified it can be removed from modified
|
||||
if (mapping.modified) {
|
||||
m_modifiedRowList.erase(mapping.rowKey);
|
||||
}
|
||||
/// \todo can it be pending? should be removed if it is.
|
||||
|
||||
}
|
||||
// remove the rows from m_rowMapping
|
||||
auto first = m_rowMapping.begin() + range.start();
|
||||
m_rowMapping.erase(first, first + range.length());
|
||||
rows_deleted += range.length();
|
||||
}
|
||||
}
|
||||
return { true, "" };
|
||||
} catch (const Pgsql::PgResultError &error) {
|
||||
return { false, QString::fromUtf8(error.details().messageDetail.c_str()) };
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CrudModel::removeRows(int row, int count, const QModelIndex &)
|
||||
{
|
||||
if (m_rowMapping.empty()) return false;
|
||||
|
||||
IntegerRange<int> range(row, count);
|
||||
auto [res, message] = removeRows({ range });
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@
|
|||
#include "ASyncDBConnection.h"
|
||||
#include "Pgsql_Connection.h"
|
||||
|
||||
#include "IntegerRange.h"
|
||||
#include "PgClass.h"
|
||||
#include "PgConstraint.h"
|
||||
#include "Pgsql_Connection.h"
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
|
@ -73,9 +75,9 @@ public:
|
|||
|
||||
void loadData();
|
||||
|
||||
std::tuple<bool, QString> removeRows(const std::set<IntegerRange<int>> &row_ranges);
|
||||
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
|
||||
/// Removes selected rows
|
||||
void removeRows();
|
||||
|
||||
public slots:
|
||||
virtual bool submit() override;
|
||||
virtual void revert() override;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,12 @@
|
|||
#include "MainWindow.h"
|
||||
#include "ResultTableModelUtil.h"
|
||||
#include "PgLabItemDelegate.h"
|
||||
#include "IntegerRange.h"
|
||||
#include <QDebug>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
|
||||
|
||||
CrudTab::CrudTab(MainWindow *parent)
|
||||
|
|
@ -22,7 +27,8 @@ CrudTab::CrudTab(MainWindow *parent)
|
|||
m_crudModel = new CrudModel(parent);
|
||||
ui->tableView->setModel(m_crudModel);
|
||||
|
||||
ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
ui->tableView->addAction(ui->actionRemove_rows);
|
||||
|
||||
auto horizontal_header = ui->tableView->horizontalHeader();
|
||||
|
|
@ -30,9 +36,6 @@ CrudTab::CrudTab(MainWindow *parent)
|
|||
connect(horizontal_header, &QHeaderView::customContextMenuRequested,
|
||||
this, &CrudTab::headerCustomContextMenu);
|
||||
|
||||
//auto selection_model = ui->tableView->selectionModel();
|
||||
// connect(ui->tableView->selectionModel(), &QItemSelectionModel::currentRowChanged, this,
|
||||
// &CrudTab::tableView_currentRowChanged);
|
||||
}
|
||||
|
||||
CrudTab::~CrudTab()
|
||||
|
|
@ -44,21 +47,9 @@ void CrudTab::setConfig(std::shared_ptr<OpenDatabase> db, const PgClass &table)
|
|||
{
|
||||
m_db = db;
|
||||
m_table = table;
|
||||
// m_catalog = cat;
|
||||
// m_tablesModel->setCatalog(cat);
|
||||
// ui->tableListTable->resizeColumnsToContents();
|
||||
// m_namespaceFilterWidget->init(cat->namespaces());
|
||||
|
||||
// auto highlighter = new SqlSyntaxHighlighter(ui->constraintSqlEdit->document());
|
||||
// highlighter->setTypes(*cat->types());
|
||||
m_crudModel->setConfig(db, table);
|
||||
}
|
||||
|
||||
//void CrudTab::tableView_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous)
|
||||
//{
|
||||
|
||||
//}
|
||||
|
||||
void CrudTab::refresh()
|
||||
{
|
||||
m_crudModel->loadData();
|
||||
|
|
@ -66,14 +57,37 @@ void CrudTab::refresh()
|
|||
|
||||
void CrudTab::on_actionRemove_rows_triggered()
|
||||
{
|
||||
// determine selection
|
||||
std::set<IntegerRange<int>> row_ranges;
|
||||
auto selection = ui->tableView->selectionModel()->selection();
|
||||
for (auto range : selection) {
|
||||
row_ranges.emplace(range.top(), range.height());
|
||||
}
|
||||
std::set<IntegerRange<int>> merged_ranges;
|
||||
merge_ranges(row_ranges.begin(), row_ranges.end(), std::inserter(merged_ranges, merged_ranges.begin()));
|
||||
|
||||
// ui->tableView->currentIndex()
|
||||
// selectionModel()->
|
||||
QString msg = tr("Are you certain you want to remove the following row(s)?");
|
||||
msg += "\n";
|
||||
bool first = true;
|
||||
for (auto range : merged_ranges) {
|
||||
if (first) first = false;
|
||||
else msg += ", ";
|
||||
|
||||
//ui->tableView->selectionModel()->selectedRows()
|
||||
m_crudModel->removeRow(ui->tableView->currentIndex().row());
|
||||
//removeRows();
|
||||
auto s = range.start() + 1, e = range.end();
|
||||
if (s == e)
|
||||
msg += QString("%1").arg(s);
|
||||
else
|
||||
msg += QString("%1 through %2").arg(s).arg(e);
|
||||
|
||||
msg += " ";
|
||||
}
|
||||
auto res = QMessageBox::question(this, "pgLab", msg, QMessageBox::Yes, QMessageBox::No);
|
||||
|
||||
if (res == QMessageBox::Yes) {
|
||||
auto [res, msg] = m_crudModel->removeRows(merged_ranges);
|
||||
if (!res) {
|
||||
QMessageBox::critical(this, "pgLab", msg, QMessageBox::Close);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<QAction*> CrudTab::getToolbarActions()
|
||||
|
|
@ -83,6 +97,8 @@ std::vector<QAction*> CrudTab::getToolbarActions()
|
|||
action->setShortcut(QKeySequence(Qt::Key_F5));
|
||||
connect(action, &QAction::triggered, this, &CrudTab::refresh);
|
||||
actions.push_back(action);
|
||||
|
||||
actions.push_back(ui->actionRemove_rows);
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,21 @@
|
|||
|
||||
#include <Qt>
|
||||
|
||||
/// Returned by a model when asked for CustomReferencedTypeRole
|
||||
///
|
||||
/// Models will probably only be asked this for columns for which they returned
|
||||
/// Oid_Oid for the CustomDataTypeRole
|
||||
enum class ReferencedType {
|
||||
PgType,
|
||||
PgNamespace,
|
||||
PgRole
|
||||
};
|
||||
|
||||
enum CustomDataRole {
|
||||
CustomDataTypeRole = Qt::UserRole,
|
||||
CustomDataTypeRole = Qt::UserRole, ///< Requist the basic type of the value
|
||||
CustomReferencedTypeRole, ///<
|
||||
// Add other enum before this one is we might want to have multiple hidden values
|
||||
FirstHiddenValue,
|
||||
FirstHiddenValue, ///< Used to request value from a model which is not handed to the view
|
||||
};
|
||||
|
||||
#endif // CUSTOMDATAROLE_H
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue