aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/parser/ast
diff options
context:
space:
mode:
authorLibravatarUnit 193 <unit193@ubuntu.com>2014-12-06 17:33:25 -0500
committerLibravatarUnit 193 <unit193@ubuntu.com>2014-12-06 17:33:25 -0500
commit7167ce41b61d2ba2cdb526777a4233eb84a3b66a (patch)
treea35c14143716e1f2c98f808c81f89426045a946f /SQLiteStudio3/coreSQLiteStudio/parser/ast
Imported Upstream version 2.99.6upstream/2.99.6
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/parser/ast')
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.cpp128
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.h46
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteanalyze.cpp80
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteanalyze.h29
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteattach.cpp63
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteattach.h28
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitebegintrans.cpp71
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitebegintrans.h38
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecolumntype.cpp88
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecolumntype.h30
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecommittrans.cpp48
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecommittrans.h25
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteconflictalgo.cpp37
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteconflictalgo.h20
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.cpp92
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.h32
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.cpp190
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.h50
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp771
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h205
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.cpp381
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.h96
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.cpp120
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.h35
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatevirtualtable.cpp120
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatevirtualtable.h42
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedeferrable.cpp54
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedeferrable.h27
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.cpp137
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.h45
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedetach.cpp48
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedetach.h27
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropindex.cpp78
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropindex.h29
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptable.cpp86
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptable.h31
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptrigger.cpp79
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedroptrigger.h29
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropview.cpp85
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedropview.h29
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteemptyquery.cpp27
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteemptyquery.h19
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp644
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.h144
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteforeignkey.cpp187
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteforeignkey.h75
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteindexedcolumn.cpp52
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteindexedcolumn.h30
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.cpp214
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.h57
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitelimit.cpp79
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitelimit.h31
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.cpp41
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.h28
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitepragma.cpp109
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitepragma.h41
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequery.cpp51
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequery.h30
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.cpp66
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.h41
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteraise.cpp70
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteraise.h38
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitereindex.cpp82
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitereindex.h31
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterelease.cpp39
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterelease.h26
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterollback.cpp57
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliterollback.h28
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesavepoint.cpp32
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesavepoint.h25
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.cpp783
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.h228
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesortorder.cpp25
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitesortorder.h17
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.cpp553
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.h339
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitetablerelatedddl.h14
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.cpp207
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.h51
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitevacuum.cpp56
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitevacuum.h28
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.cpp87
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.h45
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