#include "EditIndexDialog.h" #include "ui_EditIndexDialog.h" #include "sqlitedb.h" #include #include EditIndexDialog::EditIndexDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier& indexName, bool createIndex, QWidget* parent) : QDialog(parent), pdb(db), curIndex(indexName), index(indexName.name()), newIndex(createIndex), ui(new Ui::EditIndexDialog), m_sRestorePointName(pdb.generateSavepointName("editindex")) { // Create UI ui->setupUi(this); ui->sqlTextEdit->setReadOnly(true); // Get list of tables, sort it alphabetically and fill the combobox 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) { // 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.key(), it.value().toVariant()); ui->comboTableName->blockSignals(false); QHeaderView *tableHeaderView = ui->tableIndexColumns->horizontalHeader(); tableHeaderView->setSectionResizeMode(0, QHeaderView::Stretch); // Editing an existing index? if(!newIndex) { // Load the current layout and fill in the dialog fields index = *(pdb.getObjectByName(curIndex).dynamicCast()); ui->editIndexName->blockSignals(true); ui->editIndexName->setText(index.name()); ui->editIndexName->blockSignals(false); ui->checkIndexUnique->blockSignals(true); ui->checkIndexUnique->setChecked(index.unique()); ui->checkIndexUnique->blockSignals(false); ui->comboTableName->blockSignals(true); ui->comboTableName->setCurrentText(index.table()); ui->comboTableName->blockSignals(false); ui->editPartialClause->blockSignals(true); ui->editPartialClause->setText(index.whereExpr()); ui->editPartialClause->blockSignals(false); tableChanged(index.table(), true); } else { tableChanged(ui->comboTableName->currentText(), false); } // Add event handler for index column name changes. These are only allowed for expression columns, though. connect(ui->tableIndexColumns, static_cast(&QTableWidget::itemChanged), [=](QTableWidgetItem* item) { index.columns().at(item->row())->setName(item->text()); updateSqlText(); }); // Create a savepoint to revert back to pdb.setSavepoint(m_sRestorePointName); } EditIndexDialog::~EditIndexDialog() { delete ui; } void EditIndexDialog::tableChanged(const QString& new_table, bool initialLoad) { // Set the table name and clear all index columns if(!initialLoad) { index.setTable(sqlb::ObjectIdentifier(ui->comboTableName->currentData()).name()); index.clearColumns(); } // Stop here if table name is empty if(new_table.isEmpty()) { // Call checkInput() before to make sure the OK button is disabled checkInput(); return; } updateColumnLists(); } void EditIndexDialog::updateColumnLists() { // Fill the table column list sqlb::TablePtr table = pdb.getObjectByName(sqlb::ObjectIdentifier(ui->comboTableName->currentData())).dynamicCast(); if(!table) return; sqlb::FieldInfoList tableFields = table->fieldInformation(); ui->tableTableColumns->setRowCount(tableFields.size()); int tableRows = 0; for(int i=0;isetFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); ui->tableTableColumns->setItem(tableRows, 0, name); // Put the data type in the second column QTableWidgetItem* type = new QTableWidgetItem(tableFields.at(i).type); type->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); ui->tableTableColumns->setItem(tableRows, 1, type); tableRows++; } } // Set row count to actual count. This is needed for the intial loading, when some rows might have been omitted because they were used in the index ui->tableTableColumns->setRowCount(tableRows); // Fill the index column list. This is done separately from the table column to include expression columns (these are not found in the original // table) and to preserve the order of the index columns auto indexFields = index.columns(); ui->tableIndexColumns->blockSignals(true); ui->tableIndexColumns->setRowCount(indexFields.size()); for(int i=0;iname()); Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; if(indexFields.at(i)->expression()) flags |= Qt::ItemIsEditable; name->setFlags(flags); ui->tableIndexColumns->setItem(i, 0, name); // And put a combobox to select the order in which to index the field in the last column QComboBox* order = new QComboBox(this); order->addItem(""); order->addItem("ASC"); order->addItem("DESC"); order->setCurrentText(indexFields.at(i)->order().toUpper()); ui->tableIndexColumns->setCellWidget(i, 1, order); connect(order, static_cast(&QComboBox::currentTextChanged), [=](QString new_order) { int colnum = index.findColumn(indexFields.at(i)->name()); if(colnum != -1) { index.column(colnum)->setOrder(new_order); updateSqlText(); } }); } ui->tableIndexColumns->blockSignals(false); checkInput(); } void EditIndexDialog::addToIndex(const QModelIndex& idx) { // Get current row number int row; if(idx.isValid()) row = idx.row(); else row = ui->tableTableColumns->currentRow(); // No row selected? Abort. if(row == -1) return; // Add field to index index.addColumn(sqlb::IndexedColumnPtr(new sqlb::IndexedColumn( ui->tableTableColumns->item(row, 0)->text(), // Column name false, // Is expression ""))); // Order // Update UI updateColumnLists(); } void EditIndexDialog::removeFromIndex(const QModelIndex& idx) { // Get current row number int row; if(idx.isValid()) row = idx.row(); else row = ui->tableIndexColumns->currentRow(); // No row selected? Abort. if(row == -1) return; // If this is an expression column and the action was triggered by a double click event instead of a button click, // we won't remove the expression column because it's too likely that this was only done by accident by the user. // Instead just open the expression column for editing. if(index.column(row)->expression() && sender() != ui->buttonFromIndex) { ui->tableIndexColumns->editItem(ui->tableIndexColumns->item(row, 0)); return; } // Remove column from index index.removeColumn(ui->tableIndexColumns->item(row, 0)->text()); // Update UI updateColumnLists(); } void EditIndexDialog::checkInput() { // Check if index name is set bool valid = true; if(ui->editIndexName->text().isEmpty()) valid = false; // Check if a table is selected (this is especially important in the case where there are no tables in the database yet). if(ui->comboTableName->currentText().isNull()) valid = false; // Check if index has any columns if(index.columns().size() == 0) valid = false; // Only activate OK button if index data is valid ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid); // Set the index name and the unique flag index.setName(ui->editIndexName->text()); index.setUnique(ui->checkIndexUnique->isChecked()); index.setWhereExpr(ui->editPartialClause->text()); updateSqlText(); } void EditIndexDialog::accept() { // When editing an index, delete the old one first if(!newIndex) { if(!pdb.executeSQL(QString("DROP INDEX IF EXISTS %1;").arg(curIndex.toString()))) { QMessageBox::warning(this, qApp->applicationName(), tr("Deleting the old index failed:\n%1").arg(pdb.lastError())); return; } } // 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())); } void EditIndexDialog::reject() { // Rollback to our savepoint pdb.revertToSavepoint(m_sRestorePointName); QDialog::reject(); } void EditIndexDialog::updateSqlText() { ui->sqlTextEdit->setText(index.sql(sqlb::ObjectIdentifier(ui->comboTableName->currentData()).schema())); } void EditIndexDialog::moveColumnUp() { moveCurrentColumn(false); } void EditIndexDialog::moveColumnDown() { moveCurrentColumn(true); } void EditIndexDialog::moveCurrentColumn(bool down) { // Get current row number and calculate row number after the movement. Check the values int currentRow = ui->tableIndexColumns->currentRow(); if(currentRow == -1) return; int newRow = currentRow + (down ? 1 : -1); if(newRow < 0) return; if(newRow >= ui->tableIndexColumns->rowCount()) return; // Get the column information, swap the columns, and save the new column list back in the index auto columns = index.columns(); std::swap(columns[currentRow], columns[newRow]); index.setColumns(columns); // Update UI updateColumnLists(); // Select old row at new position ui->tableIndexColumns->selectRow(newRow); } void EditIndexDialog::addExpressionColumn() { // Check if there already is an empty expression column int row = index.findColumn(""); if(row == -1) { // There is no empty expression column yet, so add one. // Add new expression column to the index index.addColumn(sqlb::IndexedColumnPtr(new sqlb::IndexedColumn( "", // Column name true, // Is expression ""))); // Order // Update UI updateColumnLists(); // Get row number of new column row = ui->tableIndexColumns->rowCount() - 1; } // Now we should have the row number of the empty expression column, no matter if it was newly added or it already existed. // Select the row for editing ui->tableIndexColumns->editItem(ui->tableIndexColumns->item(row, 0)); }