diff options
| author | 2014-12-06 17:33:25 -0500 | |
|---|---|---|
| committer | 2014-12-06 17:33:25 -0500 | |
| commit | 7167ce41b61d2ba2cdb526777a4233eb84a3b66a (patch) | |
| tree | a35c14143716e1f2c98f808c81f89426045a946f /SQLiteStudio3/coreSQLiteStudio/parser/ast | |
Imported Upstream version 2.99.6upstream/2.99.6
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/parser/ast')
83 files changed, 8476 insertions, 0 deletions
diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.cpp new file mode 100644 index 0000000..6c840e7 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.cpp @@ -0,0 +1,128 @@ +#include "sqlitealtertable.h" +#include "sqlitequerytype.h" +#include "common/global.h" + +SqliteAlterTable::SqliteAlterTable() +{ + queryType = SqliteQueryType::AlterTable; +} + +SqliteAlterTable::SqliteAlterTable(const SqliteAlterTable& other) + : SqliteQuery(other), command(other.command), newName(other.newName), database(other.database), table(other.table), columnKw(other.columnKw) +{ + DEEP_COPY_FIELD(SqliteCreateTable::Column, newColumn); +} + +SqliteAlterTable::SqliteAlterTable(const QString &name1, const QString &name2, const QString &newName) + : SqliteAlterTable() +{ + command = Command::RENAME; + initName(name1, name2); + this->newName = newName; +} + +SqliteAlterTable::SqliteAlterTable(const QString& name1, const QString& name2, bool columnKw, SqliteCreateTable::Column *column) + : SqliteAlterTable() +{ + command = Command::ADD_COLUMN; + initName(name1, name2); + this->columnKw = columnKw; + this->newColumn = column; + if (column) + column->setParent(this); +} + +SqliteAlterTable::~SqliteAlterTable() +{ +// if (newColumn) + // delete newColumn; +} + +SqliteStatement* SqliteAlterTable::clone() +{ + return new SqliteAlterTable(*this); +} + +QStringList SqliteAlterTable::getTablesInStatement() +{ + QStringList list; + if (!table.isNull()) + list << table; + + if (!newName.isNull()) + list << newName; + + return list; +} + +QStringList SqliteAlterTable::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteAlterTable::getTableTokensInStatement() +{ + return getObjectTokenListFromFullname(); +} + +TokenList SqliteAlterTable::getDatabaseTokensInStatement() +{ + return getDbTokenListFromFullname(); +} + +QList<SqliteStatement::FullObject> SqliteAlterTable::getFullObjectsInStatement() +{ + QList<FullObject> result; + + FullObject fullObj = getFullObjectFromFullname(FullObject::TABLE); + if (fullObj.isValid()) + result << fullObj; + + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + { + result << fullObj; + dbTokenForFullObjects = fullObj.database; + } + + return result; +} + +void SqliteAlterTable::initName(const QString &name1, const QString &name2) +{ + if (!name2.isNull()) + { + database = name1; + table = name2; + } + else + table = name1; +} + +TokenList SqliteAlterTable::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("ALTER").withSpace().withKeyword("TABLE").withSpace(); + + if (!database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(table).withSpace(); + + if (newColumn) + { + builder.withKeyword("ADD").withSpace(); + if (columnKw) + builder.withKeyword("COLUMN").withSpace(); + + builder.withStatement(newColumn); + } + else if (!newName.isNull()) + { + builder.withKeyword("RENAME").withSpace().withKeyword("TO").withSpace().withOther(newName, dialect); + } + + builder.withOperator(";"); + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.h new file mode 100644 index 0000000..360db45 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.h @@ -0,0 +1,46 @@ +#ifndef SQLITEALTERTABLE_H +#define SQLITEALTERTABLE_H + +#include "sqlitequery.h" +#include "sqlitecreatetable.h" + +class API_EXPORT SqliteAlterTable : public SqliteQuery +{ + public: + enum class Command + { + RENAME, + ADD_COLUMN, + null + }; + + SqliteAlterTable(); + SqliteAlterTable(const SqliteAlterTable& other); + SqliteAlterTable(const QString& name1, const QString& name2, const QString& newName); + SqliteAlterTable(const QString& name1, const QString& name2, bool columnKw, SqliteCreateTable::Column* column); + ~SqliteAlterTable(); + SqliteStatement* clone(); + + protected: + QStringList getTablesInStatement(); + QStringList getDatabasesInStatement(); + TokenList getTableTokensInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); + + private: + void initName(const QString& name1, const QString& name2); + + public: + Command command = Command::null; + QString newName = QString::null; + QString database = QString::null; + QString table = QString::null; + bool columnKw = false; + SqliteCreateTable::Column* newColumn = nullptr; +}; + +typedef QSharedPointer<SqliteAlterTable> SqliteAlterTablePtr; + +#endif // SQLITEALTERTABLE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteanalyze.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteanalyze.cpp new file mode 100644 index 0000000..d4e5778 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteanalyze.cpp @@ -0,0 +1,80 @@ +#include "sqliteanalyze.h" +#include "sqlitequerytype.h" + +#include <parser/statementtokenbuilder.h> + +SqliteAnalyze::SqliteAnalyze() +{ + queryType = SqliteQueryType::Analyze; +} + +SqliteAnalyze::SqliteAnalyze(const SqliteAnalyze& other) : + SqliteQuery(other), database(other.database), table(other.table) +{ +} + +SqliteAnalyze::SqliteAnalyze(const QString &name1, const QString &name2) + : SqliteAnalyze() +{ + if (!name2.isNull()) + { + database = name1; + table = name2; + } + else + table = name1; +} + +SqliteStatement* SqliteAnalyze::clone() +{ + return new SqliteAnalyze(*this); +} + +QStringList SqliteAnalyze::getTablesInStatement() +{ + return getStrListFromValue(table); +} + +QStringList SqliteAnalyze::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteAnalyze::getTableTokensInStatement() +{ + return getObjectTokenListFromNmDbnm(); +} + +TokenList SqliteAnalyze::getDatabaseTokensInStatement() +{ + return getDbTokenListFromNmDbnm(); +} + +QList<SqliteStatement::FullObject> SqliteAnalyze::getFullObjectsInStatement() +{ + QList<FullObject> result; + + FullObject fullObj = getFullObjectFromNmDbnm(FullObject::TABLE); + if (fullObj.isValid()) + result << fullObj; + + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + result << fullObj; + + return result; +} + + +TokenList SqliteAnalyze::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("ANALYZE").withSpace(); + + if (!database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(table).withOperator(";"); + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteanalyze.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteanalyze.h new file mode 100644 index 0000000..194e4c9 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteanalyze.h @@ -0,0 +1,29 @@ +#ifndef SQLITEANALYZE_H +#define SQLITEANALYZE_H + +#include "sqlitequery.h" +#include <QString> + +class API_EXPORT SqliteAnalyze : public SqliteQuery +{ + public: + SqliteAnalyze(); + SqliteAnalyze(const SqliteAnalyze& other); + SqliteAnalyze(const QString& name1, const QString& name2); + SqliteStatement* clone(); + + QString database = QString::null; + QString table = QString::null; + + protected: + QStringList getTablesInStatement(); + QStringList getDatabasesInStatement(); + TokenList getTableTokensInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteAnalyze> SqliteAnalyzePtr; + +#endif // SQLITEANALYZE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteattach.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteattach.cpp new file mode 100644 index 0000000..7d0b8a5 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteattach.cpp @@ -0,0 +1,63 @@ +#include "sqliteattach.h" +#include "sqlitequerytype.h" +#include "sqliteexpr.h" +#include "parser/statementtokenbuilder.h" +#include "common/global.h" + +SqliteAttach::SqliteAttach() +{ + queryType = SqliteQueryType::Attach; +} + +SqliteAttach::SqliteAttach(bool dbKw, SqliteExpr *url, SqliteExpr *name, SqliteExpr *key) + : SqliteAttach() +{ + databaseKw = dbKw; + databaseUrl = url; + this->name = name; + this->key = key; + + if (databaseUrl) + databaseUrl->setParent(this); + + if (name) + name->setParent(this); + + if (key) + key->setParent(this); +} + +SqliteAttach::SqliteAttach(const SqliteAttach& other) : + SqliteQuery(other), databaseKw(other.databaseKw) +{ + DEEP_COPY_FIELD(SqliteExpr, databaseUrl); + DEEP_COPY_FIELD(SqliteExpr, name); + DEEP_COPY_FIELD(SqliteExpr, key); +} + +SqliteAttach::~SqliteAttach() +{ +} + +SqliteStatement* SqliteAttach::clone() +{ + return new SqliteAttach(*this); +} + +TokenList SqliteAttach::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("ATTACH").withSpace(); + + if (databaseKw) + builder.withKeyword("DATABASE").withSpace(); + + builder.withStatement(databaseUrl).withSpace().withKeyword("AS").withSpace().withStatement(name); + if (key) + builder.withSpace().withKeyword("KEY").withSpace().withStatement(key); + + builder.withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteattach.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteattach.h new file mode 100644 index 0000000..55151a5 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteattach.h @@ -0,0 +1,28 @@ +#ifndef SQLITEATTACHDATABASE_H +#define SQLITEATTACHDATABASE_H + +#include "sqlitequery.h" + +class SqliteExpr; + +class API_EXPORT SqliteAttach : public SqliteQuery +{ + public: + SqliteAttach(); + SqliteAttach(const SqliteAttach& other); + SqliteAttach(bool dbKw, SqliteExpr* url, SqliteExpr* name, SqliteExpr* key); + ~SqliteAttach(); + SqliteStatement* clone(); + + bool databaseKw = false; + SqliteExpr* databaseUrl = nullptr; + SqliteExpr* name = nullptr; + SqliteExpr* key = nullptr; + + protected: + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteAttach> SqliteAttachPtr; + +#endif // SQLITEATTACHDATABASE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitebegintrans.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitebegintrans.cpp new file mode 100644 index 0000000..dcd9740 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitebegintrans.cpp @@ -0,0 +1,71 @@ +#include "sqlitebegintrans.h" +#include "sqlitequerytype.h" + +#include <parser/statementtokenbuilder.h> + +SqliteBeginTrans::SqliteBeginTrans() +{ + queryType = SqliteQueryType::BeginTrans; +} + +SqliteBeginTrans::SqliteBeginTrans(const SqliteBeginTrans& other) : + SqliteQuery(other), onConflict(other.onConflict), name(other.name), transactionKw(other.transactionKw), type(other.type) +{ +} + +SqliteBeginTrans::SqliteBeginTrans(SqliteBeginTrans::Type type, bool transactionKw, const QString& name) + : SqliteBeginTrans() +{ + this->type = type; + this->transactionKw = transactionKw; + this->name = name; +} + +SqliteBeginTrans::SqliteBeginTrans(bool transactionKw, const QString &name, SqliteConflictAlgo onConflict) +{ + this->onConflict = onConflict; + this->transactionKw = transactionKw; + this->name = name; +} + +SqliteStatement*SqliteBeginTrans::clone() +{ + return new SqliteBeginTrans(*this); +} + +QString SqliteBeginTrans::typeToString(SqliteBeginTrans::Type type) +{ + switch (type) + { + case Type::null: + return QString(); + case Type::DEFERRED: + return "DEFERRED"; + case Type::IMMEDIATE: + return "IMMEDIATE"; + case Type::EXCLUSIVE: + return "EXCLUSIVE"; + } + return QString(); +} + +TokenList SqliteBeginTrans::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("BEGIN"); + + if (type != Type::null) + builder.withSpace().withKeyword(typeToString(type)); + + if (transactionKw) + { + builder.withSpace().withKeyword("TRANSACTION"); + if (!name.isNull()) + builder.withSpace().withOther(name, dialect); + } + + builder.withConflict(onConflict).withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitebegintrans.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitebegintrans.h new file mode 100644 index 0000000..48f5b37 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitebegintrans.h @@ -0,0 +1,38 @@ +#ifndef SQLITEBEGINTRANS_H +#define SQLITEBEGINTRANS_H + +#include "sqlitequery.h" +#include "sqliteconflictalgo.h" +#include <QString> + +class API_EXPORT SqliteBeginTrans : public SqliteQuery +{ + public: + enum class Type + { + null, + DEFERRED, + IMMEDIATE, + EXCLUSIVE + }; + + SqliteBeginTrans(); + SqliteBeginTrans(const SqliteBeginTrans& other); + SqliteBeginTrans(Type type, bool transactionKw, const QString& name); + SqliteBeginTrans(bool transactionKw, const QString& name, SqliteConflictAlgo onConflict); + 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 + + static QString typeToString(Type type); + + protected: + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteBeginTrans> SqliteBeginTransPtr; + +#endif // SQLITEBEGINTRANS_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecolumntype.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecolumntype.cpp new file mode 100644 index 0000000..cc773bb --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecolumntype.cpp @@ -0,0 +1,88 @@ +#include "sqlitecolumntype.h" +#include "parser/statementtokenbuilder.h" +#include "common/utils_sql.h" + +SqliteColumnType::SqliteColumnType() +{ +} + +SqliteColumnType::SqliteColumnType(const SqliteColumnType& other) : + SqliteStatement(other), name(other.name), scale(other.scale), precision(other.precision) +{ +} + +SqliteColumnType::SqliteColumnType(const QString &name) +{ + this->name = name; +} + +SqliteColumnType::SqliteColumnType(const QString &name, const QVariant& scale) +{ + this->name = name; + this->scale = scale; +} + +SqliteColumnType::SqliteColumnType(const QString &name, const QVariant& scale, const QVariant& precision) +{ + this->name = name; + this->precision = precision; + this->scale = scale; +} + +SqliteStatement* SqliteColumnType::clone() +{ + return new SqliteColumnType(*this); +} + +bool SqliteColumnType::isPrecisionDouble() +{ + return !precision.isNull() && precision.toString().indexOf(".") > -1; +} + +bool SqliteColumnType::isScaleDouble() +{ + return !scale.isNull() && scale.toString().indexOf(".") > -1; +} + +TokenList SqliteColumnType::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + if (name.isEmpty()) + return TokenList(); + + builder.withOther(name); + + if (!scale.isNull()) + { + builder.withSpace().withParLeft(); + if (scale.userType() == QVariant::Int) + builder.withInteger(scale.toInt()); + else if (scale.userType() == QVariant::LongLong) + builder.withInteger(scale.toLongLong()); + else if (scale.userType() == QVariant::Double) + builder.withFloat(scale.toDouble()); + else + builder.withOther(scale.toString()); + + if (!precision.isNull()) + { + builder.withOperator(",").withSpace(); + if (precision.userType() == QVariant::Int) + builder.withInteger(precision.toInt()); + else if (precision.userType() == QVariant::LongLong) + builder.withInteger(precision.toLongLong()); + else if (precision.userType() == QVariant::Double) + builder.withFloat(precision.toDouble()); + else + builder.withOther(precision.toString()); + } + builder.withParRight(); + } + + return builder.build(); +} + +DataType SqliteColumnType::toDataType() const +{ + return DataType(name, scale, precision); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecolumntype.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecolumntype.h new file mode 100644 index 0000000..fc87b6b --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecolumntype.h @@ -0,0 +1,30 @@ +#ifndef SQLITECOLUMNTYPE_H +#define SQLITECOLUMNTYPE_H + +#include "sqlitestatement.h" +#include "datatype.h" +#include <QVariant> + +class API_EXPORT SqliteColumnType : public SqliteStatement +{ + public: + SqliteColumnType(); + SqliteColumnType(const SqliteColumnType& other); + explicit SqliteColumnType(const QString& name); + SqliteColumnType(const QString& name, const QVariant &scale); + SqliteColumnType(const QString& name, const QVariant &scale, const QVariant &precision); + SqliteStatement* clone(); + + bool isPrecisionDouble(); + bool isScaleDouble(); + TokenList rebuildTokensFromContents(); + DataType toDataType() const; + + QString name = QString::null; + QVariant scale = QVariant(); // first size number + QVariant precision = QVariant(); // second size number +}; + +typedef QSharedPointer<SqliteColumnType> SqliteColumnTypePtr; + +#endif // SQLITECOLUMNTYPE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecommittrans.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecommittrans.cpp new file mode 100644 index 0000000..97be8a9 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecommittrans.cpp @@ -0,0 +1,48 @@ +#include "sqlitecommittrans.h" +#include "sqlitequerytype.h" + +#include <parser/statementtokenbuilder.h> + +SqliteCommitTrans::SqliteCommitTrans() +{ + queryType = SqliteQueryType::CommitTrans; +} + +SqliteCommitTrans::SqliteCommitTrans(bool transactionKw, const QString& name, bool endKw) + : SqliteCommitTrans() +{ + this->endKw = endKw; + this->transactionKw = transactionKw; + this->name = name; +} + +SqliteCommitTrans::SqliteCommitTrans(const SqliteCommitTrans& other) : + SqliteQuery(other), endKw(other.endKw), name(other.name), transactionKw(other.transactionKw) +{ +} + +SqliteStatement* SqliteCommitTrans::clone() +{ + return new SqliteCommitTrans(*this); +} + +TokenList SqliteCommitTrans::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + if (endKw) + builder.withKeyword("END"); + else + builder.withKeyword("COMMIT"); + + if (transactionKw) + { + builder.withSpace().withKeyword("TRANSACTION"); + if (!name.isNull()) + builder.withSpace().withOther(name, dialect); + } + + builder.withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecommittrans.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecommittrans.h new file mode 100644 index 0000000..ec418a6 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecommittrans.h @@ -0,0 +1,25 @@ +#ifndef SQLITECOMMITTRANS_H +#define SQLITECOMMITTRANS_H + +#include "sqlitequery.h" +#include <QString> + +class API_EXPORT SqliteCommitTrans : public SqliteQuery +{ + public: + SqliteCommitTrans(); + SqliteCommitTrans(bool transactionKw, const QString &name, bool endKw); + SqliteCommitTrans(const SqliteCommitTrans& other); + SqliteStatement* clone(); + + bool endKw = false; + QString name = QString::null; + bool transactionKw = false; + + protected: + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteCommitTrans> SqliteCommitTransPtr; + +#endif // SQLITECOMMITTRANS_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteconflictalgo.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteconflictalgo.cpp new file mode 100644 index 0000000..56fb42d --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteconflictalgo.cpp @@ -0,0 +1,37 @@ +#include "sqliteconflictalgo.h" + +SqliteConflictAlgo sqliteConflictAlgo(const QString& value) +{ + QString upper = value.toUpper(); + if (upper == "ROLLBACK") + return SqliteConflictAlgo::ROLLBACK; + else if (upper == "ABORT") + return SqliteConflictAlgo::ABORT; + else if (upper == "FAIL") + return SqliteConflictAlgo::FAIL; + else if (upper == "IGNORE") + return SqliteConflictAlgo::IGNORE; + else if (upper == "REPLACE") + return SqliteConflictAlgo::REPLACE; + else + return SqliteConflictAlgo::null; +} + +QString sqliteConflictAlgo(SqliteConflictAlgo value) +{ + switch (value) + { + case SqliteConflictAlgo::ROLLBACK: + return "ROLLBACK"; + case SqliteConflictAlgo::ABORT: + return "ABORT"; + case SqliteConflictAlgo::FAIL: + return "FAIL"; + case SqliteConflictAlgo::IGNORE: + return "IGNORE"; + case SqliteConflictAlgo::REPLACE: + return "REPLACE"; + default: + return QString::null; + } +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteconflictalgo.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteconflictalgo.h new file mode 100644 index 0000000..754672d --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteconflictalgo.h @@ -0,0 +1,20 @@ +#ifndef SQLITECONFLICTALGO_H +#define SQLITECONFLICTALGO_H + +#include "coreSQLiteStudio_global.h" +#include <QString> + +enum class SqliteConflictAlgo +{ + ROLLBACK, + ABORT, + FAIL, + IGNORE, + REPLACE, + null +}; + +API_EXPORT SqliteConflictAlgo sqliteConflictAlgo(const QString& value); +API_EXPORT QString sqliteConflictAlgo(SqliteConflictAlgo value); + +#endif // SQLITECONFLICTALGO_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.cpp new file mode 100644 index 0000000..009f836 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.cpp @@ -0,0 +1,92 @@ +#include "sqlitecopy.h" +#include "sqlitequerytype.h" + +#include <parser/statementtokenbuilder.h> + +SqliteCopy::SqliteCopy() +{ + queryType = SqliteQueryType::Copy; +} + +SqliteCopy::SqliteCopy(const SqliteCopy& other) : + SqliteQuery(other), onConflict(other.onConflict), database(other.database), table(other.table), file(other.file), delimiter(other.delimiter) +{ +} + +SqliteCopy::SqliteCopy(SqliteConflictAlgo onConflict, const QString &name1, const QString &name2, const QString &name3, const QString &delim) + : SqliteCopy() +{ + this->onConflict = onConflict; + + if (!name2.isNull()) + { + database = name1; + table = name2; + } + else + table = name1; + + file = name3; + delimiter = delim; +} + +SqliteStatement* SqliteCopy::clone() +{ + return new SqliteCopy(*this); +} + +QStringList SqliteCopy::getTablesInStatement() +{ + return getStrListFromValue(table); +} + +QStringList SqliteCopy::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteCopy::getTableTokensInStatement() +{ + return getObjectTokenListFromNmDbnm(); +} + +TokenList SqliteCopy::getDatabaseTokensInStatement() +{ + return getDbTokenListFromNmDbnm(); +} + +QList<SqliteStatement::FullObject> SqliteCopy::getFullObjectsInStatement() +{ + QList<FullObject> result; + + FullObject fullObj = getFullObjectFromNmDbnm(FullObject::TABLE); + if (fullObj.isValid()) + result << fullObj; + + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + result << fullObj; + + return result; +} + +TokenList SqliteCopy::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("COPY").withSpace(); + if (onConflict != SqliteConflictAlgo::null) + builder.withKeyword("OR").withSpace().withKeyword(sqliteConflictAlgo(onConflict)).withSpace(); + + if (!database.isNull()) + builder.withOther(database, dialect).withSpace(); + + builder.withOther(table, dialect).withSpace().withKeyword("FROM").withSpace().withString(file); + + if (!delimiter.isNull()) + builder.withSpace().withKeyword("USING").withSpace().withKeyword("DELIMITERS").withSpace().withString(delimiter); + + builder.withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.h new file mode 100644 index 0000000..ff586df --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.h @@ -0,0 +1,32 @@ +#ifndef SQLITECOPY_H +#define SQLITECOPY_H + +#include "sqlitequery.h" +#include "sqliteconflictalgo.h" + +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); + SqliteStatement* clone(); + + SqliteConflictAlgo onConflict = SqliteConflictAlgo::null; + QString database = QString::null; + QString table = QString::null; + QString file = QString::null; + QString delimiter = QString::null; + + protected: + QStringList getTablesInStatement(); + QStringList getDatabasesInStatement(); + TokenList getTableTokensInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteCopy> SqliteCopyPtr; + +#endif // SQLITECOPY_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.cpp new file mode 100644 index 0000000..36f7aa9 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.cpp @@ -0,0 +1,190 @@ +#include "sqlitecreateindex.h" +#include "sqlitequerytype.h" +#include "sqliteindexedcolumn.h" +#include "parser/statementtokenbuilder.h" +#include "parser/ast/sqliteexpr.h" +#include "common/global.h" + +SqliteCreateIndex::SqliteCreateIndex() +{ + queryType = SqliteQueryType::CreateIndex; +} + +SqliteCreateIndex::SqliteCreateIndex(const SqliteCreateIndex& other) : + SqliteQuery(other), uniqueKw(other.uniqueKw), ifNotExistsKw(other.ifNotExistsKw), database(other.database), index(other.index), + table(other.table) +{ + DEEP_COPY_COLLECTION(SqliteIndexedColumn, 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 = columns; + + foreach (SqliteIndexedColumn* idxCol, columns) + idxCol->setParent(this); +} + +SqliteCreateIndex::SqliteCreateIndex(bool unique, bool ifNotExists, const QString& name1, const QString& name2, const QString& name3, + const QList<SqliteIndexedColumn*>& columns, SqliteExpr* where) + : SqliteCreateIndex() +{ + // Constructor for SQLite 3 + uniqueKw = unique; + ifNotExistsKw = ifNotExists; + + if (!name2.isNull()) + { + database = name1; + index = name2; + } + else + index = name1; + + table = name3; + this->indexedColumns = columns; + + foreach (SqliteIndexedColumn* idxCol, columns) + idxCol->setParent(this); + + this->where = where; +} + +SqliteCreateIndex::~SqliteCreateIndex() +{ +} + +SqliteStatement*SqliteCreateIndex::clone() +{ + return new SqliteCreateIndex(*this); +} + +QString SqliteCreateIndex::getTargetTable() const +{ + return table; +} + +QStringList SqliteCreateIndex::getTablesInStatement() +{ + return getStrListFromValue(table); +} + +QStringList SqliteCreateIndex::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteCreateIndex::getTableTokensInStatement() +{ + if (dialect == Dialect::Sqlite2) + return getObjectTokenListFromNmDbnm("nm2", "dbnm"); + else + return getTokenListFromNamedKey("nm2"); +} + +TokenList SqliteCreateIndex::getDatabaseTokensInStatement() +{ + if (dialect == Dialect::Sqlite2) + return getDbTokenListFromNmDbnm("nm2", "dbnm"); + else + return getDbTokenListFromNmDbnm(); +} + +QList<SqliteStatement::FullObject> SqliteCreateIndex::getFullObjectsInStatement() +{ + QList<FullObject> result; + + // 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]); + } + + if (fullObj.isValid()) + result << fullObj; + + // Db object + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + { + result << fullObj; + dbTokenForFullObjects = fullObj.database; + } + + // 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"); + + return result; +} + +TokenList SqliteCreateIndex::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + builder.withKeyword("CREATE").withSpace(); + if (uniqueKw) + builder.withKeyword("UNIQUE").withSpace(); + + builder.withKeyword("INDEX").withSpace(); + + 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("."); + + builder.withOther(index, dialect).withSpace().withKeyword("ON").withSpace().withOther(table, dialect).withSpace().withParLeft() + .withStatementList(indexedColumns).withParRight(); + } + + if (where) + builder.withSpace().withKeyword("WHERE").withStatement(where); + + builder.withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.h new file mode 100644 index 0000000..9251b18 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.h @@ -0,0 +1,50 @@ +#ifndef SQLITECREATEINDEX_H +#define SQLITECREATEINDEX_H + +#include "sqlitequery.h" +#include "sqlitetablerelatedddl.h" +#include "sqliteconflictalgo.h" +#include "sqliteexpr.h" +#include <QString> +#include <QList> + +class SqliteIndexedColumn; + +class API_EXPORT SqliteCreateIndex : public SqliteQuery, public SqliteTableRelatedDdl +{ + public: + SqliteCreateIndex(); + SqliteCreateIndex(const SqliteCreateIndex& other); + SqliteCreateIndex(bool unique, bool ifNotExists, const QString& name1, const QString& name2, + const QString& name3, const QList<SqliteIndexedColumn*>& columns, + SqliteConflictAlgo onConflict = SqliteConflictAlgo::null); + SqliteCreateIndex(bool unique, bool ifNotExists, const QString& name1, const QString& name2, + const QString& name3, const QList<SqliteIndexedColumn*>& columns, + SqliteExpr* where); + ~SqliteCreateIndex(); + SqliteStatement* clone(); + + QString getTargetTable() const; + + bool uniqueKw = false; + bool ifNotExistsKw = false; + QList<SqliteIndexedColumn*> 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; + SqliteConflictAlgo onConflict = SqliteConflictAlgo::null; + SqliteExpr* where = nullptr; + + protected: + QStringList getTablesInStatement(); + QStringList getDatabasesInStatement(); + TokenList getTableTokensInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteCreateIndex> SqliteCreateIndexPtr; + +#endif // SQLITECREATEINDEX_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp new file mode 100644 index 0000000..e31e512 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp @@ -0,0 +1,771 @@ +#include "sqlitecreatetable.h" +#include "parser/statementtokenbuilder.h" +#include "common/utils_sql.h" +#include "common/global.h" + +SqliteCreateTable::SqliteCreateTable() +{ + queryType = SqliteQueryType::CreateTable; +} + +SqliteCreateTable::SqliteCreateTable(const SqliteCreateTable& other) : + SqliteQuery(other), ifNotExistsKw(other.ifNotExistsKw), tempKw(other.tempKw), temporaryKw(other.temporaryKw), + database(other.database), table(other.table), withOutRowId(other.withOutRowId) +{ + DEEP_COPY_COLLECTION(Column, columns); + DEEP_COPY_COLLECTION(Constraint, constraints); + DEEP_COPY_FIELD(SqliteSelect, select); +} + +SqliteCreateTable::SqliteCreateTable(bool ifNotExistsKw, int temp, const QString &name1, const QString &name2, const QList<Column *> &columns, const QList<Constraint*>& constraints) + : SqliteCreateTable() +{ + init(ifNotExistsKw, temp, name1, name2); + this->columns = columns; + foreach (Column* column, columns) + column->setParent(this); + + SqliteCreateTable::Constraint* constr = nullptr; + foreach (constr, constraints) + { + if (this->constraints.size() > 0 && + this->constraints.last()->type == SqliteCreateTable::Constraint::NAME_ONLY) + { + constr->name = this->constraints.last()->name; + delete this->constraints.takeLast(); + } + this->constraints << constr; + constr->setParent(this); + } +} + +SqliteCreateTable::SqliteCreateTable(bool ifNotExistsKw, int temp, const QString& name1, const QString& name2, const QList<SqliteCreateTable::Column*>& columns, const QList<SqliteCreateTable::Constraint*>& constraints, const QString& withOutRowId) : + SqliteCreateTable(ifNotExistsKw, temp, name1, name2, columns, constraints) +{ + this->withOutRowId = withOutRowId; +} + +SqliteCreateTable::SqliteCreateTable(bool ifNotExistsKw, int temp, const QString &name1, const QString &name2, SqliteSelect *select) + : SqliteCreateTable() +{ + init(ifNotExistsKw, temp, name1, name2); + this->select = select; + if (select) + select->setParent(this); +} + +SqliteCreateTable::~SqliteCreateTable() +{ +} + +SqliteStatement*SqliteCreateTable::clone() +{ + return new SqliteCreateTable(*this); +} + +QList<SqliteCreateTable::Constraint*> SqliteCreateTable::getConstraints(SqliteCreateTable::Constraint::Type type) const +{ + QList<SqliteCreateTable::Constraint*> results; + foreach (Constraint* constr, constraints) + if (constr->type == type) + results << constr; + + return results; +} + +SqliteStatement* SqliteCreateTable::getPrimaryKey() const +{ + foreach (Constraint* constr, getConstraints(Constraint::PRIMARY_KEY)) + return constr; + + Column::Constraint* colConstr = nullptr; + foreach (Column* col, columns) + { + colConstr = col->getConstraint(Column::Constraint::PRIMARY_KEY); + if (colConstr) + return colConstr; + } + + return nullptr; +} + +QStringList SqliteCreateTable::getPrimaryKeyColumns() const +{ + QStringList colNames; + SqliteStatement* primaryKey = getPrimaryKey(); + if (!primaryKey) + return colNames; + + SqliteCreateTable::Column::Constraint* columnConstr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(primaryKey); + if (columnConstr) + { + colNames << dynamic_cast<SqliteCreateTable::Column*>(columnConstr->parentStatement())->name; + return colNames; + } + + SqliteCreateTable::Constraint* tableConstr = dynamic_cast<SqliteCreateTable::Constraint*>(primaryKey); + if (tableConstr) + { + foreach (SqliteIndexedColumn* idxCol, tableConstr->indexedColumns) + colNames << idxCol->name; + } + return colNames; +} + +SqliteCreateTable::Column* SqliteCreateTable::getColumn(const QString& colName) +{ + foreach (Column* col, columns) + { + if (col->name.compare(colName, Qt::CaseInsensitive) == 0) + return col; + } + return nullptr; +} + +QList<SqliteCreateTable::Constraint*> SqliteCreateTable::getForeignKeysByTable(const QString& foreignTable) const +{ + QList<Constraint*> results; + foreach (Constraint* constr, constraints) + if (constr->type == Constraint::FOREIGN_KEY && constr->foreignKey->foreignTable.compare(foreignTable, Qt::CaseInsensitive) == 0) + results << constr; + + return results; +} + +QList<SqliteCreateTable::Column::Constraint*> SqliteCreateTable::getColumnForeignKeysByTable(const QString& foreignTable) const +{ + QList<Column::Constraint*> results; + foreach (Column* col, columns) + results += col->getForeignKeysByTable(foreignTable); + + return results; +} + +QStringList SqliteCreateTable::getColumnNames() const +{ + QStringList names; + foreach (Column* col, columns) + names << col->name; + + return names; +} + +QHash<QString, QString> SqliteCreateTable::getModifiedColumnsMap(bool lowercaseKeys, Qt::CaseSensitivity cs) const +{ + QHash<QString, QString> colMap; + QString key; + foreach (Column* col, columns) + { + key = lowercaseKeys ? col->originalName.toLower() : col->originalName; + if (col->name.compare(col->originalName, cs) != 0) + colMap[key] = col->name; + } + + return colMap; +} + +QStringList SqliteCreateTable::getTablesInStatement() +{ + return getStrListFromValue(table); +} + +QStringList SqliteCreateTable::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteCreateTable::getTableTokensInStatement() +{ + return getObjectTokenListFromFullname(); +} + +TokenList SqliteCreateTable::getDatabaseTokensInStatement() +{ + return getDbTokenListFromFullname(); +} + +QList<SqliteStatement::FullObject> SqliteCreateTable::getFullObjectsInStatement() +{ + QList<FullObject> result; + + // Table object + FullObject fullObj = getFullObjectFromFullname(FullObject::TABLE); + + if (fullObj.isValid()) + result << fullObj; + + // Db object + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + { + result << fullObj; + dbTokenForFullObjects = fullObj.database; + } + + return result; +} + +TokenList SqliteCreateTable::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("CREATE"); + if (tempKw) + builder.withSpace().withKeyword("TEMP"); + else if (temporaryKw) + builder.withSpace().withKeyword("TEMPORARY"); + + builder.withSpace().withKeyword("TABLE"); + if (ifNotExistsKw) + builder.withSpace().withKeyword("IF").withSpace().withKeyword("NOT").withSpace().withKeyword("EXISTS"); + + builder.withSpace(); + if (dialect == Dialect::Sqlite3 && !database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(table, dialect); + + if (select) + builder.withSpace().withKeyword("AS").withSpace().withStatement(select); + else + { + builder.withSpace().withParLeft().withStatementList(columns); + if (constraints.size() > 0) + builder.withOperator(",").withStatementList(constraints); + + builder.withParRight(); + + if (!withOutRowId.isNull()) + builder.withSpace().withKeyword("WITHOUT").withSpace().withOther("ROWID"); + } + + builder.withOperator(";"); + + return builder.build(); +} + +void SqliteCreateTable::init(bool ifNotExistsKw, int temp, const QString &name1, const QString &name2) +{ + this->ifNotExistsKw = ifNotExistsKw; + if (temp == 2) + temporaryKw = true; + else if (temp == 1) + tempKw = true; + + if (name2.isNull()) + table = name1; + else + { + database = name1; + table = name2; + } +} + + +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) +{ + DEEP_COPY_FIELD(SqliteExpr, expr); + DEEP_COPY_FIELD(SqliteForeignKey, foreignKey); +} + +SqliteCreateTable::Column::Constraint::~Constraint() +{ +} + +SqliteStatement* SqliteCreateTable::Column::Constraint::clone() +{ + return new SqliteCreateTable::Column::Constraint(*this); +} + +void SqliteCreateTable::Column::Constraint::initDefNameOnly(const QString &name) +{ + this->type = SqliteCreateTable::Column::Constraint::NAME_ONLY; + this->name = name; +} + +void SqliteCreateTable::Column::Constraint::initDefId(const QString &id) +{ + this->type = SqliteCreateTable::Column::Constraint::DEFAULT; + this->id = id; +} + +void SqliteCreateTable::Column::Constraint::initDefTerm(const QVariant &value, bool minus) +{ + this->type = SqliteCreateTable::Column::Constraint::DEFAULT; + if (minus) + { + if (value.type() == QVariant::Double) + literalValue = -(value.toDouble()); + else if (value.type() == QVariant::LongLong) + literalValue = -(value.toLongLong()); + } + else if (value.isNull()) + { + literalValue = value; + literalNull = true; + } + else + literalValue = value; +} + +void SqliteCreateTable::Column::Constraint::initDefCTime(const QString &name) +{ + this->type = SqliteCreateTable::Column::Constraint::DEFAULT; + ctime = name; +} + +void SqliteCreateTable::Column::Constraint::initDefExpr(SqliteExpr *expr) +{ + this->type = SqliteCreateTable::Column::Constraint::DEFAULT; + this->expr = expr; + if (expr) + expr->setParent(this); +} + +void SqliteCreateTable::Column::Constraint::initNull(SqliteConflictAlgo algo) +{ + this->type = SqliteCreateTable::Column::Constraint::NULL_; + onConflict = algo; +} + +void SqliteCreateTable::Column::Constraint::initNotNull(SqliteConflictAlgo algo) +{ + this->type = SqliteCreateTable::Column::Constraint::NOT_NULL; + onConflict = algo; +} + +void SqliteCreateTable::Column::Constraint::initPk(SqliteSortOrder order, SqliteConflictAlgo algo, bool autoincr) +{ + this->type = SqliteCreateTable::Column::Constraint::PRIMARY_KEY; + sortOrder = order; + onConflict = algo; + autoincrKw = autoincr; +} + +void SqliteCreateTable::Column::Constraint::initUnique(SqliteConflictAlgo algo) +{ + this->type = SqliteCreateTable::Column::Constraint::UNIQUE; + onConflict = algo; +} + +void SqliteCreateTable::Column::Constraint::initCheck() +{ + this->type = SqliteCreateTable::Column::Constraint::CHECK; +} + +void SqliteCreateTable::Column::Constraint::initCheck(SqliteExpr *expr) +{ + this->type = SqliteCreateTable::Column::Constraint::CHECK; + this->expr = expr; + if (expr) + expr->setParent(this); +} + +void SqliteCreateTable::Column::Constraint::initCheck(SqliteExpr *expr, SqliteConflictAlgo algo) +{ + initCheck(expr); + this->onConflict = algo; +} + +void SqliteCreateTable::Column::Constraint::initFk(const QString& table, const QList<SqliteIndexedColumn*>& indexedColumns, const QList<SqliteForeignKey::Condition*>& conditions) +{ + this->type = SqliteCreateTable::Column::Constraint::FOREIGN_KEY; + + SqliteForeignKey* fk = new SqliteForeignKey(); + fk->foreignTable = table; + fk->indexedColumns = indexedColumns; + fk->conditions = conditions; + foreignKey = fk; + fk->setParent(this); + + foreach (SqliteIndexedColumn* idxCol, indexedColumns) + idxCol->setParent(fk); + + foreach (SqliteForeignKey::Condition* cond, conditions) + cond->setParent(fk); +} + +void SqliteCreateTable::Column::Constraint::initDefer(SqliteInitially initially, SqliteDeferrable deferrable) +{ + this->type = SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY; + this->deferrable = deferrable; + this->initially = initially; +} + +void SqliteCreateTable::Column::Constraint::initColl(const QString &name) +{ + this->type = SqliteCreateTable::Column::Constraint::COLLATE; + this->collationName = name; +} + +QString SqliteCreateTable::Column::Constraint::typeString() const +{ + switch (type) + { + case SqliteCreateTable::Column::Constraint::PRIMARY_KEY: + return "PRIMARY KEY"; + case SqliteCreateTable::Column::Constraint::NOT_NULL: + return "NOT NULL"; + case SqliteCreateTable::Column::Constraint::UNIQUE: + return "UNIQUE"; + case SqliteCreateTable::Column::Constraint::CHECK: + return "CHECK"; + case SqliteCreateTable::Column::Constraint::DEFAULT: + return "DEFAULT"; + case SqliteCreateTable::Column::Constraint::COLLATE: + return "COLLATE"; + case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: + return "FOREIGN KEY"; + case SqliteCreateTable::Column::Constraint::NULL_: + case SqliteCreateTable::Column::Constraint::NAME_ONLY: + case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY: + break; + } + return QString::null; +} + +SqliteCreateTable::Constraint::Constraint() +{ +} + +SqliteCreateTable::Constraint::Constraint(const SqliteCreateTable::Constraint& other) : + SqliteStatement(other), type(other.type), name(other.name), autoincrKw(other.autoincrKw), onConflict(other.onConflict), + afterComma(other.afterComma) +{ + DEEP_COPY_FIELD(SqliteForeignKey, foreignKey); + DEEP_COPY_FIELD(SqliteExpr, expr); + DEEP_COPY_COLLECTION(SqliteIndexedColumn, indexedColumns); +} + +SqliteCreateTable::Constraint::~Constraint() +{ +} + +SqliteStatement*SqliteCreateTable::Constraint::clone() +{ + return new SqliteCreateTable::Constraint(*this); +} + +void SqliteCreateTable::Constraint::initNameOnly(const QString &name) +{ + this->type = SqliteCreateTable::Constraint::NAME_ONLY; + this->name = name; +} + +void SqliteCreateTable::Constraint::initPk(const QList<SqliteIndexedColumn *> &indexedColumns, bool autoincr, SqliteConflictAlgo algo) +{ + this->type = SqliteCreateTable::Constraint::PRIMARY_KEY; + this->indexedColumns = indexedColumns; + autoincrKw = autoincr; + onConflict = algo; + + foreach (SqliteIndexedColumn* idxCol, indexedColumns) + idxCol->setParent(this); +} + +void SqliteCreateTable::Constraint::initUnique(const QList<SqliteIndexedColumn *> &indexedColumns, SqliteConflictAlgo algo) +{ + this->type = SqliteCreateTable::Constraint::UNIQUE; + this->indexedColumns = indexedColumns; + onConflict = algo; + + foreach (SqliteIndexedColumn* idxCol, indexedColumns) + idxCol->setParent(this); +} + +void SqliteCreateTable::Constraint::initCheck(SqliteExpr *expr, SqliteConflictAlgo algo) +{ + this->type = SqliteCreateTable::Constraint::CHECK; + this->expr = expr; + onConflict = algo; + if (expr) + expr->setParent(this); +} + +void SqliteCreateTable::Constraint::initCheck() +{ + this->type = SqliteCreateTable::Constraint::CHECK; +} + +void SqliteCreateTable::Constraint::initFk(const QList<SqliteIndexedColumn *> &indexedColumns, const QString& table, const QList<SqliteIndexedColumn *> &fkColumns, const QList<SqliteForeignKey::Condition *> &conditions, SqliteInitially initially, SqliteDeferrable deferrable) +{ + this->type = SqliteCreateTable::Constraint::FOREIGN_KEY; + this->indexedColumns = indexedColumns; + + foreach (SqliteIndexedColumn* idxCol, indexedColumns) + idxCol->setParent(this); + + SqliteForeignKey* fk = new SqliteForeignKey(); + fk->foreignTable = table; + fk->indexedColumns = fkColumns; + fk->conditions = conditions; + fk->deferrable = deferrable; + fk->initially = initially; + + fk->setParent(this); + + foreach (SqliteIndexedColumn* idxCol, fkColumns) + idxCol->setParent(fk); + + foreach (SqliteForeignKey::Condition* cond, conditions) + cond->setParent(fk); + + this->foreignKey = fk; +} + +bool SqliteCreateTable::Constraint::doesAffectColumn(const QString& columnName) +{ + return getAffectedColumnIdx(columnName) > -1; +} + +int SqliteCreateTable::Constraint::getAffectedColumnIdx(const QString& columnName) +{ + int i = 0; + foreach (SqliteIndexedColumn* idxCol, indexedColumns) + { + if (idxCol->name.compare(columnName, Qt::CaseInsensitive) == 0) + return i; + + i++; + } + + return -1; +} + +QString SqliteCreateTable::Constraint::typeString() const +{ + switch (type) + { + case SqliteCreateTable::Constraint::PRIMARY_KEY: + return "PRIMARY KEY"; + case SqliteCreateTable::Constraint::UNIQUE: + return "UNIQUE"; + case SqliteCreateTable::Constraint::CHECK: + return "CHECK"; + case SqliteCreateTable::Constraint::FOREIGN_KEY: + return "FOREIGN KEY"; + case SqliteCreateTable::Constraint::NAME_ONLY: + return QString::null; + } + return QString::null; +} + +TokenList SqliteCreateTable::Constraint::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + if (!name.isNull()) + builder.withKeyword("CONSTRAINT").withSpace().withOther(name, dialect).withSpace(); + + switch (type) + { + case SqliteCreateTable::Constraint::PRIMARY_KEY: + { + builder.withKeyword("PRIMARY").withSpace().withKeyword("KEY").withSpace().withParLeft().withStatementList(indexedColumns).withParRight(); + + if (autoincrKw) + builder.withSpace().withKeyword("AUTOINCREMENT"); + + builder.withConflict(onConflict); + break; + } + case SqliteCreateTable::Constraint::UNIQUE: + { + builder.withKeyword("UNIQUE").withSpace().withParLeft().withStatementList(indexedColumns).withParRight().withConflict(onConflict); + break; + } + case SqliteCreateTable::Constraint::CHECK: + { + builder.withKeyword("CHECK").withSpace().withParLeft().withStatement(expr).withParRight().withConflict(onConflict); + break; + } + case SqliteCreateTable::Constraint::FOREIGN_KEY: + { + builder.withKeyword("FOREIGN").withSpace().withKeyword("KEY").withSpace().withParLeft().withStatementList(indexedColumns) + .withParRight().withStatement(foreignKey); + break; + } + case SqliteCreateTable::Constraint::NAME_ONLY: + break; + } + + return builder.build(); +} + +SqliteCreateTable::Column::Column() +{ +} + +SqliteCreateTable::Column::Column(const SqliteCreateTable::Column& other) : + SqliteStatement(other), name(other.name), originalName(other.originalName) +{ + DEEP_COPY_FIELD(SqliteColumnType, type); + DEEP_COPY_COLLECTION(Constraint, constraints); +} + +SqliteCreateTable::Column::Column(const QString &name, SqliteColumnType *type, const QList<Constraint *> &constraints) +{ + this->name = name; + this->originalName = name; + this->type = type; + + if (type) + type->setParent(this); + + SqliteCreateTable::Column::Constraint* constr = nullptr; + foreach (constr, constraints) + { + // If last constraint on list is NAME_ONLY we apply the name + // to current constraint and remove NAME_ONLY. + // Exception is DEFERRABLE_ONLY. + if (this->constraints.size() > 0 && + this->constraints.last()->type == SqliteCreateTable::Column::Constraint::NAME_ONLY && + constr->type != SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY) + { + constr->name = this->constraints.last()->name; + delete this->constraints.takeLast(); + } + + // And the opposite of above. Now we apply DEFERRABLE_ONLY, + // but only if last item in the list is not NAME_ONLY. + if (constr->type == SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY && + this->constraints.size() > 0 && + this->constraints.last()->type != SqliteCreateTable::Column::Constraint::NAME_ONLY) + { + SqliteCreateTable::Column::Constraint* last = this->constraints.last(); + last->deferrable = constr->deferrable; + last->initially = constr->initially; + delete constr; + + // We don't want deleted constr to be added to list. We finish this now. + continue; + } + + this->constraints << constr; + constr->setParent(this); + } +} + +SqliteCreateTable::Column::~Column() +{ +} + +SqliteStatement*SqliteCreateTable::Column::clone() +{ + return new SqliteCreateTable::Column(*this); +} + +bool SqliteCreateTable::Column::hasConstraint(SqliteCreateTable::Column::Constraint::Type type) const +{ + return getConstraint(type) != nullptr; +} + +SqliteCreateTable::Column::Constraint* SqliteCreateTable::Column::getConstraint(SqliteCreateTable::Column::Constraint::Type type) const +{ + foreach (Constraint* constr, constraints) + if (constr->type == type) + return constr; + + return nullptr; +} + +QList<SqliteCreateTable::Column::Constraint*> SqliteCreateTable::Column::getForeignKeysByTable(const QString& foreignTable) const +{ + QList<Constraint*> results; + foreach (Constraint* constr, constraints) + if (constr->type == Constraint::FOREIGN_KEY && constr->foreignKey->foreignTable.compare(foreignTable, Qt::CaseInsensitive) == 0) + results << constr; + + return results; +} + +QStringList SqliteCreateTable::Column::getColumnsInStatement() +{ + return getStrListFromValue(name); +} + +TokenList SqliteCreateTable::Column::getColumnTokensInStatement() +{ + return getTokenListFromNamedKey("columnid"); +} + +TokenList SqliteCreateTable::Column::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + builder.withOther(name, dialect).withStatement(type).withStatementList(constraints, ""); + return builder.build(); +} + +TokenList SqliteCreateTable::Column::Constraint::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + if (!name.isNull()) + builder.withKeyword("CONSTRAINT").withSpace().withOther(name, dialect).withSpace(); + + switch (type) + { + case SqliteCreateTable::Column::Constraint::PRIMARY_KEY: + { + builder.withKeyword("PRIMARY").withSpace().withKeyword("KEY").withSortOrder(sortOrder).withConflict(onConflict); + if (autoincrKw) + builder.withSpace().withKeyword("AUTOINCREMENT"); + + break; + } + case SqliteCreateTable::Column::Constraint::NOT_NULL: + { + builder.withKeyword("NOT").withSpace().withKeyword("NULL").withConflict(onConflict); + break; + } + case SqliteCreateTable::Column::Constraint::UNIQUE: + { + builder.withKeyword("UNIQUE").withConflict(onConflict); + break; + } + case SqliteCreateTable::Column::Constraint::CHECK: + { + builder.withKeyword("CHECK").withSpace().withParLeft().withStatement(expr).withParRight().withConflict(onConflict); + break; + } + case SqliteCreateTable::Column::Constraint::DEFAULT: + { + builder.withKeyword("DEFAULT").withSpace(); + if (!id.isNull()) + builder.withOther(id); + else if (!ctime.isNull()) + builder.withKeyword(ctime.toUpper()); + else if (expr) + builder.withParLeft().withStatement(expr).withParRight(); + else if (literalNull) + builder.withKeyword("NULL"); + else + builder.withLiteralValue(literalValue); + + break; + } + case SqliteCreateTable::Column::Constraint::COLLATE: + { + builder.withKeyword("COLLATE").withSpace().withOther(collationName, dialect); + break; + } + case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: + { + builder.withStatement(foreignKey); + break; + } + case SqliteCreateTable::Column::Constraint::NULL_: + case SqliteCreateTable::Column::Constraint::NAME_ONLY: + case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY: + break; + } + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h new file mode 100644 index 0000000..f3be244 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h @@ -0,0 +1,205 @@ +#ifndef SQLITECREATETABLE_H +#define SQLITECREATETABLE_H + +#include "sqlitequery.h" +#include "sqliteconflictalgo.h" +#include "sqliteexpr.h" +#include "sqliteforeignkey.h" +#include "sqliteindexedcolumn.h" +#include "sqliteselect.h" +#include "sqlitecolumntype.h" +#include "sqlitesortorder.h" +#include "sqlitedeferrable.h" +#include <QVariant> +#include <QList> + +class API_EXPORT SqliteCreateTable : public SqliteQuery +{ + public: + class API_EXPORT Column : public SqliteStatement + { + public: + class API_EXPORT Constraint : public SqliteStatement + { + public: + enum Type + { + PRIMARY_KEY, + NOT_NULL, + UNIQUE, + CHECK, + DEFAULT, + COLLATE, + FOREIGN_KEY, + NULL_, // not officially supported + NAME_ONLY, // unofficial, because of bizarre sqlite grammar + DEFERRABLE_ONLY // unofficial, because of bizarre sqlite grammar + }; + + Constraint(); + Constraint(const Constraint& other); + ~Constraint(); + SqliteStatement* clone(); + + void initDefNameOnly(const QString& name); + void initDefId(const QString& id); + void initDefTerm(const QVariant& value, bool minus = false); + void initDefCTime(const QString& name); + void initDefExpr(SqliteExpr* expr); + void initNull(SqliteConflictAlgo algo); + void initNotNull(SqliteConflictAlgo algo); + void initPk(SqliteSortOrder order, SqliteConflictAlgo algo, bool autoincr); + void initUnique(SqliteConflictAlgo algo); + void initCheck(); + void initCheck(SqliteExpr* expr); + void initCheck(SqliteExpr* expr, SqliteConflictAlgo algo); + 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); + QString typeString() const; + + Type type; + QString name = QString::null; + SqliteSortOrder sortOrder = SqliteSortOrder::null; + SqliteConflictAlgo onConflict = SqliteConflictAlgo::null; + bool autoincrKw = false; + SqliteExpr* expr = nullptr; + QVariant literalValue; + bool literalNull = false; + QString ctime; + QString id; + QString collationName = QString::null; + SqliteForeignKey* foreignKey = nullptr; + SqliteDeferrable deferrable = SqliteDeferrable::null; + SqliteInitially initially = SqliteInitially::null; + + protected: + TokenList rebuildTokensFromContents(); + }; + + typedef QSharedPointer<Constraint> ConstraintPtr; + + Column(); + Column(const Column& other); + Column(const QString& name, SqliteColumnType* type, + const QList<Constraint*>& constraints); + ~Column(); + SqliteStatement* clone(); + + bool hasConstraint(Constraint::Type type) const; + Constraint* getConstraint(Constraint::Type type) const; + QList<Constraint*> getForeignKeysByTable(const QString& foreignTable) const; + + QString name = QString::null; + SqliteColumnType* type = nullptr; + QList<Constraint*> constraints; + + /** + * @brief originalName + * 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; + + protected: + QStringList getColumnsInStatement(); + TokenList getColumnTokensInStatement(); + TokenList rebuildTokensFromContents(); + }; + + typedef QSharedPointer<Column> ColumnPtr; + + class API_EXPORT Constraint : public SqliteStatement + { + public: + enum Type + { + PRIMARY_KEY, + UNIQUE, + CHECK, + FOREIGN_KEY, + NAME_ONLY // unofficial, because of bizarre sqlite grammar + }; + + Constraint(); + Constraint(const Constraint& other); + ~Constraint(); + SqliteStatement* clone(); + + void initNameOnly(const QString& name); + void initPk(const QList<SqliteIndexedColumn*>& indexedColumns, + bool autoincr, SqliteConflictAlgo algo); + void initUnique(const QList<SqliteIndexedColumn*>& indexedColumns, + SqliteConflictAlgo algo); + void initCheck(SqliteExpr* expr, SqliteConflictAlgo algo); + void initCheck(); + void initFk(const QList<SqliteIndexedColumn*>& indexedColumns, const QString& table, + const QList<SqliteIndexedColumn*>& fkColumns, const QList<SqliteForeignKey::Condition*>& conditions, + SqliteInitially initially, SqliteDeferrable deferrable); + + bool doesAffectColumn(const QString& columnName); + int getAffectedColumnIdx(const QString& columnName); + QString typeString() const; + + Type type; + QString name = QString::null; + bool autoincrKw = false; // not in docs, but needs to be supported + SqliteConflictAlgo onConflict = SqliteConflictAlgo::null; + SqliteForeignKey* foreignKey = nullptr; + SqliteExpr* expr = nullptr; + QList<SqliteIndexedColumn*> indexedColumns; + bool afterComma = false; + + protected: + TokenList rebuildTokensFromContents(); + }; + + typedef QSharedPointer<Constraint> ConstraintPtr; + + SqliteCreateTable(); + SqliteCreateTable(const SqliteCreateTable& other); + SqliteCreateTable(bool ifNotExistsKw, int temp, const QString& name1, const QString& name2, + const QList<Column*>& columns, const QList<Constraint*>& constraints); + SqliteCreateTable(bool ifNotExistsKw, int temp, const QString& name1, const QString& name2, + const QList<Column*>& columns, const QList<Constraint*>& constraints, + const QString& withOutRowId); + SqliteCreateTable(bool ifNotExistsKw, int temp, const QString& name1, const QString& name2, + SqliteSelect* select); + ~SqliteCreateTable(); + SqliteStatement* clone(); + + QList<Constraint*> getConstraints(Constraint::Type type) const; + SqliteStatement* getPrimaryKey() const; + QStringList getPrimaryKeyColumns() const; + Column* getColumn(const QString& colName); + QList<Constraint*> getForeignKeysByTable(const QString& foreignTable) const; + QList<Column::Constraint*> getColumnForeignKeysByTable(const QString& foreignTable) const; + QStringList getColumnNames() const; + QHash<QString,QString> getModifiedColumnsMap(bool lowercaseKeys = false, Qt::CaseSensitivity cs = Qt::CaseInsensitive) const; + + bool ifNotExistsKw = false; + bool tempKw = false; + bool temporaryKw = false; + QString database = QString::null; + QString table = QString::null; + QList<Column*> columns; + QList<Constraint*> constraints; + SqliteSelect* select = nullptr; + QString withOutRowId = QString::null; + + protected: + QStringList getTablesInStatement(); + QStringList getDatabasesInStatement(); + TokenList getTableTokensInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); + + private: + void init(bool ifNotExistsKw, int temp, const QString& name1, const QString& name2); + +}; + +typedef QSharedPointer<SqliteCreateTable> SqliteCreateTablePtr; + +#endif // SQLITECREATETABLE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.cpp new file mode 100644 index 0000000..3b7b0ea --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.cpp @@ -0,0 +1,381 @@ +#include "sqlitecreatetrigger.h" +#include "sqlitequerytype.h" +#include "sqliteexpr.h" +#include "sqlitedelete.h" +#include "sqliteinsert.h" +#include "sqliteupdate.h" +#include "sqliteselect.h" +#include "parser/statementtokenbuilder.h" +#include "common/global.h" + +SqliteCreateTrigger::SqliteCreateTrigger() +{ + queryType = SqliteQueryType::CreateTrigger; +} + +SqliteCreateTrigger::SqliteCreateTrigger(const SqliteCreateTrigger& other) : + SqliteQuery(other), tempKw(other.tempKw), temporaryKw(other.temporaryKw), ifNotExistsKw(other.ifNotExistsKw), database(other.database), + trigger(other.trigger), table(other.table), eventTime(other.eventTime), scope(other.scope) +{ + DEEP_COPY_FIELD(Event, event); + DEEP_COPY_FIELD(SqliteExpr, precondition); + + // Special case of deep collection copy + SqliteQuery* newQuery = nullptr; + foreach (SqliteQuery* query, other.queries) + { + switch (query->queryType) + { + case SqliteQueryType::Delete: + newQuery = new SqliteDelete(*dynamic_cast<SqliteDelete*>(query)); + break; + case SqliteQueryType::Insert: + newQuery = new SqliteInsert(*dynamic_cast<SqliteInsert*>(query)); + break; + case SqliteQueryType::Update: + newQuery = new SqliteUpdate(*dynamic_cast<SqliteUpdate*>(query)); + break; + case SqliteQueryType::Select: + newQuery = new SqliteSelect(*dynamic_cast<SqliteSelect*>(query)); + break; + default: + newQuery = nullptr; + break; + } + + if (!newQuery) + continue; + + newQuery->setParent(this); + queries << newQuery; + } +} + +SqliteCreateTrigger::SqliteCreateTrigger(int temp, bool ifNotExists, const QString &name1, const QString &name2, const QString &name3, Time time, SqliteCreateTrigger::Event *event, Scope foreachType, SqliteExpr *when, const QList<SqliteQuery *> &queries, int sqliteVersion) : + SqliteCreateTrigger() +{ + this->ifNotExistsKw = ifNotExists; + this->scope = foreachType; + if (temp == 2) + temporaryKw = true; + else if (temp == 1) + tempKw = true; + + if (sqliteVersion == 3) + { + if (name2.isNull()) + trigger = name1; + else + { + database = name1; + trigger = name2; + } + table = name3; + } + else + { + trigger = name1; + if (name3.isNull()) + table = name2; + else + { + database = name2; + table = name3; + } + } + + this->event = event; + eventTime = time; + this->precondition = when; + this->queries = queries; + + if (event) + event->setParent(this); + + if (when) + when->setParent(this); + + foreach (SqliteQuery* q, queries) + q->setParent(this); +} + +SqliteCreateTrigger::~SqliteCreateTrigger() +{ +} + +SqliteStatement*SqliteCreateTrigger::clone() +{ + return new SqliteCreateTrigger(*this); +} + +QString SqliteCreateTrigger::getTargetTable() const +{ + return table; +} + +QString SqliteCreateTrigger::time(SqliteCreateTrigger::Time eventTime) +{ + switch (eventTime) + { + case SqliteCreateTrigger::Time::BEFORE: + return "BEFORE"; + case SqliteCreateTrigger::Time::AFTER: + return "AFTER"; + case SqliteCreateTrigger::Time::INSTEAD_OF: + return "INSTEAD OF"; + case SqliteCreateTrigger::Time::null: + break; + } + return QString::null; +} + +SqliteCreateTrigger::Time SqliteCreateTrigger::time(const QString& eventTime) +{ + if (eventTime == "BEFORE") + return Time::BEFORE; + + if (eventTime == "AFTER") + return Time::AFTER; + + if (eventTime == "INSTEAD OF") + return Time::INSTEAD_OF; + + return Time::null; +} + +QString SqliteCreateTrigger::scopeToString(SqliteCreateTrigger::Scope scope) +{ + switch (scope) + { + case SqliteCreateTrigger::Scope::FOR_EACH_ROW: + return "FOR EACH ROW"; + case SqliteCreateTrigger::Scope::FOR_EACH_STATEMENT: + return "FOR EACH STATEMENT"; + case SqliteCreateTrigger::Scope::null: + break; + } + return QString::null; +} + +SqliteCreateTrigger::Scope SqliteCreateTrigger::stringToScope(const QString& scope) +{ + if (scope == "FOR EACH ROW") + return Scope::FOR_EACH_ROW; + + if (scope == "FOR EACH STATEMENT") + return Scope::FOR_EACH_STATEMENT; + + return Scope::null; +} + +QStringList SqliteCreateTrigger::getTablesInStatement() +{ + return getStrListFromValue(table); +} + +QStringList SqliteCreateTrigger::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteCreateTrigger::getTableTokensInStatement() +{ + if (dialect == Dialect::Sqlite2) + return getObjectTokenListFromNmDbnm("nm2", "dbnm"); + else + return getTokenListFromNamedKey("nm2"); +} + +TokenList SqliteCreateTrigger::getDatabaseTokensInStatement() +{ + if (dialect == Dialect::Sqlite2) + return getDbTokenListFromNmDbnm("nm2", "dbnm"); + else + return getDbTokenListFromNmDbnm(); +} + +QList<SqliteStatement::FullObject> SqliteCreateTrigger::getFullObjectsInStatement() +{ + QList<FullObject> result; + + // 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]); + } + + if (fullObj.isValid()) + result << fullObj; + + // Db object + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + { + result << fullObj; + dbTokenForFullObjects = fullObj.database; + } + + // 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"); + + return result; +} + +SqliteCreateTrigger::Event::Event() +{ + this->type = Event::null; +} + +SqliteCreateTrigger::Event::Event(SqliteCreateTrigger::Event::Type type) +{ + this->type = type; +} + +SqliteCreateTrigger::Event::Event(const SqliteCreateTrigger::Event& other) : + SqliteStatement(other), type(other.type), columnNames(other.columnNames) +{ +} + +SqliteCreateTrigger::Event::Event(const QList<QString> &columns) +{ + this->type = UPDATE_OF; + columnNames = columns; +} + +SqliteStatement*SqliteCreateTrigger::Event::clone() +{ + return new SqliteCreateTrigger::Event(*this); +} + +TokenList SqliteCreateTrigger::Event::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + switch (type) + { + case SqliteCreateTrigger::Event::INSERT: + builder.withKeyword("INSERT"); + break; + case SqliteCreateTrigger::Event::UPDATE: + builder.withKeyword("UPDATE"); + break; + case SqliteCreateTrigger::Event::DELETE: + builder.withKeyword("DELETE"); + break; + case SqliteCreateTrigger::Event::UPDATE_OF: + builder.withKeyword("UPDATE").withSpace().withKeyword("OF").withSpace().withOtherList(columnNames); + break; + case SqliteCreateTrigger::Event::null: + break; + } + + return builder.build(); +} + +QString SqliteCreateTrigger::Event::typeToString(SqliteCreateTrigger::Event::Type type) +{ + switch (type) + { + case SqliteCreateTrigger::Event::INSERT: + return "INSERT"; + case SqliteCreateTrigger::Event::UPDATE: + return "UPDATE"; + case SqliteCreateTrigger::Event::DELETE: + return "DELETE"; + case SqliteCreateTrigger::Event::UPDATE_OF: + return "UPDATE OF"; + case SqliteCreateTrigger::Event::null: + break; + } + return QString::null; +} + +SqliteCreateTrigger::Event::Type SqliteCreateTrigger::Event::stringToType(const QString& type) +{ + if (type == "INSERT") + return INSERT; + + if (type == "UPDATE") + return UPDATE; + + if (type == "DELETE") + return DELETE; + + if (type == "UPDATE OF") + return UPDATE_OF; + + return Event::null; +} + +TokenList SqliteCreateTrigger::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("CREATE").withSpace(); + if (tempKw) + builder.withKeyword("TEMP").withSpace(); + else if (temporaryKw) + builder.withKeyword("TEMPORARY").withSpace(); + + builder.withKeyword("TRIGGER").withSpace(); + if (ifNotExistsKw) + builder.withKeyword("IF").withSpace().withKeyword("NOT").withSpace().withKeyword("EXISTS").withSpace(); + + if (dialect == Dialect::Sqlite3 && !database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(trigger, dialect).withSpace(); + switch (eventTime) + { + case Time::BEFORE: + builder.withKeyword("BEFORE").withSpace(); + break; + case Time::AFTER: + builder.withKeyword("AFTER").withSpace(); + break; + case Time::INSTEAD_OF: + builder.withKeyword("INSTEAD").withSpace().withKeyword("OF").withSpace(); + break; + case Time::null: + break; + } + + builder.withStatement(event).withSpace().withKeyword("ON").withSpace(); + if (dialect == Dialect::Sqlite2 && !database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(table, dialect).withSpace(); + + switch (scope) + { + case SqliteCreateTrigger::Scope::FOR_EACH_ROW: + builder.withKeyword("FOR").withSpace().withKeyword("EACH").withSpace().withKeyword("ROW").withSpace(); + break; + case SqliteCreateTrigger::Scope::FOR_EACH_STATEMENT: + builder.withKeyword("FOR").withSpace().withKeyword("EACH").withSpace().withKeyword("STATEMENT").withSpace(); + break; + case SqliteCreateTrigger::Scope::null: + break; + } + + if (precondition) + builder.withKeyword("WHEN").withStatement(precondition).withSpace(); + + builder.withKeyword("BEGIN").withSpace().withStatementList(queries, ";").withOperator(";").withSpace().withKeyword("END"); + + builder.withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.h new file mode 100644 index 0000000..2fb8ae4 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.h @@ -0,0 +1,96 @@ +#ifndef SQLITECREATETRIGGER_H +#define SQLITECREATETRIGGER_H + +#include "sqlitequery.h" +#include "sqlitetablerelatedddl.h" + +#include <QString> +#include <QList> + +class SqliteExpr; + +class API_EXPORT SqliteCreateTrigger : public SqliteQuery, public SqliteTableRelatedDdl +{ + public: + enum class Time + { + BEFORE, + AFTER, + INSTEAD_OF, + null + }; + + class API_EXPORT Event : public SqliteStatement + { + public: + enum Type + { + INSERT, + UPDATE, + DELETE, + UPDATE_OF, + null + }; + + Event(); + explicit Event(Type type); + Event(const Event& other); + explicit Event(const QList<QString>& columns); + SqliteStatement* clone(); + + TokenList rebuildTokensFromContents(); + + static QString typeToString(Type type); + static Type stringToType(const QString& type); + + Type type; + QStringList columnNames; + }; + + enum class Scope + { + FOR_EACH_ROW, + FOR_EACH_STATEMENT, // Sqlite2 only + null + }; + + SqliteCreateTrigger(); + SqliteCreateTrigger(const SqliteCreateTrigger& other); + SqliteCreateTrigger(int temp, bool ifNotExists, const QString& name1, const QString& name2, + const QString& name3, Time time, Event* event, Scope scope, + SqliteExpr* precondition, const QList<SqliteQuery*>& queries, int sqliteVersion); + ~SqliteCreateTrigger(); + SqliteStatement* clone(); + + QString getTargetTable() const; + + 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 + Event* event = nullptr; + Time eventTime = Time::null; + Scope scope = Scope::null; + SqliteExpr* precondition = nullptr; + QList<SqliteQuery*> queries; + + static QString time(Time eventTime); + static Time time(const QString& eventTime); + static QString scopeToString(Scope scope); + static Scope stringToScope(const QString& scope); + + protected: + QStringList getTablesInStatement(); + QStringList getDatabasesInStatement(); + TokenList getTableTokensInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteCreateTrigger> SqliteCreateTriggerPtr; + +#endif // SQLITECREATETRIGGER_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.cpp new file mode 100644 index 0000000..6b11c3c --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.cpp @@ -0,0 +1,120 @@ +#include "sqlitecreateview.h" +#include "sqliteselect.h" +#include "sqlitequerytype.h" +#include "parser/statementtokenbuilder.h" +#include "common/global.h" + +SqliteCreateView::SqliteCreateView() +{ + queryType = SqliteQueryType::CreateView; +} + +SqliteCreateView::SqliteCreateView(const SqliteCreateView& other) : + SqliteQuery(other), tempKw(other.tempKw), temporaryKw(other.temporaryKw), ifNotExists(other.ifNotExists), + database(other.database), view(other.view) +{ + DEEP_COPY_FIELD(SqliteSelect, select); + +} + +SqliteCreateView::SqliteCreateView(int temp, bool ifNotExists, const QString &name1, const QString &name2, SqliteSelect *select) : + SqliteCreateView() +{ + this->ifNotExists = ifNotExists; + + if (name2.isNull()) + view = name1; + else + { + database = name1; + view = name2; + } + + if (temp == 2) + temporaryKw = true; + else if (temp == 1) + tempKw = true; + + this->select = select; + + if (select) + select->setParent(this); +} + +SqliteCreateView::~SqliteCreateView() +{ +} + +SqliteStatement*SqliteCreateView::clone() +{ + return new SqliteCreateView(*this); +} + +QStringList SqliteCreateView::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteCreateView::getDatabaseTokensInStatement() +{ + if (dialect == Dialect::Sqlite3) + return getDbTokenListFromFullname(); + else + return TokenList(); +} + +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]); + } + + if (fullObj.isValid()) + result << fullObj; + + // Db object + if (dialect == Dialect::Sqlite3) + { + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + { + result << fullObj; + dbTokenForFullObjects = fullObj.database; + } + } + + return result; +} + +TokenList SqliteCreateView::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("CREATE").withSpace(); + if (tempKw) + builder.withKeyword("TEMP").withSpace(); + else if (temporaryKw) + builder.withKeyword("TEMPORARY").withSpace(); + + builder.withKeyword("VIEW").withSpace(); + if (ifNotExists) + builder.withKeyword("IF").withSpace().withKeyword("NOT").withSpace().withKeyword("EXISTS").withSpace(); + + if (dialect == Dialect::Sqlite3 && !database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(view, dialect).withSpace().withKeyword("AS").withStatement(select); + + builder.withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.h new file mode 100644 index 0000000..4858227 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.h @@ -0,0 +1,35 @@ +#ifndef SQLITECREATEVIEW_H +#define SQLITECREATEVIEW_H + +#include "sqlitequery.h" +#include <QString> + +class SqliteSelect; + +class API_EXPORT SqliteCreateView : public SqliteQuery +{ + public: + SqliteCreateView(); + SqliteCreateView(const SqliteCreateView& other); + SqliteCreateView(int temp, bool ifNotExists, const QString& name1, const QString& name2, SqliteSelect* select); + ~SqliteCreateView(); + + SqliteStatement* clone(); + + bool tempKw = false; + bool temporaryKw = false; + bool ifNotExists = false; + QString database = QString::null; + QString view = QString::null; + SqliteSelect* select = nullptr; + + protected: + QStringList getDatabasesInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteCreateView> SqliteCreateViewPtr; + +#endif // SQLITECREATEVIEW_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatevirtualtable.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatevirtualtable.cpp new file mode 100644 index 0000000..bc38dc9 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatevirtualtable.cpp @@ -0,0 +1,120 @@ +#include "sqlitecreatevirtualtable.h" +#include "sqlitequerytype.h" + +#include <parser/lexer.h> +#include <parser/statementtokenbuilder.h> + +SqliteCreateVirtualTable::SqliteCreateVirtualTable() +{ + queryType = SqliteQueryType::CreateVirtualTable; +} + +SqliteCreateVirtualTable::SqliteCreateVirtualTable(const SqliteCreateVirtualTable& other) : + SqliteQuery(other), ifNotExistsKw(other.ifNotExistsKw), database(other.database), table(other.table), module(other.module), args(other.args) +{ +} + +SqliteCreateVirtualTable::SqliteCreateVirtualTable(bool ifNotExists, const QString &name1, const QString &name2, const QString &name3) : + SqliteCreateVirtualTable() +{ + initName(name1, name2); + this->ifNotExistsKw = ifNotExists; + module = name3; +} + +SqliteCreateVirtualTable::SqliteCreateVirtualTable(bool ifNotExists, const QString &name1, const QString &name2, const QString &name3, const QList<QString> &args) : + SqliteCreateVirtualTable() +{ + initName(name1, name2); + this->ifNotExistsKw = ifNotExists; + module = name3; + this->args = args; +} + +SqliteStatement*SqliteCreateVirtualTable::clone() +{ + return new SqliteCreateVirtualTable(*this); +} + +QStringList SqliteCreateVirtualTable::getTablesInStatement() +{ + return getStrListFromValue(table); +} + +QStringList SqliteCreateVirtualTable::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteCreateVirtualTable::getTableTokensInStatement() +{ + return getObjectTokenListFromFullname(); +} + +TokenList SqliteCreateVirtualTable::getDatabaseTokensInStatement() +{ + return getDbTokenListFromFullname(); +} + +QList<SqliteStatement::FullObject> SqliteCreateVirtualTable::getFullObjectsInStatement() +{ + QList<FullObject> result; + + // Table object + FullObject fullObj = getFullObjectFromFullname(FullObject::TABLE); + + if (fullObj.isValid()) + result << fullObj; + + // Db object + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + { + result << fullObj; + dbTokenForFullObjects = fullObj.database; + } + + return result; +} + +void SqliteCreateVirtualTable::initName(const QString &name1, const QString &name2) +{ + if (!name2.isNull()) + { + database = name1; + table = name2; + } + else + table = name1; +} + +TokenList SqliteCreateVirtualTable::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("CREATE").withSpace().withKeyword("VIRTUAL").withSpace().withKeyword("TABLE"); + if (ifNotExistsKw) + builder.withKeyword("IF").withSpace().withKeyword("NOT").withSpace().withKeyword("EXISTS").withSpace(); + + if (!database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withKeyword("USING").withSpace().withOther(module, dialect); + if (!args.isEmpty()) + { + builder.withSpace(); + int i = 0; + for (const QString& arg : args) + { + if (i > 0) + builder.withOperator(",").withSpace(); + + builder.withTokens(Lexer::tokenize(arg, Dialect::Sqlite3)); + i++; + } + } + + builder.withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatevirtualtable.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatevirtualtable.h new file mode 100644 index 0000000..cb2d231 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatevirtualtable.h @@ -0,0 +1,42 @@ +#ifndef SQLITECREATEVIRTUALTABLE_H +#define SQLITECREATEVIRTUALTABLE_H + +#include "sqlitequery.h" + +#include <QString> +#include <QList> + +class API_EXPORT SqliteCreateVirtualTable : public SqliteQuery +{ + public: + SqliteCreateVirtualTable(); + SqliteCreateVirtualTable(const SqliteCreateVirtualTable& other); + SqliteCreateVirtualTable(bool ifNotExists, const QString& name1, const QString& name2, + const QString& name3); + SqliteCreateVirtualTable(bool ifNotExists, const QString& name1, const QString& name2, + const QString& name3, const QList<QString>& args); + + SqliteStatement* clone(); + + protected: + QStringList getTablesInStatement(); + QStringList getDatabasesInStatement(); + TokenList getTableTokensInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); + + private: + void initName(const QString& name1, const QString& name2); + + public: + bool ifNotExistsKw = false; + QString database = QString::null; + QString table = QString::null; + QString module = QString::null; + QList<QString> args; +}; + +typedef QSharedPointer<SqliteCreateVirtualTable> SqliteCreateVirtualTablePtr; + +#endif // SQLITECREATEVIRTUALTABLE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedeferrable.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedeferrable.cpp new file mode 100644 index 0000000..4ef4d51 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedeferrable.cpp @@ -0,0 +1,54 @@ +#include "sqlitedeferrable.h" + +QString sqliteDeferrable(SqliteDeferrable deferrable) +{ + switch (deferrable) + { + case SqliteDeferrable::NOT_DEFERRABLE: + return "NOT DEFERRABLE"; + case SqliteDeferrable::DEFERRABLE: + return "DEFERRABLE"; + case SqliteDeferrable::null: + break; + } + return QString::null; +} + +SqliteDeferrable sqliteDeferrable(const QString& deferrable) +{ + QString upper = deferrable.toUpper(); + if (upper == "NOT DEFERRABLE") + return SqliteDeferrable::NOT_DEFERRABLE; + + if (upper == "DEFERRABLE") + return SqliteDeferrable::DEFERRABLE; + + return SqliteDeferrable::null; +} + + +QString sqliteInitially(SqliteInitially initially) +{ + switch (initially) + { + case SqliteInitially::DEFERRED: + return "DEFERRED"; + case SqliteInitially::IMMEDIATE: + return "IMMEDIATE"; + case SqliteInitially::null: + break; + } + return QString::null; +} + +SqliteInitially sqliteInitially(const QString& initially) +{ + QString upper = initially.toUpper(); + if (upper == "DEFERRED") + return SqliteInitially::DEFERRED; + + if (upper == "IMMEDIATE") + return SqliteInitially::IMMEDIATE; + + return SqliteInitially::null; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedeferrable.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedeferrable.h new file mode 100644 index 0000000..8fdf06a --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedeferrable.h @@ -0,0 +1,27 @@ +#ifndef SQLITEDEFERRABLE_H +#define SQLITEDEFERRABLE_H + +#include "coreSQLiteStudio_global.h" +#include <QString> + +enum class SqliteDeferrable +{ + null, + NOT_DEFERRABLE, + DEFERRABLE +}; + +enum class SqliteInitially +{ + null, + DEFERRED, + IMMEDIATE +}; + +API_EXPORT QString sqliteDeferrable(SqliteDeferrable deferrable); +API_EXPORT SqliteDeferrable sqliteDeferrable(const QString& deferrable); + +API_EXPORT QString sqliteInitially(SqliteInitially initially); +API_EXPORT SqliteInitially sqliteInitially(const QString& initially); + +#endif // SQLITEDEFERRABLE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.cpp new file mode 100644 index 0000000..60e5878 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.cpp @@ -0,0 +1,137 @@ +#include "sqlitedelete.h" +#include "sqlitequerytype.h" +#include "sqliteexpr.h" +#include "parser/statementtokenbuilder.h" +#include "common/global.h" +#include "sqlitewith.h" + +SqliteDelete::SqliteDelete() +{ + queryType = SqliteQueryType::Delete; +} + +SqliteDelete::SqliteDelete(const SqliteDelete& other) : + SqliteQuery(other), database(other.database), table(other.table), indexedByKw(other.indexedByKw), notIndexedKw(other.notIndexedKw), + indexedBy(other.indexedBy) +{ + DEEP_COPY_FIELD(SqliteExpr, where); + DEEP_COPY_FIELD(SqliteWith, with); +} + +SqliteDelete::SqliteDelete(const QString &name1, const QString &name2, const QString& indexedByName, SqliteExpr *where, SqliteWith* with) + : SqliteDelete() +{ + init(name1, name2, where, with); + this->indexedBy = indexedByName; + this->indexedByKw = true; +} + +SqliteDelete::SqliteDelete(const QString &name1, const QString &name2, bool notIndexedKw, SqliteExpr *where, SqliteWith* with) + : SqliteDelete() +{ + init(name1, name2, where, with); + this->notIndexedKw = notIndexedKw; +} + +SqliteDelete::~SqliteDelete() +{ +} + +SqliteStatement*SqliteDelete::clone() +{ + return new SqliteDelete(*this); +} + +QStringList SqliteDelete::getTablesInStatement() +{ + return getStrListFromValue(table); +} + +QStringList SqliteDelete::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteDelete::getTableTokensInStatement() +{ + return getObjectTokenListFromFullname(); +} + +TokenList SqliteDelete::getDatabaseTokensInStatement() +{ + if (tokensMap.contains("fullname")) + return getDbTokenListFromFullname(); + + if (tokensMap.contains("nm")) + return extractPrintableTokens(tokensMap["nm"]); + + return TokenList(); +} + +QList<SqliteStatement::FullObject> SqliteDelete::getFullObjectsInStatement() +{ + QList<FullObject> result; + if (!tokensMap.contains("fullname")) + return result; + + // Table object + FullObject fullObj = getFullObjectFromFullname(FullObject::TABLE); + + if (fullObj.isValid()) + result << fullObj; + + // Db object + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + { + result << fullObj; + dbTokenForFullObjects = fullObj.database; + } + + return result; +} + +void SqliteDelete::init(const QString &name1, const QString &name2, SqliteExpr *where, SqliteWith* with) +{ + this->where = where; + if (where) + where->setParent(this); + + this->with = with; + if (with) + with->setParent(this); + + if (!name2.isNull()) + { + database = name1; + table = name2; + } + else + table = name1; +} + +TokenList SqliteDelete::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + if (with) + builder.withStatement(with); + + builder.withKeyword("DELETE").withSpace().withKeyword("FROM").withSpace(); + if (!database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(table, dialect); + + if (indexedByKw) + builder.withSpace().withKeyword("INDEXED").withSpace().withKeyword("BY").withSpace().withOther(indexedBy, dialect); + else if (notIndexedKw) + builder.withSpace().withKeyword("NOT").withSpace().withKeyword("INDEXED"); + + if (where) + builder.withSpace().withKeyword("WHERE").withStatement(where); + + builder.withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.h new file mode 100644 index 0000000..90e1385 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.h @@ -0,0 +1,45 @@ +#ifndef SQLITEDELETE_H +#define SQLITEDELETE_H + +#include "sqlitequery.h" + +#include <QString> + +class SqliteExpr; +class SqliteWith; + +class API_EXPORT SqliteDelete : public SqliteQuery +{ + public: + SqliteDelete(); + SqliteDelete(const SqliteDelete& other); + SqliteDelete(const QString& name1, const QString& name2, const QString& indexedByName, SqliteExpr* where, SqliteWith* with); + SqliteDelete(const QString& name1, const QString& name2, bool notIndexedKw, SqliteExpr* where, SqliteWith* with); + ~SqliteDelete(); + + SqliteStatement* clone(); + + protected: + QStringList getTablesInStatement(); + QStringList getDatabasesInStatement(); + TokenList getTableTokensInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); + + private: + void init(const QString& name1, const QString& name2, SqliteExpr* where, SqliteWith* with); + + public: + QString database = QString::null; + QString table = QString::null; + bool indexedByKw = false; + bool notIndexedKw = false; + QString indexedBy = QString::null; + SqliteExpr* where = nullptr; + SqliteWith* with = nullptr; +}; + +typedef QSharedPointer<SqliteDelete> SqliteDeletePtr; + +#endif // SQLITEDELETE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedetach.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedetach.cpp new file mode 100644 index 0000000..1f874d3 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedetach.cpp @@ -0,0 +1,48 @@ +#include "sqlitedetach.h" +#include "sqlitequerytype.h" +#include "sqliteexpr.h" +#include "common/global.h" +#include "parser/statementtokenbuilder.h" + +SqliteDetach::SqliteDetach() +{ + queryType = SqliteQueryType::Detach; +} + +SqliteDetach::SqliteDetach(const SqliteDetach& other) : + SqliteQuery(other), databaseKw(other.databaseKw) +{ + DEEP_COPY_FIELD(SqliteExpr, name); +} + +SqliteDetach::SqliteDetach(bool databaseKw, SqliteExpr *name) + :SqliteDetach() +{ + this->databaseKw = databaseKw; + this->name = name; + if (name) + name->setParent(this); +} + +SqliteDetach::~SqliteDetach() +{ +} + +SqliteStatement*SqliteDetach::clone() +{ + return new SqliteDetach(*this); +} + +TokenList SqliteDetach::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("DETACH").withSpace(); + + if (databaseKw) + builder.withKeyword("DATABASE").withSpace(); + + builder.withStatement(name).withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedetach.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedetach.h new file mode 100644 index 0000000..725cd47 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedetach.h @@ -0,0 +1,27 @@ +#ifndef SQLITEDETACH_H +#define SQLITEDETACH_H + +#include "sqlitequery.h" + +class SqliteExpr; + +class API_EXPORT SqliteDetach : public SqliteQuery +{ + public: + SqliteDetach(); + SqliteDetach(const SqliteDetach& other); + SqliteDetach(bool databaseKw, SqliteExpr* name); + ~SqliteDetach(); + + SqliteStatement* clone(); + + bool databaseKw = false; + SqliteExpr* name = nullptr; + + protected: + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteDetach> SqliteDetachPtr; + +#endif // SQLITEDETACH_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropindex.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropindex.cpp new file mode 100644 index 0000000..df924fe --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropindex.cpp @@ -0,0 +1,78 @@ +#include "sqlitedropindex.h" +#include "sqlitequerytype.h" + +#include <parser/statementtokenbuilder.h> + +SqliteDropIndex::SqliteDropIndex() +{ + queryType = SqliteQueryType::DropIndex; +} + +SqliteDropIndex::SqliteDropIndex(const SqliteDropIndex& other) : + SqliteQuery(other), ifExistsKw(other.ifExistsKw), database(other.database), index(other.index) +{ +} + +SqliteDropIndex::SqliteDropIndex(bool ifExists, const QString &name1, const QString &name2) + : SqliteDropIndex() +{ + this->ifExistsKw = ifExists; + if (!name2.isNull()) + { + database = name1; + index = name2; + } + else + index = name1; +} + +SqliteStatement*SqliteDropIndex::clone() +{ + return new SqliteDropIndex(*this); +} + +QStringList SqliteDropIndex::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteDropIndex::getDatabaseTokensInStatement() +{ + return getDbTokenListFromFullname(); +} + +QList<SqliteStatement::FullObject> SqliteDropIndex::getFullObjectsInStatement() +{ + QList<FullObject> result; + + // Index object + FullObject fullObj = getFullObjectFromFullname(FullObject::INDEX); + + if (fullObj.isValid()) + result << fullObj; + + // Db object + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + result << fullObj; + + return result; +} + + +TokenList SqliteDropIndex::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("DROP").withSpace().withKeyword("INDEX").withSpace(); + + if (ifExistsKw) + builder.withKeyword("IF").withSpace().withKeyword("EXISTS").withSpace(); + + if (!database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(index).withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropindex.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropindex.h new file mode 100644 index 0000000..9ce7065 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropindex.h @@ -0,0 +1,29 @@ +#ifndef SQLITEDROPINDEX_H +#define SQLITEDROPINDEX_H + +#include "sqlitequery.h" +#include <QString> + +class API_EXPORT SqliteDropIndex : public SqliteQuery +{ + public: + SqliteDropIndex(); + SqliteDropIndex(const SqliteDropIndex& other); + SqliteDropIndex(bool ifExistsKw, const QString& name1, const QString& name2); + + SqliteStatement* clone(); + + bool ifExistsKw = false; + QString database = QString::null; + QString index = QString::null; + + protected: + QStringList getDatabasesInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteDropIndex> SqliteDropIndexPtr; + +#endif // SQLITEDROPINDEX_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptable.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptable.cpp new file mode 100644 index 0000000..9c4aa37 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptable.cpp @@ -0,0 +1,86 @@ +#include "sqlitedroptable.h" +#include "sqlitequerytype.h" +#include <parser/statementtokenbuilder.h> + +SqliteDropTable::SqliteDropTable() +{ + queryType = SqliteQueryType::DropTable; +} + +SqliteDropTable::SqliteDropTable(const SqliteDropTable& other) : + SqliteQuery(other), ifExistsKw(other.ifExistsKw), database(other.database), table(other.table) +{ +} + +SqliteDropTable::SqliteDropTable(bool ifExists, const QString& name1, const QString& name2) + : SqliteDropTable() +{ + this->ifExistsKw = ifExists; + if (name2.isNull()) + this->table = name1; + else + { + this->database = name1; + this->table = name2; + } +} + +SqliteStatement* SqliteDropTable::clone() +{ + return new SqliteDropTable(*this); +} + +QStringList SqliteDropTable::getTablesInStatement() +{ + return getStrListFromValue(table); +} + +QStringList SqliteDropTable::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteDropTable::getTableTokensInStatement() +{ + return getObjectTokenListFromFullname(); +} + +TokenList SqliteDropTable::getDatabaseTokensInStatement() +{ + return getDbTokenListFromFullname(); +} + +QList<SqliteStatement::FullObject> SqliteDropTable::getFullObjectsInStatement() +{ + QList<FullObject> result; + + // Table object + FullObject fullObj = getFullObjectFromFullname(FullObject::TABLE); + + if (fullObj.isValid()) + result << fullObj; + + // Db object + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + result << fullObj; + + return result; +} + +TokenList SqliteDropTable::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("DROP").withSpace().withKeyword("TABLE").withSpace(); + + if (ifExistsKw) + builder.withKeyword("IF").withSpace().withKeyword("EXISTS").withSpace(); + + if (!database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(table).withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptable.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptable.h new file mode 100644 index 0000000..edc4da4 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptable.h @@ -0,0 +1,31 @@ +#ifndef SQLITEDROPTABLE_H +#define SQLITEDROPTABLE_H + +#include "sqlitequery.h" +#include <QString> + +class API_EXPORT SqliteDropTable : public SqliteQuery +{ + public: + SqliteDropTable(); + SqliteDropTable(const SqliteDropTable& other); + SqliteDropTable(bool ifExistsKw, const QString& name1, const QString& name2); + + SqliteStatement* clone(); + + bool ifExistsKw = false; + QString database = QString::null; + QString table = QString::null; + + protected: + QStringList getTablesInStatement(); + QStringList getDatabasesInStatement(); + TokenList getTableTokensInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteDropTable> SqliteDropTablePtr; + +#endif // SQLITEDROPTABLE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptrigger.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptrigger.cpp new file mode 100644 index 0000000..471d558 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptrigger.cpp @@ -0,0 +1,79 @@ +#include "sqlitedroptrigger.h" +#include "sqlitequerytype.h" + +#include <parser/statementtokenbuilder.h> + +SqliteDropTrigger::SqliteDropTrigger() +{ + queryType = SqliteQueryType::DropTrigger; +} + +SqliteDropTrigger::SqliteDropTrigger(const SqliteDropTrigger& other) : + SqliteQuery(other), ifExistsKw(other.ifExistsKw), database(other.database), trigger(other.trigger) +{ +} + +SqliteDropTrigger::SqliteDropTrigger(bool ifExists, const QString &name1, const QString &name2) + : SqliteDropTrigger() +{ + this->ifExistsKw = ifExists; + + if (name2.isNull()) + trigger = name1; + else + { + database = name1; + trigger = name2; + } +} + +SqliteStatement*SqliteDropTrigger::clone() +{ + return new SqliteDropTrigger(*this); +} + +QStringList SqliteDropTrigger::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteDropTrigger::getDatabaseTokensInStatement() +{ + return getDbTokenListFromFullname(); +} + +QList<SqliteStatement::FullObject> SqliteDropTrigger::getFullObjectsInStatement() +{ + QList<FullObject> result; + + // Table object + FullObject fullObj = getFullObjectFromFullname(FullObject::TRIGGER); + + if (fullObj.isValid()) + result << fullObj; + + // Db object + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + result << fullObj; + + return result; +} + + +TokenList SqliteDropTrigger::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("DROP").withSpace().withKeyword("TRIGGER").withSpace(); + + if (ifExistsKw) + builder.withKeyword("IF").withSpace().withKeyword("EXISTS").withSpace(); + + if (!database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(trigger).withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptrigger.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptrigger.h new file mode 100644 index 0000000..5221959 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptrigger.h @@ -0,0 +1,29 @@ +#ifndef SQLITEDROPTRIGGER_H +#define SQLITEDROPTRIGGER_H + +#include "sqlitequery.h" +#include <QString> + +class API_EXPORT SqliteDropTrigger : public SqliteQuery +{ + public: + SqliteDropTrigger(); + SqliteDropTrigger(const SqliteDropTrigger& other); + SqliteDropTrigger(bool ifExistsKw, const QString& name1, const QString& name2); + + SqliteStatement* clone(); + + bool ifExistsKw = false; + QString database = QString::null; + QString trigger = QString::null; + + protected: + QStringList getDatabasesInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteDropTrigger> SqliteDropTriggerPtr; + +#endif // SQLITEDROPTRIGGER_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropview.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropview.cpp new file mode 100644 index 0000000..06d70db --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropview.cpp @@ -0,0 +1,85 @@ +#include "sqlitedropview.h" +#include "sqlitequerytype.h" + +#include <parser/statementtokenbuilder.h> + +SqliteDropView::SqliteDropView() +{ + queryType = SqliteQueryType::DropView; +} + +SqliteDropView::SqliteDropView(const SqliteDropView& other) : + SqliteQuery(other), ifExistsKw(other.ifExistsKw), database(other.database), view(other.view) +{ +} + +SqliteDropView::SqliteDropView(bool ifExists, const QString &name1, const QString &name2) + : SqliteDropView() +{ + this->ifExistsKw = ifExists; + + if (name2.isNull()) + view = name1; + else + { + database = name1; + view = name2; + } +} + +SqliteStatement*SqliteDropView::clone() +{ + return new SqliteDropView(*this); +} + +QStringList SqliteDropView::getDatabasesInStatement() +{ + if (dialect == Dialect::Sqlite2) + return QStringList(); + + return getStrListFromValue(database); +} + +TokenList SqliteDropView::getDatabaseTokensInStatement() +{ + if (dialect == Dialect::Sqlite2) + return TokenList(); + + return getDbTokenListFromFullname(); +} + +QList<SqliteStatement::FullObject> SqliteDropView::getFullObjectsInStatement() +{ + QList<FullObject> result; + + // Table object + FullObject fullObj = getFullObjectFromFullname(FullObject::VIEW); + + if (fullObj.isValid()) + result << fullObj; + + // Db object + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + result << fullObj; + + return result; +} + + +TokenList SqliteDropView::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("DROP").withSpace().withKeyword("VIEW").withSpace(); + + if (ifExistsKw) + builder.withKeyword("IF").withSpace().withKeyword("EXISTS").withSpace(); + + if (!database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(view).withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropview.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropview.h new file mode 100644 index 0000000..853ccef --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropview.h @@ -0,0 +1,29 @@ +#ifndef SQLITEDROPVIEW_H +#define SQLITEDROPVIEW_H + +#include "sqlitequery.h" +#include <QString> + +class API_EXPORT SqliteDropView : public SqliteQuery +{ + public: + SqliteDropView(); + SqliteDropView(const SqliteDropView& other); + SqliteDropView(bool ifExistsKw, const QString& name1, const QString& name2); + + SqliteStatement* clone(); + + bool ifExistsKw = false; + QString database = QString::null; + QString view = QString::null; + + protected: + QStringList getDatabasesInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteDropView> SqliteDropViewPtr; + +#endif // SQLITEDROPVIEW_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteemptyquery.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteemptyquery.cpp new file mode 100644 index 0000000..628aea5 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteemptyquery.cpp @@ -0,0 +1,27 @@ +#include "sqliteemptyquery.h" +#include "sqlitequerytype.h" +#include "parser/statementtokenbuilder.h" +#include "common/unused.h" + +SqliteEmptyQuery::SqliteEmptyQuery() +{ + queryType = SqliteQueryType::EMPTY; +} + +SqliteEmptyQuery::SqliteEmptyQuery(const SqliteEmptyQuery& other) : + SqliteEmptyQuery() +{ + UNUSED(other); +} + +SqliteStatement*SqliteEmptyQuery::clone() +{ + return new SqliteEmptyQuery(*this); +} + +TokenList SqliteEmptyQuery::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + builder.withOperator(";"); + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteemptyquery.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteemptyquery.h new file mode 100644 index 0000000..3380532 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteemptyquery.h @@ -0,0 +1,19 @@ +#ifndef SQLITEEMPTYQUERY_H +#define SQLITEEMPTYQUERY_H + +#include "sqlitequery.h" + +class API_EXPORT SqliteEmptyQuery : public SqliteQuery +{ + public: + SqliteEmptyQuery(); + SqliteEmptyQuery(const SqliteEmptyQuery& other); + + SqliteStatement* clone(); + + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteEmptyQuery> SqliteEmptyQueryPtr; + +#endif // SQLITEEMPTYQUERY_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp new file mode 100644 index 0000000..969f029 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp @@ -0,0 +1,644 @@ +#include "sqliteexpr.h" +#include "sqliteraise.h" +#include "sqliteselect.h" +#include "sqlitecolumntype.h" +#include "parser/statementtokenbuilder.h" +#include "common/utils_sql.h" +#include "common/global.h" +#include <QDebug> + +SqliteExpr::SqliteExpr() +{ +} + +SqliteExpr::SqliteExpr(const SqliteExpr& other) : + SqliteStatement(other), + mode(other.mode), literalValue(other.literalValue), literalNull(other.literalNull), bindParam(other.bindParam), database(other.database), table(other.table), + column(other.column), unaryOp(other.unaryOp), binaryOp(other.binaryOp), function(other.function), collation(other.collation), + ctime(other.ctime), distinctKw(other.distinctKw), allKw(other.allKw), star(other.star), notKw(other.notKw), like(other.like), + notNull(other.notNull), possibleDoubleQuotedString(other.possibleDoubleQuotedString) +{ + DEEP_COPY_FIELD(SqliteColumnType, columnType); + DEEP_COPY_FIELD(SqliteExpr, expr1); + DEEP_COPY_FIELD(SqliteExpr, expr2); + DEEP_COPY_FIELD(SqliteExpr, expr3); + DEEP_COPY_COLLECTION(SqliteExpr, exprList); + DEEP_COPY_FIELD(SqliteSelect, select); + DEEP_COPY_FIELD(SqliteRaise, raiseFunction); +} + +SqliteExpr::~SqliteExpr() +{ +} + +SqliteExpr::LikeOp SqliteExpr::likeOp(const QString &value) +{ + QString upper = value.toUpper(); + if (upper == "LIKE") + return SqliteExpr::LikeOp::LIKE; + else if (upper == "GLOB") + return SqliteExpr::LikeOp::GLOB; + else if (upper == "REGEXP") + return SqliteExpr::LikeOp::REGEXP; + else if (upper == "MATCH") + return SqliteExpr::LikeOp::MATCH; + else + return SqliteExpr::LikeOp::null; +} + +QString SqliteExpr::likeOp(SqliteExpr::LikeOp value) +{ + switch (value) + { + case SqliteExpr::LikeOp::LIKE: + return "LIKE"; + case SqliteExpr::LikeOp::GLOB: + return "GLOB"; + case SqliteExpr::LikeOp::REGEXP: + return "REGEXP"; + case SqliteExpr::LikeOp::MATCH: + return "MATCH"; + default: + return QString::null; + } +} + +SqliteExpr::NotNull SqliteExpr::notNullOp(const QString &value) +{ + if (value == "ISNULL") + return SqliteExpr::NotNull::ISNULL; + else if (value == "NOTNULL") + return SqliteExpr::NotNull::NOTNULL; + else if (value == "NOT NULL") + return SqliteExpr::NotNull::NOT_NULL; + else + return SqliteExpr::NotNull::null; +} + +QString SqliteExpr::notNullOp(SqliteExpr::NotNull value) +{ + switch (value) + { + case SqliteExpr::NotNull::ISNULL: + return "ISNULL"; + case SqliteExpr::NotNull::NOT_NULL: + return "NOT NULL"; + case SqliteExpr::NotNull::NOTNULL: + return "NOTNULL"; + default: + return QString::null; + } +} + +SqliteStatement*SqliteExpr::clone() +{ + return new SqliteExpr(*this); +} + +void SqliteExpr::initLiteral(const QVariant &value) +{ + mode = SqliteExpr::Mode::LITERAL_VALUE; + if (value.isNull()) + initNull(); + + literalValue = value; +} + +void SqliteExpr::initNull() +{ + literalNull = true; +} + +void SqliteExpr::initCTime(const QString &name) +{ + mode = SqliteExpr::Mode::CTIME; + ctime = name; +} + +void SqliteExpr::initSubExpr(SqliteExpr *expr) +{ + mode = SqliteExpr::Mode::SUB_EXPR; + expr1 = expr; + if (expr) + expr->setParent(this); +} + +void SqliteExpr::initBindParam(const QString& value) +{ + mode = SqliteExpr::Mode::BIND_PARAM; + bindParam = value; +} + +void SqliteExpr::initCollate(SqliteExpr *expr, const QString& value) +{ + mode = SqliteExpr::Mode::COLLATE; + expr1 = expr; + collation = value; + if (expr) + expr->setParent(this); +} + +void SqliteExpr::initCast(SqliteExpr *expr, SqliteColumnType *type) +{ + mode = SqliteExpr::Mode::CAST; + expr1 = expr; + columnType = type; + if (expr) + expr->setParent(this); +} + +void SqliteExpr::initFunction(const QString& fnName, int distinct, const QList<SqliteExpr*>& exprList) +{ + mode = SqliteExpr::Mode::FUNCTION; + function = fnName; + this->exprList = exprList; + if (distinct == 1) + distinctKw = true; + else if (distinct == 2) + allKw = true; + + foreach (SqliteExpr* expr, exprList) + expr->setParent(this); +} + +void SqliteExpr::initFunction(const QString& fnName, bool star) +{ + mode = SqliteExpr::Mode::FUNCTION; + function = fnName; + this->star = star; +} + +void SqliteExpr::initBinOp(SqliteExpr *expr1, const QString& op, SqliteExpr *expr2) +{ + mode = SqliteExpr::Mode::BINARY_OP; + this->expr1 = expr1; + this->expr2 = expr2; + binaryOp = op; + if (expr1) + expr1->setParent(this); + + if (expr2) + expr2->setParent(this); +} + +void SqliteExpr::initUnaryOp(SqliteExpr *expr, const QString& op) +{ + mode = SqliteExpr::Mode::UNARY_OP; + expr1 = expr; + unaryOp = op; + if (expr) + expr->setParent(this); +} + +void SqliteExpr::initLike(SqliteExpr *expr1, bool notKw, LikeOp likeOp, SqliteExpr *expr2, SqliteExpr *expr3) +{ + mode = SqliteExpr::Mode::LIKE; + this->expr1 = expr1; + this->expr2 = expr2; + this->expr3 = expr3; + this->notKw = notKw; + this->like = likeOp; + if (expr1) + expr1->setParent(this); + + if (expr2) + expr2->setParent(this); + + if (expr3) + expr3->setParent(this); +} + +void SqliteExpr::initNull(SqliteExpr *expr, const QString& value) +{ + mode = SqliteExpr::Mode::NOTNULL; + expr1 = expr; + notNull = notNullOp(value); + if (expr) + expr->setParent(this); +} + +void SqliteExpr::initIs(SqliteExpr *expr1, bool notKw, SqliteExpr *expr2) +{ + mode = SqliteExpr::Mode::IS; + this->expr1 = expr1; + this->notKw = notKw; + this->expr2 = expr2; + if (expr1) + expr1->setParent(this); + + if (expr2) + expr2->setParent(this); +} + +void SqliteExpr::initBetween(SqliteExpr *expr1, bool notKw, SqliteExpr *expr2, SqliteExpr *expr3) +{ + mode = SqliteExpr::Mode::BETWEEN; + this->expr1 = expr1; + this->expr2 = expr2; + this->expr3 = expr3; + this->notKw = notKw; + if (expr1) + expr1->setParent(this); + + if (expr2) + expr2->setParent(this); + + if (expr3) + expr3->setParent(this); +} + +void SqliteExpr::initIn(SqliteExpr *expr, bool notKw, const QList<SqliteExpr*>& exprList) +{ + mode = SqliteExpr::Mode::IN; + expr1 = expr; + this->notKw = notKw; + this->exprList = exprList; + foreach (SqliteExpr* expr, exprList) + expr->setParent(this); +} + +void SqliteExpr::initIn(SqliteExpr *expr, bool notKw, SqliteSelect *select) +{ + mode = SqliteExpr::Mode::IN; + expr1 = expr; + this->notKw = notKw; + this->select = select; + if (expr) + expr->setParent(this); + + if (select) + select->setParent(this); +} + +void SqliteExpr::initIn(SqliteExpr *expr, bool notKw, const QString& name1, const QString& name2) +{ + mode = SqliteExpr::Mode::IN; + expr1 = expr; + this->notKw = notKw; + if (!name2.isNull()) + { + database = name1; + table = name2; + } + else + table = name1; + + if (expr) + expr->setParent(this); +} + +void SqliteExpr::initExists(SqliteSelect *select) +{ + mode = SqliteExpr::Mode::EXISTS; + this->select = select; + if (select) + select->setParent(this); +} + +void SqliteExpr::initSubSelect(SqliteSelect *select) +{ + mode = SqliteExpr::Mode::SUB_SELECT; + this->select = select; + if (select) + select->setParent(this); +} + +void SqliteExpr::initCase(SqliteExpr *expr1, const QList<SqliteExpr*>& exprList, SqliteExpr *expr2) +{ + mode = SqliteExpr::Mode::CASE; + this->expr1 = expr1; + this->expr2 = expr2; + this->exprList = exprList; + if (expr1) + expr1->setParent(this); + + if (expr2) + expr2->setParent(this); + + foreach (SqliteExpr* expr, exprList) + expr->setParent(this); +} + +void SqliteExpr::initRaise(const QString& type, const QString& text) +{ + mode = SqliteExpr::Mode::RAISE; + raiseFunction = new SqliteRaise(type, text); +} + +QStringList SqliteExpr::getColumnsInStatement() +{ + return getStrListFromValue(column); +} + +QStringList SqliteExpr::getTablesInStatement() +{ + return getStrListFromValue(table); +} + +QStringList SqliteExpr::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteExpr::getColumnTokensInStatement() +{ + TokenList list; + if (!column.isNull()) + { + if (!table.isNull()) + { + if (!database.isNull()) + list << tokens[4]; + else + list << tokens[2]; + } + else + list << tokens[0]; + } + return list; +} + +TokenList SqliteExpr::getTableTokensInStatement() +{ + TokenList list; + if (!table.isNull()) + { + if (!database.isNull()) + list << tokens[2]; + else + list << tokens[0]; + } + + return list; +} + +TokenList SqliteExpr::getDatabaseTokensInStatement() +{ + TokenList list; + if (!database.isNull()) + list << tokens[0]; + + return list; +} + +QList<SqliteStatement::FullObject> SqliteExpr::getFullObjectsInStatement() +{ + QList<FullObject> result; + if (mode != Mode::ID) + return result; + + if (!table.isNull()) + { + if (!database.isNull()) + { + FullObject dbFullObject = getDbFullObject(tokens[0]); + result << dbFullObject; + dbTokenForFullObjects = dbFullObject.database; + + result << getFullObject(FullObject::TABLE, dbTokenForFullObjects, tokens[2]); + } + else + result << getFullObject(FullObject::TABLE, dbTokenForFullObjects, tokens[0]); + } + + return result; +} + +void SqliteExpr::initId(const QString &db, const QString &table, const QString &column) +{ + mode = SqliteExpr::Mode::ID; + database = db; + this->table = table; + this->column = column; +} + +void SqliteExpr::initId(const QString& table, const QString& column) +{ + mode = SqliteExpr::Mode::ID; + this->table = table; + this->column = column; +} + +void SqliteExpr::initId(const QString& column) +{ + mode = SqliteExpr::Mode::ID; + this->column = column; +} + +TokenList SqliteExpr::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + switch (mode) + { + case SqliteExpr::Mode::null: + break; + case SqliteExpr::Mode::LITERAL_VALUE: + { + if (literalNull) + builder.withKeyword("NULL"); + else + builder.withLiteralValue(literalValue); + break; + } + case SqliteExpr::Mode::CTIME: + builder.withKeyword(ctime.toUpper()); + break; + case SqliteExpr::Mode::BIND_PARAM: + builder.withBindParam(bindParam); + break; + case SqliteExpr::Mode::ID: + builder.withTokens(rebuildId()); + break; + case SqliteExpr::Mode::UNARY_OP: + builder.withOperator(unaryOp).withSpace().withStatement(expr1); + break; + case SqliteExpr::Mode::BINARY_OP: + builder.withStatement(expr1).withSpace().withOperator(binaryOp).withSpace().withStatement(expr2); + break; + case SqliteExpr::Mode::FUNCTION: + builder.withOther(function).withParLeft().withStatementList(exprList).withParRight(); + break; + case SqliteExpr::Mode::SUB_EXPR: + builder.withParLeft().withStatement(expr1).withParRight(); + break; + case SqliteExpr::Mode::CAST: + builder.withKeyword("CAST").withSpace().withParLeft().withStatement(expr1).withSpace().withKeyword("AS") + .withStatement(columnType).withParRight(); + break; + case SqliteExpr::Mode::COLLATE: + builder.withStatement(expr1).withSpace().withKeyword("COLLATE").withSpace().withOther(collation, dialect); + break; + case SqliteExpr::Mode::LIKE: + builder.withTokens(rebuildLike()); + break; + case SqliteExpr::Mode::NULL_: + builder.withKeyword("NULL"); + break; + case SqliteExpr::Mode::NOTNULL: + builder.withTokens(rebuildNotNull()); + break; + case SqliteExpr::Mode::IS: + builder.withTokens(rebuildIs()); + break; + case SqliteExpr::Mode::BETWEEN: + builder.withTokens(rebuildBetween()); + break; + case SqliteExpr::Mode::IN: + builder.withTokens(rebuildIn()); + break; + case SqliteExpr::Mode::EXISTS: + builder.withKeyword("EXISTS").withParLeft().withStatement(select).withParRight(); + break; + case SqliteExpr::Mode::CASE: + builder.withTokens(rebuildCase()); + break; + case SqliteExpr::Mode::SUB_SELECT: + builder.withParLeft().withStatement(select).withParRight(); + break; + case SqliteExpr::Mode::RAISE: + builder.withStatement(raiseFunction); + break; + } + + return builder.build(); +} + +void SqliteExpr::evaluatePostParsing() +{ + if (tokens.size() > 0) + { + QString val = tokens.first()->value; + if (val[0] == '"' && val[0] == val[val.length() - 1]) + possibleDoubleQuotedString = true; + } +} + +TokenList SqliteExpr::rebuildId() +{ + StatementTokenBuilder builder; + if (!database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + if (!table.isNull()) + builder.withOther(table, dialect).withOperator("."); + + if (possibleDoubleQuotedString) + builder.withStringPossiblyOther(column, dialect); + else + builder.withOther(column, dialect); + + return builder.build(); +} + +TokenList SqliteExpr::rebuildLike() +{ + StatementTokenBuilder builder; + builder.withStatement(expr1).withSpace(); + if (notKw) + builder.withKeyword("NOT").withSpace(); + + builder.withKeyword(likeOp(like)).withSpace().withStatement(expr2); + if (expr3) + builder.withSpace().withKeyword("ESCAPE").withStatement(expr3); + + return builder.build(); +} + +TokenList SqliteExpr::rebuildNotNull() +{ + StatementTokenBuilder builder; + switch (notNull) + { + case SqliteExpr::NotNull::ISNULL: + builder.withKeyword("ISNULL"); + break; + case SqliteExpr::NotNull::NOT_NULL: + builder.withKeyword("NOT").withSpace().withKeyword("NULL"); + break; + case SqliteExpr::NotNull::NOTNULL: + builder.withKeyword("NOTNULL"); + break; + case SqliteExpr::NotNull::null: + break; + } + return builder.build(); +} + +TokenList SqliteExpr::rebuildIs() +{ + StatementTokenBuilder builder; + builder.withStatement(expr1).withSpace().withKeyword("IS"); + if (notKw) + builder.withSpace().withKeyword("NOT"); + + builder.withStatement(expr2); + return builder.build(); +} + +TokenList SqliteExpr::rebuildBetween() +{ + StatementTokenBuilder builder; + builder.withStatement(expr1); + + if (notKw) + builder.withSpace().withKeyword("NOT"); + + builder.withSpace().withKeyword("BETWEEN").withStatement(expr2).withSpace().withKeyword("AND").withStatement(expr3); + return builder.build(); +} + +TokenList SqliteExpr::rebuildIn() +{ + StatementTokenBuilder builder; + builder.withStatement(expr1); + + if (notKw) + builder.withSpace().withKeyword("NOT"); + + builder.withSpace().withKeyword("IN").withSpace(); + if (select) + { + builder.withParLeft().withStatement(select).withParRight(); + } + else if (exprList.size() > 0) + { + builder.withParLeft().withStatementList(exprList).withParRight(); + } + else + { + if (!database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(table, dialect); + } + return builder.build(); +} + +TokenList SqliteExpr::rebuildCase() +{ + StatementTokenBuilder builder; + builder.withKeyword("CASE"); + if (expr1) + builder.withStatement(expr1); + + builder.withSpace(); + + bool then = false; + foreach (SqliteExpr* expr, exprList) + { + if (then) + builder.withKeyword("THEN"); + else + builder.withKeyword("WHEN"); + + builder.withStatement(expr).withSpace(); + then = !then; + } + + if (expr2) + builder.withKeyword("ELSE").withStatement(expr2).withSpace(); + + builder.withKeyword("END"); + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.h new file mode 100644 index 0000000..f57004f --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.h @@ -0,0 +1,144 @@ +#ifndef SQLITEEXPR_H +#define SQLITEEXPR_H + +#include "sqlitestatement.h" +#include <QString> +#include <QVariant> +#include <QList> + +class SqliteSelect; +class SqliteColumnType; +class SqliteRaise; + +class API_EXPORT SqliteExpr : public SqliteStatement +{ + public: + enum class Mode + { + null, + LITERAL_VALUE, + CTIME, + BIND_PARAM, + ID, + UNARY_OP, + BINARY_OP, + FUNCTION, + SUB_EXPR, + CAST, + COLLATE, // in Sqlite2 exists only in expr of sortlist + LIKE, + NULL_, + NOTNULL, + IS, + BETWEEN, + IN, + EXISTS, + CASE, + SUB_SELECT, + RAISE + }; + + enum class NotNull + { + ISNULL, + NOT_NULL, + NOTNULL, + null + }; + + enum class LikeOp + { + LIKE, + GLOB, + REGEXP, + MATCH, + null + }; + + SqliteExpr(); + SqliteExpr(const SqliteExpr& other); + ~SqliteExpr(); + + static LikeOp likeOp(const QString& value); + static QString likeOp(LikeOp value); + static NotNull notNullOp(const QString& value); + static QString notNullOp(NotNull value); + + SqliteStatement* clone(); + void initLiteral(const QVariant& value); + void initNull(); + void initCTime(const QString& name); + void initSubExpr(SqliteExpr* expr); + void initId(const QString& db, const QString& table, const QString& column); + void initId(const QString& table, const QString& column); + void initId(const QString& column); + void initBindParam(const QString& value); + void initCollate(SqliteExpr* expr, const QString& value); + 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 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); + void initNull(SqliteExpr* expr, const QString& value); + void initIs(SqliteExpr* expr1, bool notKw, SqliteExpr* expr2); + void initBetween(SqliteExpr* expr1, bool notKw, SqliteExpr* expr2, SqliteExpr* expr3); + void initIn(SqliteExpr* expr, bool notKw, const QList<SqliteExpr*>& exprList); + void initIn(SqliteExpr* expr, bool notKw, SqliteSelect* select); + void initIn(SqliteExpr* expr, bool notKw, const QString& name1, const QString& name2); + 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); + + 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; + SqliteColumnType* columnType = nullptr; + SqliteExpr* expr1 = nullptr; + SqliteExpr* expr2 = nullptr; + SqliteExpr* expr3 = nullptr; + QList<SqliteExpr*> exprList; + SqliteSelect* select = nullptr; + bool distinctKw = false; + bool allKw = false; // alias for DISTINCT as for sqlite3 grammar + bool star = false; + bool notKw = false; + LikeOp like = LikeOp::null; + NotNull notNull = NotNull::null; + SqliteRaise* raiseFunction = nullptr; + bool possibleDoubleQuotedString = false; + + protected: + QStringList getColumnsInStatement(); + QStringList getTablesInStatement(); + QStringList getDatabasesInStatement(); + TokenList getColumnTokensInStatement(); + TokenList getTableTokensInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); + void evaluatePostParsing(); + + private: + TokenList rebuildId(); + TokenList rebuildLike(); + TokenList rebuildNotNull(); + TokenList rebuildIs(); + TokenList rebuildBetween(); + TokenList rebuildIn(); + TokenList rebuildCase(); +}; + +typedef QSharedPointer<SqliteExpr> SqliteExprPtr; + +#endif // SQLITEEXPR_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteforeignkey.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteforeignkey.cpp new file mode 100644 index 0000000..9a29db2 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteforeignkey.cpp @@ -0,0 +1,187 @@ +#include "sqliteforeignkey.h" +#include "parser/statementtokenbuilder.h" +#include "common/global.h" +#include <QDebug> + +SqliteForeignKey::Condition::Condition(SqliteForeignKey::Condition::Action action, SqliteForeignKey::Condition::Reaction reaction) +{ + this->action = action; + this->reaction = reaction; +} + +SqliteForeignKey::Condition::Condition(const QString &name) +{ + this->action = SqliteForeignKey::Condition::MATCH; + this->name = name; +} + +SqliteForeignKey::Condition::Condition(const SqliteForeignKey::Condition& other) : + SqliteStatement(other), action(other.action), name(other.name), reaction(other.reaction) +{ +} + +QString SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::Reaction reaction) +{ + switch (reaction) + { + case SqliteForeignKey::Condition::SET_NULL: + return "SET NULL"; + case SqliteForeignKey::Condition::SET_DEFAULT: + return "SET DEFAULT"; + case SqliteForeignKey::Condition::CASCADE: + return "CASCADE"; + case SqliteForeignKey::Condition::RESTRICT: + return "RESTRICT"; + case SqliteForeignKey::Condition::NO_ACTION: + return "NO ACTION"; + } + return QString::null; +} + +SqliteForeignKey::Condition::Reaction SqliteForeignKey::Condition::toEnum(const QString& reaction) +{ + QString upper = reaction.toUpper(); + if (upper == "SET NULL") + return SET_NULL; + + if (upper == "SET DEFAULT") + return SET_DEFAULT; + + if (upper == "CASCADE") + return CASCADE; + + if (upper == "RESTRICT") + return RESTRICT; + + if (upper == "NO ACTION") + return NO_ACTION; + + qCritical() << "Unknown Reaction value. Cannot convert to Condition::Reaction. Returning default, the SET_NULL."; + return SET_NULL; +} + +SqliteStatement*SqliteForeignKey::Condition::clone() +{ + return new SqliteForeignKey::Condition(*this); +} + +SqliteForeignKey::SqliteForeignKey() +{ +} + +SqliteForeignKey::SqliteForeignKey(const SqliteForeignKey& other) : + SqliteStatement(other), foreignTable(other.foreignTable), deferrable(other.deferrable), initially(other.initially) +{ + DEEP_COPY_COLLECTION(SqliteIndexedColumn, indexedColumns); + DEEP_COPY_COLLECTION(Condition, conditions); +} + +SqliteForeignKey::~SqliteForeignKey() +{ +} + +SqliteStatement*SqliteForeignKey::clone() +{ + return new SqliteForeignKey(*this); +} + +QStringList SqliteForeignKey::getTablesInStatement() +{ + return getStrListFromValue(foreignTable); +} + +TokenList SqliteForeignKey::getTableTokensInStatement() +{ + return parentStatement()->getContextTableTokens(false, false); +} + +QList<SqliteStatement::FullObject> SqliteForeignKey::getFullObjectsInStatement() +{ + QList<FullObject> result; + + // Table object + FullObject fullObj; + TokenList tokens = getTableTokensInStatement(); + if (tokens.size() > 0) + fullObj = getFullObject(FullObject::TABLE, dbTokenForFullObjects, tokens[0]); + + if (fullObj.isValid()) + result << fullObj; + + return result; +} + +TokenList SqliteForeignKey::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("REFERENCES").withSpace().withOther(foreignTable, dialect); + + if (indexedColumns.size() > 0) + builder.withSpace().withParLeft().withStatementList(indexedColumns).withParRight(); + + if (conditions.size() > 0) + builder.withSpace().withStatementList(conditions, ""); + + if (deferrable != SqliteDeferrable::null) + { + if (deferrable == SqliteDeferrable::NOT_DEFERRABLE) + builder.withSpace().withKeyword("NOT").withSpace().withKeyword("DEFERRABLE"); + else if (deferrable == SqliteDeferrable::DEFERRABLE) + builder.withSpace().withKeyword("DEFERRABLE"); + + if (initially != SqliteInitially::null) + builder.withSpace().withKeyword("INITIALLY").withSpace().withKeyword(sqliteInitially(initially)); + } + + return builder.build(); +} + + +TokenList SqliteForeignKey::Condition::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + switch (action) + { + case SqliteForeignKey::Condition::UPDATE: + builder.withKeyword("ON").withSpace().withKeyword("UPDATE").withSpace(); + applyReactionToBuilder(builder); + break; + case SqliteForeignKey::Condition::INSERT: + builder.withKeyword("ON").withSpace().withKeyword("INSERT").withSpace(); + applyReactionToBuilder(builder); + break; + case SqliteForeignKey::Condition::DELETE: + builder.withKeyword("ON").withSpace().withKeyword("DELETE").withSpace(); + applyReactionToBuilder(builder); + break; + case SqliteForeignKey::Condition::MATCH: + builder.withKeyword("MATCH").withSpace().withOther(name); + break; + } + + return builder.build(); +} + +void SqliteForeignKey::Condition::applyReactionToBuilder(StatementTokenBuilder& builder) +{ + switch (reaction) + { + case SqliteForeignKey::Condition::SET_NULL: + builder.withKeyword("SET").withSpace().withKeyword("NULL"); + break; + case SqliteForeignKey::Condition::SET_DEFAULT: + builder.withKeyword("SET").withSpace().withKeyword("DEFAULT"); + break; + case SqliteForeignKey::Condition::CASCADE: + builder.withKeyword("CASCADE"); + break; + case SqliteForeignKey::Condition::RESTRICT: + builder.withKeyword("RESTRICT"); + break; + case SqliteForeignKey::Condition::NO_ACTION: + builder.withKeyword("NO").withSpace().withKeyword("ACTION"); + break; + } +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteforeignkey.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteforeignkey.h new file mode 100644 index 0000000..18e0bcb --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteforeignkey.h @@ -0,0 +1,75 @@ +#ifndef SQLITEFOREIGNKEY_H +#define SQLITEFOREIGNKEY_H + +#include "sqlitestatement.h" +#include "sqliteindexedcolumn.h" +#include "sqlitedeferrable.h" +#include "parser/statementtokenbuilder.h" +#include <QList> +#include <QString> + +class API_EXPORT SqliteForeignKey : public SqliteStatement +{ + public: + class API_EXPORT Condition : public SqliteStatement + { + public: + enum Action + { + UPDATE, + INSERT, + DELETE, + MATCH + }; + + enum Reaction + { + SET_NULL, + SET_DEFAULT, + CASCADE, + RESTRICT, + NO_ACTION + }; + + Condition(Action action, Reaction reaction); + explicit Condition(const QString& name); + Condition(const Condition& other); + + static QString toString(Reaction reaction); + static Reaction toEnum(const QString& reaction); + + SqliteStatement* clone(); + + Action action; + QString name = QString::null; + Reaction reaction = NO_ACTION; + + protected: + TokenList rebuildTokensFromContents(); + + private: + void applyReactionToBuilder(StatementTokenBuilder& builder); + }; + + SqliteForeignKey(); + SqliteForeignKey(const SqliteForeignKey& other); + ~SqliteForeignKey(); + + SqliteStatement* clone(); + + QString foreignTable = QString::null; + QList<SqliteIndexedColumn*> indexedColumns; + QList<Condition*> conditions; + SqliteDeferrable deferrable = SqliteDeferrable::null; // Those two are for table constraint only, + SqliteInitially initially = SqliteInitially::null; // because column has its own fields for that. + + protected: + QStringList getTablesInStatement(); + TokenList getTableTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteForeignKey> SqliteForeignKeyPtr; + +#endif // SQLITEFOREIGNKEY_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteindexedcolumn.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteindexedcolumn.cpp new file mode 100644 index 0000000..5e65eab --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteindexedcolumn.cpp @@ -0,0 +1,52 @@ +#include "sqliteindexedcolumn.h" +#include "parser/statementtokenbuilder.h" + +SqliteIndexedColumn::SqliteIndexedColumn() +{ +} + +SqliteIndexedColumn::SqliteIndexedColumn(const SqliteIndexedColumn& other) : + SqliteStatement(other), name(other.name), sortOrder(other.sortOrder), collate(other.collate) +{ +} + +SqliteIndexedColumn::SqliteIndexedColumn(const QString &name, const QString &collate, SqliteSortOrder sortOrder) + : SqliteIndexedColumn() +{ + this->name = name; + this->sortOrder = sortOrder; + this->collate = collate; +} + +SqliteIndexedColumn::SqliteIndexedColumn(const QString& name) + : SqliteIndexedColumn() +{ + this->name = name; +} + +SqliteStatement*SqliteIndexedColumn::clone() +{ + return new SqliteIndexedColumn(*this); +} + +QStringList SqliteIndexedColumn::getColumnsInStatement() +{ + return getStrListFromValue(name); +} + +TokenList SqliteIndexedColumn::getColumnTokensInStatement() +{ + return getTokenListFromNamedKey("nm"); +} + + +TokenList SqliteIndexedColumn::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + builder.withOther(name, dialect); + if (!collate.isNull()) + builder.withSpace().withKeyword("COLLATE").withSpace().withOther(collate, dialect); + + builder.withSortOrder(sortOrder); + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteindexedcolumn.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteindexedcolumn.h new file mode 100644 index 0000000..c013d17 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteindexedcolumn.h @@ -0,0 +1,30 @@ +#ifndef SQLITEINDEXEDCOLUMN_H +#define SQLITEINDEXEDCOLUMN_H + +#include "sqlitestatement.h" +#include "sqlitesortorder.h" +#include <QString> + +class API_EXPORT SqliteIndexedColumn : public SqliteStatement +{ + public: + SqliteIndexedColumn(); + SqliteIndexedColumn(const SqliteIndexedColumn& other); + SqliteIndexedColumn(const QString& name, const QString& collate, SqliteSortOrder sortOrder); + explicit SqliteIndexedColumn(const QString& name); + + SqliteStatement* clone(); + + QString name = QString::null; + SqliteSortOrder sortOrder = SqliteSortOrder::null; + QString collate = QString::null; + + protected: + QStringList getColumnsInStatement(); + TokenList getColumnTokensInStatement(); + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteIndexedColumn> SqliteIndexedColumnPtr; + +#endif // SQLITEINDEXEDCOLUMN_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.cpp new file mode 100644 index 0000000..6c26e8d --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.cpp @@ -0,0 +1,214 @@ +#include "sqliteinsert.h" +#include "sqlitequerytype.h" +#include "sqliteexpr.h" +#include "sqliteselect.h" +#include "parser/statementtokenbuilder.h" +#include "common/global.h" +#include "sqlitewith.h" + +SqliteInsert::SqliteInsert() +{ + queryType = SqliteQueryType::Insert; +} + +SqliteInsert::SqliteInsert(const SqliteInsert& other) : + SqliteQuery(other), replaceKw(other.replaceKw), defaultValuesKw(other.defaultValuesKw), onConflict(other.onConflict), database(other.database), + table(other.table), columnNames(other.columnNames) +{ + DEEP_COPY_COLLECTION(SqliteExpr, values); + DEEP_COPY_FIELD(SqliteSelect, select); + DEEP_COPY_FIELD(SqliteWith, with); +} + +SqliteInsert::SqliteInsert(bool replace, SqliteConflictAlgo onConflict, const QString &name1, const QString &name2, const QList<QString> &columns, + const QList<SqliteExpr *> &row, SqliteWith* with) : + SqliteInsert() +{ + initName(name1, name2); + initMode(replace, onConflict); + columnNames = columns; + values = row; + + this->with = with; + if (with) + with->setParent(this); + + foreach (SqliteExpr* expr, row) + expr->setParent(this); +} + +SqliteInsert::SqliteInsert(bool replace, SqliteConflictAlgo onConflict, const QString &name1, const QString &name2, const QList<QString> &columns, + SqliteSelect *select, SqliteWith* with) : + SqliteInsert() +{ + initName(name1, name2); + initMode(replace, onConflict); + + this->with = with; + if (with) + with->setParent(this); + + columnNames = columns; + this->select = select; + if (select) + select->setParent(this); +} + +SqliteInsert::SqliteInsert(bool replace, SqliteConflictAlgo onConflict, const QString &name1, const QString &name2, const QList<QString> &columns, + SqliteWith* with) : + SqliteInsert() +{ + initName(name1, name2); + initMode(replace, onConflict); + + this->with = with; + if (with) + with->setParent(this); + + columnNames = columns; + defaultValuesKw = true; +} + +SqliteInsert::~SqliteInsert() +{ +} + +SqliteStatement*SqliteInsert::clone() +{ + return new SqliteInsert(*this); +} + +QStringList SqliteInsert::getColumnsInStatement() +{ + QStringList columns; + columns += columnNames; + return columns; +} + +QStringList SqliteInsert::getTablesInStatement() +{ + return getStrListFromValue(table); +} + +QStringList SqliteInsert::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteInsert::getColumnTokensInStatement() +{ + TokenList list; + foreach (TokenPtr token, getTokenListFromNamedKey("inscollist_opt", -1)) + { + if (token->type != Token::OTHER && token->type != Token::KEYWORD) + continue; + + list << token; + } + return list; +} + +TokenList SqliteInsert::getTableTokensInStatement() +{ + return getObjectTokenListFromFullname(); +} + +TokenList SqliteInsert::getDatabaseTokensInStatement() +{ + if (tokensMap.contains("fullname")) + return getDbTokenListFromFullname(); + + if (tokensMap.contains("nm")) + return extractPrintableTokens(tokensMap["nm"]); + + return TokenList(); +} + +QList<SqliteStatement::FullObject> SqliteInsert::getFullObjectsInStatement() +{ + QList<FullObject> result; + if (!tokensMap.contains("fullname")) + return result; + + // Table object + FullObject fullObj = getFullObjectFromFullname(FullObject::TABLE); + + if (fullObj.isValid()) + result << fullObj; + + // Db object + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + { + result << fullObj; + dbTokenForFullObjects = fullObj.database; + } + + return result; +} + +void SqliteInsert::initName(const QString& name1, const QString& name2) +{ + if (!name2.isNull()) + { + database = name1; + table = name2; + } + else + table = name1; +} + +void SqliteInsert::initMode(bool replace, SqliteConflictAlgo onConflict) +{ + replaceKw = replace; + this->onConflict = onConflict; +} + +TokenList SqliteInsert::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + if (with) + builder.withStatement(with); + + if (replaceKw) + { + builder.withKeyword("REPLACE").withSpace(); + } + else + { + builder.withKeyword("INSERT").withSpace(); + if (onConflict != SqliteConflictAlgo::null) + builder.withKeyword("OR").withSpace().withKeyword(sqliteConflictAlgo(onConflict)).withSpace(); + } + + builder.withKeyword("INTO").withSpace(); + + if (!database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(table, dialect).withSpace(); + + if (defaultValuesKw) + { + builder.withKeyword("DEFAULT").withSpace().withKeyword("VALUES"); + } + else + { + if (columnNames.size() > 0) + builder.withParLeft().withOtherList(columnNames, dialect).withParRight().withSpace(); + + if (select) + { + builder.withStatement(select); + } + else if (dialect == Dialect::Sqlite2) // Sqlite2 uses classic single row values + { + builder.withKeyword("VALUES").withSpace().withParLeft().withStatementList(values).withParRight(); + } + } + + builder.withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.h new file mode 100644 index 0000000..4287680 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.h @@ -0,0 +1,57 @@ +#ifndef SQLITEINSERT_H +#define SQLITEINSERT_H + +#include "sqlitequery.h" +#include "sqliteconflictalgo.h" +#include <QString> +#include <QList> + +class SqliteSelect; +class SqliteExpr; +class SqliteWith; + +class API_EXPORT SqliteInsert : public SqliteQuery +{ + public: + SqliteInsert(); + SqliteInsert(const SqliteInsert& other); + SqliteInsert(bool replace, SqliteConflictAlgo onConflict, const QString& name1, + const QString& name2, const QList<QString>& columns, + const QList<SqliteExpr*>& row, SqliteWith* with); + SqliteInsert(bool replace, SqliteConflictAlgo onConflict, const QString& name1, + const QString& name2, const QList<QString>& columns, SqliteSelect* select, SqliteWith* with); + SqliteInsert(bool replace, SqliteConflictAlgo onConflict, const QString& name1, + const QString& name2, const QList<QString>& columns, SqliteWith* with); + ~SqliteInsert(); + + SqliteStatement* clone(); + + protected: + QStringList getColumnsInStatement(); + QStringList getTablesInStatement(); + QStringList getDatabasesInStatement(); + TokenList getColumnTokensInStatement(); + TokenList getTableTokensInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); + + private: + void initName(const QString& name1, const QString& name2); + void initMode(bool replace, SqliteConflictAlgo onConflict); + + public: + bool replaceKw = false; + bool defaultValuesKw = false; + SqliteConflictAlgo onConflict = SqliteConflictAlgo::null; + QString database = QString::null; + QString table = QString::null; + QStringList columnNames; + QList<SqliteExpr*> values; + SqliteSelect* select = nullptr; + SqliteWith* with = nullptr; +}; + +typedef QSharedPointer<SqliteInsert> SqliteInsertPtr; + +#endif // SQLITEINSERT_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitelimit.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitelimit.cpp new file mode 100644 index 0000000..c44228a --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitelimit.cpp @@ -0,0 +1,79 @@ +#include "sqlitelimit.h" +#include "sqliteexpr.h" +#include "parser/statementtokenbuilder.h" +#include "common/global.h" + +SqliteLimit::SqliteLimit() +{ +} + +SqliteLimit::SqliteLimit(const SqliteLimit& other) : + SqliteStatement(other) +{ + DEEP_COPY_FIELD(SqliteExpr, limit); + DEEP_COPY_FIELD(SqliteExpr, offset); +} + +SqliteLimit::SqliteLimit(SqliteExpr *expr) +{ + limit = expr; + if (expr) + expr->setParent(this); +} + +SqliteLimit::SqliteLimit(SqliteExpr *expr1, SqliteExpr *expr2, bool offsetKeyword) +{ + limit = expr1; + offset = expr2; + offsetKw = offsetKeyword; + if (expr1) + expr1->setParent(this); + + if (expr2) + expr2->setParent(this); +} + +SqliteLimit::SqliteLimit(const QVariant &positiveInt) +{ + limit = new SqliteExpr(); + limit->initLiteral(positiveInt); + limit->setParent(this); +} + +SqliteLimit::SqliteLimit(const QVariant &positiveInt1, const QVariant &positiveInt2) +{ + limit = new SqliteExpr(); + limit->initLiteral(positiveInt1); + limit->setParent(this); + + offset = new SqliteExpr(); + offset->initLiteral(positiveInt2); + offset->setParent(this); +} + +SqliteLimit::~SqliteLimit() +{ +} + +SqliteStatement*SqliteLimit::clone() +{ + return new SqliteLimit(*this); +} + + +TokenList SqliteLimit::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + builder.withKeyword("LIMIT").withStatement(limit); + if (offset) + { + if (offsetKw) + builder.withSpace().withKeyword("OFFSET"); + else + builder.withOperator(","); + + builder.withStatement(offset); + } + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitelimit.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitelimit.h new file mode 100644 index 0000000..284cf49 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitelimit.h @@ -0,0 +1,31 @@ +#ifndef SQLITELIMIT_H +#define SQLITELIMIT_H + +#include "sqlitestatement.h" + +class SqliteExpr; + +class API_EXPORT SqliteLimit : public SqliteStatement +{ + public: + SqliteLimit(); + SqliteLimit(const SqliteLimit& other); + explicit SqliteLimit(SqliteExpr* expr); + SqliteLimit(SqliteExpr* expr1, SqliteExpr* expr2, bool offsetKeyword); + explicit SqliteLimit(const QVariant& positiveInt); + SqliteLimit(const QVariant& positiveInt1, const QVariant& positiveInt2); + ~SqliteLimit(); + + SqliteStatement* clone(); + + SqliteExpr* limit = nullptr; + SqliteExpr* offset = nullptr; + bool offsetKw = false; + + protected: + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteLimit> SqliteLimitPtr; + +#endif // SQLITELIMIT_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.cpp new file mode 100644 index 0000000..3bb1b44 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.cpp @@ -0,0 +1,41 @@ +#include "sqliteorderby.h" +#include "sqliteexpr.h" +#include "parser/statementtokenbuilder.h" +#include "common/global.h" + +SqliteOrderBy::SqliteOrderBy() +{ +} + +SqliteOrderBy::SqliteOrderBy(const SqliteOrderBy& other) : + SqliteStatement(other), order(other.order) +{ + DEEP_COPY_FIELD(SqliteExpr, expr); +} + +SqliteOrderBy::SqliteOrderBy(SqliteExpr *expr, SqliteSortOrder order) +{ + this->expr = expr; + this->order = order; + if (expr) + expr->setParent(this); +} + +SqliteOrderBy::~SqliteOrderBy() +{ +} + +SqliteStatement*SqliteOrderBy::clone() +{ + return new SqliteOrderBy(*this); +} + +TokenList SqliteOrderBy::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + builder.withStatement(expr); + if (order != SqliteSortOrder::null) + builder.withSpace().withKeyword(sqliteSortOrder(order)); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.h new file mode 100644 index 0000000..598423d --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.h @@ -0,0 +1,28 @@ +#ifndef SQLITEORDERBY_H +#define SQLITEORDERBY_H + +#include "sqlitestatement.h" +#include "sqlitesortorder.h" + +class SqliteExpr; + +class API_EXPORT SqliteOrderBy : public SqliteStatement +{ + public: + SqliteOrderBy(); + SqliteOrderBy(const SqliteOrderBy& other); + SqliteOrderBy(SqliteExpr* expr, SqliteSortOrder order); + ~SqliteOrderBy(); + + SqliteStatement* clone(); + + SqliteExpr* expr = nullptr; + SqliteSortOrder order; + + protected: + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteOrderBy> SqliteOrderByPtr; + +#endif // SQLITEORDERBY_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitepragma.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitepragma.cpp new file mode 100644 index 0000000..0e4f056 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitepragma.cpp @@ -0,0 +1,109 @@ +#include "sqlitepragma.h" +#include "sqlitequerytype.h" + +#include <parser/statementtokenbuilder.h> + +SqlitePragma::SqlitePragma() +{ + queryType = SqliteQueryType::Pragma; +} + +SqlitePragma::SqlitePragma(const SqlitePragma& other) : + SqliteQuery(other), database(other.database), pragmaName(other.pragmaName), value(other.value), equalsOp(other.equalsOp), parenthesis(other.parenthesis) +{ +} + +SqlitePragma::SqlitePragma(const QString &name1, const QString &name2) + : SqlitePragma() +{ + initName(name1, name2); +} + +SqlitePragma::SqlitePragma(const QString &name1, const QString &name2, const QVariant& value, bool equals) + : SqlitePragma() +{ + initName(name1, name2); + this->value = value; + if (equals) + equalsOp = true; + else + parenthesis = true; +} + +SqlitePragma::SqlitePragma(const QString &name1, const QString &name2, const QString &value, bool equals) + : SqlitePragma() +{ + initName(name1, name2); + this->value = value; + if (equals) + equalsOp = true; + else + parenthesis = true; +} + +SqliteStatement*SqlitePragma::clone() +{ + return new SqlitePragma(*this); +} + +QStringList SqlitePragma::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqlitePragma::getDatabaseTokensInStatement() +{ + if (dialect == Dialect::Sqlite2 || database.isNull()) + return TokenList(); + + return getTokenListFromNamedKey("nm"); +} + +QList<SqliteStatement::FullObject> SqlitePragma::getFullObjectsInStatement() +{ + QList<FullObject> result; + if (dialect == Dialect::Sqlite2 || database.isNull()) + return result; + + // Db object + FullObject fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + { + result << fullObj; + dbTokenForFullObjects = fullObj.database; + } + + return result; +} + +void SqlitePragma::initName(const QString &name1, const QString &name2) +{ + if (!name2.isNull()) + { + database = name1; + pragmaName = name2; + } + else + pragmaName = name1; +} + +TokenList SqlitePragma::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("PRAGMA").withSpace(); + + if (!database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(pragmaName, dialect); + + if (equalsOp) + builder.withSpace().withOperator("=").withSpace().withLiteralValue(value); + else if (parenthesis) + builder.withParLeft().withLiteralValue(value).withParRight(); + + builder.withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitepragma.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitepragma.h new file mode 100644 index 0000000..364a16f --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitepragma.h @@ -0,0 +1,41 @@ +#ifndef SQLITEPRAGMA_H +#define SQLITEPRAGMA_H + +#include "sqlitequery.h" + +#include <QString> +#include <QVariant> + +class API_EXPORT SqlitePragma : public SqliteQuery +{ + public: + SqlitePragma(); + SqlitePragma(const SqlitePragma& other); + SqlitePragma(const QString& name1, const QString& name2); + SqlitePragma(const QString& name1, const QString& name2, const QVariant& value, + bool equals); + SqlitePragma(const QString& name1, const QString& name2, const QString& value, + bool equals); + + SqliteStatement* clone(); + + protected: + QStringList getDatabasesInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); + + private: + void initName(const QString& name1, const QString& name2); + + public: + QString database = QString::null; + QString pragmaName = QString::null; + QVariant value = QVariant(); + bool equalsOp = false; + bool parenthesis = false; +}; + +typedef QSharedPointer<SqlitePragma> SqlitePragmaPtr; + +#endif // SQLITEPRAGMA_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequery.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequery.cpp new file mode 100644 index 0000000..19c3c40 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequery.cpp @@ -0,0 +1,51 @@ +#include "sqlitequery.h" + +SqliteQuery::SqliteQuery() +{ +} + +SqliteQuery::SqliteQuery(const SqliteQuery& other) : + SqliteStatement(other), queryType(other.queryType), explain(other.explain), queryPlan(other.queryPlan) +{ +} + +bool SqliteQuery::isReadOnly() +{ + bool readOnly = true; + switch (queryType) + { + case SqliteQueryType::EMPTY: + case SqliteQueryType::Analyze: + case SqliteQueryType::Pragma: + case SqliteQueryType::Select: + readOnly = true; + break; + case SqliteQueryType::UNDEFINED: + case SqliteQueryType::AlterTable: + 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: + readOnly = false; + break; + } + return readOnly; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequery.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequery.h new file mode 100644 index 0000000..1667dc9 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequery.h @@ -0,0 +1,30 @@ +#ifndef SQLITEQUERY_H +#define SQLITEQUERY_H + +#include "sqlitestatement.h" +#include "sqlitequerytype.h" + +/** + * @addtogroup sqlite_statement + * @brief The SqliteQuery class + */ +class API_EXPORT SqliteQuery : public SqliteStatement +{ + public: + SqliteQuery(); + SqliteQuery(const SqliteQuery& other); + + bool isReadOnly(); + + SqliteQueryType queryType = SqliteQueryType::UNDEFINED; + + bool explain = false; + bool queryPlan = false; +}; + +/** + * Shared pointer to SqliteQuery. + */ +typedef QSharedPointer<SqliteQuery> SqliteQueryPtr; + +#endif // SQLITEQUERY_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.cpp new file mode 100644 index 0000000..c369e0e --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.cpp @@ -0,0 +1,66 @@ +#include "sqlitequerytype.h" + +QString sqliteQueryTypeToString(const SqliteQueryType& type) +{ + switch (type) + { + case SqliteQueryType::UNDEFINED: + return "UNDEFINED"; + case SqliteQueryType::EMPTY: + return "EMPTY"; + case SqliteQueryType::AlterTable: + return "AlterTable"; + case SqliteQueryType::Analyze: + return "Analyze"; + case SqliteQueryType::Attach: + return "Attach"; + case SqliteQueryType::BeginTrans: + return "BeginTrans"; + case SqliteQueryType::CommitTrans: + return "CommitTrans"; + case SqliteQueryType::Copy: + return "Copy"; + case SqliteQueryType::CreateIndex: + return "CreateIndex"; + case SqliteQueryType::CreateTable: + return "CreateTable"; + case SqliteQueryType::CreateTrigger: + return "CreateTrigger"; + case SqliteQueryType::CreateView: + return "CreateView"; + case SqliteQueryType::CreateVirtualTable: + return "CreateVirtualTable"; + case SqliteQueryType::Delete: + return "Delete"; + case SqliteQueryType::Detach: + return "Detach"; + case SqliteQueryType::DropIndex: + return "DropIndex"; + case SqliteQueryType::DropTable: + return "DropTable"; + case SqliteQueryType::DropTrigger: + return "DropTrigger"; + case SqliteQueryType::DropView: + return "DropView"; + case SqliteQueryType::Insert: + return "Insert"; + case SqliteQueryType::Pragma: + return "Pragma"; + case SqliteQueryType::Reindex: + return "Reindex"; + case SqliteQueryType::Release: + return "Release"; + case SqliteQueryType::Rollback: + return "Rollback"; + case SqliteQueryType::Savepoint: + return "Savepoint"; + case SqliteQueryType::Select: + return "Select"; + case SqliteQueryType::Update: + return "Update"; + case SqliteQueryType::Vacuum: + return "Vacuum"; + default: + return QString::null; + } +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.h new file mode 100644 index 0000000..763fcfa --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.h @@ -0,0 +1,41 @@ +#ifndef SQLITEQUERYTYPE_H +#define SQLITEQUERYTYPE_H + +#include "coreSQLiteStudio_global.h" +#include <QString> + +enum class SqliteQueryType +{ + UNDEFINED, + EMPTY, // still can hold comments + AlterTable, + Analyze, + Attach, + BeginTrans, + CommitTrans, + Copy, + CreateIndex, + CreateTable, + CreateTrigger, + CreateView, + CreateVirtualTable, + Delete, + Detach, + DropIndex, + DropTable, + DropTrigger, + DropView, + Insert, + Pragma, + Reindex, + Release, + Rollback, + Savepoint, + Select, + Update, + Vacuum +}; + +QString API_EXPORT sqliteQueryTypeToString(const SqliteQueryType& type); + +#endif // SQLITEQUERYTYPE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteraise.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteraise.cpp new file mode 100644 index 0000000..b606baa --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteraise.cpp @@ -0,0 +1,70 @@ +#include "sqliteraise.h" +#include "parser/statementtokenbuilder.h" + +SqliteRaise::SqliteRaise() +{ +} + +SqliteRaise::SqliteRaise(const SqliteRaise& other) : + SqliteStatement(other), type(other.type), message(other.message) +{ +} + +SqliteRaise::SqliteRaise(const QString &type) +{ + this->type = raiseType(type); +} + +SqliteRaise::SqliteRaise(const QString &type, const QString &text) +{ + this->type = raiseType(type); + message = text; +} + +SqliteStatement*SqliteRaise::clone() +{ + return new SqliteRaise(*this); +} + +SqliteRaise::Type SqliteRaise::raiseType(const QString &value) +{ + QString upper = value.toUpper(); + if (upper == "IGNORE") + return SqliteRaise::Type::IGNORE; + else if (upper == "ROLLBACK") + return SqliteRaise::Type::ROLLBACK; + else if (upper == "ABORT") + return SqliteRaise::Type::ABORT; + else if (upper == "FAIL") + return SqliteRaise::Type::FAIL; + else + return SqliteRaise::Type::null; +} + +QString SqliteRaise::raiseType(SqliteRaise::Type value) +{ + switch (value) + { + case SqliteRaise::Type::IGNORE: + return "IGNORE"; + case SqliteRaise::Type::ROLLBACK: + return "ROLLBACK"; + case SqliteRaise::Type::ABORT: + return "ABORT"; + case SqliteRaise::Type::FAIL: + return "FAIL"; + default: + return QString::null; + } +} + +TokenList SqliteRaise::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + builder.withKeyword("RAISE").withSpace().withParLeft().withKeyword(raiseType(type)); + if (type != Type::IGNORE) + builder.withOperator(",").withSpace().withString(message); + + builder.withParRight(); + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteraise.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteraise.h new file mode 100644 index 0000000..1b844f8 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteraise.h @@ -0,0 +1,38 @@ +#ifndef SQLITERAISE_H +#define SQLITERAISE_H + +#include "sqlitestatement.h" +#include <QString> + +class API_EXPORT SqliteRaise : public SqliteStatement +{ + public: + enum class Type + { + IGNORE, + ROLLBACK, + ABORT, + FAIL, + null + }; + + SqliteRaise(); + SqliteRaise(const SqliteRaise& other); + explicit SqliteRaise(const QString& type); + SqliteRaise(const QString& type, const QString& text); + + SqliteStatement* clone(); + + static Type raiseType(const QString& value); + static QString raiseType(Type value); + + Type type = Type::null; + QString message = QString::null; + + protected: + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteRaise> SqliteRaisePtr; + +#endif // SQLITERAISE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitereindex.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitereindex.cpp new file mode 100644 index 0000000..7584a37 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitereindex.cpp @@ -0,0 +1,82 @@ +#include "sqlitereindex.h" +#include "sqlitequerytype.h" + +#include <parser/statementtokenbuilder.h> + +SqliteReindex::SqliteReindex() +{ + queryType = SqliteQueryType::Reindex; +} + +SqliteReindex::SqliteReindex(const SqliteReindex& other) : + SqliteQuery(other), database(other.database), table(other.table) +{ +} + +SqliteReindex::SqliteReindex(const QString& name1, const QString& name2) + : SqliteReindex() +{ + if (!name2.isNull()) + { + database = name1; + table = name2; + } + else + table = name1; +} + +SqliteStatement*SqliteReindex::clone() +{ + return new SqliteReindex(*this); +} + +QStringList SqliteReindex::getTablesInStatement() +{ + return getStrListFromValue(table); +} + +QStringList SqliteReindex::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteReindex::getTableTokensInStatement() +{ + return getObjectTokenListFromNmDbnm(); +} + +TokenList SqliteReindex::getDatabaseTokensInStatement() +{ + return getDbTokenListFromNmDbnm(); +} + +QList<SqliteStatement::FullObject> SqliteReindex::getFullObjectsInStatement() +{ + QList<FullObject> result; + + // Table object + FullObject fullObj = getFullObjectFromNmDbnm(FullObject::TABLE); + + if (fullObj.isValid()) + result << fullObj; + + // Db object + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + result << fullObj; + + return result; +} + +TokenList SqliteReindex::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("REINDEX"); + if (!database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(table).withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitereindex.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitereindex.h new file mode 100644 index 0000000..642b5bf --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitereindex.h @@ -0,0 +1,31 @@ +#ifndef SQLITEREINDEX_H +#define SQLITEREINDEX_H + +#include "sqlitequery.h" + +#include <QString> + +class API_EXPORT SqliteReindex : public SqliteQuery +{ + public: + SqliteReindex(); + SqliteReindex(const SqliteReindex& other); + SqliteReindex(const QString& name1, const QString& name2); + + SqliteStatement* clone(); + + QString database = QString::null; + QString table = QString::null; + + protected: + QStringList getTablesInStatement(); + QStringList getDatabasesInStatement(); + TokenList getTableTokensInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<SqliteStatement::FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteReindex> SqliteReindexPtr; + +#endif // SQLITEREINDEX_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterelease.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterelease.cpp new file mode 100644 index 0000000..8510524 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterelease.cpp @@ -0,0 +1,39 @@ +#include "sqliterelease.h" +#include "sqlitequerytype.h" + +#include <parser/statementtokenbuilder.h> + +SqliteRelease::SqliteRelease() +{ + queryType = SqliteQueryType::Release; +} + +SqliteRelease::SqliteRelease(const SqliteRelease& other) : + SqliteQuery(other), name(other.name), savepointKw(other.savepointKw) +{ +} + +SqliteRelease::SqliteRelease(bool savepointKw, const QString& name) + : SqliteRelease() +{ + this->name = name; + this->savepointKw = savepointKw; +} + +SqliteStatement*SqliteRelease::clone() +{ + return new SqliteRelease(*this); +} + +TokenList SqliteRelease::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("RELEASE").withSpace(); + if (savepointKw) + builder.withKeyword("SAVEPOINT").withSpace(); + + builder.withOther(name, dialect).withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterelease.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterelease.h new file mode 100644 index 0000000..d115669 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterelease.h @@ -0,0 +1,26 @@ +#ifndef SQLITERELEASE_H +#define SQLITERELEASE_H + +#include "sqlitequery.h" + +#include <QString> + +class API_EXPORT SqliteRelease : public SqliteQuery +{ + public: + SqliteRelease(); + SqliteRelease(const SqliteRelease& other); + SqliteRelease(bool savepointKw, const QString &name); + + SqliteStatement* clone(); + + QString name = QString::null; + bool savepointKw = false; + + protected: + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteRelease> SqliteReleasePtr; + +#endif // SQLITERELEASE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterollback.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterollback.cpp new file mode 100644 index 0000000..a13fd4c --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterollback.cpp @@ -0,0 +1,57 @@ +#include "sqliterollback.h" +#include "sqlitequerytype.h" + +#include <parser/statementtokenbuilder.h> + +SqliteRollback::SqliteRollback() +{ + queryType = SqliteQueryType::Rollback; +} + +SqliteRollback::SqliteRollback(const SqliteRollback& other) : + SqliteQuery(other), transactionKw(other.transactionKw), toKw(other.toKw), savepointKw(other.savepointKw), name(other.name) +{ +} + +SqliteRollback::SqliteRollback(bool transactionKw, const QString& name) + : SqliteRollback() +{ + this->name = name; + this->transactionKw = transactionKw; +} + +SqliteRollback::SqliteRollback(bool transactionKw, bool savePoint, const QString& name) +{ + // we ignore name from trans_opt, + // it's not officialy supported in sqlite3 + this->name = name; + this->transactionKw = transactionKw; + toKw = true; + savepointKw = savePoint; +} + +SqliteStatement*SqliteRollback::clone() +{ + return new SqliteRollback(*this); +} + +TokenList SqliteRollback::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("ROLLBACK").withSpace(); + if (transactionKw) + builder.withKeyword("TRANSACTION").withSpace(); + + if (!name.isNull()) + { + builder.withKeyword("TO").withSpace(); + if (savepointKw) + builder.withKeyword("SAVEPOINT").withSpace(); + + builder.withOther(name, dialect); + } + builder.withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterollback.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterollback.h new file mode 100644 index 0000000..1dc1574 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterollback.h @@ -0,0 +1,28 @@ +#ifndef SQLITEROLLBACK_H +#define SQLITEROLLBACK_H + +#include "sqlitequery.h" +#include <QString> + +class API_EXPORT SqliteRollback : public SqliteQuery +{ + public: + SqliteRollback(); + SqliteRollback(const SqliteRollback& other); + SqliteRollback(bool transactionKw, const QString& name); + SqliteRollback(bool transactionKw, bool savePoint, const QString& name); + + SqliteStatement* clone(); + + bool transactionKw = false; + bool toKw = false; + bool savepointKw = false; + QString name = QString::null; + + protected: + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteRollback> SqliteRollPtr; + +#endif // SQLITEROLLBACK_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesavepoint.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesavepoint.cpp new file mode 100644 index 0000000..9003086 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesavepoint.cpp @@ -0,0 +1,32 @@ +#include "sqlitesavepoint.h" +#include "sqlitequerytype.h" + +#include <parser/statementtokenbuilder.h> + +SqliteSavepoint::SqliteSavepoint() +{ + queryType = SqliteQueryType::Savepoint; +} + +SqliteSavepoint::SqliteSavepoint(const SqliteSavepoint& other) : + SqliteQuery(other), name(other.name) +{ +} + +SqliteSavepoint::SqliteSavepoint(const QString &name) + : SqliteSavepoint() +{ + this->name = name; +} + +SqliteStatement*SqliteSavepoint::clone() +{ + return new SqliteSavepoint(*this); +} + +TokenList SqliteSavepoint::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + builder.withKeyword("SAVEPOINT").withSpace().withOther(name, dialect).withOperator(";"); + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesavepoint.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesavepoint.h new file mode 100644 index 0000000..bd75c76 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesavepoint.h @@ -0,0 +1,25 @@ +#ifndef SQLITESAVEPOINT_H +#define SQLITESAVEPOINT_H + +#include "sqlitequery.h" + +#include <QString> + +class API_EXPORT SqliteSavepoint : public SqliteQuery +{ + public: + SqliteSavepoint(); + SqliteSavepoint(const SqliteSavepoint& other); + explicit SqliteSavepoint(const QString& name); + + SqliteStatement* clone(); + + QString name = QString::null; + + protected: + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteSavepoint> SqliteSavepointPtr; + +#endif // SQLITESAVEPOINT_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.cpp new file mode 100644 index 0000000..5452795 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.cpp @@ -0,0 +1,783 @@ +#include "sqliteselect.h" +#include "sqlitequerytype.h" +#include "parser/statementtokenbuilder.h" +#include "common/global.h" +#include "sqlitewith.h" +#include <QSet> + +SqliteSelect::SqliteSelect() +{ + queryType = SqliteQueryType::Select; +} + +SqliteSelect::SqliteSelect(const SqliteSelect& other) : + SqliteQuery(other) +{ + DEEP_COPY_COLLECTION(Core, coreSelects); + DEEP_COPY_FIELD(SqliteWith, with); +} + +SqliteSelect* SqliteSelect::append(Core* core) +{ + SqliteSelect* select = new SqliteSelect(); + select->coreSelects << core; + core->setParent(select); + return select; +} + +SqliteSelect* SqliteSelect::append(SqliteSelect* select, SqliteSelect::CompoundOperator op, Core* core) +{ + if (!select) + select = new SqliteSelect(); + + core->compoundOp = op; + select->coreSelects << core; + core->setParent(select); + return select; +} + +SqliteSelect* SqliteSelect::append(const QList<QList<SqliteExpr*>>& values) +{ + return append(nullptr, CompoundOperator::null, values); +} + +SqliteSelect* SqliteSelect::append(SqliteSelect* select, SqliteSelect::CompoundOperator op, const QList<QList<SqliteExpr*>>& values) +{ + if (!select) + select = new SqliteSelect(); + + bool first = true; + + Core::ResultColumn* resCol = nullptr; + QList<Core::ResultColumn*> resColList; + foreach (const QList<SqliteExpr*>& singleValues, values) + { + Core* core = new Core(); + core->setParent(select); + core->compoundOp = op; + core->valuesMode = true; + if (first) + { + op = CompoundOperator::UNION_ALL; + first = false; + } + select->coreSelects << core; + + resColList.clear(); + foreach (SqliteExpr* value, singleValues) + { + resCol = new Core::ResultColumn(value, false, QString::null); + resCol->rebuildTokens(); + resCol->setParent(core); + core->resultColumns << resCol; + } + } + + return select; +} + +SqliteStatement*SqliteSelect::clone() +{ + return new SqliteSelect(*this); +} + +QString SqliteSelect::compoundOperator(SqliteSelect::CompoundOperator op) +{ + switch (op) + { + case SqliteSelect::CompoundOperator::UNION: + return "UNION"; + case SqliteSelect::CompoundOperator::UNION_ALL: + return "UNION ALL"; + case SqliteSelect::CompoundOperator::INTERSECT: + return "INTERSECT"; + case SqliteSelect::CompoundOperator::EXCEPT: + return "EXCEPT"; + case SqliteSelect::CompoundOperator::null: + break; + } + return QString::null; +} + +SqliteSelect::CompoundOperator SqliteSelect::compoundOperator(const QString& op) +{ + QString upStr = op.toUpper(); + if (upStr == "UNION") + return CompoundOperator::UNION; + else if (upStr == "UNION ALL") + return CompoundOperator::UNION_ALL; + else if (upStr == "EXCEPT") + return CompoundOperator::EXCEPT; + else if (upStr == "INTERSECT") + return CompoundOperator::INTERSECT; + else + return CompoundOperator::null; +} + +void SqliteSelect::reset() +{ + foreach (Core* core, coreSelects) + delete core; + + coreSelects.clear(); +} + +void SqliteSelect::setWith(SqliteWith* with) +{ + this->with = with; + if (with) + with->setParent(this); +} + +SqliteSelect::Core::Core() +{ +} + +SqliteSelect::Core::Core(const SqliteSelect::Core& other) : + SqliteStatement(other), compoundOp(other.compoundOp), distinctKw(other.distinctKw), allKw(other.allKw) +{ + DEEP_COPY_COLLECTION(ResultColumn, resultColumns); + DEEP_COPY_FIELD(JoinSource, from); + DEEP_COPY_FIELD(SqliteExpr, where); + DEEP_COPY_FIELD(SqliteExpr, having); + DEEP_COPY_COLLECTION(SqliteExpr, groupBy); + 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) +{ + if (distinct == 1) + distinctKw = true; + else if (distinct == 2) + allKw = true; + + from = src; + this->where = where; + this->having = having; + this->groupBy = groupBy; + resultColumns = resCols; + this->limit = limit; + this->orderBy = orderBy; + + if (from) + from->setParent(this); + + if (where) + where->setParent(this); + + if (having) + having->setParent(this); + + if (limit) + limit->setParent(this); + + foreach (SqliteOrderBy* order, orderBy) + order->setParent(this); + + foreach (SqliteExpr* expr, groupBy) + expr->setParent(this); + + foreach (SqliteSelect::Core::ResultColumn* resCol, resCols) + resCol->setParent(this); +} + +SqliteStatement*SqliteSelect::Core::clone() +{ + return new SqliteSelect::Core(*this); +} + +SqliteSelect::Core::ResultColumn::ResultColumn() +{ +} + +SqliteSelect::Core::ResultColumn::ResultColumn(const SqliteSelect::Core::ResultColumn& other) : + SqliteStatement(other), star(other.star), asKw(other.asKw), alias(other.alias), table(other.table) +{ + DEEP_COPY_FIELD(SqliteExpr, expr); +} + +SqliteSelect::Core::ResultColumn::ResultColumn(SqliteExpr *expr, bool asKw, const QString &alias) +{ + this->expr = expr; + this->asKw = asKw; + this->alias = alias; + if (expr) + expr->setParent(this); +} + +SqliteSelect::Core::ResultColumn::ResultColumn(bool star, const QString &table) +{ + this->star = star; + this->table = table; +} + +SqliteSelect::Core::ResultColumn::ResultColumn(bool star) +{ + this->star = star; +} + +bool SqliteSelect::Core::ResultColumn::isRowId() +{ + if (!expr) + return false; + + if (expr->column.isEmpty()) + return false; + + return expr->column.compare("rowid", Qt::CaseInsensitive) == 0; +} + +SqliteStatement*SqliteSelect::Core::ResultColumn::clone() +{ + return new SqliteSelect::Core::ResultColumn(*this); +} + +QStringList SqliteSelect::Core::ResultColumn::getTablesInStatement() +{ + return getStrListFromValue(table); +} + +TokenList SqliteSelect::Core::ResultColumn::getTableTokensInStatement() +{ + // To avoid warnings about missing "nm" key + if (table.isNull()) + return TokenList(); + + // Now, we know table was specified + return getTokenListFromNamedKey("nm"); +} + +QList<SqliteStatement::FullObject> SqliteSelect::Core::ResultColumn::getFullObjectsInStatement() +{ + QList<FullObject> result; + + // Table object + TokenList tableTokens = getTableTokensInStatement(); + FullObject fullObj; + if (tableTokens.size() > 0) + fullObj = getFullObject(FullObject::TABLE, dbTokenForFullObjects, tableTokens[0]); + + if (fullObj.isValid()) + result << fullObj; + + return result; +} + +SqliteSelect::Core::SingleSource::SingleSource() +{ +} + +SqliteSelect::Core::SingleSource::SingleSource(const SqliteSelect::Core::SingleSource& other) : + SqliteStatement(other), database(other.database), table(other.table), alias(other.alias), asKw(other.asKw), indexedByKw(other.indexedByKw), + notIndexedKw(other.notIndexedKw), indexedBy(other.indexedBy) +{ + DEEP_COPY_FIELD(SqliteSelect, select); + DEEP_COPY_FIELD(JoinSource, joinSource); +} + +SqliteSelect::Core::SingleSource::SingleSource(const QString& name1, const QString& name2, bool asKw, const QString& alias, bool notIndexedKw, const QString& indexedBy) +{ + if (!name2.isNull()) + { + database = name1; + table = name2; + } + else + table = name1; + + this->asKw = asKw; + this->alias = alias; + this->indexedBy = indexedBy; + this->indexedByKw = !(indexedBy.isNull()); + this->notIndexedKw = notIndexedKw; +} + +SqliteSelect::Core::SingleSource::SingleSource(SqliteSelect *select, bool asKw, const QString &alias) +{ + this->select = select; + this->asKw = asKw; + this-> alias = alias; + if (select) + select->setParent(this); +} + +SqliteSelect::Core::SingleSource::SingleSource(JoinSource *src, bool asKw, const QString &alias) +{ + this->joinSource = src; + this->asKw = asKw; + this-> alias = alias; + if (src) + src->setParent(this); +} + +SqliteStatement*SqliteSelect::Core::SingleSource::clone() +{ + return new SqliteSelect::Core::SingleSource(*this); +} + +QStringList SqliteSelect::Core::SingleSource::getTablesInStatement() +{ + // This method returns tables only! No aliases. + // Otherwise the completion sorter won't work correctly. + // To return tables with aliases use/create other method. + return getStrListFromValue(table); +} + +QStringList SqliteSelect::Core::SingleSource::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteSelect::Core::SingleSource::getTableTokensInStatement() +{ + if (table.isNull()) + return TokenList(); + + return getObjectTokenListFromNmDbnm(); +} + +TokenList SqliteSelect::Core::SingleSource::getDatabaseTokensInStatement() +{ + if (database.isNull()) + return TokenList(); + + return getDbTokenListFromNmDbnm(); +} + +QList<SqliteStatement::FullObject> SqliteSelect::Core::SingleSource::getFullObjectsInStatement() +{ + QList<FullObject> result; + + // Table object + if (!table.isNull()) + { + FullObject fullObj = getFullObjectFromNmDbnm(FullObject::TABLE); + + if (fullObj.isValid()) + result << fullObj; + } + + // Db object + if (!database.isNull()) + { + FullObject fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + { + result << fullObj; + dbTokenForFullObjects = fullObj.database; + } + } + + return result; +} + +SqliteSelect::Core::JoinConstraint::JoinConstraint() +{ +} + +SqliteSelect::Core::JoinConstraint::JoinConstraint(const SqliteSelect::Core::JoinConstraint& other) : + SqliteStatement(other), columnNames(other.columnNames) +{ + DEEP_COPY_FIELD(SqliteExpr, expr); +} + +SqliteSelect::Core::JoinConstraint::JoinConstraint(SqliteExpr *expr) +{ + this->expr = expr; + if (expr) + expr->setParent(this); +} + +SqliteSelect::Core::JoinConstraint::JoinConstraint(const QList<QString> &strList) +{ + columnNames = strList; +} + +SqliteStatement*SqliteSelect::Core::JoinConstraint::clone() +{ + return new SqliteSelect::Core::JoinConstraint(*this); +} + +QStringList SqliteSelect::Core::JoinConstraint::getColumnsInStatement() +{ + QStringList list; + list += columnNames; + return list; +} + +TokenList SqliteSelect::Core::JoinConstraint::getColumnTokensInStatement() +{ + TokenList list; + foreach (TokenPtr token, getTokenListFromNamedKey("inscollist", -1)) + { + if (token->type == Token::OPERATOR) // a COMMA + continue; + + list << token; + } + return list; +} + +SqliteSelect::Core::JoinOp::JoinOp() +{ +} + +SqliteSelect::Core::JoinOp::JoinOp(const SqliteSelect::Core::JoinOp& other) : + SqliteStatement(other), comma(other.comma), joinKw(other.joinKw), naturalKw(other.naturalKw), leftKw(other.leftKw), outerKw(other.outerKw), + innerKw(other.innerKw), crossKw(other.crossKw), rightKw(other.rightKw), fullKw(other.fullKw), customKw1(other.customKw1), + customKw2(other.customKw2), customKw3(other.customKw3) +{ +} + +SqliteSelect::Core::JoinOp::JoinOp(bool comma) +{ + this->comma = comma; + this->joinKw = !comma; +} + +SqliteSelect::Core::JoinOp::JoinOp(const QString &joinToken) +{ + this->joinKw = true; + init(joinToken); +} + +SqliteSelect::Core::JoinOp::JoinOp(const QString &joinToken, const QString &word1) +{ + this->joinKw = true; + init(joinToken); + init(word1); +} + +SqliteSelect::Core::JoinOp::JoinOp(const QString &joinToken, const QString &word1, const QString &word2) +{ + this->joinKw = true; + init(joinToken); + init(word1); + init(word2); +} + +SqliteStatement*SqliteSelect::Core::JoinOp::clone() +{ + return new SqliteSelect::Core::JoinOp(*this); +} + +void SqliteSelect::Core::JoinOp::init(const QString &str) +{ + QString upStr = str.toUpper(); + if (upStr == "NATURAL") + naturalKw = true; + else if (upStr == "LEFT") + leftKw = true; + else if (upStr == "RIGHT") + rightKw = true; + else if (upStr == "FULL") + fullKw = true; + else if (upStr == "OUTER") + outerKw = true; + else if (upStr == "INNER") + innerKw = true; + else if (upStr == "CROSS") + crossKw = true; + else if (customKw1.isNull()) + customKw1 = str; + else if (customKw2.isNull()) + customKw2 = str; + else + customKw3 = str; +} + + +SqliteSelect::Core::JoinSourceOther::JoinSourceOther() +{ +} + +SqliteSelect::Core::JoinSourceOther::JoinSourceOther(const SqliteSelect::Core::JoinSourceOther& other) : + SqliteStatement(other) +{ + DEEP_COPY_FIELD(JoinOp, joinOp); + DEEP_COPY_FIELD(SingleSource, singleSource); + DEEP_COPY_FIELD(JoinConstraint, joinConstraint); +} + +SqliteSelect::Core::JoinSourceOther::JoinSourceOther(SqliteSelect::Core::JoinOp *op, SingleSource *src, JoinConstraint *constr) +{ + this->joinConstraint = constr; + this->joinOp = op; + this->singleSource = src; + if (constr) + constr->setParent(this); + + if (op) + op->setParent(this); + + if (src) + src->setParent(this); +} + +SqliteStatement*SqliteSelect::Core::JoinSourceOther::clone() +{ + return new SqliteSelect::Core::JoinSourceOther(*this); +} + + +SqliteSelect::Core::JoinSource::JoinSource() +{ +} + +SqliteSelect::Core::JoinSource::JoinSource(const JoinSource& other) : + SqliteStatement(other) +{ + DEEP_COPY_FIELD(SingleSource, singleSource); + DEEP_COPY_COLLECTION(JoinSourceOther, otherSources); +} + +SqliteSelect::Core::JoinSource::JoinSource(SqliteSelect::Core::SingleSource *singleSource, const QList<SqliteSelect::Core::JoinSourceOther *> &list) +{ + this->singleSource = singleSource; + this->otherSources = list; + if (singleSource) + singleSource->setParent(this); + + foreach (JoinSourceOther* other, otherSources) + other->setParent(this); +} + +SqliteStatement*SqliteSelect::Core::JoinSource::clone() +{ + return new SqliteSelect::Core::JoinSource(*this); +} + + +TokenList SqliteSelect::Core::ResultColumn::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + if (star) + { + if (!table.isNull()) + builder.withOther(table, dialect).withOperator("."); + + builder.withOperator("*"); + } + else + { + builder.withStatement(expr); + if (!alias.isNull()) + { + if (asKw) + builder.withSpace().withKeyword("AS"); + + builder.withSpace().withOther(alias, dialect); + } + } + + return builder.build(); +} + +TokenList SqliteSelect::Core::SingleSource::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + if (!table.isNull()) + { + if (!database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(table, dialect); + + if (!alias.isNull()) + { + if (asKw) + builder.withSpace().withKeyword("AS"); + + builder.withSpace().withOther(alias, dialect); + + if (indexedByKw) + builder.withSpace().withKeyword("INDEXED").withSpace().withKeyword("BY").withSpace().withOther(indexedBy, dialect); + else if (notIndexedKw) + builder.withSpace().withKeyword("NOT").withSpace().withKeyword("INDEXED"); + } + } + else if (select) + { + builder.withParLeft().withStatement(select).withParRight(); + if (!alias.isNull()) + { + if (asKw) + builder.withSpace().withKeyword("AS"); + + builder.withSpace().withOther(alias, dialect); + } + } + else + { + builder.withParLeft().withStatement(joinSource).withParRight(); + } + + return builder.build(); +} + +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(); +} + +TokenList SqliteSelect::Core::JoinOp::rebuildTokensForSqlite3() +{ + StatementTokenBuilder builder; + if (comma) + { + builder.withOperator(","); + } + else + { + if (naturalKw) + builder.withKeyword("NATURAL").withSpace(); + + if (leftKw) + { + builder.withKeyword("LEFT").withSpace(); + if (outerKw) + builder.withKeyword("OUTER").withSpace(); + } + else if (innerKw) + builder.withKeyword("INNER").withSpace(); + else if (crossKw) + builder.withKeyword("CROSS").withSpace(); + + builder.withKeyword("JOIN"); + } + + return builder.build(); +} + + +TokenList SqliteSelect::Core::JoinConstraint::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + if (expr) + builder.withKeyword("ON").withStatement(expr); + else + builder.withKeyword("USING").withSpace().withParLeft().withOtherList(columnNames, dialect).withParRight(); + + return builder.build(); +} + + +TokenList SqliteSelect::Core::JoinSourceOther::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + builder.withStatement(joinOp).withStatement(singleSource).withStatement(joinConstraint); + return builder.build(); +} + + +TokenList SqliteSelect::Core::JoinSource::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + builder.withStatement(singleSource).withStatementList(otherSources, ""); + return builder.build(); +} + + +TokenList SqliteSelect::Core::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + if (valuesMode) + { + builder.withKeyword("VALUES").withSpace().withParLeft().withStatementList(resultColumns).withParRight(); + return builder.build(); + } + + builder.withKeyword("SELECT"); + if (distinctKw) + builder.withSpace().withKeyword("DISTINCT"); + else if (allKw) + builder.withSpace().withKeyword("ALL"); + + builder.withStatementList(resultColumns); + if (from) + builder.withSpace().withKeyword("FROM").withStatement(from); + + if (where) + builder.withSpace().withKeyword("WHERE").withStatement(where); + + if (groupBy.size() > 0) + { + builder.withSpace().withKeyword("GROUP").withSpace().withKeyword("BY").withStatementList(groupBy); + if (having) + builder.withSpace().withKeyword("HAVING").withStatement(having); + } + + if (orderBy.size() > 0) + builder.withSpace().withKeyword("ORDER").withSpace().withKeyword("BY").withStatementList(orderBy); + + if (limit) + builder.withStatement(limit); + + return builder.build(); +} + +TokenList SqliteSelect::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + if (with) + builder.withStatement(with); + + foreach (SqliteSelect::Core* core, coreSelects) + { + if (core->compoundOp == CompoundOperator::UNION_ALL) + { + if (core->valuesMode) + builder.withSpace().withOperator(","); + else + builder.withSpace().withKeyword("UNION").withSpace().withKeyword("ALL"); + } + else if (core->compoundOp != CompoundOperator::null) + builder.withSpace().withKeyword(compoundOperator(core->compoundOp)); + + builder.withStatement(core); + } + + builder.withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.h new file mode 100644 index 0000000..22d1921 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.h @@ -0,0 +1,228 @@ +#ifndef SQLITESELECT_H +#define SQLITESELECT_H + +#include "sqlitequery.h" +#include "sqliteexpr.h" +#include "sqlitelimit.h" +#include "sqliteorderby.h" + +#include <QList> + +class SqliteWith; + +/** + * @addtogroup sqlite_statement + * @brief The SqliteSelect class + */ +class API_EXPORT SqliteSelect : public SqliteQuery +{ + public: + enum class CompoundOperator + { + UNION, + UNION_ALL, + INTERSECT, + EXCEPT, + null + }; + + class API_EXPORT Core : public SqliteStatement + { + public: + class API_EXPORT ResultColumn : public SqliteStatement + { + public: + ResultColumn(); + ResultColumn(const ResultColumn& other); + ResultColumn(SqliteExpr* expr, bool asKw, const QString& alias); + ResultColumn(bool star, const QString& table); + explicit ResultColumn(bool star); + + bool isRowId(); + SqliteStatement* clone(); + + SqliteExpr* expr = nullptr; + bool star = false; + bool asKw = false; + QString alias = QString::null; + QString table = QString::null; + + protected: + QStringList getTablesInStatement(); + TokenList getTableTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); + }; + + class JoinSource; // forward declaration + + class API_EXPORT SingleSource : public SqliteStatement + { + public: + SingleSource(); + SingleSource(const SingleSource& other); + SingleSource(const QString& name1, const QString& name2, + bool asKw, const QString& alias, bool notIndexedKw, const QString& indexedBy); + SingleSource(SqliteSelect* select, bool asKw, const QString& alias); + SingleSource(JoinSource* src, bool asKw, const QString& alias); + + SqliteStatement* clone(); + + QString database = QString::null; + QString table = QString::null; + QString alias = QString::null; + bool asKw = false; + bool indexedByKw = false; + bool notIndexedKw = false; + QString indexedBy = QString::null; + SqliteSelect* select = nullptr; + JoinSource* joinSource = nullptr; + + protected: + QStringList getTablesInStatement(); + QStringList getDatabasesInStatement(); + TokenList getTableTokensInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); + }; + + class API_EXPORT JoinOp : public SqliteStatement + { + public: + JoinOp(); + JoinOp(const JoinOp& other); + explicit JoinOp(bool comma); + explicit JoinOp(const QString& joinToken); + JoinOp(const QString& joinToken, const QString& word1); + JoinOp(const QString& joinToken, const QString& word1, const QString& word2); + + SqliteStatement* clone(); + + private: + void init(const QString& str); + + public: + bool comma = false; + bool joinKw = false; + bool naturalKw = false; + bool leftKw = false; + bool outerKw = false; + bool innerKw = false; + bool crossKw = false; + bool rightKw = false; + bool fullKw = false; + QString customKw1 = QString::null; + QString customKw2 = QString::null; + QString customKw3 = QString::null; + + protected: + TokenList rebuildTokensFromContents(); + + private: + TokenList rebuildTokensForSqlite2(); + TokenList rebuildTokensForSqlite3(); + }; + + class API_EXPORT JoinConstraint : public SqliteStatement + { + public: + JoinConstraint(); + JoinConstraint(const JoinConstraint& other); + explicit JoinConstraint(SqliteExpr* expr); + explicit JoinConstraint(const QList<QString>& strList); + + SqliteStatement* clone(); + + SqliteExpr* expr = nullptr; + QList<QString> columnNames; + + protected: + QStringList getColumnsInStatement(); + TokenList getColumnTokensInStatement(); + TokenList rebuildTokensFromContents(); + }; + + class API_EXPORT JoinSourceOther : public SqliteStatement + { + public: + JoinSourceOther(); + JoinSourceOther(const JoinSourceOther& other); + JoinSourceOther(SqliteSelect::Core::JoinOp *op, + SqliteSelect::Core::SingleSource* src, + SqliteSelect::Core::JoinConstraint* constr); + + SqliteStatement* clone(); + + JoinOp* joinOp = nullptr; + SingleSource* singleSource = nullptr; + JoinConstraint* joinConstraint = nullptr; + + protected: + TokenList rebuildTokensFromContents(); + }; + + class API_EXPORT JoinSource : public SqliteStatement + { + public: + JoinSource(); + JoinSource(const JoinSource& other); + JoinSource(SingleSource* singleSource, const QList<JoinSourceOther*>& list); + + SqliteStatement* clone(); + + SingleSource* singleSource = nullptr; + QList<JoinSourceOther*> otherSources; + + protected: + TokenList rebuildTokensFromContents(); + }; + + 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); + + SqliteStatement* clone(); + + CompoundOperator compoundOp = CompoundOperator::null; + QList<ResultColumn*> resultColumns; + JoinSource* from = nullptr; + bool distinctKw = false; + bool allKw = false; + SqliteExpr* where = nullptr; + SqliteExpr* having = nullptr; + QList<SqliteExpr*> groupBy; + QList<SqliteOrderBy*> orderBy; + SqliteLimit* limit = nullptr; + bool valuesMode = false; + + protected: + TokenList rebuildTokensFromContents(); + }; + + SqliteSelect(); + SqliteSelect(const SqliteSelect& other); + + static SqliteSelect* append(SqliteSelect::Core* core); + static SqliteSelect* append(SqliteSelect* select, CompoundOperator op, SqliteSelect::Core* core); + static SqliteSelect* append(const QList<QList<SqliteExpr*>>& values); + static SqliteSelect* append(SqliteSelect* select, SqliteSelect::CompoundOperator op, const QList<QList<SqliteExpr*>>& values); + + SqliteStatement* clone(); + QString compoundOperator(CompoundOperator op); + CompoundOperator compoundOperator(const QString& op); + void reset(); + void setWith(SqliteWith* with); + + QList<Core*> coreSelects; + SqliteWith* with = nullptr; + + protected: + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteSelect> SqliteSelectPtr; + +#endif // SQLITESELECT_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesortorder.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesortorder.cpp new file mode 100644 index 0000000..13f3951 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesortorder.cpp @@ -0,0 +1,25 @@ +#include "sqlitesortorder.h" + +SqliteSortOrder sqliteSortOrder(const QString& value) +{ + if (value == "ASC") + return SqliteSortOrder::ASC; + else if (value == "DESC") + return SqliteSortOrder::DESC; + else + return SqliteSortOrder::null; +} + +QString sqliteSortOrder(SqliteSortOrder value) +{ + switch (value) + { + case SqliteSortOrder::ASC: + return "ASC"; + case SqliteSortOrder::DESC: + return "DESC"; + default: + return QString::null; + + } +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesortorder.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesortorder.h new file mode 100644 index 0000000..51c7052 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesortorder.h @@ -0,0 +1,17 @@ +#ifndef SQLITESORTORDER_H +#define SQLITESORTORDER_H + +#include "coreSQLiteStudio_global.h" +#include <QString> + +enum class SqliteSortOrder +{ + ASC, + DESC, + null +}; + +API_EXPORT SqliteSortOrder sqliteSortOrder(const QString& value); +API_EXPORT QString sqliteSortOrder(SqliteSortOrder value); + +#endif // SQLITESORTORDER_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.cpp new file mode 100644 index 0000000..482baf8 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.cpp @@ -0,0 +1,553 @@ +#include "sqlitestatement.h" +#include "../token.h" +#include "../lexer.h" +#include "common/unused.h" +#include <QDebug> + +SqliteStatement::SqliteStatement() +{ +} + +SqliteStatement::SqliteStatement(const SqliteStatement& other) : + QObject(), tokens(other.tokens), tokensMap(other.tokensMap), dialect(other.dialect) +{ + +} + +SqliteStatement::~SqliteStatement() +{ +} + +QString SqliteStatement::detokenize() +{ + return tokens.detokenize(); +} + +QStringList SqliteStatement::getContextColumns(bool checkParent, bool checkChilds) +{ + return getContextColumns(this, checkParent, checkChilds); +} + +QStringList SqliteStatement::getContextTables(bool checkParent, bool checkChilds) +{ + return getContextTables(this, checkParent, checkChilds); +} + +QStringList SqliteStatement::getContextDatabases(bool checkParent, bool checkChilds) +{ + return getContextDatabases(this, checkParent, checkChilds); +} + +TokenList SqliteStatement::getContextColumnTokens(bool checkParent, bool checkChilds) +{ + return getContextColumnTokens(this, checkParent, checkChilds); +} + +TokenList SqliteStatement::getContextTableTokens(bool checkParent, bool checkChilds) +{ + return getContextTableTokens(this, checkParent, checkChilds); +} + +TokenList SqliteStatement::getContextDatabaseTokens(bool checkParent, bool checkChilds) +{ + return getContextDatabaseTokens(this, checkParent, checkChilds); +} + +QList<SqliteStatement::FullObject> SqliteStatement::getContextFullObjects(bool checkParent, bool checkChilds) +{ + QList<FullObject> fullObjects = getContextFullObjects(this, checkParent, checkChilds); + + FullObject fullObj; + QMutableListIterator<FullObject> it(fullObjects); + while (it.hasNext()) + { + fullObj = it.next(); + if (fullObj.type == SqliteStatement::FullObject::NONE) + { + qWarning() << "FullObject of type NONE!"; + it.remove(); + continue; + } + + if (fullObj.type != SqliteStatement::FullObject::DATABASE && !fullObj.object) + { + qWarning() << "No 'object' member in FullObject that is not of DATABASE type!"; + it.remove(); + continue; + } + + if (fullObj.type == SqliteStatement::FullObject::DATABASE && !fullObj.database) + { + qWarning() << "No 'database' member in FullObject that is of DATABASE type!"; + it.remove(); + continue; + } + } + + return fullObjects; +} + +void SqliteStatement::setSqliteDialect(Dialect dialect) +{ + this->dialect = dialect; + foreach (SqliteStatement* stmt, childStatements()) + stmt->setSqliteDialect(dialect); +} + +SqliteStatementPtr SqliteStatement::detach() +{ + if (!parent()) + qWarning() << "Detaching " << this << ", but there's no parent!"; + + setParent(nullptr); + return SqliteStatementPtr(this); +} + +void SqliteStatement::processPostParsing() +{ + evaluatePostParsing(); + foreach (SqliteStatement* stmt, childStatements()) + stmt->processPostParsing(); +} + +QStringList SqliteStatement::getContextColumns(SqliteStatement *caller, bool checkParent, bool checkChilds) +{ + QStringList results = getColumnsInStatement(); + foreach (SqliteStatement* stmt, getContextStatements(caller, checkParent, checkChilds)) + results += stmt->getContextColumns(this, checkParent, checkChilds); + + return results; +} + +QStringList SqliteStatement::getContextTables(SqliteStatement *caller, bool checkParent, bool checkChilds) +{ + QStringList results = getTablesInStatement(); + foreach (SqliteStatement* stmt, getContextStatements(caller, checkParent, checkChilds)) + results += stmt->getContextTables(this, checkParent, checkChilds); + + return results; +} + +QStringList SqliteStatement::getContextDatabases(SqliteStatement *caller, bool checkParent, bool checkChilds) +{ + QStringList results = getDatabasesInStatement(); + foreach (SqliteStatement* stmt, getContextStatements(caller, checkParent, checkChilds)) + results += stmt->getContextDatabases(this, checkParent, checkChilds); + + return results; +} + +TokenList SqliteStatement::getContextColumnTokens(SqliteStatement *caller, bool checkParent, bool checkChilds) +{ + TokenList results = getColumnTokensInStatement(); + foreach (SqliteStatement* stmt, getContextStatements(caller, checkParent, checkChilds)) + results += stmt->getContextColumnTokens(this, checkParent, checkChilds); + + return results; +} + +TokenList SqliteStatement::getContextTableTokens(SqliteStatement *caller, bool checkParent, bool checkChilds) +{ + TokenList results = getTableTokensInStatement(); + foreach (SqliteStatement* stmt, getContextStatements(caller, checkParent, checkChilds)) + results += stmt->getContextTableTokens(this, checkParent, checkChilds); + + return results; +} + +TokenList SqliteStatement::getContextDatabaseTokens(SqliteStatement *caller, bool checkParent, bool checkChilds) +{ + TokenList results = getDatabaseTokensInStatement(); + foreach (SqliteStatement* stmt, getContextStatements(caller, checkParent, checkChilds)) + results += stmt->getContextDatabaseTokens(this, checkParent, checkChilds); + + return results; +} + +QList<SqliteStatement::FullObject> SqliteStatement::getContextFullObjects(SqliteStatement* caller, bool checkParent, bool checkChilds) +{ + QList<SqliteStatement::FullObject> results = getFullObjectsInStatement(); + foreach (SqliteStatement* stmt, getContextStatements(caller, checkParent, checkChilds)) + { + stmt->setContextDbForFullObject(dbTokenForFullObjects); + results += stmt->getContextFullObjects(this, checkParent, checkChilds); + } + + return results; +} + +QStringList SqliteStatement::getColumnsInStatement() +{ + return QStringList(); +} + +QStringList SqliteStatement::getTablesInStatement() +{ + return QStringList(); +} + +QStringList SqliteStatement::getDatabasesInStatement() +{ + return QStringList(); +} + +TokenList SqliteStatement::getColumnTokensInStatement() +{ + return TokenList(); +} + +TokenList SqliteStatement::getTableTokensInStatement() +{ + return TokenList(); +} + +TokenList SqliteStatement::getDatabaseTokensInStatement() +{ + return TokenList(); +} + +QList<SqliteStatement::FullObject> SqliteStatement::getFullObjectsInStatement() +{ + return QList<SqliteStatement::FullObject>(); +} + +TokenList SqliteStatement::rebuildTokensFromContents() +{ + qCritical() << "called rebuildTokensFromContents() for SqliteStatement that has no implementation for it."; + return TokenList(); +} + +void SqliteStatement::evaluatePostParsing() +{ +} + +QList<SqliteStatement *> SqliteStatement::getContextStatements(SqliteStatement *caller, bool checkParent, bool checkChilds) +{ + QList<SqliteStatement *> results; + + SqliteStatement* stmt = parentStatement(); + if (checkParent && stmt && stmt != caller) + results += stmt; + + if (checkChilds) + { + foreach (stmt, childStatements()) + { + if (stmt == caller) + continue; + + results += stmt; + } + } + + return results; +} + +TokenList SqliteStatement::extractPrintableTokens(const TokenList &tokens, bool skipMeaningless) +{ + TokenList list; + foreach (TokenPtr token, tokens) + { + switch (token->type) + { + case Token::OTHER: + case Token::STRING: + case Token::FLOAT: + case Token::INTEGER: + case Token::BIND_PARAM: + case Token::OPERATOR: + case Token::PAR_LEFT: + case Token::PAR_RIGHT: + case Token::BLOB: + case Token::KEYWORD: + list << token; + break; + case Token::COMMENT: + case Token::SPACE: + if (!skipMeaningless) + list << token; + break; + default: + break; + } + } + return list; +} + +QStringList SqliteStatement::getStrListFromValue(const QString &value) +{ + QStringList list; + if (!value.isNull()) + list << value; + + return list; +} + +TokenList SqliteStatement::getTokenListFromNamedKey(const QString &tokensMapKey, int idx) +{ + TokenList list; + if (tokensMap.contains(tokensMapKey)) + { + if (idx < 0) + list += extractPrintableTokens(tokensMap[tokensMapKey]); + else if (tokensMap[tokensMapKey].size() > idx) + list << extractPrintableTokens(tokensMap[tokensMapKey])[idx]; + } + else + qCritical() << "No '" << tokensMapKey << "' in tokens map when asked for it in getTokenListFromNamedKey()."; + + return list; +} + +TokenPtr SqliteStatement::getDbTokenFromFullname(const QString &tokensMapKey) +{ + if (!tokensMap.contains(tokensMapKey)) + { + qCritical() << "No '" << tokensMapKey << "' in tokens map when asked for it getDbTokenFromFullname()."; + return TokenPtr(); + } + + TokenList tokens = extractPrintableTokens(tokensMap[tokensMapKey]); + + if (tokens.size() == 3) + return tokens[0]; + else if (tokens.size() == 1) + return TokenPtr(); + else + qCritical() << "Expected 1 or 3 tokens in '" << tokensMapKey << "' in tokens map, but got" << tokens.size(); + + return TokenPtr(); +} + +TokenPtr SqliteStatement::getObjectTokenFromFullname(const QString &tokensMapKey) +{ + if (!tokensMap.contains(tokensMapKey)) + { + qCritical() << "No '" << tokensMapKey << "' in tokens map when asked for it."; + return TokenPtr(); + } + + TokenList tokens = extractPrintableTokens(tokensMap[tokensMapKey]); + if (tokens.size() == 3) + return tokens[2]; + else if (tokens.size() == 1) + return tokens[0]; + else + qCritical() << "Expected 1 or 3 tokens in '" << tokensMapKey << "' in tokens map, but got" << tokens.size(); + + return TokenPtr(); +} + +TokenPtr SqliteStatement::getDbTokenFromNmDbnm(const QString &tokensMapKey1, const QString &tokensMapKey2) +{ + if (!tokensMap.contains(tokensMapKey1)) + { + qCritical() << "No '" << tokensMapKey1 << "' in tokens map when asked for it in getDbTokenFromNmDbnm()."; + return TokenPtr(); + } + + // It seems like checking tokensMapKey2 has no added value to the logic, while it prevents from reporting + // database token in case of: SELECT * FROM dbName. <- the valid query with "minor" error + UNUSED(tokensMapKey2); +// if (!tokensMap.contains(tokensMapKey2)) +// { +// qCritical() << "No '" << tokensMapKey2 << "' in tokens map when asked for it in getDbTokenFromNmDbnm()."; +// return TokenPtr(); +// } + +// if (tokensMap[tokensMapKey2].size() == 0) +// return TokenPtr(); + + if (!tokensMap.contains("DOT") && tokensMap[tokensMapKey2].size() == 0) + { + // In this case the query is "SELECT * FROM test" and there is no database, + // but if there was a dot after the "test", then the "test" is a database name, + // so this block won't be executed. Instead the name of the database will be returned below. + return TokenPtr(); + } + + return extractPrintableTokens(tokensMap[tokensMapKey1])[0]; +} + +TokenPtr SqliteStatement::getObjectTokenFromNmDbnm(const QString &tokensMapKey1, const QString &tokensMapKey2) +{ + if (!tokensMap.contains(tokensMapKey1)) + { + qCritical() << "No '" << tokensMapKey1 << "' in tokens map when asked for it in getObjectTokenFromNmDbnm()."; + return TokenPtr(); + } + + if (!tokensMap.contains(tokensMapKey2)) + { + qCritical() << "No '" << tokensMapKey2 << "' in tokens map when asked for it in getObjectTokenFromNmDbnm()."; + return TokenPtr(); + } + + if (tokensMap[tokensMapKey2].size() == 0) + return extractPrintableTokens(tokensMap[tokensMapKey1])[0]; + + return extractPrintableTokens(tokensMap[tokensMapKey2])[1]; +} + +TokenList SqliteStatement::getDbTokenListFromFullname(const QString &tokensMapKey) +{ + TokenList list; + TokenPtr token = getDbTokenFromFullname(tokensMapKey); + if (token) + list << token; + + return list; +} + +TokenList SqliteStatement::getObjectTokenListFromFullname(const QString &tokensMapKey) +{ + TokenList list; + TokenPtr token = getObjectTokenFromFullname(tokensMapKey); + if (token) + list << token; + + return list; +} + +TokenList SqliteStatement::getDbTokenListFromNmDbnm(const QString &tokensMapKey1, const QString &tokensMapKey2) +{ + TokenList list; + TokenPtr token = getDbTokenFromNmDbnm(tokensMapKey1, tokensMapKey2); + if (token) + list << token; + + return list; +} + +TokenList SqliteStatement::getObjectTokenListFromNmDbnm(const QString &tokensMapKey1, const QString &tokensMapKey2) +{ + TokenList list; + TokenPtr token = getObjectTokenFromNmDbnm(tokensMapKey1, tokensMapKey2); + if (token) + list << token; + + return list; +} + +SqliteStatement::FullObject SqliteStatement::getFullObjectFromFullname(SqliteStatement::FullObject::Type type, const QString& tokensMapKey) +{ + return getFullObject(type, getDbTokenFromFullname(tokensMapKey), getObjectTokenFromFullname(tokensMapKey)); +} + +SqliteStatement::FullObject SqliteStatement::getFullObjectFromNmDbnm(SqliteStatement::FullObject::Type type, const QString& tokensMapKey1, const QString& tokensMapKey2) +{ + return getFullObject(type, getDbTokenFromNmDbnm(tokensMapKey1, tokensMapKey2), getObjectTokenFromNmDbnm(tokensMapKey1, tokensMapKey2)); +} + +SqliteStatement::FullObject SqliteStatement::getFullObject(SqliteStatement::FullObject::Type type, TokenPtr dbToken, TokenPtr objToken) +{ + FullObject fullObj; + if (!objToken) + return fullObj; + + fullObj.database = dbToken; + fullObj.object = objToken; + fullObj.type = type; + return fullObj; +} + +void SqliteStatement::setContextDbForFullObject(TokenPtr dbToken) +{ + dbTokenForFullObjects = dbToken; +} + +SqliteStatement::FullObject SqliteStatement::getFirstDbFullObject() +{ + TokenList dbTokens = getDatabaseTokensInStatement(); + return getDbFullObject(dbTokens.size() > 0 ? dbTokens[0] : TokenPtr()); +} + +SqliteStatement::FullObject SqliteStatement::getDbFullObject(TokenPtr dbToken) +{ + FullObject fullObj; + if (!dbToken) + return fullObj; + + fullObj.database = dbToken; + fullObj.type = FullObject::DATABASE; + return fullObj; +} + +Range SqliteStatement::getRange() +{ + if (tokens.size() == 0) + return Range(0, 0); + + return Range(tokens.first()->start, tokens.last()->end); +} + +SqliteStatement *SqliteStatement::findStatementWithToken(TokenPtr token) +{ + SqliteStatement* stmtWithToken = nullptr; + foreach (SqliteStatement* stmt, childStatements()) + { + stmtWithToken = stmt->findStatementWithToken(token); + if (stmtWithToken) + return stmtWithToken; + } + + if (tokens.contains(token)) + return this; + + return nullptr; +} + +SqliteStatement *SqliteStatement::findStatementWithPosition(quint64 cursorPosition) +{ + TokenPtr token = tokens.atCursorPosition(cursorPosition); + if (!token) + return nullptr; + + return findStatementWithToken(token); +} + +SqliteStatement *SqliteStatement::parentStatement() +{ + if (!parent()) + return nullptr; + + return dynamic_cast<SqliteStatement*>(parent()); +} + +QList<SqliteStatement *> SqliteStatement::childStatements() +{ + QList<SqliteStatement*> results; + foreach (QObject* obj, children()) + results += dynamic_cast<SqliteStatement*>(obj); + + return results; +} + +void SqliteStatement::rebuildTokens() +{ + tokens.clear(); + tokensMap.clear(); + tokens = rebuildTokensFromContents(); + // TODO rebuild tokensMap as well + // It shouldn't be hard to write unit tests that parse a query, remembers it tokensMap, then rebuilds tokens from contents + // 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; + childStatementToAttach->setParent(this); +} + +bool SqliteStatement::FullObject::isValid() const +{ + return (object != nullptr || (type == DATABASE && database != nullptr)); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.h new file mode 100644 index 0000000..bacb2d7 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.h @@ -0,0 +1,339 @@ +#ifndef SQLITESTATEMENT_H +#define SQLITESTATEMENT_H + +#include "common/utils.h" +#include "parser/token.h" +#include "dialect.h" +#include <QList> +#include <QHash> +#include <QObject> +#include <QPair> +#include <QStringList> +#include <QSharedPointer> + +// TODO start using attach() in most cases where setParent() is used + +class SqliteStatement; +typedef QSharedPointer<SqliteStatement> SqliteStatementPtr; + +/** + * @defgroup sqlite_statement Parser result containers + */ + +/** + * @ingroup sqlite_statement + * + * @brief General container for any type of parsed object. + * + * Statements can have multiple children statements and single parent statement. + * This way they create a tree of objects. Since those statements represent various language + * statements (here SQL), such tree is also called an Abstract Syntax Tree (aka AST). + * + * The AST lets you to examine structure of the query in a logical manner. + * In other words, once you parse query into AST, you have a tree of object, where each oject in + * the tree represents some part of the query and provides all its parameters as member variables. + * + * Deleting single statement causes all it's children to be deleted automatically. + * + * @section output_from_parser SqliteStatement as output from Parser + * + * The SqliteStatement is the most generic representation of Parser processing results. + * Every parsed query is represented by its specialized, derived class, but it's also + * the SqliteQuery and every SqliteQuery is also the SqliteStatement. + * + * Apart from SqliteQuery objects, which represent complete SQLite queries, there are also + * other statements, like expressions (http://sqlite.org/lang_expr.html) and others. + * Those statements don't inherit from SqliteQuery, but directly from SqliteStatement. + * + * Every parsed statement contains list of tokens that were used to parse this statement + * in SqliteStatement::tokens. + * + * 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, + * that you're examining SqliteStatement::tokensMap for. + * + * @note SqliteStatement::tokensMap is a low level API and it's not very predictible, + * unless you get to know it very well. That's why it's not recommended to use it. + * + * Example of working with SqliteStatement::tokensMap: you have a SqliteAttachPtr from the parser. + * You can learn the "attach name" from SqliteAttach::name, like this: + * @code + * QString name; + * if (attachPtr->name) + * name = attachPtr->name->detokenize(); + * @endcode + * or you can use tokensMap like this: + * @code + * QString name; + * if (attachPtr->tokensMap["expr2"]) + * name = attachPtr->tokensMap["expr2"]->detokenize(); + * @endcode + * + * Why using tokensMap, when you can read values from object member easly? Well, object members + * have plain values (string, integer, etc), while tokensMap has tokens, so you can examine + * at which character exactly was the value placed, where it ended, etc. + * + * @section query_generation SqliteStatement as utility for generating query string + * + * Generating query string with SqliteStatement makes sense only in case, when you have parsed + * query and got SqliteStatement as a result. You can modify some parameters of the query + * and detokenize it back to SQL string. This is done in 4 steps: + * <ul> + * <li>Parse SQL query string,</li> + * <li>Modify values in parsed statements,</li> + * <li>Re-generate tokens in all modified statements,</li> + * <li>Detokenize tokens from statements.</li> + * </ul> + * + * This is how it's usually done: + * @code + * // STEP 1 + * Parser parser(db->getDialect()); + * 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) + * return; + * } + * + * SqliteQueryPtr query = parser.getQueries().first(); + * SqliteSelectPtr select = query.dynamicCast<SqliteSelect>(); + * if (!select) + * { + * // we want to deal with the SELECT only + * return; + * } + * + * // STEP 2 + * SqliteSelect::Core* core = select->coreSelects.first(); + * + * // Prepare new result column statement + * SqliteSelect::Core::ResultColumn* resCol = new SqliteSelect::Core::ResultColumn(); + * + * SqliteExpr* expr = new SqliteExpr(); // create expression for result column + * expr->initLiteral("test value"); // let the expression be a constant literal value + * + * resCol->attach(resCol->expr, expr); // put the defined expression into result column statement + * core->attach(core->resultColumns, resCol); // add new result column to rest of columns + * + * // STEP 3 + * select->rebuildTokens(); + * + * // STEP 4 + * QString newQuery = select->detokenize(); + * @endcode + * + * In the result, the newQuery will contain: <tt>SELECT column, 'test value' FROM test WHERE value = 5</tt>. + * + * @warning It is important to use SqliteStatement::attach() and SqliteStatement::detach() + * when modifying AST layout. The tree hierarchy is used to delete objects recurrently, + * so deleting the SqliteSelect will also delete all it's children. Assembling or disassembling statements + * manually (without SqliteStatement::attach() and SqliteStatement::detach()) is not safe and will most likely + * result in a memory leak, or application crash. + * + * For example of SqliteStatement::detach() usage see section below. + * + * @section ptr_vs_shared_ptr C++ pointer to SqliteStatement vs. SqliteStatementPtr + * + * SqliteStatementPtr is a shared pointer (QSharedPointer) to SqliteStatement. All derived classes + * also have variations of their types as shared pointers. However only the top level objects + * returned from the Parser are actually provided as shared pointers. All children objects are + * regular C++ pointers. The reason behind this is to avoid memory leaks. Top level objects from Parser + * are shared pointers, so you can use them casually, without worrying who and when should delete them. + * On the other hand, any children of those top level objects are regular pointers, cause they will + * be deleted automatically when their parent is deleted. + * + * Sometimes you might want to use just some child statement from parsed query. Normally you would need to + * assign that child statement to some local variable and reset its parent to nullptr. Fortunately + * SqliteStatement provides handful method SqliteStatement::detach(), which does all that for you. + * It also provides detached statement as a new shared pointer, so it's easier to manage it. + * Additionally there's a template version of detach() method which can return detached statement + * as provided statement type. + * + * Example: + * @code + * Parser parser(db->getDialect()); + * 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) + * return SqliteExprPtr(); + * } + * + * SqliteQueryPtr query = parser.getQueries().first(); + * SqliteSelectPtr select = query.dynamicCast<SqliteSelect>(); + * if (!select) + * { + * // we want to deal with the SELECT only + * return SqliteExprPtr(); + * } + * + * SqliteSelect::Core* core = select->coreSelects.first(); + * + * // Our point is to get the SqliteExpr which represents the result column "column". + * SqliteExprPtr expr = core->resultColumns.first().detach<SqliteExpr>(); + * return expr; + * @endcode + * + * After the above <tt>parser</tt> goes out of scope, so it's deleted and all its parsed + * queries get deleted as well, because their shared pointers were not copied anywhere else. + * The captured <tt>expr</tt> would normally also be deleted, but when we detached it, it became + * an independed entity, with its own lifetime. + * + * For the opposite operation, use SqliteStatement::attach(). + */ +class API_EXPORT SqliteStatement : public QObject +{ + Q_OBJECT + + public: + struct FullObject + { + enum Type + { + TABLE, + INDEX, + TRIGGER, + VIEW, + DATABASE, + NONE + }; + + bool isValid() const; + + Type type = NONE; + TokenPtr database; + TokenPtr object; + }; + + SqliteStatement(); + SqliteStatement(const SqliteStatement& other); + virtual ~SqliteStatement(); + + QString detokenize(); + Range getRange(); + SqliteStatement* findStatementWithToken(TokenPtr token); + SqliteStatement* findStatementWithPosition(quint64 cursorPosition); + SqliteStatement* parentStatement(); + QList<SqliteStatement*> childStatements(); + QStringList getContextColumns(bool checkParent = true, bool checkChilds = true); + QStringList getContextTables(bool checkParent = true, bool checkChilds = true); + QStringList getContextDatabases(bool checkParent = true, bool checkChilds = true); + TokenList getContextColumnTokens(bool checkParent = true, bool checkChilds = true); + 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(); + virtual SqliteStatement* clone() = 0; + + template <class T> + void attach(QList<T*>& listMemberForChild, T* childStatementToAttach) + { + listMemberForChild << childStatementToAttach; + childStatementToAttach->setParent(this); + } + + template <class X> + QSharedPointer<X> detach() {return detach().dynamicCast<X>();} + + template <class T> + QList<T*> getAllTypedStatements() + { + QList<T*> results; + + T* casted = dynamic_cast<T*>(this); + if (casted) + results << casted; + + foreach (SqliteStatement* stmt, getContextStatements(this, false, true)) + results += stmt->getAllTypedStatements<T>(); + + return results; + } + + /** + * @brief Ordered list of all tokens for this statement. + * An ordered list of tokens that represent current statement. Tokens include + * Token::SPACE and Token::COMMENT types, so detokenizing this list results + * in the equal SQL string as was passed to the parser at the begining. + */ + TokenList tokens; + + /** + * @brief Map of grammar terminals and non-terminals into their tokens. + * This is map of ordered token lists that represent each node of SQLite grammar definition + * used to build this statement. For example grammar definition: + * test ::= value1 TERMINAL_TOKEN value2 + * will result in tokens map containing following entries: + * value1 = {list of tokens from value1} + * TERMINAL_TOKEN = {list of only one token: TERMINAL_TOKEN} + * value2 = {list of tokens from value2} + * + * In case there are two non-terminals with same name used (for example "name name name"), + * then first non-terminal is used as a key just as is, but second is renamed to "name2", + * and third to "name3". If we had example: + * test ::= value TERMINAL_TOKEN value + * then it will result in tokens map containing following entries: + * value = {list of tokens from first value} + * TERMINAL_TOKEN = {list of only one token: TERMINAL_TOKEN} + * value2 = {list of tokens from second value} + */ + 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); + QStringList getContextDatabases(SqliteStatement* caller, bool checkParent, bool checkChilds); + TokenList getContextColumnTokens(SqliteStatement* caller, bool checkParent, bool checkChilds); + TokenList getContextTableTokens(SqliteStatement* caller, bool checkParent, bool checkChilds); + TokenList getContextDatabaseTokens(SqliteStatement* caller, bool checkParent, bool checkChilds); + QList<FullObject> getContextFullObjects(SqliteStatement* caller, bool checkParent, bool checkChilds); + + virtual QStringList getColumnsInStatement(); + virtual QStringList getTablesInStatement(); + virtual QStringList getDatabasesInStatement(); + virtual TokenList getColumnTokensInStatement(); + virtual TokenList getTableTokensInStatement(); + virtual TokenList getDatabaseTokensInStatement(); + virtual QList<FullObject> getFullObjectsInStatement(); + virtual TokenList rebuildTokensFromContents(); + virtual void evaluatePostParsing(); + + static TokenList extractPrintableTokens(const TokenList& tokens, bool skipMeaningless = true); + QStringList getStrListFromValue(const QString& value); + TokenList getTokenListFromNamedKey(const QString& tokensMapKey, int idx = 0); + TokenPtr getDbTokenFromFullname(const QString& tokensMapKey = "fullname"); + TokenPtr getObjectTokenFromFullname(const QString& tokensMapKey = "fullname"); + TokenPtr getDbTokenFromNmDbnm(const QString& tokensMapKey1 = "nm", const QString& tokensMapKey2 = "dbnm"); + TokenPtr getObjectTokenFromNmDbnm(const QString& tokensMapKey1 = "nm", const QString& tokensMapKey2 = "dbnm"); + TokenList getDbTokenListFromFullname(const QString& tokensMapKey = "fullname"); + TokenList getObjectTokenListFromFullname(const QString& tokensMapKey = "fullname"); + TokenList getDbTokenListFromNmDbnm(const QString& tokensMapKey1 = "nm", const QString& tokensMapKey2 = "dbnm"); + TokenList getObjectTokenListFromNmDbnm(const QString& tokensMapKey1 = "nm", const QString& tokensMapKey2 = "dbnm"); + FullObject getFullObjectFromFullname(FullObject::Type type, const QString& tokensMapKey = "fullname"); + FullObject getFullObjectFromNmDbnm(FullObject::Type type, const QString& tokensMapKey1 = "nm", const QString& tokensMapKey2 = "dbnm"); + FullObject getFirstDbFullObject(); + FullObject getDbFullObject(TokenPtr dbToken); + FullObject getFullObject(SqliteStatement::FullObject::Type type, TokenPtr dbToken, TokenPtr objToken); + void setContextDbForFullObject(TokenPtr dbToken); + + /** + * @brief Token representing a database. + * Keeps db context for getFullObjectsInStatement(), so for example "CREATE TABLE xyz.abc (id)" will know, + * that for column "id" the database is "xyz" and table "abc". The value is spread across childrens + * of this statement when getContextFullObjects() is called. + * The value of this variable is defined in overwritten implementation of getFullObjectsInStatement() method. + */ + TokenPtr dbTokenForFullObjects; + + private: + QList<SqliteStatement*> getContextStatements(SqliteStatement* caller, bool checkParent, bool checkChilds); +}; + +#endif // SQLITESTATEMENT_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitetablerelatedddl.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitetablerelatedddl.h new file mode 100644 index 0000000..599849a --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitetablerelatedddl.h @@ -0,0 +1,14 @@ +#ifndef SQLITETABLERELATEDDDL_H +#define SQLITETABLERELATEDDDL_H + +#include "coreSQLiteStudio_global.h" + +class API_EXPORT SqliteTableRelatedDdl +{ + public: + virtual QString getTargetTable() const = 0; +}; + +typedef QSharedPointer<SqliteTableRelatedDdl> SqliteTableRelatedDdlPtr; + +#endif // SQLITETABLERELATEDDDL_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.cpp new file mode 100644 index 0000000..88ac28b --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.cpp @@ -0,0 +1,207 @@ +#include "sqliteupdate.h" +#include "sqlitequerytype.h" +#include "sqliteexpr.h" +#include "parser/statementtokenbuilder.h" +#include "common/global.h" +#include "sqlitewith.h" +#include <QDebug> + +SqliteUpdate::SqliteUpdate() +{ + queryType = SqliteQueryType::Update; +} + +SqliteUpdate::SqliteUpdate(const SqliteUpdate& other) : + SqliteQuery(other), onConflict(other.onConflict), database(other.database), table(other.table), indexedByKw(other.indexedByKw), + notIndexedKw(other.notIndexedKw), indexedBy(other.indexedBy) +{ + // Special case of deep collection copy + SqliteExpr* newExpr = nullptr; + foreach (const ColumnAndValue& keyValue, other.keyValueMap) + { + newExpr = new SqliteExpr(*keyValue.second); + newExpr->setParent(this); + keyValueMap << ColumnAndValue(keyValue.first, newExpr); + } + + DEEP_COPY_FIELD(SqliteExpr, where); + DEEP_COPY_FIELD(SqliteWith, with); +} + +SqliteUpdate::~SqliteUpdate() +{ +} + +SqliteUpdate::SqliteUpdate(SqliteConflictAlgo onConflict, const QString &name1, const QString &name2, bool notIndexedKw, const QString &indexedBy, + const QList<QPair<QString,SqliteExpr*> > values, SqliteExpr *where, SqliteWith* with) + : SqliteUpdate() +{ + this->onConflict = onConflict; + + if (!name2.isNull()) + { + database = name1; + table = name2; + } + else + table = name1; + + this->indexedBy = indexedBy; + this->indexedByKw = !(indexedBy.isNull()); + this->notIndexedKw = notIndexedKw; + keyValueMap = values; + + this->where = where; + if (where) + where->setParent(this); + + this->with = with; + if (with) + with->setParent(this); + + foreach (const ColumnAndValue& keyValue, keyValueMap) + keyValue.second->setParent(this); +} + +SqliteStatement*SqliteUpdate::clone() +{ + return new SqliteUpdate(*this); +} + +SqliteExpr* SqliteUpdate::getValueForColumnSet(const QString& column) +{ + foreach (const ColumnAndValue& keyValue, keyValueMap) + { + if (keyValue.first == column) + return keyValue.second; + } + return nullptr; +} + +QStringList SqliteUpdate::getColumnsInStatement() +{ + QStringList columns; + foreach (const ColumnAndValue& keyValue, keyValueMap) + columns += keyValue.first; + + return columns; +} + +QStringList SqliteUpdate::getTablesInStatement() +{ + return getStrListFromValue(table); +} + +QStringList SqliteUpdate::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteUpdate::getColumnTokensInStatement() +{ + // This case is not simple. We only have "setlist" in tokensMap + // and it contains entire: col = expr, col = expr. + // In order to extract 'col' token, we go through all 'expr', + // for each 'expr' we get its first token, then locate it + // in entire "setlist", get back 2 tokens to get what's before "=". + TokenList list; + TokenList setListTokens = getTokenListFromNamedKey("setlist"); + int setListTokensSize = setListTokens.size(); + int colNameTokenIdx; + SqliteExpr* expr = nullptr; + foreach (const ColumnAndValue& keyValue, keyValueMap) + { + expr = keyValue.second; + colNameTokenIdx = setListTokens.indexOf(expr->tokens[0]) - 2; + if (colNameTokenIdx < 0 || colNameTokenIdx > setListTokensSize) + { + qCritical() << "Went out of bounds while looking for column tokens in SqliteUpdate::getColumnTokensInStatement()."; + continue; + } + list << setListTokens[colNameTokenIdx]; + } + return list; +} + +TokenList SqliteUpdate::getTableTokensInStatement() +{ + if (tokensMap.contains("fullname")) + return getObjectTokenListFromFullname(); + + return TokenList(); +} + +TokenList SqliteUpdate::getDatabaseTokensInStatement() +{ + if (tokensMap.contains("fullname")) + return getDbTokenListFromFullname(); + + if (tokensMap.contains("nm")) + return extractPrintableTokens(tokensMap["nm"]); + + return TokenList(); +} + +QList<SqliteStatement::FullObject> SqliteUpdate::getFullObjectsInStatement() +{ + QList<FullObject> result; + if (!tokensMap.contains("fullname")) + return result; + + // Table object + FullObject fullObj = getFullObjectFromFullname(FullObject::TABLE); + + if (fullObj.isValid()) + result << fullObj; + + // Db object + fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + { + result << fullObj; + dbTokenForFullObjects = fullObj.database; + } + + return result; +} + +TokenList SqliteUpdate::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + if (with) + builder.withStatement(with); + + builder.withKeyword("UPDATE").withSpace(); + if (onConflict != SqliteConflictAlgo::null) + builder.withKeyword("OR").withSpace().withKeyword(sqliteConflictAlgo(onConflict)).withSpace(); + + if (!database.isNull()) + builder.withOther(database, dialect).withOperator("."); + + builder.withOther(table, dialect).withSpace(); + + if (indexedByKw) + builder.withKeyword("INDEXED").withSpace().withKeyword("BY").withSpace().withOther(indexedBy, dialect).withSpace(); + else if (notIndexedKw) + builder.withKeyword("NOT").withSpace().withKeyword("INDEXED").withSpace(); + + builder.withKeyword("SET").withSpace(); + + bool first = true; + foreach (const ColumnAndValue& keyVal, keyValueMap) + { + if (!first) + builder.withOperator(",").withSpace(); + + builder.withOther(keyVal.first, dialect).withSpace().withOperator("=").withStatement(keyVal.second); + first = false; + } + + if (where) + builder.withSpace().withKeyword("WHERE").withStatement(where); + + builder.withOperator(";"); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.h new file mode 100644 index 0000000..7d6e0c1 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.h @@ -0,0 +1,51 @@ +#ifndef SQLITEUPDATE_H +#define SQLITEUPDATE_H + +#include "sqlitequery.h" +#include "sqliteconflictalgo.h" + +#include <QStringList> +#include <QMap> + +class SqliteExpr; +class SqliteWith; + +class API_EXPORT SqliteUpdate : public SqliteQuery +{ + public: + typedef QPair<QString,SqliteExpr*> ColumnAndValue; + + SqliteUpdate(); + SqliteUpdate(const SqliteUpdate& other); + ~SqliteUpdate(); + SqliteUpdate(SqliteConflictAlgo onConflict, const QString& name1, const QString& name2, + bool notIndexedKw, const QString& indexedBy, const QList<QPair<QString,SqliteExpr*> > values, + SqliteExpr* where, SqliteWith* with); + + SqliteStatement* clone(); + SqliteExpr* getValueForColumnSet(const QString& column); + + SqliteConflictAlgo onConflict = SqliteConflictAlgo::null; + QString database = QString::null; + QString table = QString::null; + bool indexedByKw = false; + bool notIndexedKw = false; + QString indexedBy = QString::null; + QList<ColumnAndValue> keyValueMap; + SqliteExpr* where = nullptr; + SqliteWith* with = nullptr; + + protected: + QStringList getColumnsInStatement(); + QStringList getTablesInStatement(); + QStringList getDatabasesInStatement(); + TokenList getColumnTokensInStatement(); + TokenList getTableTokensInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteUpdate> SqliteUpdatePtr; + +#endif // SQLITEUPDATE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitevacuum.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitevacuum.cpp new file mode 100644 index 0000000..ab7d00e --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitevacuum.cpp @@ -0,0 +1,56 @@ +#include "sqlitevacuum.h" +#include "sqlitequerytype.h" + +#include <parser/statementtokenbuilder.h> + +SqliteVacuum::SqliteVacuum() +{ + queryType = SqliteQueryType::Vacuum; +} + +SqliteVacuum::SqliteVacuum(const SqliteVacuum& other) : + SqliteQuery(other), database(other.database) +{ +} + +SqliteVacuum::SqliteVacuum(const QString& name) + : SqliteVacuum() +{ + if (!name.isNull()) + database = name; +} + +SqliteStatement*SqliteVacuum::clone() +{ + return new SqliteVacuum(*this); +} + +QStringList SqliteVacuum::getDatabasesInStatement() +{ + return getStrListFromValue(database); +} + +TokenList SqliteVacuum::getDatabaseTokensInStatement() +{ + return getTokenListFromNamedKey("nm"); +} + +QList<SqliteStatement::FullObject> SqliteVacuum::getFullObjectsInStatement() +{ + QList<FullObject> result; + + // Db object + FullObject fullObj = getFirstDbFullObject(); + if (fullObj.isValid()) + result << fullObj; + + return result; +} + + +TokenList SqliteVacuum::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + builder.withKeyword("VACUUM").withOperator(";"); + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitevacuum.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitevacuum.h new file mode 100644 index 0000000..871b8f4 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitevacuum.h @@ -0,0 +1,28 @@ +#ifndef SQLITEVACUUM_H +#define SQLITEVACUUM_H + +#include "sqlitequery.h" + +#include <QString> + +class API_EXPORT SqliteVacuum : public SqliteQuery +{ + public: + SqliteVacuum(); + SqliteVacuum(const SqliteVacuum& other); + explicit SqliteVacuum(const QString &name); + + SqliteStatement* clone(); + + QString database; + + protected: + QStringList getDatabasesInStatement(); + TokenList getDatabaseTokensInStatement(); + QList<FullObject> getFullObjectsInStatement(); + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteVacuum> SqliteVacuumPtr; + +#endif // SQLITEVACUUM_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.cpp new file mode 100644 index 0000000..2b9c99f --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.cpp @@ -0,0 +1,87 @@ +#include "sqlitewith.h" +#include "parser/statementtokenbuilder.h" +#include "sqliteselect.h" +#include "common/global.h" + +SqliteWith::SqliteWith() +{ +} + +SqliteWith::SqliteWith(const SqliteWith& other) : + SqliteStatement(other), recursive(other.recursive) +{ + 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); +} + +TokenList SqliteWith::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + + builder.withKeyword("WITH").withSpace(); + if (recursive) + builder.withKeyword("RECURSIVE").withSpace(); + + builder.withStatementList(cteList); + + return builder.build(); +} + +SqliteWith::CommonTableExpression::CommonTableExpression() +{ +} + +SqliteWith::CommonTableExpression::CommonTableExpression(const SqliteWith::CommonTableExpression& other) : + SqliteStatement(other), table(other.table) +{ + DEEP_COPY_COLLECTION(SqliteIndexedColumn, indexedColumns); + DEEP_COPY_FIELD(SqliteSelect, select); +} + +SqliteWith::CommonTableExpression::CommonTableExpression(const QString& tableName, const QList<SqliteIndexedColumn*>& indexedColumns, SqliteSelect* select) : + table(tableName), indexedColumns(indexedColumns), select(select) +{ + select->setParent(this); +} + +SqliteStatement*SqliteWith::CommonTableExpression::clone() +{ + return new SqliteWith::CommonTableExpression(*this); +} + +TokenList SqliteWith::CommonTableExpression::rebuildTokensFromContents() +{ + StatementTokenBuilder builder; + builder.withOther(table, dialect); + + if (indexedColumns.size() > 0) + builder.withSpace().withParLeft().withStatementList(indexedColumns).withParRight(); + + builder.withSpace().withKeyword("AS").withSpace().withParLeft().withStatement(select).withParRight(); + + return builder.build(); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.h new file mode 100644 index 0000000..fe64c9c --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.h @@ -0,0 +1,45 @@ +#ifndef SQLITEWITH_H +#define SQLITEWITH_H + +#include "sqlitestatement.h" +#include "sqliteindexedcolumn.h" + +class SqliteSelect; + +class SqliteWith : public SqliteStatement +{ + public: + class CommonTableExpression : public SqliteStatement + { + public: + CommonTableExpression(); + CommonTableExpression(const CommonTableExpression& other); + CommonTableExpression(const QString& tableName, const QList<SqliteIndexedColumn*>& indexedColumns, SqliteSelect* select); + + SqliteStatement* clone(); + + QString table; + QList<SqliteIndexedColumn*> indexedColumns; + SqliteSelect* select = nullptr; + + protected: + TokenList rebuildTokensFromContents(); + }; + + 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(); + + QList<CommonTableExpression*> cteList; + bool recursive = false; + + protected: + TokenList rebuildTokensFromContents(); +}; + +typedef QSharedPointer<SqliteWith> SqliteWithPtr; + +#endif // SQLITEWITH_H |
