Refactor the constraints tab in the Edit Table dialog

After moving both foreign keys and check constraints to different tabs
in the Edit Table dialog, the previous constraints tab now only shows
primary keys and unique constraints. To accomodate for this reduced
feature set, this commit renames their internal names and uses a
narrower data type for handling the constraints.
This commit is contained in:
Martin Kleusberg
2022-07-24 14:22:14 +02:00
parent 54d82eab44
commit dd62577818
3 changed files with 66 additions and 76 deletions

View File

@@ -15,7 +15,7 @@
#include <algorithm>
Q_DECLARE_METATYPE(sqlb::ConstraintPtr)
Q_DECLARE_METATYPE(std::shared_ptr<sqlb::UniqueConstraint>)
Q_DECLARE_METATYPE(std::shared_ptr<sqlb::ForeignKeyClause>)
Q_DECLARE_METATYPE(std::shared_ptr<sqlb::CheckConstraint>)
Q_DECLARE_METATYPE(sqlb::StringVector)
@@ -43,7 +43,7 @@ EditTableDialog::EditTableDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier&
ui->setupUi(this);
ui->widgetExtension->setVisible(false);
connect(ui->treeWidget, &QTreeWidget::itemChanged, this, &EditTableDialog::fieldItemChanged);
connect(ui->tableConstraints, &QTableWidget::itemChanged, this, &EditTableDialog::constraintItemChanged);
connect(ui->tableIndexConstraints, &QTableWidget::itemChanged, this, &EditTableDialog::indexConstraintItemChanged);
connect(ui->tableForeignKeys, &QTableWidget::itemChanged, this, &EditTableDialog::foreignKeyItemChanged);
connect(ui->tableCheckConstraints, &QTableWidget::itemChanged, this, &EditTableDialog::checkConstraintItemChanged);
@@ -63,9 +63,9 @@ EditTableDialog::EditTableDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier&
QMenu* constraint_menu = new QMenu(this);
constraint_menu->addAction(ui->actionAddPrimaryKey);
constraint_menu->addAction(ui->actionAddUniqueConstraint);
connect(ui->actionAddPrimaryKey, &QAction::triggered, this, [this]() { addConstraint(TableConstraintType::PrimaryKey); });
connect(ui->actionAddUniqueConstraint, &QAction::triggered, this, [this]() { addConstraint(TableConstraintType::Unique); });
ui->buttonAddConstraint->setMenu(constraint_menu);
connect(ui->actionAddPrimaryKey, &QAction::triggered, this, [this]() { addIndexConstraint(true); });
connect(ui->actionAddUniqueConstraint, &QAction::triggered, this, [this]() { addIndexConstraint(false); });
ui->buttonAddIndexConstraint->setMenu(constraint_menu);
// Get list of all collations
db.executeSQL("PRAGMA collation_list;", false, true, [this](int column_count, std::vector<QByteArray> columns, std::vector<QByteArray>) -> bool {
@@ -110,7 +110,7 @@ EditTableDialog::EditTableDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier&
}
populateFields();
populateConstraints();
populateIndexConstraints();
populateForeignKeys();
populateCheckConstraints();
} else {
@@ -121,9 +121,9 @@ EditTableDialog::EditTableDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier&
}
// Enable/disable remove constraint button depending on whether a constraint is selected
connect(ui->tableConstraints, &QTableWidget::itemSelectionChanged, this, [this]() {
bool hasSelection = ui->tableConstraints->selectionModel()->hasSelection();
ui->buttonRemoveConstraint->setEnabled(hasSelection);
connect(ui->tableIndexConstraints, &QTableWidget::itemSelectionChanged, this, [this]() {
bool hasSelection = ui->tableIndexConstraints->selectionModel()->hasSelection();
ui->buttonRemoveIndexConstraint->setEnabled(hasSelection);
});
connect(ui->tableForeignKeys, &QTableWidget::itemSelectionChanged, this, [this]() {
bool hasSelection = ui->tableForeignKeys->selectionModel()->hasSelection();
@@ -142,7 +142,7 @@ EditTableDialog::EditTableDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier&
updateColumnWidth();
// Allow editing of constraint columns by double clicking the columns column of the constraints table
connect(ui->tableConstraints, &QTableWidget::itemDoubleClicked, this, &EditTableDialog::constraintItemDoubleClicked);
connect(ui->tableIndexConstraints, &QTableWidget::itemDoubleClicked, this, &EditTableDialog::indexConstraintItemDoubleClicked);
connect(ui->tableForeignKeys, &QTableWidget::itemDoubleClicked, this, &EditTableDialog::foreignKeyItemDoubleClicked);
// (De-)activate fields
@@ -179,10 +179,10 @@ void EditTableDialog::updateColumnWidth()
ui->treeWidget->setColumnWidth(kUnique, 25);
ui->treeWidget->setColumnWidth(kForeignKey, 500);
ui->tableConstraints->setColumnWidth(kConstraintColumns, 180);
ui->tableConstraints->setColumnWidth(kConstraintType, 130);
ui->tableConstraints->setColumnWidth(kConstraintName, 120);
ui->tableConstraints->setColumnWidth(kConstraintSql, 300);
ui->tableIndexConstraints->setColumnWidth(kConstraintColumns, 180);
ui->tableIndexConstraints->setColumnWidth(kConstraintType, 130);
ui->tableIndexConstraints->setColumnWidth(kConstraintName, 120);
ui->tableIndexConstraints->setColumnWidth(kConstraintSql, 300);
ui->tableForeignKeys->setColumnWidth(kForeignKeyColumns, 120);
ui->tableForeignKeys->setColumnWidth(kForeignKeyName, 120);
@@ -265,13 +265,13 @@ void EditTableDialog::populateFields()
ui->treeWidget->blockSignals(false);
}
void EditTableDialog::populateConstraints()
void EditTableDialog::populateIndexConstraints()
{
// Disable the itemChanged signal or the table item will be updated while filling the treewidget
ui->tableConstraints->blockSignals(true);
ui->tableIndexConstraints->blockSignals(true);
const auto& indexConstraints = m_table.indexConstraints();
ui->tableConstraints->setRowCount(static_cast<int>(indexConstraints.size()));
ui->tableIndexConstraints->setRowCount(static_cast<int>(indexConstraints.size()));
int row = 0;
for(const auto& it : indexConstraints)
@@ -280,7 +280,7 @@ void EditTableDialog::populateConstraints()
QTableWidgetItem* column = new QTableWidgetItem(QString::fromStdString(sqlb::joinStringVector(it.first, ",")));
column->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
column->setData(Qt::UserRole, QVariant::fromValue(it.first)); // Remember columns of constraint object. This is used for modifying it later
ui->tableConstraints->setItem(row, kConstraintColumns, column);
ui->tableIndexConstraints->setItem(row, kConstraintColumns, column);
// Type
QComboBox* type = new QComboBox(this);
@@ -289,7 +289,6 @@ void EditTableDialog::populateConstraints()
type->setCurrentIndex(it.second->type());
connect(type, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this, type, it](int index) {
// Handle change of constraint type. Effectively this means removing the old constraint and replacing it by an entirely new one.
// Only the column list and the name can be migrated to the new constraint.
// Make sure there is only one primary key at a time
if(index == 0 && m_table.primaryKey())
@@ -322,29 +321,29 @@ void EditTableDialog::populateConstraints()
// Update SQL and view
populateFields();
populateConstraints();
populateIndexConstraints();
updateSqlText();
});
QTableWidgetItem* typeColumn = new QTableWidgetItem();
typeColumn->setData(Qt::UserRole, QVariant::fromValue<sqlb::ConstraintPtr>(it.second)); // Remember address of constraint object. This is used for modifying it later
ui->tableConstraints->setCellWidget(row, kConstraintType, type);
ui->tableConstraints->setItem(row, kConstraintType, typeColumn);
typeColumn->setData(Qt::UserRole, QVariant::fromValue<std::shared_ptr<sqlb::UniqueConstraint>>(it.second)); // Remember address of constraint object. This is used for modifying it later
ui->tableIndexConstraints->setCellWidget(row, kConstraintType, type);
ui->tableIndexConstraints->setItem(row, kConstraintType, typeColumn);
// Name
QTableWidgetItem* name = new QTableWidgetItem(QString::fromStdString(it.second->name()));
name->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable);
ui->tableConstraints->setItem(row, kConstraintName, name);
ui->tableIndexConstraints->setItem(row, kConstraintName, name);
// SQL
QTableWidgetItem* sql = new QTableWidgetItem(QString::fromStdString(it.second->toSql(it.first)));
sql->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
ui->tableConstraints->setItem(row, kConstraintSql, sql);
ui->tableIndexConstraints->setItem(row, kConstraintSql, sql);
row++;
}
ui->tableConstraints->blockSignals(false);
ui->tableIndexConstraints->blockSignals(false);
}
void EditTableDialog::populateForeignKeys()
@@ -614,7 +613,7 @@ void EditTableDialog::fieldItemChanged(QTreeWidgetItem *item, int column)
}
// Update the constraints view
populateConstraints();
populateIndexConstraints();
populateForeignKeys();
} break;
case kType:
@@ -651,7 +650,7 @@ void EditTableDialog::fieldItemChanged(QTreeWidgetItem *item, int column)
}
// Update the constraints view
populateConstraints();
populateIndexConstraints();
}
break;
case kNotNull:
@@ -816,10 +815,10 @@ void EditTableDialog::fieldItemChanged(QTreeWidgetItem *item, int column)
checkInput();
}
void EditTableDialog::constraintItemChanged(QTableWidgetItem* item)
void EditTableDialog::indexConstraintItemChanged(QTableWidgetItem* item)
{
// Find modified constraint
sqlb::ConstraintPtr constraint = ui->tableConstraints->item(item->row(), kConstraintType)->data(Qt::UserRole).value<sqlb::ConstraintPtr>();
auto constraint = ui->tableIndexConstraints->item(item->row(), kConstraintType)->data(Qt::UserRole).value<std::shared_ptr<sqlb::UniqueConstraint>>();
// Which column has been modified?
switch(item->column())
@@ -830,7 +829,7 @@ void EditTableDialog::constraintItemChanged(QTableWidgetItem* item)
}
// Update SQL
populateConstraints();
populateIndexConstraints();
checkInput();
}
@@ -875,28 +874,25 @@ void EditTableDialog::checkConstraintItemChanged(QTableWidgetItem* item)
checkInput();
}
void EditTableDialog::constraintItemDoubleClicked(QTableWidgetItem* item)
void EditTableDialog::indexConstraintItemDoubleClicked(QTableWidgetItem* item)
{
// Check whether the double clicked item is in the columns column
if(item->column() == kConstraintColumns)
{
sqlb::StringVector columns = ui->tableConstraints->item(item->row(), item->column())->data(Qt::UserRole).value<sqlb::StringVector>();
if(columns.empty())
{
// When we've got no columns, try the other type of columns vector
sqlb::IndexedColumnVector indexed_columns = ui->tableConstraints->item(item->row(), item->column())->data(Qt::UserRole).value<sqlb::IndexedColumnVector>();
std::transform(indexed_columns.begin(), indexed_columns.end(), std::back_inserter(columns), [](const auto& e) { return e.name(); });
}
sqlb::IndexedColumnVector indexed_columns = ui->tableIndexConstraints->item(item->row(), item->column())->data(Qt::UserRole).value<sqlb::IndexedColumnVector>();
sqlb::StringVector columns;
std::transform(indexed_columns.begin(), indexed_columns.end(), std::back_inserter(columns), [](const auto& e) { return e.name(); });
// Show the select items popup dialog
SelectItemsPopup* dialog = new SelectItemsPopup(m_table.fieldNames(), columns, this);
QRect item_rect = ui->tableConstraints->visualItemRect(item);
dialog->move(ui->tableConstraints->mapToGlobal(QPoint(ui->tableConstraints->x() + item_rect.x(),
ui->tableConstraints->y() + item_rect.y() + item_rect.height() / 2)));
QRect item_rect = ui->tableIndexConstraints->visualItemRect(item);
dialog->move(ui->tableIndexConstraints->mapToGlobal(QPoint(ui->tableIndexConstraints->x() + item_rect.x(),
ui->tableIndexConstraints->y() + item_rect.y() + item_rect.height() / 2)));
dialog->show();
// Get constraint
sqlb::ConstraintPtr constraint = ui->tableConstraints->item(item->row(), kConstraintType)->data(Qt::UserRole).value<sqlb::ConstraintPtr>();
auto constraint = ui->tableIndexConstraints->item(item->row(), kConstraintType)->data(Qt::UserRole).value<std::shared_ptr<sqlb::UniqueConstraint>>();
// When clicking the Apply button in the popup dialog, save the new columns list
connect(dialog, &SelectItemsPopup::accepted, this, [this, dialog, constraint, columns]() {
@@ -913,7 +909,7 @@ void EditTableDialog::constraintItemDoubleClicked(QTableWidgetItem* item)
// Update the UI
populateFields();
populateConstraints();
populateIndexConstraints();
updateSqlText();
}
});
@@ -1043,7 +1039,7 @@ void EditTableDialog::removeField()
delete ui->treeWidget->currentItem();
// Update the constraints view
populateConstraints();
populateIndexConstraints();
populateForeignKeys();
checkInput();
@@ -1230,29 +1226,29 @@ void EditTableDialog::setOnConflict(const QString& on_conflict)
updateSqlText();
}
void EditTableDialog::removeConstraint()
void EditTableDialog::removeIndexConstraint()
{
// Is there any item selected to delete?
if(!ui->tableConstraints->currentItem())
if(!ui->tableIndexConstraints->currentItem())
return;
// Find constraint to delete
int row = ui->tableConstraints->currentRow();
sqlb::ConstraintPtr constraint = ui->tableConstraints->item(row, kConstraintType)->data(Qt::UserRole).value<sqlb::ConstraintPtr>();
int row = ui->tableIndexConstraints->currentRow();
auto constraint = ui->tableIndexConstraints->item(row, kConstraintType)->data(Qt::UserRole).value<std::shared_ptr<sqlb::UniqueConstraint>>();
// Remove the constraint. If there is more than one constraint with this combination of columns and constraint type, only delete the first one.
m_table.removeConstraint(constraint);
ui->tableConstraints->removeRow(ui->tableConstraints->currentRow());
ui->tableIndexConstraints->removeRow(ui->tableIndexConstraints->currentRow());
// Update SQL and view
updateSqlText();
populateFields();
}
void EditTableDialog::addConstraint(TableConstraintType type)
void EditTableDialog::addIndexConstraint(bool primary_key)
{
// There can only be one primary key
if(type == TableConstraintType::PrimaryKey)
if(primary_key)
{
if(m_table.primaryKey())
{
@@ -1263,12 +1259,12 @@ void EditTableDialog::addConstraint(TableConstraintType type)
// Create new constraint
m_table.addConstraint(sqlb::IndexedColumnVector{}, std::make_shared<sqlb::PrimaryKeyConstraint>());
} else if(type == TableConstraintType::Unique)
} else
m_table.addConstraint(sqlb::IndexedColumnVector{}, std::make_shared<sqlb::UniqueConstraint>());
// Update SQL and view
populateFields();
populateConstraints();
populateIndexConstraints();
updateSqlText();
}

