From 5d9314f134ddd3dc4c853e398ac90ba247fb2e4f Mon Sep 17 00:00:00 2001 From: Unit 193 Date: Mon, 13 Jun 2016 18:42:42 -0400 Subject: Imported Upstream version 3.1.0 --- .../guiSQLiteStudio/dialogs/indexdialog.cpp | 564 ++++++++++++++++++--- 1 file changed, 492 insertions(+), 72 deletions(-) (limited to 'SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp') diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp index cdae91d..c5da317 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp @@ -1,7 +1,6 @@ #include "indexdialog.h" #include "ui_indexdialog.h" #include "schemaresolver.h" -#include "parser/ast/sqliteindexedcolumn.h" #include "services/notifymanager.h" #include "common/utils_sql.h" #include "db/chainexecutor.h" @@ -11,6 +10,7 @@ #include "services/config.h" #include "uiutils.h" #include "sqlite3.h" +#include "indexexprcolumndialog.h" #include "windows/editorwindow.h" #include "services/codeformatter.h" #include @@ -40,6 +40,7 @@ IndexDialog::IndexDialog(Db* db, const QString& index, QWidget* parent) : IndexDialog::~IndexDialog() { + clearColumns(); delete ui; } @@ -67,20 +68,34 @@ void IndexDialog::init() return; } + ui->moveUpButton->setIcon(ICONS.MOVE_UP); + ui->moveDownButton->setIcon(ICONS.MOVE_DOWN); + ui->addExprColumnButton->setIcon(ICONS.INDEX_EXPR_ADD); + ui->editExprColumnButton->setIcon(ICONS.INDEX_EXPR_EDIT); + ui->delExprColumnButton->setIcon(ICONS.INDEX_EXPR_DEL); + connect(ui->moveUpButton, SIGNAL(clicked()), this, SLOT(moveColumnUp())); + connect(ui->moveDownButton, SIGNAL(clicked()), this, SLOT(moveColumnDown())); + connect(ui->addExprColumnButton, SIGNAL(clicked()), this, SLOT(addExprColumn())); + connect(ui->editExprColumnButton, SIGNAL(clicked()), this, SLOT(editExprColumn())); + connect(ui->delExprColumnButton, SIGNAL(clicked()), this, SLOT(delExprColumn())); + connect(ui->columnsTable, SIGNAL(cellDoubleClicked(int,int)), this, SLOT(editExprColumn(int))); + ui->columnsTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + connect(ui->columnsTable->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), this, SLOT(updateToolBarButtons(QModelIndex))); ui->partialIndexEdit->setDb(db); connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); columnStateSignalMapping = new QSignalMapper(this); - connect(columnStateSignalMapping, SIGNAL(mapped(int)), this, SLOT(updateColumnState(int))); + connect(columnStateSignalMapping, SIGNAL(mapped(QString)), this, SLOT(updateColumnState(QString))); SchemaResolver resolver(db); ui->tableCombo->addItem(QString::null); ui->tableCombo->addItems(resolver.getTables()); connect(ui->tableCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateTable(QString))); connect(ui->tableCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateValidation())); + connect(ui->uniqueCheck, SIGNAL(toggled(bool)), this, SLOT(updateValidation())); if (existingIndex) ui->tableCombo->setEnabled(false); @@ -98,6 +113,9 @@ void IndexDialog::init() ui->partialIndexCheck->setVisible(false); ui->partialIndexEdit->setVisible(false); ui->columnsTable->setColumnHidden(1, true); + ui->addExprColumnButton->setVisible(false); + ui->editExprColumnButton->setVisible(false); + ui->delExprColumnButton->setVisible(false); } readCollations(); @@ -139,17 +157,17 @@ void IndexDialog::readIndex() void IndexDialog::buildColumns() { // Clean up + clearColumns(); ui->columnsTable->setRowCount(0); - columnCheckBoxes.clear(); - sortComboBoxes.clear(); - collateComboBoxes.clear(); totalColumns = tableColumns.size(); ui->columnsTable->setRowCount(totalColumns); int row = 0; - foreach (const QString& column, tableColumns) + for (const QString& column : tableColumns) buildColumn(column, row++); + + updateToolBarButtons(); } void IndexDialog::updateTable(const QString& value) @@ -166,15 +184,16 @@ void IndexDialog::updateValidation() { bool tableOk = ui->tableCombo->currentIndex() > 0; bool colSelected = false; + bool hasExprColumn = false; if (tableOk) { - foreach (QCheckBox* cb, columnCheckBoxes) + for (Column* col : columns.values()) { - if (cb->isChecked()) + if (col->getCheck()->isChecked()) { colSelected = true; - break; + hasExprColumn |= col->isExpr(); } } } @@ -182,11 +201,14 @@ void IndexDialog::updateValidation() bool partialConditionOk = (!ui->partialIndexCheck->isChecked() || (ui->partialIndexEdit->isSyntaxChecked() && !ui->partialIndexEdit->haveErrors())); + bool uniqueAndExprOk = !(ui->uniqueCheck->isChecked() && hasExprColumn); + + setValidState(ui->uniqueCheck, uniqueAndExprOk, tr("Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option.")); setValidState(ui->tableCombo, tableOk, tr("Pick the table for the index.")); setValidState(ui->columnsTable, colSelected, tr("Select at least one column.")); setValidState(ui->partialIndexCheck, partialConditionOk, tr("Enter a valid condition.")); - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(colSelected && partialConditionOk); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(colSelected && partialConditionOk && uniqueAndExprOk); } void IndexDialog::setTable(const QString& value) @@ -207,9 +229,30 @@ void IndexDialog::readCollations() void IndexDialog::buildColumn(const QString& name, int row) { - int col = 0; + Column* column = new Column(name, ui->columnsTable); + buildColumn(column, row); +} + +IndexDialog::Column* IndexDialog::buildColumn(SqliteOrderBy* orderBy, int row) +{ + SqliteExpr* expr = dynamic_cast(orderBy->expr->clone()); + return buildColumn(expr, row); +} + +IndexDialog::Column* IndexDialog::buildColumn(SqliteExpr* expr, int row) +{ + Column* column = new Column(expr, ui->columnsTable); + buildColumn(column, row); + return column; +} - QWidget* checkParent = new QWidget(); +void IndexDialog::buildColumn(Column* column, int row) +{ + QString key = column->getKey(); + columns[key] = column; + columnsByRow << column; + + column->setCheckParent(new QWidget()); QHBoxLayout* layout = new QHBoxLayout(); QMargins margins = layout->contentsMargins(); margins.setTop(0); @@ -217,51 +260,45 @@ void IndexDialog::buildColumn(const QString& name, int row) margins.setLeft(4); margins.setRight(4); layout->setContentsMargins(margins); - checkParent->setLayout(layout); + column->getCheckParent()->setLayout(layout); - QCheckBox* check = new QCheckBox(name); - checkParent->layout()->addWidget(check); + column->setCheck(new QCheckBox(key)); + column->getCheckParent()->layout()->addWidget(column->getCheck()); - ui->columnsTable->setCellWidget(row, col++, checkParent); - columnStateSignalMapping->setMapping(check, row); - connect(check, SIGNAL(toggled(bool)), columnStateSignalMapping, SLOT(map())); - connect(check, SIGNAL(toggled(bool)), this, SLOT(updateValidation())); - columnCheckBoxes << check; + columnStateSignalMapping->setMapping(column->getCheck(), key); + connect(column->getCheck(), SIGNAL(toggled(bool)), columnStateSignalMapping, SLOT(map())); + connect(column->getCheck(), SIGNAL(toggled(bool)), this, SLOT(updateValidation())); - QComboBox* collation = nullptr; if (db->getDialect() == Dialect::Sqlite3) { - collation = new QComboBox(); - collation->setEditable(true); - collation->lineEdit()->setPlaceholderText(tr("default", "index dialog")); - collation->setModel(&collations); - ui->columnsTable->setCellWidget(row, col++, collation); - collateComboBoxes << collation; - } - else - { - col++; + column->setCollation(new QComboBox()); + column->getCollation()->setEditable(true); + column->getCollation()->lineEdit()->setPlaceholderText(tr("default", "index dialog")); + column->getCollation()->setModel(&collations); } - QComboBox* sortOrder = new QComboBox(); - sortOrder->setToolTip(tr("Sort order", "table constraints")); - ui->columnsTable->setCellWidget(row, col++, sortOrder); - sortComboBoxes << sortOrder; + column->setSort(new QComboBox()); + column->getSort()->setToolTip(tr("Sort order", "table constraints")); QStringList sortList = {"", sqliteSortOrder(SqliteSortOrder::ASC), sqliteSortOrder(SqliteSortOrder::DESC)}; - sortOrder->addItems(sortList); + column->getSort()->addItems(sortList); + + column->prepareForNewRow(); + column->assignToNewRow(row); totalColumns++; - updateColumnState(row); + updateColumnState(key); } -void IndexDialog::updateColumnState(int row) +void IndexDialog::updateColumnState(const QString& columnKey) { - bool enabled = columnCheckBoxes[row]->isChecked(); - sortComboBoxes[row]->setEnabled(enabled); - if (db->getDialect() == Dialect::Sqlite3) - collateComboBoxes[row]->setEnabled(enabled); + Column* col = columns[columnKey]; + + bool enabled = col->getCheck()->isChecked(); + col->getSort()->setEnabled(enabled); + if (col->hasCollation()) + col->getCollation()->setEnabled(enabled); } void IndexDialog::updatePartialConditionState() @@ -283,26 +320,204 @@ void IndexDialog::tabChanged(int tab) updateDdl(); } +void IndexDialog::moveColumnUp() +{ + QModelIndex idx = ui->columnsTable->selectionModel()->currentIndex(); + if (!idx.isValid()) + return; + + int row = idx.row(); + if (row <= 0) + return; + + columnsByRow.move(row, row - 1); + rebuildColumnsByNewOrder(); + + idx = ui->columnsTable->model()->index(row - 1, 0); + ui->columnsTable->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); +} + +void IndexDialog::moveColumnDown() +{ + QModelIndex idx = ui->columnsTable->selectionModel()->currentIndex(); + if (!idx.isValid()) + return; + + int row = idx.row(); + int cols = tableColumns.size(); + + if ((row + 1) >= cols) + return; + + columnsByRow.move(row, row + 1); + rebuildColumnsByNewOrder(); + + idx = ui->columnsTable->model()->index(row + 1, 0); + ui->columnsTable->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); +} + +void IndexDialog::updateToolBarButtons(const QModelIndex& idx) +{ + if (!idx.isValid()) + { + ui->editExprColumnButton->setEnabled(false); + ui->delExprColumnButton->setEnabled(false); + ui->moveUpButton->setEnabled(false); + ui->moveDownButton->setEnabled(false); + return; + } + + int row = idx.row(); + int cols = tableColumns.size(); + ui->moveUpButton->setEnabled(row > 0); + ui->moveDownButton->setEnabled((row + 1) < cols); + + bool isExpr = columnsByRow[row]->isExpr(); + ui->editExprColumnButton->setEnabled(isExpr); + ui->delExprColumnButton->setEnabled(isExpr); +} + +void IndexDialog::addExprColumn() +{ + IndexExprColumnDialog dialog(db, this); + dialog.setExistingExprColumnKeys(getExistingColumnExprs()); + dialog.setTableColumns(getTableColumns()); + if (!dialog.exec()) + return; + + SqliteExpr* expr = dialog.getColumn(); + if (!expr) + { + qCritical() << "Null expr in IndexDialog::addExprColumn(). Aborting."; + return; + } + + int row = columnsByRow.size(); + ui->columnsTable->insertRow(row); + + Column* col = buildColumn(expr, row); + col->getCheck()->setChecked(true); + rebuildColumnsByNewOrder(); + + ui->columnsTable->scrollToBottom(); + updateValidation(); +} + +void IndexDialog::editExprColumn(int row) +{ + if (row < 0) + row = ui->columnsTable->currentRow(); + + if (row < 0 || row >= columnsByRow.size()) + { + qWarning() << "IndexDialog::editExprColumn() called for row out of bounds:" << row << "while there are" << columnsByRow.size() << "rows."; + return; + } + + Column* col = columnsByRow[row]; + if (!col->isExpr()) + { + qWarning() << "IndexDialog::editExprColumn() called for non-expr index column."; + return; + } + + IndexExprColumnDialog dialog(db, col->getExpr(), this); + dialog.setExistingExprColumnKeys(getExistingColumnExprs(col->getKey())); + dialog.setTableColumns(getTableColumns()); + if (!dialog.exec()) + return; + + SqliteExpr* expr = dialog.getColumn(); + if (!expr) + { + qCritical() << "Null expr in IndexDialog::editExprColumn(). Aborting."; + return; + } + + QString oldKey = col->getKey(); + col->setExpr(expr); + QString newKey = col->getKey(); + + columns.remove(oldKey); + columns[newKey] = col; + + col->getCheck()->setText(newKey); + col->getCheck()->setChecked(true); + rebuildColumnsByNewOrder(); + updateValidation(); +} + +void IndexDialog::delExprColumn() +{ + int row = ui->columnsTable->currentRow(); + + if (row < 0 || row >= columnsByRow.size()) + { + qWarning() << "IndexDialog::delExprColumn() called for row out of bounds:" << row << "while there are" << columnsByRow.size() << "rows."; + return; + } + + Column* col = columnsByRow[row]; + if (!col->isExpr()) + { + qWarning() << "IndexDialog::delExprColumn() called for non-expr index column."; + return; + } + + // Removes all widgets in the row + ui->columnsTable->removeRow(row); + + columnsByRow.removeOne(col); + columns.remove(col->getKey()); + delete col; + + rebuildColumnsByNewOrder(); + updateValidation(); +} + void IndexDialog::applyColumnValues() { - Dialect dialect = db->getDialect(); - int row; - foreach (SqliteIndexedColumn* idxCol, createIndex->indexedColumns) + Column* column = nullptr; + QString key; + int row = 0; + int totalRows = tableColumns.size(); + bool orderChanged = false; + for (SqliteOrderBy* idxCol : createIndex->indexedColumns) { - row = indexOf(tableColumns, idxCol->name, Qt::CaseInsensitive); - if (row == -1) + key = getKey(idxCol); + + if (idxCol->isSimpleColumn()) { - qCritical() << "Cannot find column from index in the table columns! Indexed column:" << idxCol->name - << ", table columns:" << tableColumns << ", index name:" << index << ", table name:" << table; - continue; + column = columns[key]; + if (!column) + { + qCritical() << "Cannot find column by name or expression! Column name/expression:" << key + << ", available columns:" << columns.keys() << ", index name:" << index; + continue; + } + } + else + column = buildColumn(idxCol, totalRows++); + + column->getCheck()->setChecked(true); + updateColumnState(key); + column->getSort()->setCurrentText(sqliteSortOrder(idxCol->order)); + if (column->hasCollation()) + column->getCollation()->setCurrentText(idxCol->getCollation()); + + // Setting proper order + int currentRow = columnsByRow.indexOf(column); + if (currentRow != row) + { + columnsByRow.move(currentRow, row); + orderChanged = true; } - columnCheckBoxes[row]->setChecked(true); - updateColumnState(row); - sortComboBoxes[row]->setCurrentText(sqliteSortOrder(idxCol->sortOrder)); - if (dialect == Dialect::Sqlite3) - collateComboBoxes[row]->setCurrentText(idxCol->collate); + row++; } + + if (orderChanged) + rebuildColumnsByNewOrder(); } void IndexDialog::applyIndex() @@ -315,15 +530,43 @@ void IndexDialog::applyIndex() ui->partialIndexEdit->setPlainText(createIndex->where->detokenize()); } -SqliteIndexedColumn* IndexDialog::addIndexedColumn(const QString& name) +SqliteOrderBy* IndexDialog::addIndexedColumn(const QString& name) +{ + SqliteOrderBy* idxCol = new SqliteOrderBy(); + idxCol->setParent(createIndex.data()); + + SqliteExpr* expr = new SqliteExpr(); + expr->initId(name); + idxCol->expr = expr; + expr->setParent(idxCol); + + createIndex->indexedColumns << idxCol; + return idxCol; +} + +SqliteOrderBy* IndexDialog::addIndexedColumn(SqliteExpr* expr) { - SqliteIndexedColumn* idxCol = new SqliteIndexedColumn(); - idxCol->name = name; + SqliteOrderBy* idxCol = new SqliteOrderBy(); idxCol->setParent(createIndex.data()); + + SqliteExpr* clonedExpr = dynamic_cast(expr->clone()); + idxCol->expr = clonedExpr; + clonedExpr->setParent(idxCol); + createIndex->indexedColumns << idxCol; return idxCol; } +void IndexDialog::addCollation(SqliteOrderBy* col, const QString& name) +{ + SqliteExpr* expr = new SqliteExpr(); + col->expr->setParent(expr); + expr->initCollate(col->expr, name); + expr->setParent(col); + + col->expr = expr; +} + void IndexDialog::rebuildCreateIndex() { createIndex = SqliteCreateIndexPtr::create(); @@ -333,22 +576,22 @@ void IndexDialog::rebuildCreateIndex() createIndex->uniqueKw = ui->uniqueCheck->isChecked(); - Dialect dialect = db->getDialect(); - SqliteIndexedColumn* idxCol = nullptr; - int i = -1; - for (const QString& column : tableColumns) + SqliteOrderBy* idxCol = nullptr; + for (Column* column : columnsByRow) { - i++; - - if (!columnCheckBoxes[i]->isChecked()) + if (!column->getCheck()->isChecked()) continue; - idxCol = addIndexedColumn(column); - if (dialect == Dialect::Sqlite3 && !collateComboBoxes[i]->currentText().isEmpty()) - idxCol->collate = collateComboBoxes[i]->currentText(); + if (column->isExpr()) + idxCol = addIndexedColumn(column->getExpr()); + else + idxCol = addIndexedColumn(column->getName()); + + if (column->hasCollation() && !column->getCollation()->currentText().isEmpty()) + addCollation(idxCol, column->getCollation()->currentText()); - if (sortComboBoxes[i]->currentIndex() > 0) - idxCol->sortOrder = sqliteSortOrder(sortComboBoxes[i]->currentText()); + if (column->getSort()->currentIndex() > 0) + idxCol->order = sqliteSortOrder(column->getSort()->currentText()); } if (ui->partialIndexCheck->isChecked()) @@ -388,10 +631,9 @@ void IndexDialog::queryDuplicates() QStringList countCols; QString wrappedCol; QString countColName; - int i = 0; for (const QString& column : tableColumns) { - if (!columnCheckBoxes[i++]->isChecked()) + if (!columns[column]->getCheck()->isChecked()) continue; wrappedCol = wrapObjIfNeeded(column, dialect); @@ -413,6 +655,62 @@ void IndexDialog::queryDuplicates() editor->execute(); } +void IndexDialog::clearColumns() +{ + for (Column* c : columns.values()) + delete c; + + columns.clear(); + columnsByRow.clear(); +} + +void IndexDialog::rebuildColumnsByNewOrder() +{ + int row = 0; + for (Column* column : columnsByRow) + { + column->prepareForNewRow(); + column->assignToNewRow(row++); + } +} + +QString IndexDialog::getKey(SqliteOrderBy* col) const +{ + if (col->isSimpleColumn()) + return col->getColumnName(); + + return col->expr->tokens.filterWhiteSpaces(false).detokenize(); +} + +QStringList IndexDialog::getExistingColumnExprs(const QString& exceptThis) const +{ + QString key; + QStringList exprs; + for (Column* col : columnsByRow) + { + if (col->isExpr()) + { + key = col->getKey(); + if (!exceptThis.isNull() && key == exceptThis) + continue; + + exprs << key; + } + } + return exprs; +} + +QStringList IndexDialog::getTableColumns() const +{ + QStringList cols; + for (Column* col : columnsByRow) + { + if (!col->isExpr()) + cols << col->getKey(); + } + return cols; +} + void IndexDialog::accept() { rebuildCreateIndex(); @@ -468,3 +766,125 @@ void IndexDialog::accept() .arg(executor.getErrorsMessages().join(",\n")), QMessageBox::Ok); } } + +IndexDialog::Column::Column(const QString& name, QTableWidget* table) +{ + this->name = name; + this->table = table; +} + +IndexDialog::Column::Column(SqliteExpr* expr, QTableWidget* table) +{ + this->expr = expr; + this->table = table; +} + +IndexDialog::Column::~Column() +{ + safe_delete(expr); +} + +void IndexDialog::Column::assignToNewRow(int row) +{ + table->setCellWidget(row, 0, column1Contrainer); + table->setCellWidget(row, 1, column2Contrainer); + table->setCellWidget(row, 2, column3Contrainer); +} + +void IndexDialog::Column::prepareForNewRow() +{ + column1Contrainer = defineContainer(checkParent); + column2Contrainer = defineContainer(sort); + if (collation) + column3Contrainer = defineContainer(collation); +} + +QCheckBox* IndexDialog::Column::getCheck() const +{ + return check; +} + +void IndexDialog::Column::setCheck(QCheckBox* cb) +{ + check = cb; +} + +QWidget* IndexDialog::Column::getCheckParent() const +{ + return checkParent; +} + +void IndexDialog::Column::setCheckParent(QWidget* w) +{ + checkParent = w; +} + +QComboBox* IndexDialog::Column::getSort() const +{ + return sort; +} + +void IndexDialog::Column::setSort(QComboBox* cb) +{ + sort = cb; +} + +QComboBox* IndexDialog::Column::getCollation() const +{ + return collation; +} + +void IndexDialog::Column::setCollation(QComboBox* cb) +{ + collation = cb; +} + +bool IndexDialog::Column::hasCollation() const +{ + return collation != nullptr; +} + +QString IndexDialog::Column::getName() const +{ + return name; +} + +SqliteExpr* IndexDialog::Column::getExpr() const +{ + return expr; +} + +void IndexDialog::Column::setExpr(SqliteExpr* expr) +{ + safe_delete(this->expr); + this->expr = expr; +} + +bool IndexDialog::Column::isExpr() const +{ + return expr != nullptr; +} + +QString IndexDialog::Column::getKey() const +{ + if (expr) + return expr->tokens.filterWhiteSpaces(false).detokenize(); + else + return name; +} + +QWidget* IndexDialog::Column::defineContainer(QWidget* w) +{ + QHBoxLayout* layout = new QHBoxLayout(); + QMargins margins = layout->contentsMargins(); + margins.setTop(0); + margins.setBottom(0); + margins.setLeft(0); + margins.setRight(0); + layout->setContentsMargins(margins); + + QWidget* container = new QWidget(); + container->setLayout(layout); + container->layout()->addWidget(w); + return container; +} -- cgit v1.2.3