diff options
| author | 2021-12-17 07:06:30 -0500 | |
|---|---|---|
| committer | 2021-12-17 07:06:30 -0500 | |
| commit | 1fdc150116cad39aae5c5da407c3312b47a59e3a (patch) | |
| tree | 123c79a4d7ad2d45781ba03ce939f7539fb428d8 /SQLiteStudio3/coreSQLiteStudio/parser/ast | |
| parent | feda8a7db8d1d7c5439aa8f8feef7cc0dd2b59a0 (diff) | |
New upstream version 3.3.3+dfsg1.upstream/3.3.3+dfsg1
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/parser/ast')
76 files changed, 1256 insertions, 412 deletions
diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.cpp index 49763a5..5ca593f 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.cpp @@ -106,7 +106,7 @@ TokenList SqliteAlterTable::rebuildTokensFromContents() builder.withKeyword("ALTER").withSpace().withKeyword("TABLE").withSpace(); if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + builder.withOther(database).withOperator("."); builder.withOther(table).withSpace(); @@ -120,7 +120,7 @@ TokenList SqliteAlterTable::rebuildTokensFromContents() } else if (!newName.isNull()) { - builder.withKeyword("RENAME").withSpace().withKeyword("TO").withSpace().withOther(newName, dialect); + builder.withKeyword("RENAME").withSpace().withKeyword("TO").withSpace().withOther(newName); } builder.withOperator(";"); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.h index 360db45..fbac3fe 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.h @@ -34,9 +34,9 @@ class API_EXPORT SqliteAlterTable : public SqliteQuery public: Command command = Command::null; - QString newName = QString::null; - QString database = QString::null; - QString table = QString::null; + QString newName = QString(); + QString database = QString(); + QString table = QString(); bool columnKw = false; SqliteCreateTable::Column* newColumn = nullptr; }; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteanalyze.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteanalyze.cpp index 30396c1..4e79f3d 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteanalyze.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteanalyze.cpp @@ -73,7 +73,7 @@ TokenList SqliteAnalyze::rebuildTokensFromContents() builder.withKeyword("ANALYZE").withSpace(); if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + builder.withOther(database).withOperator("."); builder.withOther(table).withOperator(";"); return builder.build(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteanalyze.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteanalyze.h index 194e4c9..a786621 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteanalyze.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteanalyze.h @@ -12,8 +12,8 @@ class API_EXPORT SqliteAnalyze : public SqliteQuery SqliteAnalyze(const QString& name1, const QString& name2); SqliteStatement* clone(); - QString database = QString::null; - QString table = QString::null; + QString database = QString(); + QString table = QString(); protected: QStringList getTablesInStatement(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitebegintrans.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitebegintrans.cpp index 00ec9ac..899f8bb 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitebegintrans.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitebegintrans.cpp @@ -9,7 +9,7 @@ SqliteBeginTrans::SqliteBeginTrans() } SqliteBeginTrans::SqliteBeginTrans(const SqliteBeginTrans& other) : - SqliteQuery(other), onConflict(other.onConflict), name(other.name), transactionKw(other.transactionKw), type(other.type) + SqliteQuery(other), name(other.name), transactionKw(other.transactionKw), type(other.type) { } @@ -21,9 +21,8 @@ SqliteBeginTrans::SqliteBeginTrans(SqliteBeginTrans::Type type, bool transaction this->name = name; } -SqliteBeginTrans::SqliteBeginTrans(bool transactionKw, const QString &name, SqliteConflictAlgo onConflict) +SqliteBeginTrans::SqliteBeginTrans(bool transactionKw, const QString &name) { - this->onConflict = onConflict; this->transactionKw = transactionKw; this->name = name; } @@ -62,10 +61,10 @@ TokenList SqliteBeginTrans::rebuildTokensFromContents() { builder.withSpace().withKeyword("TRANSACTION"); if (!name.isNull()) - builder.withSpace().withOther(name, dialect); + builder.withSpace().withOther(name); } - builder.withConflict(onConflict).withOperator(";"); + builder.withOperator(";"); return builder.build(); } diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitebegintrans.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitebegintrans.h index 48f5b37..b16b6c0 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitebegintrans.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitebegintrans.h @@ -19,10 +19,9 @@ class API_EXPORT SqliteBeginTrans : public SqliteQuery SqliteBeginTrans(); SqliteBeginTrans(const SqliteBeginTrans& other); SqliteBeginTrans(Type type, bool transactionKw, const QString& name); - SqliteBeginTrans(bool transactionKw, const QString& name, SqliteConflictAlgo onConflict); + SqliteBeginTrans(bool transactionKw, const QString& name); SqliteStatement* clone(); - SqliteConflictAlgo onConflict = SqliteConflictAlgo::null; // sqlite2 only QString name; // in docs sqlite2 only, but in gramma it's also sqlite3 bool transactionKw = false; Type type = Type::null; // sqlite3 only diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecolumntype.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecolumntype.cpp index 2c48119..5872ab1 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecolumntype.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecolumntype.cpp @@ -1,6 +1,7 @@ #include "sqlitecolumntype.h" #include "parser/statementtokenbuilder.h" #include "common/utils_sql.h" +#include "parser/lexer.h" SqliteColumnType::SqliteColumnType() { @@ -50,7 +51,7 @@ TokenList SqliteColumnType::rebuildTokensFromContents() if (name.isEmpty()) return TokenList(); - builder.withOther(name); + TokenList resultTokens = Lexer::tokenize(name); if (!scale.isNull()) { @@ -79,7 +80,7 @@ TokenList SqliteColumnType::rebuildTokensFromContents() builder.withParRight(); } - return builder.build(); + return resultTokens + builder.build(); } DataType SqliteColumnType::toDataType() const diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecolumntype.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecolumntype.h index fc87b6b..f003f9f 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecolumntype.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecolumntype.h @@ -20,7 +20,7 @@ class API_EXPORT SqliteColumnType : public SqliteStatement TokenList rebuildTokensFromContents(); DataType toDataType() const; - QString name = QString::null; + QString name = QString(); QVariant scale = QVariant(); // first size number QVariant precision = QVariant(); // second size number }; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecommittrans.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecommittrans.cpp index 2b1b707..4f8de49 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecommittrans.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecommittrans.cpp @@ -39,7 +39,7 @@ TokenList SqliteCommitTrans::rebuildTokensFromContents() { builder.withSpace().withKeyword("TRANSACTION"); if (!name.isNull()) - builder.withSpace().withOther(name, dialect); + builder.withSpace().withOther(name); } builder.withOperator(";"); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecommittrans.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecommittrans.h index ec418a6..b5557a7 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecommittrans.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecommittrans.h @@ -13,7 +13,7 @@ class API_EXPORT SqliteCommitTrans : public SqliteQuery SqliteStatement* clone(); bool endKw = false; - QString name = QString::null; + QString name = QString(); bool transactionKw = false; protected: diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteconflictalgo.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteconflictalgo.cpp index 56fb42d..2246979 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteconflictalgo.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteconflictalgo.cpp @@ -32,6 +32,6 @@ QString sqliteConflictAlgo(SqliteConflictAlgo value) case SqliteConflictAlgo::REPLACE: return "REPLACE"; default: - return QString::null; + return QString(); } } diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.cpp index 0d9ed9f..c3ff500 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.cpp @@ -79,9 +79,9 @@ TokenList SqliteCopy::rebuildTokensFromContents() builder.withKeyword("OR").withSpace().withKeyword(sqliteConflictAlgo(onConflict)).withSpace(); if (!database.isNull()) - builder.withOther(database, dialect).withSpace(); + builder.withOther(database).withSpace(); - builder.withOther(table, dialect).withSpace().withKeyword("FROM").withSpace().withString(file); + builder.withOther(table).withSpace().withKeyword("FROM").withSpace().withString(file); if (!delimiter.isNull()) builder.withSpace().withKeyword("USING").withSpace().withKeyword("DELIMITERS").withSpace().withString(delimiter); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.h index ff586df..1e4b5b7 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.h @@ -9,14 +9,14 @@ class API_EXPORT SqliteCopy : public SqliteQuery public: SqliteCopy(); SqliteCopy(const SqliteCopy& other); - SqliteCopy(SqliteConflictAlgo onConflict, const QString& name1, const QString& name2, const QString& name3, const QString& delim = QString::null); + SqliteCopy(SqliteConflictAlgo onConflict, const QString& name1, const QString& name2, const QString& name3, const QString& delim = QString()); SqliteStatement* clone(); SqliteConflictAlgo onConflict = SqliteConflictAlgo::null; - QString database = QString::null; - QString table = QString::null; - QString file = QString::null; - QString delimiter = QString::null; + QString database = QString(); + QString table = QString(); + QString file = QString(); + QString delimiter = QString(); protected: QStringList getTablesInStatement(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.cpp index 5c11ff5..b747c33 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.cpp @@ -17,32 +17,10 @@ SqliteCreateIndex::SqliteCreateIndex(const SqliteCreateIndex& other) : DEEP_COPY_COLLECTION(SqliteOrderBy, indexedColumns); } -SqliteCreateIndex::SqliteCreateIndex(bool unique, bool ifNotExists, const QString& name1, const QString& name2, const QString& name3, const QList<SqliteIndexedColumn*>& columns, SqliteConflictAlgo onConflict) - : SqliteCreateIndex() -{ - // Constructor for SQLite 2 - uniqueKw = unique; - ifNotExistsKw = ifNotExists; - - index = name1; - - if (!name3.isNull()) - { - database = name2; - table = name3; - } - else - table = name2; - - this->onConflict = onConflict; - this->indexedColumns = toOrderColumns(columns); -} - SqliteCreateIndex::SqliteCreateIndex(bool unique, bool ifNotExists, const QString& name1, const QString& name2, const QString& name3, const QList<SqliteOrderBy*>& columns, SqliteExpr* where) : SqliteCreateIndex() { - // Constructor for SQLite 3 uniqueKw = unique; ifNotExistsKw = ifNotExists; @@ -89,18 +67,12 @@ QStringList SqliteCreateIndex::getDatabasesInStatement() TokenList SqliteCreateIndex::getTableTokensInStatement() { - if (dialect == Dialect::Sqlite2) - return getObjectTokenListFromNmDbnm("nm2", "dbnm"); - else - return getTokenListFromNamedKey("nm2"); + return getTokenListFromNamedKey("nm2"); } TokenList SqliteCreateIndex::getDatabaseTokensInStatement() { - if (dialect == Dialect::Sqlite2) - return getDbTokenListFromNmDbnm("nm2", "dbnm"); - else - return getDbTokenListFromNmDbnm(); + return getDbTokenListFromNmDbnm(); } QList<SqliteStatement::FullObject> SqliteCreateIndex::getFullObjectsInStatement() @@ -109,14 +81,9 @@ QList<SqliteStatement::FullObject> SqliteCreateIndex::getFullObjectsInStatement( // Table object FullObject fullObj; - if (dialect == Dialect::Sqlite2) - fullObj = getFullObjectFromNmDbnm(FullObject::TABLE, "nm2", "dbnm"); - else - { - TokenList tableTokens = getTokenListFromNamedKey("nm2"); - if (tableTokens.size() > 0) - fullObj = getFullObject(FullObject::TABLE, TokenPtr(), tableTokens[0]); - } + TokenList tableTokens = getTokenListFromNamedKey("nm2"); + if (tableTokens.size() > 0) + fullObj = getFullObject(FullObject::TABLE, TokenPtr(), tableTokens[0]); if (fullObj.isValid()) result << fullObj; @@ -130,14 +97,10 @@ QList<SqliteStatement::FullObject> SqliteCreateIndex::getFullObjectsInStatement( } // Index object - if (dialect == Dialect::Sqlite2) - { - TokenList tableTokens = getTokenListFromNamedKey("nm"); - if (tableTokens.size() > 0) - fullObj = getFullObject(FullObject::INDEX, TokenPtr(), tableTokens[0]); - } - else - fullObj = getFullObjectFromNmDbnm(FullObject::INDEX, "nm", "dbnm"); + fullObj = getFullObjectFromNmDbnm(FullObject::INDEX, "nm", "dbnm"); + + if (fullObj.isValid()) + result << fullObj; return result; } @@ -155,28 +118,11 @@ TokenList SqliteCreateIndex::rebuildTokensFromContents() if (ifNotExistsKw) builder.withKeyword("IF").withSpace().withKeyword("NOT").withSpace().withKeyword("EXISTS").withSpace(); - if (dialect == Dialect::Sqlite2) - { - builder.withOther(index, dialect).withSpace().withKeyword("ON").withSpace(); - - if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); - - builder.withOther(table, dialect).withSpace(); - builder.withParLeft().withStatementList(indexedColumns).withParRight(); - - - if (onConflict != SqliteConflictAlgo::null) - builder.withSpace().withKeyword(sqliteConflictAlgo(onConflict)); - } - else - { - if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + if (!database.isNull()) + builder.withOther(database).withOperator("."); - builder.withOther(index, dialect).withSpace().withKeyword("ON").withSpace().withOther(table, dialect).withSpace().withParLeft() - .withStatementList(indexedColumns).withParRight(); - } + builder.withOther(index).withSpace().withKeyword("ON").withSpace().withOther(table).withSpace().withParLeft() + .withStatementList(indexedColumns).withParRight(); if (where) builder.withSpace().withKeyword("WHERE").withStatement(where); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.h index 033a663..870ed17 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.h @@ -33,10 +33,9 @@ class API_EXPORT SqliteCreateIndex : public SqliteQuery, public SqliteTableRelat bool uniqueKw = false; bool ifNotExistsKw = false; QList<SqliteOrderBy*> indexedColumns; - // The database refers to index name in Sqlite3, but in Sqlite2 it refers to the table. - QString database = QString::null; - QString index = QString::null; - QString table = QString::null; + QString database = QString(); + QString index = QString(); + QString table = QString(); SqliteConflictAlgo onConflict = SqliteConflictAlgo::null; SqliteExpr* where = nullptr; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp index eb4ae85..d82270e 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp @@ -3,6 +3,8 @@ #include "common/utils_sql.h" #include "common/global.h" +const QRegExp SqliteCreateTable::Column::GENERATED_ALWAYS_REGEXP = QRegExp("GENERATED\\s+ALWAYS"); + SqliteCreateTable::SqliteCreateTable() { queryType = SqliteQueryType::CreateTable; @@ -219,10 +221,10 @@ TokenList SqliteCreateTable::rebuildTokensFromContents() builder.withSpace().withKeyword("IF").withSpace().withKeyword("NOT").withSpace().withKeyword("EXISTS"); builder.withSpace(); - if (dialect == Dialect::Sqlite3 && !database.isNull()) - builder.withOther(database, dialect).withOperator("."); + if (!database.isNull()) + builder.withOther(database).withOperator("."); - builder.withOther(table, dialect); + builder.withOther(table); if (select) builder.withSpace().withKeyword("AS").withSpace().withStatement(select); @@ -267,8 +269,9 @@ SqliteCreateTable::Column::Constraint::Constraint() SqliteCreateTable::Column::Constraint::Constraint(const SqliteCreateTable::Column::Constraint& other) : SqliteStatement(other), type(other.type), name(other.name), sortOrder(other.sortOrder), onConflict(other.onConflict), - autoincrKw(other.autoincrKw), literalValue(other.literalValue), literalNull(other.literalNull), ctime(other.ctime), id(other.id), - collationName(other.collationName), deferrable(other.deferrable), initially(other.initially) + autoincrKw(other.autoincrKw), generatedKw(other.generatedKw), literalValue(other.literalValue), literalNull(other.literalNull), + ctime(other.ctime), id(other.id), collationName(other.collationName), generatedType(other.generatedType), + deferrable(other.deferrable), initially(other.initially) { DEEP_COPY_FIELD(SqliteExpr, expr); DEEP_COPY_FIELD(SqliteForeignKey, foreignKey); @@ -283,6 +286,30 @@ SqliteStatement* SqliteCreateTable::Column::Constraint::clone() return new SqliteCreateTable::Column::Constraint(*this); } +QString SqliteCreateTable::Column::Constraint::toString(SqliteCreateTable::Column::Constraint::GeneratedType type) +{ + switch (type) { + case SqliteCreateTable::Column::Constraint::GeneratedType::STORED: + return "STORED"; + case SqliteCreateTable::Column::Constraint::GeneratedType::VIRTUAL: + return "VIRTUAL"; + case SqliteCreateTable::Column::Constraint::GeneratedType::null: + break; + } + return QString(); +} + +SqliteCreateTable::Column::Constraint::GeneratedType SqliteCreateTable::Column::Constraint::generatedTypeFrom(const QString& type) +{ + QString upType = type.toUpper(); + if (upType == "STORED") + return GeneratedType::STORED; + else if (upType == "VIRTUAL") + return GeneratedType::VIRTUAL; + else + return GeneratedType::null; +} + void SqliteCreateTable::Column::Constraint::initDefNameOnly(const QString &name) { this->type = SqliteCreateTable::Column::Constraint::NAME_ONLY; @@ -404,6 +431,14 @@ void SqliteCreateTable::Column::Constraint::initColl(const QString &name) this->collationName = name; } +void SqliteCreateTable::Column::Constraint::initGeneratedAs(SqliteExpr* expr, bool genKw, const QString& type) +{ + this->type = SqliteCreateTable::Column::Constraint::GENERATED; + this->expr = expr; + this->generatedKw = genKw; + this->generatedType = generatedTypeFrom(type); +} + QString SqliteCreateTable::Column::Constraint::typeString() const { switch (type) @@ -418,6 +453,8 @@ QString SqliteCreateTable::Column::Constraint::typeString() const return "CHECK"; case SqliteCreateTable::Column::Constraint::DEFAULT: return "DEFAULT"; + case SqliteCreateTable::Column::Constraint::GENERATED: + return "GENERATED"; case SqliteCreateTable::Column::Constraint::COLLATE: return "COLLATE"; case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: @@ -427,7 +464,7 @@ QString SqliteCreateTable::Column::Constraint::typeString() const case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY: break; } - return QString::null; + return QString(); } SqliteCreateTable::Constraint::Constraint() @@ -551,9 +588,9 @@ QString SqliteCreateTable::Constraint::typeString() const case SqliteCreateTable::Constraint::FOREIGN_KEY: return "FOREIGN KEY"; case SqliteCreateTable::Constraint::NAME_ONLY: - return QString::null; + return QString(); } - return QString::null; + return QString(); } TokenList SqliteCreateTable::Constraint::rebuildTokensFromContents() @@ -561,7 +598,7 @@ TokenList SqliteCreateTable::Constraint::rebuildTokensFromContents() StatementTokenBuilder builder; if (!name.isNull()) - builder.withKeyword("CONSTRAINT").withSpace().withOther(name, dialect).withSpace(); + builder.withKeyword("CONSTRAINT").withSpace().withOther(name).withSpace(); switch (type) { @@ -694,6 +731,33 @@ QList<SqliteCreateTable::Column::Constraint*> SqliteCreateTable::Column::getFore return results; } +void SqliteCreateTable::Column::fixTypeVsGeneratedAs() +{ + // This is a workaround for lemon parser taking "GENERATED ALWAYS" as part of the typename, + // despite 2 days effort of forcing proper precedense to parse it as part of a constraint. + // Lemon keeps reducing these 2 keywords into the typename by using fallback of GENERATED & ALWAYS to ID, + // regardless of rule order and explicit precedence. By throwing the GENERATED keyword out of the fallback list, + // we would make the syntax incompatible with official SQLite syntax, which allows usage of GENERATED as ID. + // I've tried to use more recent Lemon parser, but it's different a lot from current one and it is no longer possible + // to collect tokens parsed per rule (needed tor SqliteStatement's tokens & tokenMap). At least not in the way + // that it used to be so far. + // This is the last resort to make it right. + // This method is called from parser rule that reduces column definition (rule "column(X)"). + Constraint* generatedConstr = getConstraint(Constraint::GENERATED); + if (generatedConstr && !generatedConstr->generatedKw && type && type->name.toUpper().contains(GENERATED_ALWAYS_REGEXP)) + { + type->name.replace(GENERATED_ALWAYS_REGEXP, ""); + type->tokens = type->rebuildTokensFromContents(); + type->tokensMap["typename"] = type->tokens; + generatedConstr->generatedKw = true; + } +} + +void SqliteCreateTable::Column::evaluatePostParsing() +{ + fixTypeVsGeneratedAs(); +} + QStringList SqliteCreateTable::Column::getColumnsInStatement() { return getStrListFromValue(name); @@ -707,7 +771,7 @@ TokenList SqliteCreateTable::Column::getColumnTokensInStatement() TokenList SqliteCreateTable::Column::rebuildTokensFromContents() { StatementTokenBuilder builder; - builder.withOther(name, dialect).withStatement(type).withStatementList(constraints, ""); + builder.withOther(name).withStatement(type).withStatementList(constraints, ""); return builder.build(); } @@ -715,7 +779,7 @@ TokenList SqliteCreateTable::Column::Constraint::rebuildTokensFromContents() { StatementTokenBuilder builder; if (!name.isNull()) - builder.withKeyword("CONSTRAINT").withSpace().withOther(name, dialect).withSpace(); + builder.withKeyword("CONSTRAINT").withSpace().withOther(name).withSpace(); switch (type) { @@ -746,7 +810,7 @@ TokenList SqliteCreateTable::Column::Constraint::rebuildTokensFromContents() { builder.withKeyword("DEFAULT").withSpace(); if (!id.isNull()) - builder.withOther(id, dialect); + builder.withOther(id); else if (!ctime.isNull()) builder.withKeyword(ctime.toUpper()); else if (expr) @@ -760,7 +824,7 @@ TokenList SqliteCreateTable::Column::Constraint::rebuildTokensFromContents() } case SqliteCreateTable::Column::Constraint::COLLATE: { - builder.withKeyword("COLLATE").withSpace().withOther(collationName, dialect); + builder.withKeyword("COLLATE").withSpace().withOther(collationName); break; } case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: @@ -768,6 +832,17 @@ TokenList SqliteCreateTable::Column::Constraint::rebuildTokensFromContents() builder.withStatement(foreignKey); break; } + case SqliteCreateTable::Column::Constraint::GENERATED: + { + if (generatedKw) + builder.withKeyword("GENERATED").withSpace().withKeyword("ALWAYS").withSpace(); + + builder.withKeyword("AS").withSpace().withParLeft().withStatement(expr).withParRight(); + if (generatedType != GeneratedType::null) + builder.withSpace().withOther(toString(generatedType), false); + + break; + } case SqliteCreateTable::Column::Constraint::NULL_: case SqliteCreateTable::Column::Constraint::NAME_ONLY: case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY: diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h index 74f4fce..d34688c 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h @@ -13,6 +13,7 @@ #include "sqliteddlwithdbcontext.h" #include <QVariant> #include <QList> +#include <QRegExp> class API_EXPORT SqliteCreateTable : public SqliteQuery, public SqliteDdlWithDbContext { @@ -34,16 +35,27 @@ class API_EXPORT SqliteCreateTable : public SqliteQuery, public SqliteDdlWithDbC DEFAULT, COLLATE, FOREIGN_KEY, + GENERATED, NULL_, // not officially supported NAME_ONLY, // unofficial, because of bizarre sqlite grammar DEFERRABLE_ONLY // unofficial, because of bizarre sqlite grammar }; + enum class GeneratedType + { + STORED, + VIRTUAL, + null + }; + Constraint(); Constraint(const Constraint& other); ~Constraint(); SqliteStatement* clone(); + static QString toString(GeneratedType type); + static GeneratedType generatedTypeFrom(const QString& type); + void initDefNameOnly(const QString& name); void initDefId(const QString& id); void initDefTerm(const QVariant& value, bool minus = false); @@ -59,19 +71,22 @@ class API_EXPORT SqliteCreateTable : public SqliteQuery, public SqliteDdlWithDbC void initFk(const QString& table, const QList<SqliteIndexedColumn*>& indexedColumns, const QList<SqliteForeignKey::Condition*>& conditions); void initDefer(SqliteInitially initially, SqliteDeferrable deferrable); void initColl(const QString& name); + void initGeneratedAs(SqliteExpr* expr, bool genKw, const QString& type); QString typeString() const; Type type; - QString name = QString::null; + QString name = QString(); SqliteSortOrder sortOrder = SqliteSortOrder::null; SqliteConflictAlgo onConflict = SqliteConflictAlgo::null; bool autoincrKw = false; + bool generatedKw = false; SqliteExpr* expr = nullptr; QVariant literalValue; bool literalNull = false; QString ctime; QString id; - QString collationName = QString::null; + QString collationName = QString(); + GeneratedType generatedType = GeneratedType::null; SqliteForeignKey* foreignKey = nullptr; protected: @@ -96,8 +111,10 @@ class API_EXPORT SqliteCreateTable : public SqliteQuery, public SqliteDdlWithDbC Constraint* getConstraint(Constraint::Type type) const; QList<Constraint*> getConstraints(Constraint::Type type) const; QList<Constraint*> getForeignKeysByTable(const QString& foreignTable) const; + void fixTypeVsGeneratedAs(); + void evaluatePostParsing(); - QString name = QString::null; + QString name = QString(); SqliteColumnType* type = nullptr; QList<Constraint*> constraints; @@ -106,12 +123,15 @@ class API_EXPORT SqliteCreateTable : public SqliteQuery, public SqliteDdlWithDbC * Used to remember original name when column was edited and the name was changed. * It's defined in the constructor to the same value as the name member. */ - QString originalName = QString::null; + QString originalName = QString(); protected: QStringList getColumnsInStatement(); TokenList getColumnTokensInStatement(); TokenList rebuildTokensFromContents(); + + private: + static const QRegExp GENERATED_ALWAYS_REGEXP; }; typedef QSharedPointer<Column> ColumnPtr; @@ -149,7 +169,7 @@ class API_EXPORT SqliteCreateTable : public SqliteQuery, public SqliteDdlWithDbC QString typeString() const; Type type; - QString name = QString::null; + QString name = QString(); bool autoincrKw = false; // not in docs, but needs to be supported SqliteConflictAlgo onConflict = SqliteConflictAlgo::null; SqliteForeignKey* foreignKey = nullptr; @@ -189,12 +209,12 @@ class API_EXPORT SqliteCreateTable : public SqliteQuery, public SqliteDdlWithDbC bool ifNotExistsKw = false; bool tempKw = false; bool temporaryKw = false; - QString database = QString::null; - QString table = QString::null; + QString database = QString(); + QString table = QString(); QList<Column*> columns; QList<Constraint*> constraints; SqliteSelect* select = nullptr; - QString withOutRowId = QString::null; + QString withOutRowId = QString(); protected: QStringList getTablesInStatement(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.cpp index 001dd2d..3f2c4e3 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.cpp @@ -126,7 +126,7 @@ QString SqliteCreateTrigger::time(SqliteCreateTrigger::Time eventTime) case SqliteCreateTrigger::Time::null: break; } - return QString::null; + return QString(); } SqliteCreateTrigger::Time SqliteCreateTrigger::time(const QString& eventTime) @@ -154,7 +154,7 @@ QString SqliteCreateTrigger::scopeToString(SqliteCreateTrigger::Scope scope) case SqliteCreateTrigger::Scope::null: break; } - return QString::null; + return QString(); } SqliteCreateTrigger::Scope SqliteCreateTrigger::stringToScope(const QString& scope) @@ -180,18 +180,12 @@ QStringList SqliteCreateTrigger::getDatabasesInStatement() TokenList SqliteCreateTrigger::getTableTokensInStatement() { - if (dialect == Dialect::Sqlite2) - return getObjectTokenListFromNmDbnm("nm2", "dbnm"); - else - return getTokenListFromNamedKey("nm2"); + return getTokenListFromNamedKey("nm2"); } TokenList SqliteCreateTrigger::getDatabaseTokensInStatement() { - if (dialect == Dialect::Sqlite2) - return getDbTokenListFromNmDbnm("nm2", "dbnm"); - else - return getDbTokenListFromNmDbnm(); + return getDbTokenListFromNmDbnm(); } QList<SqliteStatement::FullObject> SqliteCreateTrigger::getFullObjectsInStatement() @@ -200,14 +194,9 @@ QList<SqliteStatement::FullObject> SqliteCreateTrigger::getFullObjectsInStatemen // Table object FullObject fullObj; - if (dialect == Dialect::Sqlite2) - fullObj = getFullObjectFromNmDbnm(FullObject::TABLE, "nm2", "dbnm"); - else - { - TokenList tableTokens = getTokenListFromNamedKey("nm2"); - if (tableTokens.size() > 0) - fullObj = getFullObject(FullObject::TABLE, TokenPtr(), tableTokens[0]); - } + TokenList tableTokens = getTokenListFromNamedKey("nm2"); + if (tableTokens.size() > 0) + fullObj = getFullObject(FullObject::TABLE, TokenPtr(), tableTokens[0]); if (fullObj.isValid()) result << fullObj; @@ -221,14 +210,9 @@ QList<SqliteStatement::FullObject> SqliteCreateTrigger::getFullObjectsInStatemen } // Trigger object - if (dialect == Dialect::Sqlite2) - { - TokenList tableTokens = getTokenListFromNamedKey("nm"); - if (tableTokens.size() > 0) - fullObj = getFullObject(FullObject::TRIGGER, TokenPtr(), tableTokens[0]); - } - else - fullObj = getFullObjectFromNmDbnm(FullObject::TRIGGER, "nm", "dbnm"); + fullObj = getFullObjectFromNmDbnm(FullObject::TRIGGER, "nm", "dbnm"); + if (fullObj.isValid()) + result << fullObj; return result; } @@ -299,7 +283,7 @@ QString SqliteCreateTrigger::Event::typeToString(SqliteCreateTrigger::Event::Typ case SqliteCreateTrigger::Event::null: break; } - return QString::null; + return QString(); } SqliteCreateTrigger::Event::Type SqliteCreateTrigger::Event::stringToType(const QString& type) @@ -333,10 +317,10 @@ TokenList SqliteCreateTrigger::rebuildTokensFromContents() if (ifNotExistsKw) builder.withKeyword("IF").withSpace().withKeyword("NOT").withSpace().withKeyword("EXISTS").withSpace(); - if (dialect == Dialect::Sqlite3 && !database.isNull()) - builder.withOther(database, dialect).withOperator("."); + if (!database.isNull()) + builder.withOther(database).withOperator("."); - builder.withOther(trigger, dialect).withSpace(); + builder.withOther(trigger).withSpace(); switch (eventTime) { case Time::BEFORE: @@ -353,10 +337,7 @@ TokenList SqliteCreateTrigger::rebuildTokensFromContents() } builder.withStatement(event).withSpace().withKeyword("ON").withSpace(); - if (dialect == Dialect::Sqlite2 && !database.isNull()) - builder.withOther(database, dialect).withOperator("."); - - builder.withOther(table, dialect).withSpace(); + builder.withOther(table).withSpace(); switch (scope) { diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.h index 2ccf876..ac7b81f 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.h @@ -70,10 +70,9 @@ class API_EXPORT SqliteCreateTrigger : public SqliteQuery, public SqliteTableRel bool tempKw = false; bool temporaryKw = false; bool ifNotExistsKw = false; - // The database refers to the trigger name in Sqlite3, but in Sqlite2 it refers to the table. - QString database = QString::null; - QString trigger = QString::null; - QString table = QString::null; // can also be a view name + QString database = QString(); + QString trigger = QString(); + QString table = QString(); // can also be a view name Event* event = nullptr; Time eventTime = Time::null; Scope scope = Scope::null; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.cpp index fe638db..f5b54e7 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.cpp @@ -67,10 +67,7 @@ QStringList SqliteCreateView::getDatabasesInStatement() TokenList SqliteCreateView::getDatabaseTokensInStatement() { - if (dialect == Dialect::Sqlite3) - return getDbTokenListFromFullname(); - else - return TokenList(); + return getDbTokenListFromFullname(); } QList<SqliteStatement::FullObject> SqliteCreateView::getFullObjectsInStatement() @@ -78,28 +75,17 @@ QList<SqliteStatement::FullObject> SqliteCreateView::getFullObjectsInStatement() QList<FullObject> result; // View object - FullObject fullObj; - if (dialect == Dialect::Sqlite3) - fullObj = getFullObjectFromFullname(FullObject::VIEW); - else - { - TokenList tokens = getTokenListFromNamedKey("nm"); - if (tokens.size() > 0) - fullObj = getFullObject(FullObject::VIEW, TokenPtr(), tokens[0]); - } + FullObject fullObj = getFullObjectFromFullname(FullObject::VIEW); if (fullObj.isValid()) result << fullObj; // Db object - if (dialect == Dialect::Sqlite3) + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) { - fullObj = getFirstDbFullObject(); - if (fullObj.isValid()) - { - result << fullObj; - dbTokenForFullObjects = fullObj.database; - } + result << fullObj; + dbTokenForFullObjects = fullObj.database; } return result; @@ -119,10 +105,10 @@ TokenList SqliteCreateView::rebuildTokensFromContents() if (ifNotExists) builder.withKeyword("IF").withSpace().withKeyword("NOT").withSpace().withKeyword("EXISTS").withSpace(); - if (dialect == Dialect::Sqlite3 && !database.isNull()) - builder.withOther(database, dialect).withOperator("."); + if (!database.isNull()) + builder.withOther(database).withOperator("."); - builder.withOther(view, dialect).withSpace(); + builder.withOther(view).withSpace(); if (columns.size() > 0) builder.withParLeft().withStatementList<SqliteIndexedColumn>(columns).withParRight().withSpace(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.h index 1f46d5e..74f6b91 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.h @@ -25,8 +25,8 @@ class API_EXPORT SqliteCreateView : public SqliteQuery, public SqliteDdlWithDbCo bool tempKw = false; bool temporaryKw = false; bool ifNotExists = false; - QString database = QString::null; - QString view = QString::null; + QString database = QString(); + QString view = QString(); SqliteSelect* select = nullptr; QList<SqliteIndexedColumn*> columns; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatevirtualtable.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatevirtualtable.cpp index 1d98c8d..3f1639a 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatevirtualtable.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatevirtualtable.cpp @@ -97,9 +97,9 @@ TokenList SqliteCreateVirtualTable::rebuildTokensFromContents() builder.withKeyword("IF").withSpace().withKeyword("NOT").withSpace().withKeyword("EXISTS").withSpace(); if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + builder.withOther(database).withOperator("."); - builder.withKeyword("USING").withSpace().withOther(module, dialect); + builder.withKeyword("USING").withSpace().withOther(module); if (!args.isEmpty()) { builder.withSpace(); @@ -109,7 +109,7 @@ TokenList SqliteCreateVirtualTable::rebuildTokensFromContents() if (i > 0) builder.withOperator(",").withSpace(); - builder.withTokens(Lexer::tokenize(arg, Dialect::Sqlite3)); + builder.withTokens(Lexer::tokenize(arg)); i++; } } diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatevirtualtable.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatevirtualtable.h index cb2d231..28f91ce 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatevirtualtable.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatevirtualtable.h @@ -31,9 +31,9 @@ class API_EXPORT SqliteCreateVirtualTable : public SqliteQuery public: bool ifNotExistsKw = false; - QString database = QString::null; - QString table = QString::null; - QString module = QString::null; + QString database = QString(); + QString table = QString(); + QString module = QString(); QList<QString> args; }; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedeferrable.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedeferrable.cpp index 4ef4d51..50b7de2 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedeferrable.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedeferrable.cpp @@ -11,7 +11,7 @@ QString sqliteDeferrable(SqliteDeferrable deferrable) case SqliteDeferrable::null: break; } - return QString::null; + return QString(); } SqliteDeferrable sqliteDeferrable(const QString& deferrable) @@ -38,7 +38,7 @@ QString sqliteInitially(SqliteInitially initially) case SqliteInitially::null: break; } - return QString::null; + return QString(); } SqliteInitially sqliteInitially(const QString& initially) diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.cpp index 6026de3..dd4d7cb 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.cpp @@ -119,12 +119,12 @@ TokenList SqliteDelete::rebuildTokensFromContents() builder.withKeyword("DELETE").withSpace().withKeyword("FROM").withSpace(); if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + builder.withOther(database).withOperator("."); - builder.withOther(table, dialect); + builder.withOther(table); if (indexedByKw) - builder.withSpace().withKeyword("INDEXED").withSpace().withKeyword("BY").withSpace().withOther(indexedBy, dialect); + builder.withSpace().withKeyword("INDEXED").withSpace().withKeyword("BY").withSpace().withOther(indexedBy); else if (notIndexedKw) builder.withSpace().withKeyword("NOT").withSpace().withKeyword("INDEXED"); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.h index 90e1385..5d24619 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.h @@ -31,11 +31,11 @@ class API_EXPORT SqliteDelete : public SqliteQuery void init(const QString& name1, const QString& name2, SqliteExpr* where, SqliteWith* with); public: - QString database = QString::null; - QString table = QString::null; + QString database = QString(); + QString table = QString(); bool indexedByKw = false; bool notIndexedKw = false; - QString indexedBy = QString::null; + QString indexedBy = QString(); SqliteExpr* where = nullptr; SqliteWith* with = nullptr; }; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropindex.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropindex.cpp index 2e39312..8d5f878 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropindex.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropindex.cpp @@ -70,7 +70,7 @@ TokenList SqliteDropIndex::rebuildTokensFromContents() builder.withKeyword("IF").withSpace().withKeyword("EXISTS").withSpace(); if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + builder.withOther(database).withOperator("."); builder.withOther(index).withOperator(";"); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropindex.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropindex.h index 9ce7065..4f4364a 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropindex.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropindex.h @@ -14,8 +14,8 @@ class API_EXPORT SqliteDropIndex : public SqliteQuery SqliteStatement* clone(); bool ifExistsKw = false; - QString database = QString::null; - QString index = QString::null; + QString database = QString(); + QString index = QString(); protected: QStringList getDatabasesInStatement(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptable.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptable.cpp index c4fc02d..f567cc0 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptable.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptable.cpp @@ -78,7 +78,7 @@ TokenList SqliteDropTable::rebuildTokensFromContents() builder.withKeyword("IF").withSpace().withKeyword("EXISTS").withSpace(); if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + builder.withOther(database).withOperator("."); builder.withOther(table).withOperator(";"); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptable.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptable.h index edc4da4..58706b2 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptable.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptable.h @@ -14,8 +14,8 @@ class API_EXPORT SqliteDropTable : public SqliteQuery SqliteStatement* clone(); bool ifExistsKw = false; - QString database = QString::null; - QString table = QString::null; + QString database = QString(); + QString table = QString(); protected: QStringList getTablesInStatement(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptrigger.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptrigger.cpp index 8921af3..9794ed4 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptrigger.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptrigger.cpp @@ -71,7 +71,7 @@ TokenList SqliteDropTrigger::rebuildTokensFromContents() builder.withKeyword("IF").withSpace().withKeyword("EXISTS").withSpace(); if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + builder.withOther(database).withOperator("."); builder.withOther(trigger).withOperator(";"); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptrigger.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptrigger.h index 5221959..316af98 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptrigger.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptrigger.h @@ -14,8 +14,8 @@ class API_EXPORT SqliteDropTrigger : public SqliteQuery SqliteStatement* clone(); bool ifExistsKw = false; - QString database = QString::null; - QString trigger = QString::null; + QString database = QString(); + QString trigger = QString(); protected: QStringList getDatabasesInStatement(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropview.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropview.cpp index b992e98..423b1da 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropview.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropview.cpp @@ -34,17 +34,11 @@ SqliteStatement*SqliteDropView::clone() QStringList SqliteDropView::getDatabasesInStatement() { - if (dialect == Dialect::Sqlite2) - return QStringList(); - return getStrListFromValue(database); } TokenList SqliteDropView::getDatabaseTokensInStatement() { - if (dialect == Dialect::Sqlite2) - return TokenList(); - return getDbTokenListFromFullname(); } @@ -77,7 +71,7 @@ TokenList SqliteDropView::rebuildTokensFromContents() builder.withKeyword("IF").withSpace().withKeyword("EXISTS").withSpace(); if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + builder.withOther(database).withOperator("."); builder.withOther(view).withOperator(";"); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropview.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropview.h index 853ccef..2f0882c 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropview.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropview.h @@ -14,8 +14,8 @@ class API_EXPORT SqliteDropView : public SqliteQuery SqliteStatement* clone(); bool ifExistsKw = false; - QString database = QString::null; - QString view = QString::null; + QString database = QString(); + QString view = QString(); protected: QStringList getDatabasesInStatement(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp index d17205a..e2c79ea 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp @@ -2,6 +2,7 @@ #include "sqliteraise.h" #include "sqliteselect.h" #include "sqlitecolumntype.h" +#include "sqlitefilterover.h" #include "parser/statementtokenbuilder.h" #include "common/utils_sql.h" #include "common/global.h" @@ -25,6 +26,7 @@ SqliteExpr::SqliteExpr(const SqliteExpr& other) : DEEP_COPY_COLLECTION(SqliteExpr, exprList); DEEP_COPY_FIELD(SqliteSelect, select); DEEP_COPY_FIELD(SqliteRaise, raiseFunction); + DEEP_COPY_FIELD(SqliteFilterOver, filterOver); } SqliteExpr::~SqliteExpr() @@ -59,7 +61,7 @@ QString SqliteExpr::likeOp(SqliteExpr::LikeOp value) case SqliteExpr::LikeOp::MATCH: return "MATCH"; default: - return QString::null; + return QString(); } } @@ -87,7 +89,7 @@ QString SqliteExpr::notNullOp(SqliteExpr::NotNull value) case SqliteExpr::NotNull::NOTNULL: return "NOTNULL"; default: - return QString::null; + return QString(); } } @@ -168,10 +170,7 @@ void SqliteExpr::initFunction(const QString& fnName, int distinct, const QList<S mode = SqliteExpr::Mode::FUNCTION; function = fnName; this->exprList = exprList; - if (distinct == 1) - distinctKw = true; - else if (distinct == 2) - allKw = true; + initDistinct(distinct); for (SqliteExpr* expr : exprList) expr->setParent(this); @@ -184,6 +183,32 @@ void SqliteExpr::initFunction(const QString& fnName, bool star) this->star = star; } +void SqliteExpr::initWindowFunction(const QString& fnName, int distinct, const QList<SqliteExpr*>& exprList, SqliteFilterOver* filterOver) +{ + mode = SqliteExpr::Mode::WINDOW_FUNCTION; + this->function = fnName; + this->exprList = exprList; + initDistinct(distinct); + this->filterOver = filterOver; + + for (SqliteExpr* expr : exprList) + expr->setParent(this); + + if (filterOver) + filterOver->setParent(this); +} + +void SqliteExpr::initWindowFunction(const QString& fnName, SqliteFilterOver* filterOver) +{ + mode = SqliteExpr::Mode::WINDOW_FUNCTION; + this->function = fnName; + this->star = true; + this->filterOver = filterOver; + + if (filterOver) + filterOver->setParent(this); +} + void SqliteExpr::initBinOp(SqliteExpr *expr1, const QString& op, SqliteExpr *expr2) { mode = SqliteExpr::Mode::BINARY_OP; @@ -363,6 +388,36 @@ void SqliteExpr::detectDoubleQuotes(bool recursively) } } +bool SqliteExpr::replace(SqliteExpr* toBeReplaced, SqliteExpr* replaceWith) +{ + if (expr1 == toBeReplaced) + { + expr1 = replaceWith; + return true; + } + + if (expr2 == toBeReplaced) + { + expr2 = replaceWith; + return true; + } + + if (expr3 == toBeReplaced) + { + expr3 = replaceWith; + return true; + } + + int idx = exprList.indexOf(toBeReplaced); + if (idx > -1) + { + exprList.replace(idx, replaceWith); + return true; + } + + return false; +} + QStringList SqliteExpr::getColumnsInStatement() { return getStrListFromValue(column); @@ -498,6 +553,8 @@ TokenList SqliteExpr::rebuildTokensFromContents() builder.withOther(function).withParLeft(); if (distinctKw) builder.withKeyword("DISTINCT"); + else if (allKw) + builder.withKeyword("DISTINCT"); if (star) builder.withOperator("*").withParRight(); @@ -505,6 +562,20 @@ TokenList SqliteExpr::rebuildTokensFromContents() builder.withStatementList(exprList).withParRight(); break; + case SqliteExpr::Mode::WINDOW_FUNCTION: + builder.withOther(function).withParLeft(); + if (distinctKw) + builder.withKeyword("DISTINCT"); + else if (allKw) + builder.withKeyword("DISTINCT"); + + if (star) + builder.withOperator("*").withParRight(); + else + builder.withStatementList(exprList).withParRight(); + + builder.withSpace().withStatement(filterOver); + break; case SqliteExpr::Mode::SUB_EXPR: builder.withParLeft().withStatement(expr1).withParRight(); break; @@ -513,7 +584,7 @@ TokenList SqliteExpr::rebuildTokensFromContents() .withStatement(columnType).withParRight(); break; case SqliteExpr::Mode::COLLATE: - builder.withStatement(expr1).withSpace().withKeyword("COLLATE").withSpace().withOther(collation, dialect); + builder.withStatement(expr1).withSpace().withKeyword("COLLATE").withSpace().withOther(collation); break; case SqliteExpr::Mode::LIKE: builder.withTokens(rebuildLike()); @@ -562,15 +633,15 @@ TokenList SqliteExpr::rebuildId() { StatementTokenBuilder builder; if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + builder.withOther(database).withOperator("."); if (!table.isNull()) - builder.withOther(table, dialect).withOperator("."); + builder.withOther(table).withOperator("."); if (table.isNull() && possibleDoubleQuotedString) - builder.withStringPossiblyOther(column, dialect); + builder.withStringPossiblyOther(column); else - builder.withOther(column, dialect); + builder.withOther(column); return builder.build(); } @@ -652,9 +723,9 @@ TokenList SqliteExpr::rebuildIn() else { if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + builder.withOther(database).withOperator("."); - builder.withOther(table, dialect); + builder.withOther(table); } return builder.build(); } @@ -686,3 +757,11 @@ TokenList SqliteExpr::rebuildCase() builder.withKeyword("END"); return builder.build(); } + +void SqliteExpr::initDistinct(int distinct) +{ + if (distinct == 1) + distinctKw = true; + else if (distinct == 2) + allKw = true; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.h index c65a8e2..ef4c7da 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.h @@ -6,6 +6,7 @@ #include <QVariant> #include <QList> +class SqliteFilterOver; class SqliteSelect; class SqliteColumnType; class SqliteRaise; @@ -26,7 +27,7 @@ class API_EXPORT SqliteExpr : public SqliteStatement SUB_EXPR, ROW_VALUE, CAST, - COLLATE, // in Sqlite2 exists only in expr of sortlist + COLLATE, LIKE, NULL_, NOTNULL, @@ -36,7 +37,8 @@ class API_EXPORT SqliteExpr : public SqliteStatement EXISTS, CASE, SUB_SELECT, - RAISE + RAISE, + WINDOW_FUNCTION }; enum class NotNull @@ -79,6 +81,8 @@ class API_EXPORT SqliteExpr : public SqliteStatement void initCast(SqliteExpr* expr, SqliteColumnType* type); void initFunction(const QString& fnName, int distinct, const QList<SqliteExpr*>& exprList); void initFunction(const QString& fnName, bool star = false); + void initWindowFunction(const QString& fnName, int distinct, const QList<SqliteExpr*>& exprList, SqliteFilterOver* filterOver); + void initWindowFunction(const QString& fnName, SqliteFilterOver* filterOver); void initBinOp(SqliteExpr* expr1, const QString& op, SqliteExpr* expr2); void initUnaryOp(SqliteExpr* expr, const QString& op); void initLike(SqliteExpr* expr1, bool notKw, SqliteExpr::LikeOp likeOp, SqliteExpr* expr2, SqliteExpr* expr3 = nullptr); @@ -91,27 +95,29 @@ class API_EXPORT SqliteExpr : public SqliteStatement void initExists(SqliteSelect* select); void initSubSelect(SqliteSelect* select); void initCase(SqliteExpr* expr1, const QList<SqliteExpr*>& exprList, SqliteExpr* expr2); - void initRaise(const QString& type, const QString& text = QString::null); + void initRaise(const QString& type, const QString& text = QString()); void detectDoubleQuotes(bool recursively = true); + bool replace(SqliteExpr* toBeReplaced, SqliteExpr* replaceWith); Mode mode = Mode::null; QVariant literalValue = QVariant(); bool literalNull = false; - QString bindParam = QString::null; - QString database = QString::null; - QString table = QString::null; - QString column = QString::null; - QString unaryOp = QString::null; - QString binaryOp = QString::null; - QString function = QString::null; - QString collation = QString::null; - QString ctime = QString::null; + QString bindParam = QString(); + QString database = QString(); + QString table = QString(); + QString column = QString(); + QString unaryOp = QString(); + QString binaryOp = QString(); + QString function = QString(); + QString collation = QString(); + QString ctime = QString(); SqliteColumnType* columnType = nullptr; SqliteExpr* expr1 = nullptr; SqliteExpr* expr2 = nullptr; SqliteExpr* expr3 = nullptr; QList<SqliteExpr*> exprList; SqliteSelect* select = nullptr; + SqliteFilterOver* filterOver = nullptr; bool distinctKw = false; bool allKw = false; // alias for DISTINCT as for sqlite3 grammar bool star = false; @@ -141,6 +147,7 @@ class API_EXPORT SqliteExpr : public SqliteStatement TokenList rebuildBetween(); TokenList rebuildIn(); TokenList rebuildCase(); + void initDistinct(int distinct); }; typedef QSharedPointer<SqliteExpr> SqliteExprPtr; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitefilterover.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitefilterover.cpp new file mode 100644 index 0000000..08b4f88 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitefilterover.cpp @@ -0,0 +1,138 @@ +#include "sqlitefilterover.h" +#include "sqliteexpr.h" +#include "parser/statementtokenbuilder.h" +#include "common/global.h" + +SqliteFilterOver::SqliteFilterOver() +{ + +} + +SqliteFilterOver::~SqliteFilterOver() +{ +} + +SqliteFilterOver::SqliteFilterOver(const SqliteFilterOver& other) : + SqliteStatement(other) +{ + DEEP_COPY_FIELD(Filter, filter); + DEEP_COPY_FIELD(Over, over); +} + +SqliteFilterOver::SqliteFilterOver(SqliteFilterOver::Filter* filter, SqliteFilterOver::Over* over) +{ + this->filter = filter; + this->over = over; + + if (filter) + filter->setParent(this); + + if (over) + over->setParent(this); +} + +SqliteStatement* SqliteFilterOver::clone() +{ + return new SqliteFilterOver(*this); +} + +TokenList SqliteFilterOver::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + if (filter) + builder.withStatement(filter); + + if (filter || over) + builder.withSpace(); + + if (over) + builder.withStatement(over); + + return builder.build(); +} + +SqliteFilterOver::Over::Over() +{ +} + +SqliteFilterOver::Over::Over(const SqliteFilterOver::Over& other) : + SqliteStatement(other), name(other.name), mode(other.mode) +{ + DEEP_COPY_FIELD(SqliteWindowDefinition::Window, window); +} + +SqliteFilterOver::Over::~Over() +{ +} + +SqliteFilterOver::Over::Over(SqliteWindowDefinition::Window* window) +{ + this->mode = Mode::WINDOW; + this->window = window; + if (window) + window->setParent(this); +} + +SqliteFilterOver::Over::Over(const QString& name) +{ + this->mode = Mode::NAME; + this->name = name; +} + +SqliteStatement* SqliteFilterOver::Over::clone() +{ + return new SqliteFilterOver::Over(*this); +} + +TokenList SqliteFilterOver::Over::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("OVER").withSpace(); + + switch (mode) + { + case SqliteFilterOver::Over::Mode::WINDOW: + builder.withParLeft().withStatement(window).withParRight(); + break; + case SqliteFilterOver::Over::Mode::NAME: + builder.withOther(name); + break; + } + + return builder.build(); +} + + +SqliteFilterOver::Filter::Filter(SqliteExpr* expr) +{ + this->expr = expr; + if (expr) + expr->setParent(this); +} + +SqliteFilterOver::Filter::Filter(const SqliteFilterOver::Filter& other) : + SqliteStatement(other) +{ + DEEP_COPY_FIELD(SqliteExpr, expr); +} + +SqliteStatement* SqliteFilterOver::Filter::clone() +{ + return new SqliteFilterOver::Filter(*this); +} + +TokenList SqliteFilterOver::Filter::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("FILTER").withSpace().withParLeft().withKeyword("WHERE").withSpace().withStatement(expr).withParRight(); + + return builder.build(); +} + + +SqliteFilterOver::Filter::~Filter() +{ +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitefilterover.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitefilterover.h new file mode 100644 index 0000000..3f023d9 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitefilterover.h @@ -0,0 +1,65 @@ +#ifndef SQLITEFILTEROVER_H +#define SQLITEFILTEROVER_H + +#include "sqlitestatement.h" +#include "sqlitewindowdefinition.h" + +class SqliteFilterOver : public SqliteStatement +{ + public: + class Over : public SqliteStatement + { + public: + enum class Mode + { + WINDOW, + NAME + }; + + Over(); + Over(const Over& other); + ~Over(); + Over(SqliteWindowDefinition::Window* window); + Over(const QString& name); + + SqliteStatement* clone(); + + SqliteWindowDefinition::Window* window = nullptr; + QString name = QString(); + Mode mode = Mode::WINDOW; + + protected: + TokenList rebuildTokensFromContents(); + }; + + class Filter : public SqliteStatement + { + public: + Filter(); + Filter(const Filter& other); + ~Filter(); + Filter(SqliteExpr* expr); + + SqliteStatement* clone(); + + SqliteExpr* expr; + + protected: + TokenList rebuildTokensFromContents(); + }; + + SqliteFilterOver(); + ~SqliteFilterOver(); + SqliteFilterOver(const SqliteFilterOver& other); + SqliteFilterOver(Filter* filter, Over* over); + + SqliteStatement* clone(); + + Filter* filter = nullptr; + Over* over = nullptr; + + protected: + TokenList rebuildTokensFromContents(); +}; + +#endif // SQLITEFILTEROVER_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteforeignkey.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteforeignkey.cpp index 9a29db2..9bfd56c 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteforeignkey.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteforeignkey.cpp @@ -35,7 +35,7 @@ QString SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::React case SqliteForeignKey::Condition::NO_ACTION: return "NO ACTION"; } - return QString::null; + return QString(); } SqliteForeignKey::Condition::Reaction SqliteForeignKey::Condition::toEnum(const QString& reaction) @@ -115,7 +115,7 @@ TokenList SqliteForeignKey::rebuildTokensFromContents() { StatementTokenBuilder builder; - builder.withKeyword("REFERENCES").withSpace().withOther(foreignTable, dialect); + builder.withKeyword("REFERENCES").withSpace().withOther(foreignTable); if (indexedColumns.size() > 0) builder.withSpace().withParLeft().withStatementList(indexedColumns).withParRight(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteforeignkey.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteforeignkey.h index 18e0bcb..62dd84d 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteforeignkey.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteforeignkey.h @@ -41,7 +41,7 @@ class API_EXPORT SqliteForeignKey : public SqliteStatement SqliteStatement* clone(); Action action; - QString name = QString::null; + QString name = QString(); Reaction reaction = NO_ACTION; protected: @@ -57,7 +57,7 @@ class API_EXPORT SqliteForeignKey : public SqliteStatement SqliteStatement* clone(); - QString foreignTable = QString::null; + QString foreignTable = QString(); QList<SqliteIndexedColumn*> indexedColumns; QList<Condition*> conditions; SqliteDeferrable deferrable = SqliteDeferrable::null; // Those two are for table constraint only, diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteindexedcolumn.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteindexedcolumn.cpp index 142af06..54ebc89 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteindexedcolumn.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteindexedcolumn.cpp @@ -58,9 +58,9 @@ TokenList SqliteIndexedColumn::getColumnTokensInStatement() TokenList SqliteIndexedColumn::rebuildTokensFromContents() { StatementTokenBuilder builder; - builder.withOther(name, dialect); + builder.withOther(name); if (!collate.isNull()) - builder.withSpace().withKeyword("COLLATE").withSpace().withOther(collate, dialect); + builder.withSpace().withKeyword("COLLATE").withSpace().withOther(collate); builder.withSortOrder(sortOrder); return builder.build(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteindexedcolumn.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteindexedcolumn.h index c0fe680..34cf4a4 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteindexedcolumn.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteindexedcolumn.h @@ -21,9 +21,9 @@ class API_EXPORT SqliteIndexedColumn : public SqliteStatement, public SqliteExte QString getCollation() const; void clearCollation(); - QString name = QString::null; + QString name = QString(); SqliteSortOrder sortOrder = SqliteSortOrder::null; - QString collate = QString::null; + QString collate = QString(); protected: QStringList getColumnsInStatement(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.cpp index e48cffb..906b385 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.cpp @@ -191,9 +191,9 @@ TokenList SqliteInsert::rebuildTokensFromContents() builder.withKeyword("INTO").withSpace(); if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + builder.withOther(database).withOperator("."); - builder.withOther(table, dialect).withSpace(); + builder.withOther(table).withSpace(); if (defaultValuesKw) { @@ -202,7 +202,7 @@ TokenList SqliteInsert::rebuildTokensFromContents() else { if (columnNames.size() > 0) - builder.withParLeft().withOtherList(columnNames, dialect).withParRight().withSpace(); + builder.withParLeft().withOtherList(columnNames).withParRight().withSpace(); if (select) { @@ -210,10 +210,6 @@ TokenList SqliteInsert::rebuildTokensFromContents() if (upsert) builder.withSpace().withStatement(upsert); } - else if (dialect == Dialect::Sqlite2) // Sqlite2 uses classic single row values - { - builder.withKeyword("VALUES").withSpace().withParLeft().withStatementList(values).withParRight(); - } } builder.withOperator(";"); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.h index 2ee4965..40581cd 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.h @@ -45,8 +45,8 @@ class API_EXPORT SqliteInsert : public SqliteQuery bool replaceKw = false; bool defaultValuesKw = false; SqliteConflictAlgo onConflict = SqliteConflictAlgo::null; - QString database = QString::null; - QString table = QString::null; + QString database = QString(); + QString table = QString(); QStringList columnNames; QList<SqliteExpr*> values; SqliteSelect* select = nullptr; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitenulls.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitenulls.cpp new file mode 100644 index 0000000..46102a9 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitenulls.cpp @@ -0,0 +1,26 @@ +#include "sqlitenulls.h" + + +SqliteNulls sqliteNulls(const QString& value) +{ + if (value == "NULLS FIRST") + return SqliteNulls::FIRST; + else if (value == "NULLS LAST") + return SqliteNulls::LAST; + else + return SqliteNulls::null; +} + +QString sqliteNulls(SqliteNulls value) +{ + switch (value) + { + case SqliteNulls::FIRST: + return "FIRST"; + case SqliteNulls::LAST: + return "LAST"; + case SqliteNulls::null: + break; + } + return QString(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitenulls.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitenulls.h new file mode 100644 index 0000000..3553eae --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitenulls.h @@ -0,0 +1,17 @@ +#ifndef SQLITENULLS_H +#define SQLITENULLS_H + +#include "coreSQLiteStudio_global.h" +#include <QString> + +enum class SqliteNulls +{ + FIRST, + LAST, + null +}; + +API_EXPORT SqliteNulls sqliteNulls(const QString& value); +API_EXPORT QString sqliteNulls(SqliteNulls value); + +#endif // SQLITENULLS_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.cpp index 8eb7b46..732c0a2 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.cpp @@ -2,21 +2,23 @@ #include "sqliteexpr.h" #include "parser/statementtokenbuilder.h" #include "common/global.h" +#include <QDebug> SqliteOrderBy::SqliteOrderBy() { } SqliteOrderBy::SqliteOrderBy(const SqliteOrderBy& other) : - SqliteStatement(other), order(other.order) + SqliteStatement(other), order(other.order), nulls(other.nulls) { DEEP_COPY_FIELD(SqliteExpr, expr); } -SqliteOrderBy::SqliteOrderBy(SqliteExpr *expr, SqliteSortOrder order) +SqliteOrderBy::SqliteOrderBy(SqliteExpr *expr, SqliteSortOrder order, SqliteNulls nulls) { this->expr = expr; this->order = order; + this->nulls = nulls; if (expr) expr->setParent(this); } @@ -44,7 +46,7 @@ QString SqliteOrderBy::getColumnName() const return expr->column; if (expr->mode == SqliteExpr::Mode::COLLATE && expr->expr1 && expr->expr1->mode == SqliteExpr::Mode::ID) - return expr->expr1->literalValue.toString(); + return expr->expr1->column; return QString(); } @@ -107,9 +109,66 @@ TokenList SqliteOrderBy::rebuildTokensFromContents() if (order != SqliteSortOrder::null) builder.withSpace().withKeyword(sqliteSortOrder(order)); + if (nulls != SqliteNulls::null) + builder.withSpace().withKeyword("NULLS").withSpace().withKeyword(sqliteNulls(nulls)); + return builder.build(); } +void SqliteOrderBy::evaluatePostParsing() +{ + pullLastCollationAsOuterExpr(); +} + +void SqliteOrderBy::pullLastCollationAsOuterExpr() +{ + /* + * If the order statement is like: columnName + 2 COLLATE BINARY ASC + * then the COLLATE is associated with the "2" subexpr, instead of the most outer expr. + * Looks like SQLite's parser does the same, but they don't care about the depth as we do here. + * Therefore if we idenfity this case, we need to pull the inner expr to outside. + */ + TokenPtr collateToken = expr->tokens.findLast(Token::KEYWORD, "COLLATE", Qt::CaseInsensitive); + if (collateToken.isNull()) + return; + + int lastCollateIdx = expr->tokens.indexOf(collateToken); + if (expr->tokens.mid(lastCollateIdx).filterWhiteSpaces().size() != 2) + return; + + // This is the case. We need to pull the expr to the top level. + SqliteStatement* stmt = expr->findStatementWithToken(collateToken); + SqliteExpr* collateExpr = dynamic_cast<SqliteExpr*>(stmt); + if (!collateExpr) + { + qCritical() << "Could not cast statement to SqliteExpr, even though it's identified as COLLATE expr. The actual contents:" + << collateExpr->detokenize(); + return; + } + + if (collateExpr == expr) + return; // it's already the top-level expr, we're fine. + + SqliteExpr* parentExpr = dynamic_cast<SqliteExpr*>(collateExpr->parentStatement()); + if (!parentExpr) + { + qCritical() << "Could not cast parent statement to SqliteExpr, even though parent of COLLATE should be another expr at this stage." + << "The qobject type of parent:" << collateExpr->parentStatement()->metaObject()->className(); + return; + } + + // Take out COLLATE from its current place + collateExpr->expr1->setParent(parentExpr); // New parent of COLLATE's expr is now parent of COLLATE + parentExpr->replace(collateExpr, collateExpr->expr1); // New child expr of COLLATE's parent is now child of COLLATE + + // Put it at top level + collateExpr->expr1 = expr; // COLLATE's child is set to the old top level expr + expr->setParent(collateExpr); // Old top level expr gets COLLATE as parent + expr = collateExpr; // New top level is now COLLATE + collateExpr->setParent(this); // COLLATE's new parent is this + + rebuildTokens(); +} void SqliteOrderBy::clearCollation() { diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.h index 4f75c78..4f05d50 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.h @@ -3,6 +3,7 @@ #include "sqlitestatement.h" #include "sqlitesortorder.h" +#include "sqlitenulls.h" #include "sqliteextendedindexedcolumn.h" class SqliteExpr; @@ -12,7 +13,7 @@ class API_EXPORT SqliteOrderBy : public SqliteStatement, public SqliteExtendedIn public: SqliteOrderBy(); SqliteOrderBy(const SqliteOrderBy& other); - SqliteOrderBy(SqliteExpr* expr, SqliteSortOrder order); + SqliteOrderBy(SqliteExpr* expr, SqliteSortOrder order, SqliteNulls nulls); ~SqliteOrderBy(); SqliteStatement* clone(); @@ -26,9 +27,14 @@ class API_EXPORT SqliteOrderBy : public SqliteStatement, public SqliteExtendedIn SqliteExpr* expr = nullptr; SqliteSortOrder order = SqliteSortOrder::null; + SqliteNulls nulls = SqliteNulls::null; protected: TokenList rebuildTokensFromContents(); + void evaluatePostParsing(); + + private: + void pullLastCollationAsOuterExpr(); }; typedef QSharedPointer<SqliteOrderBy> SqliteOrderByPtr; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitepragma.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitepragma.cpp index 4660e4f..4d13d36 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitepragma.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitepragma.cpp @@ -53,7 +53,7 @@ QStringList SqlitePragma::getDatabasesInStatement() TokenList SqlitePragma::getDatabaseTokensInStatement() { - if (dialect == Dialect::Sqlite2 || database.isNull()) + if (database.isNull()) return TokenList(); return getTokenListFromNamedKey("nm"); @@ -62,7 +62,7 @@ TokenList SqlitePragma::getDatabaseTokensInStatement() QList<SqliteStatement::FullObject> SqlitePragma::getFullObjectsInStatement() { QList<FullObject> result; - if (dialect == Dialect::Sqlite2 || database.isNull()) + if (database.isNull()) return result; // Db object @@ -94,9 +94,9 @@ TokenList SqlitePragma::rebuildTokensFromContents() builder.withKeyword("PRAGMA").withSpace(); if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + builder.withOther(database).withOperator("."); - builder.withOther(pragmaName, dialect); + builder.withOther(pragmaName); if (equalsOp) builder.withSpace().withOperator("=").withSpace().withLiteralValue(value); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitepragma.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitepragma.h index 364a16f..8d75ae4 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitepragma.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitepragma.h @@ -29,8 +29,8 @@ class API_EXPORT SqlitePragma : public SqliteQuery void initName(const QString& name1, const QString& name2); public: - QString database = QString::null; - QString pragmaName = QString::null; + QString database = QString(); + QString pragmaName = QString(); QVariant value = QVariant(); bool equalsOp = false; bool parenthesis = false; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.cpp index c369e0e..1c04ca6 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.cpp @@ -61,6 +61,44 @@ QString sqliteQueryTypeToString(const SqliteQueryType& type) case SqliteQueryType::Vacuum: return "Vacuum"; default: - return QString::null; + return QString(); + } +} + +bool isDataReturningQuery(const SqliteQueryType& type) +{ + switch (type) + { + case SqliteQueryType::Select: + case SqliteQueryType::Pragma: + return true; + case SqliteQueryType::UNDEFINED: + case SqliteQueryType::EMPTY: + case SqliteQueryType::AlterTable: + case SqliteQueryType::Analyze: + case SqliteQueryType::Attach: + case SqliteQueryType::BeginTrans: + case SqliteQueryType::CommitTrans: + case SqliteQueryType::Copy: + case SqliteQueryType::CreateIndex: + case SqliteQueryType::CreateTable: + case SqliteQueryType::CreateTrigger: + case SqliteQueryType::CreateView: + case SqliteQueryType::CreateVirtualTable: + case SqliteQueryType::Delete: + case SqliteQueryType::Detach: + case SqliteQueryType::DropIndex: + case SqliteQueryType::DropTable: + case SqliteQueryType::DropTrigger: + case SqliteQueryType::DropView: + case SqliteQueryType::Insert: + case SqliteQueryType::Reindex: + case SqliteQueryType::Release: + case SqliteQueryType::Rollback: + case SqliteQueryType::Savepoint: + case SqliteQueryType::Update: + case SqliteQueryType::Vacuum: + default: + return false; } } diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.h index 763fcfa..9326bf4 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.h @@ -37,5 +37,6 @@ enum class SqliteQueryType }; QString API_EXPORT sqliteQueryTypeToString(const SqliteQueryType& type); +bool API_EXPORT isDataReturningQuery(const SqliteQueryType& type); #endif // SQLITEQUERYTYPE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteraise.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteraise.cpp index b606baa..1e385b9 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteraise.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteraise.cpp @@ -54,7 +54,7 @@ QString SqliteRaise::raiseType(SqliteRaise::Type value) case SqliteRaise::Type::FAIL: return "FAIL"; default: - return QString::null; + return QString(); } } diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteraise.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteraise.h index 1b844f8..2330867 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteraise.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteraise.h @@ -27,7 +27,7 @@ class API_EXPORT SqliteRaise : public SqliteStatement static QString raiseType(Type value); Type type = Type::null; - QString message = QString::null; + QString message = QString(); protected: TokenList rebuildTokensFromContents(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitereindex.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitereindex.cpp index 6f863f9..daf8893 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitereindex.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitereindex.cpp @@ -74,7 +74,7 @@ TokenList SqliteReindex::rebuildTokensFromContents() builder.withTokens(SqliteQuery::rebuildTokensFromContents()); builder.withKeyword("REINDEX"); if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + builder.withOther(database).withOperator("."); builder.withOther(table).withOperator(";"); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitereindex.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitereindex.h index 642b5bf..2fe05d5 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitereindex.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitereindex.h @@ -14,8 +14,8 @@ class API_EXPORT SqliteReindex : public SqliteQuery SqliteStatement* clone(); - QString database = QString::null; - QString table = QString::null; + QString database = QString(); + QString table = QString(); protected: QStringList getTablesInStatement(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterelease.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterelease.cpp index f71d61c..8eb53c0 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterelease.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterelease.cpp @@ -33,7 +33,7 @@ TokenList SqliteRelease::rebuildTokensFromContents() if (savepointKw) builder.withKeyword("SAVEPOINT").withSpace(); - builder.withOther(name, dialect).withOperator(";"); + builder.withOther(name).withOperator(";"); return builder.build(); } diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterelease.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterelease.h index d115669..64008f3 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterelease.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterelease.h @@ -14,7 +14,7 @@ class API_EXPORT SqliteRelease : public SqliteQuery SqliteStatement* clone(); - QString name = QString::null; + QString name = QString(); bool savepointKw = false; protected: diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterollback.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterollback.cpp index b21b6ae..3af6b8c 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterollback.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterollback.cpp @@ -50,7 +50,7 @@ TokenList SqliteRollback::rebuildTokensFromContents() if (savepointKw) builder.withKeyword("SAVEPOINT").withSpace(); - builder.withOther(name, dialect); + builder.withOther(name); } builder.withOperator(";"); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterollback.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterollback.h index 1dc1574..6adc4c0 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterollback.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterollback.h @@ -17,7 +17,7 @@ class API_EXPORT SqliteRollback : public SqliteQuery bool transactionKw = false; bool toKw = false; bool savepointKw = false; - QString name = QString::null; + QString name = QString(); protected: TokenList rebuildTokensFromContents(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesavepoint.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesavepoint.cpp index 1ac50cd..e7bd451 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesavepoint.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesavepoint.cpp @@ -28,6 +28,6 @@ TokenList SqliteSavepoint::rebuildTokensFromContents() { StatementTokenBuilder builder; builder.withTokens(SqliteQuery::rebuildTokensFromContents()); - builder.withKeyword("SAVEPOINT").withSpace().withOther(name, dialect).withOperator(";"); + builder.withKeyword("SAVEPOINT").withSpace().withOther(name).withOperator(";"); return builder.build(); } diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesavepoint.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesavepoint.h index bd75c76..f1ec356 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesavepoint.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesavepoint.h @@ -14,7 +14,7 @@ class API_EXPORT SqliteSavepoint : public SqliteQuery SqliteStatement* clone(); - QString name = QString::null; + QString name = QString(); protected: TokenList rebuildTokensFromContents(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.cpp index 8038cb6..e0439d2 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.cpp @@ -3,6 +3,7 @@ #include "parser/statementtokenbuilder.h" #include "common/global.h" #include "sqlitewith.h" +#include "sqlitewindowdefinition.h" #include <QSet> SqliteSelect::SqliteSelect() @@ -66,7 +67,7 @@ SqliteSelect* SqliteSelect::append(SqliteSelect* select, SqliteSelect::CompoundO resColList.clear(); for (SqliteExpr* value : singleValues) { - resCol = new Core::ResultColumn(value, false, QString::null); + resCol = new Core::ResultColumn(value, false, QString()); value->detectDoubleQuotes(); // invoke explicitly before rebuilding tokens not to lose this information resCol->rebuildTokens(); resCol->setParent(core); @@ -97,7 +98,7 @@ QString SqliteSelect::compoundOperator(SqliteSelect::CompoundOperator op) case SqliteSelect::CompoundOperator::null: break; } - return QString::null; + return QString(); } SqliteSelect::CompoundOperator SqliteSelect::compoundOperator(const QString& op) @@ -142,11 +143,21 @@ SqliteSelect::Core::Core(const SqliteSelect::Core& other) : DEEP_COPY_FIELD(SqliteExpr, where); DEEP_COPY_FIELD(SqliteExpr, having); DEEP_COPY_COLLECTION(SqliteExpr, groupBy); + DEEP_COPY_COLLECTION(SqliteWindowDefinition, windows); DEEP_COPY_COLLECTION(SqliteOrderBy, orderBy); DEEP_COPY_FIELD(SqliteLimit, limit); } -SqliteSelect::Core::Core(int distinct, const QList<ResultColumn *> &resCols, SqliteSelect::Core::JoinSource *src, SqliteExpr *where, const QList<SqliteExpr *> &groupBy, SqliteExpr *having, const QList<SqliteOrderBy*>& orderBy, SqliteLimit* limit) +SqliteSelect::Core::Core(int distinct, const QList<ResultColumn*>& resCols, JoinSource* src, + SqliteExpr* where, const QList<SqliteExpr*>& groupBy, SqliteExpr* having, + const QList<SqliteOrderBy*>& orderBy, SqliteLimit* limit) : + Core(distinct, resCols, src, where, groupBy, having, QList<SqliteWindowDefinition*>(), orderBy, limit) +{ +} + +SqliteSelect::Core::Core(int distinct, const QList<ResultColumn *> &resCols, JoinSource *src, + SqliteExpr *where, const QList<SqliteExpr *> &groupBy, SqliteExpr *having, const QList<SqliteWindowDefinition*> windows, + const QList<SqliteOrderBy*>& orderBy, SqliteLimit* limit) { if (distinct == 1) distinctKw = true; @@ -156,6 +167,7 @@ SqliteSelect::Core::Core(int distinct, const QList<ResultColumn *> &resCols, Sql from = src; this->where = where; this->having = having; + this->windows = windows; this->groupBy = groupBy; resultColumns = resCols; this->limit = limit; @@ -173,6 +185,9 @@ SqliteSelect::Core::Core(int distinct, const QList<ResultColumn *> &resCols, Sql if (limit) limit->setParent(this); + for (SqliteWindowDefinition* win : windows) + win->setParent(this); + for (SqliteOrderBy* order : orderBy) order->setParent(this); @@ -246,7 +261,7 @@ TokenList SqliteSelect::Core::ResultColumn::getTableTokensInStatement() return TokenList(); // Now, we know table was specified - return getTokenListFromNamedKey("nm"); + return getTokenListFromNamedKey("tnm"); } QList<SqliteStatement::FullObject> SqliteSelect::Core::ResultColumn::getFullObjectsInStatement() @@ -574,7 +589,7 @@ TokenList SqliteSelect::Core::ResultColumn::rebuildTokensFromContents() if (star) { if (!table.isNull()) - builder.withOther(table, dialect).withOperator("."); + builder.withOther(table).withOperator("."); builder.withOperator("*"); } @@ -586,7 +601,7 @@ TokenList SqliteSelect::Core::ResultColumn::rebuildTokensFromContents() if (asKw) builder.withSpace().withKeyword("AS"); - builder.withSpace().withOther(alias, dialect); + builder.withSpace().withOther(alias); } } @@ -599,35 +614,35 @@ TokenList SqliteSelect::Core::SingleSource::rebuildTokensFromContents() if (!table.isNull()) { if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + builder.withOther(database).withOperator("."); - builder.withOther(table, dialect); + builder.withOther(table); if (!alias.isNull()) { if (asKw) builder.withSpace().withKeyword("AS"); - builder.withSpace().withOther(alias, dialect); + builder.withSpace().withOther(alias); } } else if (!funcName.isNull()) { if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + builder.withOther(database).withOperator("."); - builder.withOther(funcName, dialect).withParLeft().withStatementList(funcParams).withParRight(); + builder.withOther(funcName).withParLeft().withStatementList(funcParams).withParRight(); if (!alias.isNull()) { if (asKw) builder.withSpace().withKeyword("AS"); - builder.withSpace().withOther(alias, dialect); + builder.withSpace().withOther(alias); } if (indexedByKw) - builder.withSpace().withKeyword("INDEXED").withSpace().withKeyword("BY").withSpace().withOther(indexedBy, dialect); + builder.withSpace().withKeyword("INDEXED").withSpace().withKeyword("BY").withSpace().withOther(indexedBy); else if (notIndexedKw) builder.withSpace().withKeyword("NOT").withSpace().withKeyword("INDEXED"); } @@ -639,7 +654,7 @@ TokenList SqliteSelect::Core::SingleSource::rebuildTokensFromContents() if (asKw) builder.withSpace().withKeyword("AS"); - builder.withSpace().withOther(alias, dialect); + builder.withSpace().withOther(alias); } } else @@ -652,46 +667,7 @@ TokenList SqliteSelect::Core::SingleSource::rebuildTokensFromContents() TokenList SqliteSelect::Core::JoinOp::rebuildTokensFromContents() { - switch (dialect) - { - case Dialect::Sqlite3: - return rebuildTokensForSqlite2(); - case Dialect::Sqlite2: - return rebuildTokensForSqlite3(); - } - return TokenList(); -} - -TokenList SqliteSelect::Core::JoinOp::rebuildTokensForSqlite2() -{ - StatementTokenBuilder builder; - if (comma) - { - builder.withOperator(","); - } - else - { - if (naturalKw) - builder.withKeyword("NATURAL").withSpace(); - - if (leftKw) - builder.withKeyword("LEFT").withSpace(); - else if (rightKw) - builder.withKeyword("RIGHT").withSpace(); - else if (fullKw) - builder.withKeyword("FULL").withSpace(); - - if (innerKw) - builder.withKeyword("INNER").withSpace(); - else if (crossKw) - builder.withKeyword("CROSS").withSpace(); - else if (outerKw) - builder.withKeyword("OUTER").withSpace(); - - builder.withKeyword("JOIN"); - } - - return builder.build(); + return rebuildTokensForSqlite3(); } TokenList SqliteSelect::Core::JoinOp::rebuildTokensForSqlite3() @@ -730,7 +706,7 @@ TokenList SqliteSelect::Core::JoinConstraint::rebuildTokensFromContents() if (expr) builder.withKeyword("ON").withStatement(expr); else - builder.withKeyword("USING").withSpace().withParLeft().withOtherList(columnNames, dialect).withParRight(); + builder.withKeyword("USING").withSpace().withParLeft().withOtherList(columnNames).withParRight(); return builder.build(); } @@ -785,6 +761,9 @@ TokenList SqliteSelect::Core::rebuildTokensFromContents() builder.withSpace().withKeyword("HAVING").withStatement(having); } + if (windows.size() > 0) + builder.withSpace().withKeyword("WINDOW").withStatementList(windows); + if (orderBy.size() > 0) builder.withSpace().withKeyword("ORDER").withSpace().withKeyword("BY").withStatementList(orderBy); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.h index b6f537d..58babfe 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.h @@ -9,6 +9,7 @@ #include <QList> class SqliteWith; +class SqliteWindowDefinition; /** * @addtogroup sqlite_statement @@ -44,8 +45,8 @@ class API_EXPORT SqliteSelect : public SqliteQuery SqliteExpr* expr = nullptr; bool star = false; bool asKw = false; - QString alias = QString::null; - QString table = QString::null; + QString alias = QString(); + QString table = QString(); protected: QStringList getTablesInStatement(); @@ -70,15 +71,15 @@ class API_EXPORT SqliteSelect : public SqliteQuery SqliteStatement* clone(); - QString database = QString::null; - QString table = QString::null; - QString alias = QString::null; - QString funcName = QString::null; + QString database = QString(); + QString table = QString(); + QString alias = QString(); + QString funcName = QString(); QList<SqliteExpr*> funcParams; bool asKw = false; bool indexedByKw = false; bool notIndexedKw = false; - QString indexedBy = QString::null; + QString indexedBy = QString(); SqliteSelect* select = nullptr; JoinSource* joinSource = nullptr; @@ -116,15 +117,14 @@ class API_EXPORT SqliteSelect : public SqliteQuery bool crossKw = false; bool rightKw = false; bool fullKw = false; - QString customKw1 = QString::null; - QString customKw2 = QString::null; - QString customKw3 = QString::null; + QString customKw1 = QString(); + QString customKw2 = QString(); + QString customKw3 = QString(); protected: TokenList rebuildTokensFromContents(); private: - TokenList rebuildTokensForSqlite2(); TokenList rebuildTokensForSqlite3(); }; @@ -185,8 +185,11 @@ class API_EXPORT SqliteSelect : public SqliteQuery Core(); Core(const Core& other); Core(int distinct, const QList<ResultColumn*>& resCols, JoinSource* src, SqliteExpr* where, - const QList<SqliteExpr*>& groupBy, SqliteExpr* having, const QList<SqliteOrderBy*>& orderBy, - SqliteLimit* limit); + const QList<SqliteExpr*>& groupBy, SqliteExpr* having, + const QList<SqliteOrderBy*>& orderBy, SqliteLimit* limit); + Core(int distinct, const QList<ResultColumn*>& resCols, JoinSource* src, SqliteExpr* where, + const QList<SqliteExpr*>& groupBy, SqliteExpr* having, const QList<SqliteWindowDefinition*> windows, + const QList<SqliteOrderBy*>& orderBy, SqliteLimit* limit); SqliteStatement* clone(); @@ -199,6 +202,7 @@ class API_EXPORT SqliteSelect : public SqliteQuery SqliteExpr* having = nullptr; QList<SqliteExpr*> groupBy; QList<SqliteOrderBy*> orderBy; + QList<SqliteWindowDefinition*> windows; SqliteLimit* limit = nullptr; bool valuesMode = false; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesortorder.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesortorder.cpp index 13f3951..eb0ca5c 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesortorder.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesortorder.cpp @@ -19,7 +19,7 @@ QString sqliteSortOrder(SqliteSortOrder value) case SqliteSortOrder::DESC: return "DESC"; default: - return QString::null; + return QString(); } } diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.cpp index 119461a..d349a86 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.cpp @@ -9,7 +9,7 @@ SqliteStatement::SqliteStatement() } SqliteStatement::SqliteStatement(const SqliteStatement& other) : - QObject(), tokens(other.tokens), tokensMap(other.tokensMap), dialect(other.dialect) + QObject(), tokens(other.tokens), tokensMap(other.tokensMap) { } @@ -87,13 +87,6 @@ QList<SqliteStatement::FullObject> SqliteStatement::getContextFullObjects(bool c return fullObjects; } -void SqliteStatement::setSqliteDialect(Dialect dialect) -{ - this->dialect = dialect; - for (SqliteStatement* stmt : childStatements()) - stmt->setSqliteDialect(dialect); -} - SqliteStatementPtr SqliteStatement::detach() { if (!parent()) @@ -537,14 +530,6 @@ void SqliteStatement::rebuildTokens() // and then compare new tokens map with previous one. This way we should be able to get all maps correctly. } -void SqliteStatement::setParent(QObject* parent) -{ - QObject::setParent(parent); - SqliteStatement* stmt = qobject_cast<SqliteStatement*>(parent); - if (stmt) - dialect = stmt->dialect; -} - void SqliteStatement::attach(SqliteStatement*& memberForChild, SqliteStatement* childStatementToAttach) { memberForChild = childStatementToAttach; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.h index 779bea3..43a60ba 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.h @@ -3,7 +3,6 @@ #include "common/utils.h" #include "parser/token.h" -#include "dialect.h" #include <QList> #include <QHash> #include <QObject> @@ -50,7 +49,7 @@ typedef QSharedPointer<SqliteStatement> SqliteStatementPtr; * * There is also SqliteStatement::tokensMap, which is a table mapping grammar rule name * into tokens used to fulfill that rule. To learn possible keys for each SqliteStatement, - * you have to look into sqlite2.y and sqlite3.y files and see definition of the statement, + * you have to look into sqlite3.y file and see definition of the statement, * that you're examining SqliteStatement::tokensMap for. * * @note SqliteStatement::tokensMap is a low level API and it's not very predictible, @@ -89,7 +88,7 @@ typedef QSharedPointer<SqliteStatement> SqliteStatementPtr; * This is how it's usually done: * @code * // STEP 1 - * Parser parser(db->getDialect()); + * Parser parser; * if (!parser.parse("SELECT column FROM test WHERE value = 5") || parser.getQueries().size() == 0) * { * // handle parsing error, or no queries parsed (which is also some kind of error) @@ -152,7 +151,7 @@ typedef QSharedPointer<SqliteStatement> SqliteStatementPtr; * * Example: * @code - * Parser parser(db->getDialect()); + * Parser parser; * if (!parser.parse("SELECT column FROM test WHERE value = 5") || parser.getQueries().size() == 0) * { * // handle parsing error, or no queries parsed (which is also some kind of error) @@ -222,9 +221,7 @@ class API_EXPORT SqliteStatement : public QObject TokenList getContextTableTokens(bool checkParent = true, bool checkChilds = true); TokenList getContextDatabaseTokens(bool checkParent = true, bool checkChilds = true); QList<FullObject> getContextFullObjects(bool checkParent = true, bool checkChilds = true); - void setSqliteDialect(Dialect dialect); void rebuildTokens(); - void setParent(QObject* parent); void attach(SqliteStatement*& memberForChild, SqliteStatement* childStatementToAttach); SqliteStatementPtr detach(); void processPostParsing(); @@ -284,8 +281,6 @@ class API_EXPORT SqliteStatement : public QObject */ QHash<QString,TokenList> tokensMap; - Dialect dialect = Dialect::Sqlite3; - protected: QStringList getContextColumns(SqliteStatement* caller, bool checkParent, bool checkChilds); QStringList getContextTables(SqliteStatement* caller, bool checkParent, bool checkChilds); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.cpp index 2063e0e..89c8195 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.cpp @@ -26,6 +26,7 @@ SqliteUpdate::SqliteUpdate(const SqliteUpdate& other) : DEEP_COPY_FIELD(SqliteExpr, where); DEEP_COPY_FIELD(SqliteWith, with); + DEEP_COPY_FIELD(SqliteSelect::Core::JoinSource, from); } SqliteUpdate::~SqliteUpdate() @@ -33,7 +34,7 @@ SqliteUpdate::~SqliteUpdate() } SqliteUpdate::SqliteUpdate(SqliteConflictAlgo onConflict, const QString &name1, const QString &name2, bool notIndexedKw, const QString &indexedBy, - const QList<ColumnAndValue>& values, SqliteExpr *where, SqliteWith* with) + const QList<ColumnAndValue>& values, SqliteSelect::Core::JoinSource* from, SqliteExpr *where, SqliteWith* with) : SqliteUpdate() { this->onConflict = onConflict; @@ -51,6 +52,10 @@ SqliteUpdate::SqliteUpdate(SqliteConflictAlgo onConflict, const QString &name1, this->notIndexedKw = notIndexedKw; keyValueMap = values; + this->from = from; + if (from) + from->setParent(this); + this->where = where; if (where) where->setParent(this); @@ -188,12 +193,12 @@ TokenList SqliteUpdate::rebuildTokensFromContents() builder.withKeyword("OR").withSpace().withKeyword(sqliteConflictAlgo(onConflict)).withSpace(); if (!database.isNull()) - builder.withOther(database, dialect).withOperator("."); + builder.withOther(database).withOperator("."); - builder.withOther(table, dialect).withSpace(); + builder.withOther(table).withSpace(); if (indexedByKw) - builder.withKeyword("INDEXED").withSpace().withKeyword("BY").withSpace().withOther(indexedBy, dialect).withSpace(); + builder.withKeyword("INDEXED").withSpace().withKeyword("BY").withSpace().withOther(indexedBy).withSpace(); else if (notIndexedKw) builder.withKeyword("NOT").withSpace().withKeyword("INDEXED").withSpace(); @@ -206,14 +211,17 @@ TokenList SqliteUpdate::rebuildTokensFromContents() builder.withOperator(",").withSpace(); if (keyVal.first.type() == QVariant::StringList) - builder.withParLeft().withOtherList(keyVal.first.toStringList(), dialect).withParRight(); + builder.withParLeft().withOtherList(keyVal.first.toStringList()).withParRight(); else - builder.withOther(keyVal.first.toString(), dialect); + builder.withOther(keyVal.first.toString()); builder.withSpace().withOperator("=").withStatement(keyVal.second); first = false; } + if (from) + builder.withSpace().withKeyword("FROM").withStatement(from); + if (where) builder.withSpace().withKeyword("WHERE").withStatement(where); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.h index 642473c..e843bcd 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.h @@ -3,6 +3,7 @@ #include "sqlitequery.h" #include "sqliteconflictalgo.h" +#include "sqliteselect.h" #include <QStringList> #include <QMap> @@ -20,18 +21,19 @@ class API_EXPORT SqliteUpdate : public SqliteQuery ~SqliteUpdate(); SqliteUpdate(SqliteConflictAlgo onConflict, const QString& name1, const QString& name2, bool notIndexedKw, const QString& indexedBy, const QList<ColumnAndValue>& values, - SqliteExpr* where, SqliteWith* with); + SqliteSelect::Core::JoinSource* from, SqliteExpr* where, SqliteWith* with); SqliteStatement* clone(); SqliteExpr* getValueForColumnSet(const QString& column); SqliteConflictAlgo onConflict = SqliteConflictAlgo::null; - QString database = QString::null; - QString table = QString::null; + QString database = QString(); + QString table = QString(); bool indexedByKw = false; bool notIndexedKw = false; - QString indexedBy = QString::null; + QString indexedBy = QString(); QList<ColumnAndValue> keyValueMap; + SqliteSelect::Core::JoinSource* from = nullptr; SqliteExpr* where = nullptr; SqliteWith* with = nullptr; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupsert.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupsert.cpp index ced9c2d..441cd6d 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupsert.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupsert.cpp @@ -94,9 +94,9 @@ TokenList SqliteUpsert::rebuildTokensFromContents() builder.withOperator(",").withSpace(); if (keyVal.first.type() == QVariant::StringList) - builder.withParLeft().withOtherList(keyVal.first.toStringList(), dialect).withParRight(); + builder.withParLeft().withOtherList(keyVal.first.toStringList()).withParRight(); else - builder.withOther(keyVal.first.toString(), dialect); + builder.withOther(keyVal.first.toString()); builder.withSpace().withOperator("=").withStatement(keyVal.second); first = false; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitevacuum.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitevacuum.cpp index 9d595ed..e65530a 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitevacuum.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitevacuum.cpp @@ -1,7 +1,8 @@ #include "sqlitevacuum.h" #include "sqlitequerytype.h" - -#include <parser/statementtokenbuilder.h> +#include "common/global.h" +#include "parser/statementtokenbuilder.h" +#include "sqliteexpr.h" SqliteVacuum::SqliteVacuum() { @@ -11,13 +12,26 @@ SqliteVacuum::SqliteVacuum() SqliteVacuum::SqliteVacuum(const SqliteVacuum& other) : SqliteQuery(other), database(other.database) { + DEEP_COPY_FIELD(SqliteExpr, expr); +} + +SqliteVacuum::SqliteVacuum(SqliteExpr* expr) + : SqliteVacuum() +{ + this->expr = expr; + if (expr) + expr->setParent(this); } -SqliteVacuum::SqliteVacuum(const QString& name) +SqliteVacuum::SqliteVacuum(const QString& name, SqliteExpr* expr) : SqliteVacuum() { if (!name.isNull()) database = name; + + this->expr = expr; + if (expr) + expr->setParent(this); } SqliteStatement*SqliteVacuum::clone() @@ -55,6 +69,13 @@ TokenList SqliteVacuum::rebuildTokensFromContents() { StatementTokenBuilder builder; builder.withTokens(SqliteQuery::rebuildTokensFromContents()); - builder.withKeyword("VACUUM").withOperator(";"); + builder.withKeyword("VACUUM"); + if (!database.isNull()) + builder.withSpace().withOther(database); + + if (expr) + builder.withSpace().withKeyword("INTO").withSpace().withStatement(expr); + + builder.withOperator(";"); return builder.build(); } diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitevacuum.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitevacuum.h index 871b8f4..db9af95 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitevacuum.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitevacuum.h @@ -5,16 +5,20 @@ #include <QString> +class SqliteExpr; + class API_EXPORT SqliteVacuum : public SqliteQuery { public: SqliteVacuum(); SqliteVacuum(const SqliteVacuum& other); - explicit SqliteVacuum(const QString &name); + SqliteVacuum(SqliteExpr* expr); + SqliteVacuum(const QString &name, SqliteExpr* expr); SqliteStatement* clone(); QString database; + SqliteExpr* expr = nullptr; protected: QStringList getDatabasesInStatement(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewindowdefinition.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewindowdefinition.cpp new file mode 100644 index 0000000..adf135f --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewindowdefinition.cpp @@ -0,0 +1,308 @@ +#include "sqlitewindowdefinition.h" +#include "sqliteexpr.h" +#include "sqliteorderby.h" +#include "parser/statementtokenbuilder.h" +#include "common/global.h" +#include <QDebug> + +SqliteWindowDefinition::SqliteWindowDefinition() +{ + +} + +SqliteWindowDefinition::SqliteWindowDefinition(const SqliteWindowDefinition& other) : + SqliteStatement(other), name(other.name) +{ + DEEP_COPY_FIELD(Window, window); +} + +SqliteWindowDefinition::SqliteWindowDefinition(const QString& name, SqliteWindowDefinition::Window* window) +{ + this->name = name; + this->window = window; + if (window) + window->setParent(this); +} + +SqliteStatement* SqliteWindowDefinition::clone() +{ + return new SqliteWindowDefinition(*this); +} + +TokenList SqliteWindowDefinition::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withOther(name).withSpace().withKeyword("AS").withParLeft().withStatement(window).withParRight(); + + return builder.build(); +} + +SqliteWindowDefinition::Window::Window() +{ +} + +SqliteWindowDefinition::Window::Window(const SqliteWindowDefinition::Window& other) : + SqliteStatement(other), name(other.name), mode(other.mode) +{ + DEEP_COPY_COLLECTION(SqliteExpr, exprList); + DEEP_COPY_COLLECTION(SqliteOrderBy, orderBy); + DEEP_COPY_FIELD(Frame, frame); +} + +SqliteStatement* SqliteWindowDefinition::Window::clone() +{ + return new Window(*this); +} + +void SqliteWindowDefinition::Window::initPartitionBy(const QString& name, const QList<SqliteExpr*>& exprList, const QList<SqliteOrderBy*>& orderBy, SqliteWindowDefinition::Window::Frame* frame) +{ + this->mode = Mode::PARTITION_BY; + this->name = name; + initExprList(exprList); + initOrderBy(orderBy); + initFrame(frame); +} + +void SqliteWindowDefinition::Window::initOrderBy(const QString& name, const QList<SqliteOrderBy*>& orderBy, SqliteWindowDefinition::Window::Frame* frame) +{ + this->mode = Mode::ORDER_BY; + this->name = name; + initOrderBy(orderBy); + initFrame(frame); +} + +void SqliteWindowDefinition::Window::init(const QString& name, SqliteWindowDefinition::Window::Frame* frame) +{ + this->mode = Mode::null; + this->name = name; + initFrame(frame); +} + +TokenList SqliteWindowDefinition::Window::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + if (!name.isNull()) + builder.withOther(name).withSpace(); + + switch (mode) + { + case SqliteWindowDefinition::Window::Mode::PARTITION_BY: + builder.withKeyword("PARTITION").withSpace().withKeyword("BY").withSpace().withStatementList(exprList).withSpace(); + break; + case SqliteWindowDefinition::Window::Mode::ORDER_BY: + break; + case SqliteWindowDefinition::Window::Mode::null: + break; + } + + if (orderBy.size() > 0) + builder.withKeyword("ORDER").withSpace().withKeyword("BY").withSpace().withStatementList(orderBy); + + if (frame) + builder.withStatement(frame); + + return builder.build(); +} + +void SqliteWindowDefinition::Window::initExprList(const QList<SqliteExpr*>& exprList) +{ + this->exprList = exprList; + for (SqliteExpr* expr : exprList) + expr->setParent(this); +} + +void SqliteWindowDefinition::Window::initOrderBy(const QList<SqliteOrderBy*>& orderBy) +{ + this->orderBy = orderBy; + for (SqliteOrderBy* order : orderBy) + order->setParent(this); +} + +void SqliteWindowDefinition::Window::initFrame(SqliteWindowDefinition::Window::Frame* frame) +{ + this->frame = frame; + if (frame) + frame->setParent(this); +} + +SqliteWindowDefinition::Window::Frame::RangeOrRows SqliteWindowDefinition::Window::Frame::toRangeOrRows(const QString& value) +{ + QString upper = value.toUpper(); + if (upper == "RANGE") + return RangeOrRows::RANGE; + else if (upper == "ROWS") + return RangeOrRows::ROWS; + else if (upper == "GROUPS") + return RangeOrRows::GROUPS; + else + return RangeOrRows::null; +} + +QString SqliteWindowDefinition::Window::Frame::fromRangeOrRows(SqliteWindowDefinition::Window::Frame::RangeOrRows value) +{ + switch (value) + { + case SqliteWindowDefinition::Window::Frame::RangeOrRows::RANGE: + return "RANGE"; + case SqliteWindowDefinition::Window::Frame::RangeOrRows::ROWS: + return "ROWS"; + case SqliteWindowDefinition::Window::Frame::RangeOrRows::GROUPS: + return "GROUPS"; + case SqliteWindowDefinition::Window::Frame::RangeOrRows::null: + break; + } + return QString(); +} + +SqliteWindowDefinition::Window::Frame::Exclude SqliteWindowDefinition::Window::Frame::toExclude(const QString& value) +{ + QString upper = value.toUpper(); + if (upper == "NO OTHERS") + return Exclude::NO_OTHERS; + else if (upper == "CURRENT ROW") + return Exclude::CURRENT_ROW; + else if (upper == "GROUP") + return Exclude::GROUP; + else if (upper == "TIES") + return Exclude::TIES; + else + return Exclude::null; +} + +QString SqliteWindowDefinition::Window::Frame::fromExclude(SqliteWindowDefinition::Window::Frame::Exclude value) +{ + switch (value) + { + case SqliteWindowDefinition::Window::Frame::Exclude::TIES: + return "TIES"; + case SqliteWindowDefinition::Window::Frame::Exclude::NO_OTHERS: + return "NO OTHERS"; + case SqliteWindowDefinition::Window::Frame::Exclude::CURRENT_ROW: + return "CURRENT ROW"; + case SqliteWindowDefinition::Window::Frame::Exclude::GROUP: + return "GROUP"; + case SqliteWindowDefinition::Window::Frame::Exclude::null: + break; + } + return QString(); +} + +SqliteWindowDefinition::Window::Frame::Frame() +{ +} + +SqliteWindowDefinition::Window::Frame::Frame(const SqliteWindowDefinition::Window::Frame& other) : + SqliteStatement(other), rangeOrRows(other.rangeOrRows), exclude(other.exclude) +{ + DEEP_COPY_FIELD(Bound, startBound); + DEEP_COPY_FIELD(Bound, endBound); +} + +SqliteWindowDefinition::Window::Frame::Frame(SqliteWindowDefinition::Window::Frame::RangeOrRows rangeOrRows, + SqliteWindowDefinition::Window::Frame::Bound* startBound, + SqliteWindowDefinition::Window::Frame::Bound* endBound, + SqliteWindowDefinition::Window::Frame::Exclude exclude) +{ + this->rangeOrRows = rangeOrRows; + this->startBound = startBound; + this->endBound = endBound; + this->exclude = exclude; + + if (startBound) + startBound->setParent(this); + + if (endBound) + endBound->setParent(this); +} + +SqliteStatement* SqliteWindowDefinition::Window::Frame::clone() +{ + return new Frame(*this); +} + +TokenList SqliteWindowDefinition::Window::Frame::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + if (rangeOrRows != RangeOrRows::null) + builder.withKeyword(fromRangeOrRows(rangeOrRows)).withSpace(); + + if (endBound) + builder.withKeyword("BETWEEN").withSpace().withStatement(startBound).withSpace() + .withKeyword("AND").withSpace().withStatement(endBound); + else + builder.withStatement(startBound); + + if (exclude != Exclude::null) + { + builder.withSpace().withKeyword("EXCLUDE"); + for (const QString& kw : fromExclude(exclude).split(" ")) + builder.withSpace().withKeyword(kw); + } + + return builder.build(); +} + +SqliteWindowDefinition::Window::Frame::Bound::Bound() +{ +} + +SqliteWindowDefinition::Window::Frame::Bound::Bound(const SqliteWindowDefinition::Window::Frame::Bound& other) : + SqliteStatement(other), type(other.type) +{ + DEEP_COPY_FIELD(SqliteExpr, expr); +} + +SqliteWindowDefinition::Window::Frame::Bound::Bound(SqliteExpr* expr, const QString& value) +{ + this->expr = expr; + if (expr) + expr->setParent(this); + + QString upper = value.toUpper(); + if (upper == "UNBOUNDED PRECEDING") + type = Type::UNBOUNDED_PRECEDING; + else if (expr && upper == "PRECEDING") + type = Type::EXPR_FOLLOWING; + else if (upper == "UNBOUNDED FOLLOWING") + type = Type::UNBOUNDED_FOLLOWING; + else if (expr && upper == "FOLLOWING") + type = Type::EXPR_FOLLOWING; + else if (upper == "CURRENT ROW") + type = Type::CURRENT_ROW; + else + qCritical() << "Unexpected Window Frame Bound:" << value; +} + +SqliteStatement* SqliteWindowDefinition::Window::Frame::Bound::clone() +{ + return new Bound(*this); +} + +TokenList SqliteWindowDefinition::Window::Frame::Bound::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + switch (type) + { + case SqliteWindowDefinition::Window::Frame::Bound::Type::UNBOUNDED_PRECEDING: + builder.withKeyword("UNBOUNDED").withSpace().withKeyword("PRECEDING"); + break; + case SqliteWindowDefinition::Window::Frame::Bound::Type::UNBOUNDED_FOLLOWING: + builder.withKeyword("UNBOUNDED").withSpace().withKeyword("FOLLOWING"); + break; + case SqliteWindowDefinition::Window::Frame::Bound::Type::EXPR_PRECEDING: + builder.withStatement(expr).withSpace().withKeyword("PRECEDING"); + break; + case SqliteWindowDefinition::Window::Frame::Bound::Type::EXPR_FOLLOWING: + builder.withStatement(expr).withSpace().withKeyword("FOLLOWING"); + break; + case SqliteWindowDefinition::Window::Frame::Bound::Type::CURRENT_ROW: + builder.withKeyword("CURRENT").withSpace().withKeyword("ROW"); + break; + } + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewindowdefinition.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewindowdefinition.h new file mode 100644 index 0000000..fb5cb8e --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewindowdefinition.h @@ -0,0 +1,129 @@ +#ifndef SQLITEWINDOWDEFINITION_H +#define SQLITEWINDOWDEFINITION_H + +//#include "sqliteexpr.h" +//#include "sqliteorderby.h" +#include "sqlitestatement.h" + +class SqliteExpr; +class SqliteOrderBy; + +/** + * @addtogroup sqlite_statement + * @brief The SqliteWindowDefinition class + */ +class API_EXPORT SqliteWindowDefinition : public SqliteStatement +{ + public: + class API_EXPORT Window : public SqliteStatement + { + public: + class API_EXPORT Frame : public SqliteStatement + { + public: + class API_EXPORT Bound : public SqliteStatement + { + public: + enum class Type + { + UNBOUNDED_PRECEDING, + UNBOUNDED_FOLLOWING, + EXPR_PRECEDING, + EXPR_FOLLOWING, + CURRENT_ROW + }; + + Bound(); + Bound(const Bound& other); + Bound(SqliteExpr* expr, const QString& value); + + SqliteStatement* clone(); + + Type type = Type::CURRENT_ROW; + SqliteExpr* expr = nullptr; + + protected: + TokenList rebuildTokensFromContents(); + }; + + enum class RangeOrRows + { + RANGE, + ROWS, + GROUPS, + null + }; + + enum class Exclude + { + NO_OTHERS, + CURRENT_ROW, + GROUP, + TIES, + null + }; + + static RangeOrRows toRangeOrRows(const QString& value); + static QString fromRangeOrRows(RangeOrRows value); + static Exclude toExclude(const QString& value); + static QString fromExclude(Exclude value); + + Frame(); + Frame(const Frame& other); + Frame(RangeOrRows rangeOrRows, Bound* startBound, Bound* endBound, Exclude exclude); + + SqliteStatement* clone(); + + RangeOrRows rangeOrRows = RangeOrRows::null; + Exclude exclude = Exclude::null; + Bound* startBound = nullptr; + Bound* endBound = nullptr; + + protected: + TokenList rebuildTokensFromContents(); + }; + + enum class Mode + { + PARTITION_BY, + ORDER_BY, + null + }; + + Window(); + Window(const Window& other); + + SqliteStatement* clone(); + void initPartitionBy(const QString& name, const QList<SqliteExpr*>& exprList, const QList<SqliteOrderBy*>& orderBy, Frame* frame); + void initOrderBy(const QString& name, const QList<SqliteOrderBy*>& orderBy, Frame* frame); + void init(const QString& name, Frame* frame); + + QString name; + QList<SqliteExpr*> exprList; + QList<SqliteOrderBy*> orderBy; + Frame* frame = nullptr; + Mode mode = Mode::null; + + protected: + TokenList rebuildTokensFromContents(); + + private: + void initExprList(const QList<SqliteExpr*>& exprList); + void initOrderBy(const QList<SqliteOrderBy*>& orderBy); + void initFrame(Frame* frame); + }; + + SqliteWindowDefinition(); + SqliteWindowDefinition(const SqliteWindowDefinition& other); + SqliteWindowDefinition(const QString& name, Window* window); + + SqliteStatement* clone(); + + QString name = QString(); + Window* window = nullptr; + + protected: + TokenList rebuildTokensFromContents(); +}; + +#endif // SQLITEWINDOWDEFINITION_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.cpp index 2b9c99f..901ac56 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.cpp @@ -13,26 +13,6 @@ SqliteWith::SqliteWith(const SqliteWith& other) : DEEP_COPY_COLLECTION(CommonTableExpression, cteList); } -SqliteWith* SqliteWith::append(const QString& tableName, const QList<SqliteIndexedColumn*>& indexedColumns, SqliteSelect* select) -{ - SqliteWith* with = new SqliteWith(); - CommonTableExpression* cte = new CommonTableExpression(tableName, indexedColumns, select); - cte->setParent(with); - with->cteList << cte; - return with; -} - -SqliteWith* SqliteWith::append(SqliteWith* with, const QString& tableName, const QList<SqliteIndexedColumn*>& indexedColumns, SqliteSelect* select) -{ - if (!with) - with = new SqliteWith(); - - CommonTableExpression* cte = new CommonTableExpression(tableName, indexedColumns, select); - cte->setParent(with); - with->cteList << cte; - return with; -} - SqliteStatement*SqliteWith::clone() { return new SqliteWith(*this); @@ -76,7 +56,7 @@ SqliteStatement*SqliteWith::CommonTableExpression::clone() TokenList SqliteWith::CommonTableExpression::rebuildTokensFromContents() { StatementTokenBuilder builder; - builder.withOther(table, dialect); + builder.withOther(table); if (indexedColumns.size() > 0) builder.withSpace().withParLeft().withStatementList(indexedColumns).withParRight(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.h index fe64c9c..30f8181 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.h @@ -28,8 +28,6 @@ class SqliteWith : public SqliteStatement SqliteWith(); SqliteWith(const SqliteWith& other); - static SqliteWith* append(const QString& tableName, const QList<SqliteIndexedColumn*>& indexedColumns, SqliteSelect* select); - static SqliteWith* append(SqliteWith* with, const QString& tableName, const QList<SqliteIndexedColumn*>& indexedColumns, SqliteSelect* select); SqliteStatement* clone(); |
