From 7167ce41b61d2ba2cdb526777a4233eb84a3b66a Mon Sep 17 00:00:00 2001 From: Unit 193 Date: Sat, 6 Dec 2014 17:33:25 -0500 Subject: Imported Upstream version 2.99.6 --- .../guiSQLiteStudio/dialogs/triggerdialog.cpp | 413 +++++++++++++++++++++ 1 file changed, 413 insertions(+) create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp (limited to 'SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp') diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp new file mode 100644 index 0000000..0707bd3 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp @@ -0,0 +1,413 @@ +#include "triggerdialog.h" +#include "ui_triggerdialog.h" +#include "parser/ast/sqliteselect.h" +#include "services/notifymanager.h" +#include "parser/ast/sqliteexpr.h" +#include "triggercolumnsdialog.h" +#include "common/utils_sql.h" +#include "schemaresolver.h" +#include "parser/parser.h" +#include "iconmanager.h" +#include "db/chainexecutor.h" +#include "dbtree/dbtree.h" +#include "ddlpreviewdialog.h" +#include "uiconfig.h" +#include "services/config.h" +#include "uiutils.h" +#include "services/codeformatter.h" +#include +#include +#include + +TriggerDialog::TriggerDialog(Db* db, QWidget *parent) : + QDialog(parent), + db(db), + ui(new Ui::TriggerDialog) +{ + init(); +} + +TriggerDialog::~TriggerDialog() +{ + delete ui; +} + +void TriggerDialog::setParentTable(const QString& name) +{ + forTable = true; + table = name; + initTrigger(); +} + +void TriggerDialog::setParentView(const QString& name) +{ + forTable = false; + view = name; + initTrigger(); +} + +void TriggerDialog::setTrigger(const QString& name) +{ + trigger = name; + originalTriggerName = name; + existingTrigger = true; + initTrigger(); +} + +void TriggerDialog::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +void TriggerDialog::init() +{ + ui->setupUi(this); + limitDialogWidth(this); + + connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(updateDdlTab(int))); + connect(ui->actionColumns, SIGNAL(clicked()), this, SLOT(showColumnsDialog())); + + // On object combo + ui->onCombo->setEnabled(false); + connect(ui->onCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(tableChanged(QString))); + + // Action combo + ui->actionCombo->addItems({ + SqliteCreateTrigger::Event::typeToString(SqliteCreateTrigger::Event::DELETE), + SqliteCreateTrigger::Event::typeToString(SqliteCreateTrigger::Event::INSERT), + SqliteCreateTrigger::Event::typeToString(SqliteCreateTrigger::Event::UPDATE), + SqliteCreateTrigger::Event::typeToString(SqliteCreateTrigger::Event::UPDATE_OF) + }); + connect(ui->actionCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateState())); + + // Scope combo + ui->scopeCombo->addItems({ + SqliteCreateTrigger::scopeToString(SqliteCreateTrigger::Scope::null), + SqliteCreateTrigger::scopeToString(SqliteCreateTrigger::Scope::FOR_EACH_ROW) + }); + if (db->getDialect() == Dialect::Sqlite2) + { + ui->scopeCombo->addItems({ + SqliteCreateTrigger::scopeToString(SqliteCreateTrigger::Scope::FOR_EACH_STATEMENT) + }); + } + + // Precondition + connect(ui->preconditionCheck, SIGNAL(clicked()), this, SLOT(updateState())); + connect(ui->preconditionEdit, SIGNAL(errorsChecked(bool)), this, SLOT(updateValidation())); + connect(ui->preconditionEdit, SIGNAL(textChanged()), this, SLOT(updateValidation())); + ui->preconditionEdit->setDb(db); + + // Code + connect(ui->codeEdit, SIGNAL(errorsChecked(bool)), this, SLOT(updateValidation())); + connect(ui->codeEdit, SIGNAL(textChanged()), this, SLOT(updateValidation())); + ui->codeEdit->setDb(db); +} + +void TriggerDialog::initTrigger() +{ + // Name edit + ui->nameEdit->setText(trigger); + + if (trigger.isNull()) + { + createTrigger = SqliteCreateTriggerPtr::create(); + createTrigger->event = new SqliteCreateTrigger::Event(); + } + else + { + parseDdl(); + readTrigger(); + } + + // Event combo + if (forTable) + { + ui->whenCombo->addItems({ + SqliteCreateTrigger::time(SqliteCreateTrigger::Time::null), + SqliteCreateTrigger::time(SqliteCreateTrigger::Time::BEFORE), + SqliteCreateTrigger::time(SqliteCreateTrigger::Time::AFTER) + }); + } + else + { + ui->whenCombo->addItems({ + SqliteCreateTrigger::time(SqliteCreateTrigger::Time::INSTEAD_OF) + }); + ui->whenCombo->setEnabled(false); + ui->onLabel->setText(tr("On view:")); + } + + if (!view.isNull() || !table.isNull()) + { + readColumns(); + QString target = getTargetObjectName(); + ui->onCombo->addItem(target); + ui->onCombo->setCurrentText(target); + } + + // Precondition and code edits + setupVirtualSqls(); + + updateState(); +} + +void TriggerDialog::parseDdl() +{ + SchemaResolver resolver(db); + SqliteQueryPtr parsedObject = resolver.getParsedObject(trigger, SchemaResolver::TRIGGER); + if (!parsedObject.dynamicCast()) + { + notifyError(tr("Could not process trigger %1 correctly. Unable to open a trigger dialog.").arg(trigger)); + reject(); + return; + } + + createTrigger = parsedObject.dynamicCast(); + ddl = createTrigger->detokenize(); +} + +void TriggerDialog::readTrigger() +{ + if (!createTrigger) + return; + + forTable = createTrigger->eventTime != SqliteCreateTrigger::Time::INSTEAD_OF; + if (forTable) + table = createTrigger->table; + else + view = createTrigger->table; + + ui->onCombo->addItem(createTrigger->table); + ui->onCombo->setCurrentText(createTrigger->table); + ui->whenCombo->setCurrentText(SqliteCreateTrigger::time(createTrigger->eventTime)); + ui->actionCombo->setCurrentText(SqliteCreateTrigger::Event::typeToString(createTrigger->event->type)); + ui->scopeCombo->setCurrentText(SqliteCreateTrigger::scopeToString(createTrigger->scope)); + if (createTrigger->precondition) + { + ui->preconditionCheck->setChecked(true); + ui->preconditionEdit->setPlainText(createTrigger->precondition->detokenize()); + } + + if (createTrigger->queries.size() > 0) + { + QStringList sqls; + foreach (SqliteQuery* query, createTrigger->queries) + sqls << query->detokenize(); + + ui->codeEdit->setPlainText(sqls.join(";\n")+";"); + } +} + +void TriggerDialog::setupVirtualSqls() +{ + Dialect dialect = db->getDialect(); + static QString preconditionVirtSql = QStringLiteral("CREATE TRIGGER %1 BEFORE INSERT ON %2 WHEN %3 BEGIN SELECT 1; END;"); + static QString codeVirtSql = QStringLiteral("CREATE TRIGGER %1 BEFORE INSERT ON %2 BEGIN %3 END;"); + ui->codeEdit->setVirtualSqlCompleteSemicolon(true); + if (!trigger.isNull()) + { + if (createTrigger) // if false, then there was a parsing error in parseDdl(). + { + ui->preconditionEdit->setVirtualSqlExpression( + preconditionVirtSql.arg(wrapObjIfNeeded(trigger, dialect), + wrapObjIfNeeded(createTrigger->table, dialect), + "%1")); + + ui->codeEdit->setVirtualSqlExpression( + codeVirtSql.arg( + wrapObjIfNeeded(trigger, dialect), + wrapObjIfNeeded(createTrigger->table, dialect), + "%1")); + } + } + else if (!table.isNull() || !view.isNull()) + { + ui->preconditionEdit->setVirtualSqlExpression( + preconditionVirtSql.arg("trig", + wrapObjIfNeeded(getTargetObjectName(), dialect), + "%1")); + + ui->codeEdit->setVirtualSqlExpression( + codeVirtSql.arg("trig", + wrapObjIfNeeded(getTargetObjectName(), dialect), + "%1")); + } + else + { + qCritical() << "TriggerDialog is in invalid state. Called initTrigger() but none of trigger/table/view values are set."; + } +} + +void TriggerDialog::readColumns() +{ + SchemaResolver resolver(db); + if (!table.isNull()) + targetColumns = resolver.getTableColumns(table); + else if (!view.isNull()) + targetColumns = resolver.getViewColumns(view); + else + targetColumns.clear(); + + if (createTrigger) + selectedColumns = createTrigger->event->columnNames; +} + +QString TriggerDialog::getTargetObjectName() const +{ + if (!table.isNull()) + return table; + + return view; +} + +void TriggerDialog::rebuildTrigger() +{ + /* + * Trigger is not rebuilt into SqliteCreateTrigger, because it's impossible to parse + * precondition or queries if they are invalid and we still need an invalid queries + * to be presented on the DDL tab. + */ + static const QString tempDdl = QStringLiteral("CREATE TRIGGER %1%2 %3%4 ON %5%6%7 BEGIN %8 END;"); + + Dialect dialect = db->getDialect(); + QString trigName = wrapObjIfNeeded(ui->nameEdit->text(), dialect); + QString when = ui->whenCombo->currentText(); + QString action = ui->actionCombo->currentText(); + QString columns = ""; + QString target = wrapObjIfNeeded(getTargetObjectName(), dialect); + QString scope = ui->scopeCombo->currentText(); + QString precondition = ""; + QString queries = ui->codeEdit->toPlainText(); + + // Columns + SqliteCreateTrigger::Event::Type actionType = SqliteCreateTrigger::Event::stringToType(ui->actionCombo->currentText()); + if (actionType == SqliteCreateTrigger::Event::UPDATE_OF) + { + QStringList colNames; + foreach (const QString& colName, selectedColumns) + colNames << wrapObjIfNeeded(colName, dialect); + + columns = " "+colNames.join(", "); + } + + // Precondition + if (ui->preconditionCheck->isChecked()) + precondition = " WHEN "+ui->preconditionEdit->toPlainText(); + + // Queries + if (!queries.trimmed().endsWith(";")) + queries += ";"; + + // When + if (!when.isNull()) + when.prepend(" "); + + // Scope + if (!scope.isNull()) + scope.prepend(" "); + + ddl = tempDdl.arg(trigName).arg(when).arg(action).arg(columns).arg(target).arg(scope).arg(precondition).arg(queries); +} + +void TriggerDialog::updateState() +{ + SqliteCreateTrigger::Event::Type type = SqliteCreateTrigger::Event::stringToType(ui->actionCombo->currentText()); + ui->actionColumns->setEnabled(type == SqliteCreateTrigger::Event::UPDATE_OF); + ui->preconditionEdit->setEnabled(ui->preconditionCheck->isChecked()); + updateValidation(); +} + +void TriggerDialog::updateValidation() +{ + SqliteCreateTrigger::Event::Type type = SqliteCreateTrigger::Event::stringToType(ui->actionCombo->currentText()); + bool columnsOk = (type != SqliteCreateTrigger::Event::UPDATE_OF || selectedColumns.size() > 0); + + bool preconditionOk = (!ui->preconditionCheck->isChecked() || + (ui->preconditionEdit->isSyntaxChecked() && !ui->preconditionEdit->haveErrors())); + + bool codeOk = (ui->codeEdit->isSyntaxChecked() && !ui->codeEdit->haveErrors()); + + setValidState(ui->preconditionCheck, preconditionOk, tr("Enter a valid condition.")); + setValidState(ui->codeEdit, codeOk, tr("Enter a valid trigger code.")); + ui->actionColumns->setIcon(columnsOk ? ICONS.TRIGGER_COLUMNS : ICONS.TRIGGER_COLUMNS_INVALID); + + QPushButton* okButton = ui->buttonBox->button(QDialogButtonBox::Ok); + okButton->setEnabled(columnsOk && preconditionOk && codeOk); +} + +void TriggerDialog::showColumnsDialog() +{ + TriggerColumnsDialog dialog(this); + foreach (const QString& colName, targetColumns) + dialog.addColumn(colName, selectedColumns.contains(colName, Qt::CaseInsensitive)); + + if (dialog.exec() != QDialog::Accepted) + return; + + QStringList newColumns = dialog.getCheckedColumns(); + selectedColumns = newColumns; + updateValidation(); +} + +void TriggerDialog::updateDdlTab(int tabIdx) +{ + if (tabIdx != 1) + return; + + rebuildTrigger(); + QString formatted = FORMATTER->format("sql", ddl, db); + ui->ddlEdit->setPlainText(formatted); +} + +void TriggerDialog::tableChanged(const QString& newValue) +{ + ui->preconditionEdit->setTriggerContext(newValue); + ui->codeEdit->setTriggerContext(newValue); +} + +void TriggerDialog::accept() +{ + rebuildTrigger(); + + Dialect dialect = db->getDialect(); + + QStringList sqls; + if (existingTrigger) + sqls << QString("DROP TRIGGER %1").arg(wrapObjIfNeeded(originalTriggerName, dialect)); + + sqls << ddl; + + if (!CFG_UI.General.DontShowDdlPreview.get()) + { + DdlPreviewDialog dialog(db, this); + dialog.setDdl(sqls); + if (dialog.exec() != QDialog::Accepted) + return; + } + + ChainExecutor executor; + executor.setDb(db); + executor.setAsync(false); + executor.setQueries(sqls); + executor.exec(); + + if (executor.getSuccessfulExecution()) + { + CFG->addDdlHistory(sqls.join("\n"), db->getName(), db->getPath()); + + QDialog::accept(); + DBTREE->refreshSchema(db); + return; + } + + QMessageBox::critical(this, tr("Error", "trigger dialog"), tr("An error occurred while executing SQL statements:\n%1") + .arg(executor.getErrorsMessages().join(",\n")), QMessageBox::Ok); +} -- cgit v1.2.3