View File

@@ -43,7 +43,7 @@ private:
kForeignKey = 9
};
enum ConstraintColumns {
enum IndexConstraintColumns {
kConstraintColumns = 0,
kConstraintType = 1,
kConstraintName = 2,
@@ -71,12 +71,6 @@ private:
MoveBottom
};
enum TableConstraintType
{
PrimaryKey,
Unique
};
void updateColumnWidth();
void updateSqlText();
@@ -84,7 +78,7 @@ private:
private slots:
void populateFields();
void populateConstraints();
void populateIndexConstraints();
void populateForeignKeys();
void populateCheckConstraints();
void addField();
@@ -94,10 +88,10 @@ private slots:
void reject() override;
void checkInput();
void fieldItemChanged(QTreeWidgetItem* item, int column);
void constraintItemChanged(QTableWidgetItem* item);
void indexConstraintItemChanged(QTableWidgetItem* item);
void foreignKeyItemChanged(QTableWidgetItem* item);
void checkConstraintItemChanged(QTableWidgetItem* item);
void constraintItemDoubleClicked(QTableWidgetItem* item);
void indexConstraintItemDoubleClicked(QTableWidgetItem* item);
void foreignKeyItemDoubleClicked(QTableWidgetItem* item);
void updateTypeAndCollation(QObject *object);
bool eventFilter(QObject *object, QEvent *event) override;
@@ -110,8 +104,8 @@ private slots:
void setStrict(bool strict);
void changeSchema(const QString& schema);
void setOnConflict(const QString& on_conflict);
void addConstraint(EditTableDialog::TableConstraintType type);
void removeConstraint();
void addIndexConstraint(bool primary_key);
void removeIndexConstraint();
void addForeignKey();
void removeForeignKey();
void addCheckConstraint();

View File

@@ -404,13 +404,13 @@
</widget>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Constraints</string>
<string>Index Constraints</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QToolButton" name="buttonAddConstraint">
<widget class="QToolButton" name="buttonAddIndexConstraint">
<property name="text">
<string>Add constraint</string>
</property>
@@ -430,7 +430,7 @@
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonRemoveConstraint">
<widget class="QToolButton" name="buttonRemoveIndexConstraint">
<property name="enabled">
<bool>false</bool>
</property>
@@ -465,7 +465,7 @@
</layout>
</item>
<item>
<widget class="QTableWidget" name="tableConstraints">
<widget class="QTableWidget" name="tableIndexConstraints">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
@@ -744,9 +744,9 @@
<tabstop>buttonMoveDown</tabstop>
<tabstop>buttonMoveBottom</tabstop>
<tabstop>treeWidget</tabstop>
<tabstop>buttonAddConstraint</tabstop>
<tabstop>buttonRemoveConstraint</tabstop>
<tabstop>tableConstraints</tabstop>
<tabstop>buttonAddIndexConstraint</tabstop>
<tabstop>buttonRemoveIndexConstraint</tabstop>
<tabstop>tableIndexConstraints</tabstop>
</tabstops>
<resources>
<include location="icons/icons.qrc"/>
@@ -945,10 +945,10 @@
</hints>
</connection>
<connection>
<sender>buttonRemoveConstraint</sender>
<sender>buttonRemoveIndexConstraint</sender>
<signal>clicked()</signal>
<receiver>EditTableDialog</receiver>
<slot>removeConstraint()</slot>
<slot>removeIndexConstraint()</slot>
<hints>
<hint type="sourcelabel">
<x>119</x>
@@ -1100,7 +1100,7 @@
<slot>moveDown()</slot>
<slot>setWithoutRowid(bool)</slot>
<slot>changeSchema(QString)</slot>
<slot>removeConstraint()</slot>
<slot>removeIndexConstraint()</slot>
<slot>moveTop()</slot>
<slot>moveBottom()</slot>
<slot>setOnConflict(QString)</slot>