diff options
Diffstat (limited to 'SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.cpp')
| -rw-r--r-- | SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.cpp | 245 |
1 files changed, 159 insertions, 86 deletions
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.cpp index 3d7090a..4f402b2 100644 --- a/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.cpp @@ -1,9 +1,9 @@ #include "columndefaultpanel.h" #include "ui_columndefaultpanel.h" -#include "parser/ast/sqlitecreatetable.h" #include "parser/parser.h" #include "parser/keywords.h" #include "uiutils.h" +#include "schemaresolver.h" #include <QDebug> ColumnDefaultPanel::ColumnDefaultPanel(QWidget *parent) : @@ -34,6 +34,22 @@ void ColumnDefaultPanel::changeEvent(QEvent *e) bool ColumnDefaultPanel::validate() { + if (!ui->exprEdit->isSyntaxChecked()) + { + setValidState(ui->exprEdit, false, tr("Enter a default value expression.")); + currentMode = Mode::ERROR; + return false; + } + + // First check if we already validated this text. + // This method is called twice, by both errors checking and syntax highlighting, + // because signal for textChange() is connected with call to updateValidation(). + QString text = ui->exprEdit->toPlainText(); + if (!lastValidatedText.isNull() && lastValidatedText == text) + return lastValidationResult; + + lastValidatedText = text; + bool nameOk = true; if (ui->namedCheck->isChecked() && ui->namedEdit->text().isEmpty()) nameOk = false; @@ -41,12 +57,40 @@ bool ColumnDefaultPanel::validate() bool exprOk = !ui->exprEdit->toPlainText().trimmed().isEmpty() && !ui->exprEdit->haveErrors(); - bool exprCheckedOk = exprOk && ui->exprEdit->isSyntaxChecked(); + QString exprError; + if (exprOk) + { + // Everything looks fine, so lets do the final check - if the value is considered constant by SQLite. + static QString tempDdlLiteralTpl = QStringLiteral("CREATE TABLE temp.%1 (col DEFAULT %2);"); + static QString tempDdlExprTpl = QStringLiteral("CREATE TABLE temp.%1 (col DEFAULT (%2));"); + static QString dropTempDdl = QStringLiteral("DROP TABLE IF EXISTS temp.%1;"); + + QString tableName = getTempTable(); + QString tempDdl = tempDdlExprTpl.arg(tableName, ui->exprEdit->toPlainText()); + SqlQueryPtr res = db->exec(tempDdl); + if (res->isError()) + { + tempDdl = tempDdlLiteralTpl.arg(tableName, ui->exprEdit->toPlainText()); + res = db->exec(tempDdl); + if (res->isError()) + { + exprOk = false; + exprError = tr("Invalid default value expression: %1").arg(res->getErrorText()); + } + else + currentMode = Mode::LITERAL; + } + else + currentMode = Mode::EXPR; + + db->exec(dropTempDdl.arg(tableName)); + } - setValidState(ui->exprEdit, exprOk, tr("Enter a default value expression.")); + setValidState(ui->exprEdit, exprOk, exprError); setValidState(ui->namedEdit, nameOk, tr("Enter a name of the constraint.")); - return exprCheckedOk && nameOk; + lastValidationResult = (exprOk && nameOk); + return lastValidationResult; } bool ColumnDefaultPanel::validateOnly() @@ -69,18 +113,99 @@ void ColumnDefaultPanel::storeConfiguration() if (constraint.isNull()) return; + if (currentMode == Mode::ERROR) + { + qCritical() << "Call to ColumnDefaultPanel::storeConfiguration() while its mode is in ERROR state."; + return; + } + SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data()); constr->type = SqliteCreateTable::Column::Constraint::DEFAULT; - SqliteExprPtr expr = parseExpression(ui->exprEdit->toPlainText()); - SqliteExpr* newExpr = new SqliteExpr(*expr.data()); - newExpr->setParent(constraint.data()); - constr->expr = newExpr; + switch (currentMode) + { + case Mode::EXPR: + storeExpr(constr); + break; + case Mode::LITERAL: + storeLiteral(constr); + break; + case Mode::ERROR: + return; + } if (ui->namedCheck->isChecked()) constr->name = ui->namedEdit->text(); } +void ColumnDefaultPanel::storeExpr(SqliteCreateTable::Column::Constraint* constr) +{ + QString text = ui->exprEdit->toPlainText(); + clearDefault(constr); + if (text.toUpper() == "NULL") + { + // We will just use literal null, no need to create expression with null. + constr->literalNull = true; + return; + } + + Parser parser(db->getDialect()); + SqliteExpr* newExpr = parser.parseExpr(text); + newExpr->setParent(constraint.data()); + constr->expr = newExpr; +} + +void ColumnDefaultPanel::storeLiteral(SqliteCreateTable::Column::Constraint* constr) +{ + QString text = ui->exprEdit->toPlainText(); + + Parser parser(db->getDialect()); + SqliteCreateTablePtr createTable = parser.parse<SqliteCreateTable>("CREATE TABLE tab (col DEFAULT "+text+");"); + if (!createTable || createTable->columns.size() == 0 || createTable->columns.first()->constraints.size() == 0) + { + qCritical() << "ColumnDefaultPanel::storeLiteral(): create table not parsed! Cannot store literal. Expression was:" << text; + return; + } + + SqliteCreateTable::Column::Constraint* parsedConstr = createTable->columns.first()->constraints.first(); + if (parsedConstr->type != SqliteCreateTable::Column::Constraint::Type::DEFAULT) + { + qCritical() << "ColumnDefaultPanel::storeLiteral(): parsed constraint not a DEFAULT! Cannot store literal. Expression was:" << text; + return; + } + + clearDefault(constr); + if (!parsedConstr->id.isNull()) + constr->id = parsedConstr->id; + else if (!parsedConstr->ctime.isNull()) + constr->ctime = parsedConstr->ctime.toUpper(); + else if (parsedConstr->expr) + { + qWarning() << "ColumnDefaultPanel::storeLiteral(): parsed constraint turned out to be an expression. This should be handled by ColumnDefaultPanel::storeExpr." + << "Expression was:" << text; + constr->expr = parsedConstr->expr; + parsedConstr->expr = nullptr; + constr->expr->setParent(constr); + } + else if (parsedConstr->literalNull) + constr->literalNull = true; + else + constr->literalValue = parsedConstr->literalValue; +} + +void ColumnDefaultPanel::clearDefault(SqliteCreateTable::Column::Constraint* constr) +{ + if (constr->expr) + { + delete constr->expr; + constr->expr = nullptr; + } + constr->literalNull = false; + constr->literalValue = QVariant(); + constr->id = QString(); + constr->ctime = QString(); +} + void ColumnDefaultPanel::init() { setFocusProxy(ui->exprEdit); @@ -101,101 +226,49 @@ void ColumnDefaultPanel::readConstraint() SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data()); if (constr->expr) + { ui->exprEdit->setPlainText(constr->expr->detokenize()); + currentMode = Mode::EXPR; + } else if (!constr->literalValue.isNull()) - ui->exprEdit->setPlainText(constr->literalValue.toString()); - - if (!constr->name.isNull()) { - ui->namedCheck->setChecked(true); - ui->namedEdit->setText(constr->name); + ui->exprEdit->setPlainText(constr->literalValue.toString()); + currentMode = Mode::LITERAL; } -} - -void ColumnDefaultPanel::updateVirtualSql() -{ - ui->exprEdit->setDb(db); - - SqliteCreateTable::Column* column = dynamic_cast<SqliteCreateTable::Column*>(constraint->parentStatement()); - SqliteCreateTable* createTable = dynamic_cast<SqliteCreateTable*>(column->parentStatement()); - - createTable->rebuildTokens(); - TokenList tokens = createTable->tokens; - TokenList colTokens = column->tokens; - if (createTable->columns.indexOf(column) == -1) + else if (!constr->id.isNull()) { - if (createTable->columns.size() == 0) - { - // No columns. Cannot get any context info. - return; - } - - colTokens = createTable->columns.last()->tokens; + ui->exprEdit->setPlainText(constr->id); + currentMode = Mode::LITERAL; } - - if (colTokens.size() == 0) + else if (!constr->ctime.isNull()) { - qWarning() << "CREATE TABLE tokens are invalid (0) while call to ColumnDefaultPanel::updateVirtualSql()."; - return; + ui->exprEdit->setPlainText(constr->ctime); + currentMode = Mode::LITERAL; } - - int idx = tokens.lastIndexOf(colTokens.last()); - if (idx == -1) + else if (constr->literalNull) { - qWarning() << "CREATE TABLE tokens are invalid while call to ColumnDefaultPanel::updateVirtualSql()."; - return; + ui->exprEdit->setPlainText("NULL"); + currentMode = Mode::LITERAL; } - idx++; - - TokenList newTokens; - newTokens << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::KEYWORD, "DEFAULT") - << TokenPtr::create(Token::SPACE, " "); - if (constraint->dialect == Dialect::Sqlite3) - { - newTokens << TokenPtr::create(Token::PAR_LEFT, "(") - << TokenPtr::create(Token::OTHER, "%1") - << TokenPtr::create(Token::PAR_RIGHT, ")"); - } - else + if (!constr->name.isNull()) { - newTokens << TokenPtr::create(Token::OTHER, "%1"); + ui->namedCheck->setChecked(true); + ui->namedEdit->setText(constr->name); } - - tokens.insert(idx, newTokens); - QString sql = tokens.detokenize(); - - ui->exprEdit->setVirtualSqlExpression(sql); } -SqliteExprPtr ColumnDefaultPanel::parseExpression(const QString& sql) +void ColumnDefaultPanel::updateVirtualSql() { - Parser parser(db->getDialect()); - if (!parser.parse("SELECT "+sql)) - return SqliteExprPtr(); - - QList<SqliteQueryPtr> queries = parser.getQueries(); - if (queries.size() == 0) - return SqliteExprPtr(); - - SqliteQueryPtr first = queries.first(); - if (first->queryType != SqliteQueryType::Select) - return SqliteExprPtr(); - - SqliteSelectPtr select = first.dynamicCast<SqliteSelect>(); - if (select->coreSelects.size() < 1) - return SqliteExprPtr(); - - SqliteSelect::Core* core = select->coreSelects.first(); - if (core->resultColumns.size() < 1) - return SqliteExprPtr(); - - SqliteSelect::Core::ResultColumn* resCol = core->resultColumns.first(); - if (!resCol->expr) - return SqliteExprPtr(); + static QString sql = QStringLiteral("CREATE TABLE tab (col DEFAULT %1)"); + ui->exprEdit->setDb(db); + ui->exprEdit->setVirtualSqlExpression(sql.arg(db->getDialect() == Dialect::Sqlite3 ? "(%1)" : "%1")); +} - return resCol->expr->detach().dynamicCast<SqliteExpr>(); +QString ColumnDefaultPanel::getTempTable() +{ + SchemaResolver resolver(db); + return resolver.getUniqueName("temp", "sqlitestudio_temp_table"); } void ColumnDefaultPanel::updateState() |
