aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/tablemodifier.h
blob: 0e5b383b5ab40c3b41a931a39f7667d3223bf66e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#ifndef TABLEMODIFIER_H
#define TABLEMODIFIER_H

#include "db/db.h"
#include "selectresolver.h"
#include "parser/ast/sqlitecreatetable.h"
#include "parser/ast/sqliteupdate.h"
#include "parser/ast/sqliteinsert.h"
#include "parser/ast/sqlitedelete.h"
#include "parser/ast/sqlitecreateindex.h"
#include "parser/ast/sqlitecreatetrigger.h"
#include "parser/ast/sqlitecreateview.h"
#include "common/strhash.h"

class API_EXPORT TableModifier
{
    public:
        TableModifier(Db* db, const QString& table);
        TableModifier(Db* db, const QString& database, const QString& table);

        void alterTable(SqliteCreateTablePtr newCreateTable);

        QStringList generateSqls() const;
        bool isValid() const;
        QStringList getErrors() const;
        QStringList getWarnings() const;
        QStringList getModifiedTables() const;
        QStringList getModifiedIndexes() const;
        QStringList getModifiedTriggers() const;
        QStringList getModifiedViews() const;
        bool hasMessages() const;

    private:
        void init();
        void parseDdl();
        QString getTempTableName();
        void copyDataTo(const QString& targetTable, const QStringList& srcCols, const QStringList& dstCols);
        void renameTo(const QString& newName, bool doCopyData = true);
        QString renameToTemp(bool doCopyData = true);
        void copyDataTo(const QString& table);
        void copyDataTo(SqliteCreateTablePtr newCreateTable);

        void handleIndexes();
        void handleIndex(SqliteCreateIndexPtr index);
        void handleTriggers();
        void handleTrigger(SqliteCreateTriggerPtr trigger);
        void handleTriggerQueries(SqliteCreateTriggerPtr trigger);
        void handleViews();
        void handleView(SqliteCreateViewPtr view);
        SqliteQuery* handleTriggerQuery(SqliteQuery* query, const QString& trigName, const QString& trigTable);
        SqliteSelect* handleSelect(SqliteSelect* select, const QString& trigTable = QString());
        SqliteUpdate* handleTriggerUpdate(SqliteUpdate* update, const QString& trigName, const QString& trigTable);
        SqliteInsert* handleTriggerInsert(SqliteInsert* insert, const QString& trigName, const QString& trigTable);
        SqliteDelete* handleTriggerDelete(SqliteDelete* del, const QString& trigName, const QString& trigTable);
        StrHash<SelectResolver::Table> tablesAsNameHash(const QSet<SelectResolver::Table> &resolvedTables);
        bool isTableAliasUsedForColumn(const TokenPtr& token, const StrHash<SelectResolver::Table>& resolvedTables, const QList<SqliteSelect::Core::SingleSource*>& selSources);
        bool handleSubSelects(SqliteStatement* stmt, const QString& trigTable);
        bool handleExprWithSelect(SqliteExpr* expr, const QString& trigTable);
        bool handleAllExprWithTrigTable(SqliteStatement* stmt, const QString& contextTable);
        bool handleExprListWithTrigTable(const QList<SqliteExpr*>& exprList);
        bool handleExprWithTrigTable(SqliteExpr* expr);
        bool handleExpr(SqliteExpr* expr);
        void simpleHandleIndexes();
        void simpleHandleTriggers(const QString& view = QString());
        SqliteQueryPtr parseQuery(const QString& ddl);

        /**
         * @brief alterTableHandleFks
         * @param newCreateTable
         * Finds all tables referencing currently modified table and updates their referenced table name and columns.
         */
        void handleFks();
        void handleFkAsSubModifier(const QString& oldName, const QString& theNewName);
        bool handleFkStmt(SqliteForeignKey* fk, const QString& oldName, const QString& theNewName);
        bool handleFkConstrains(SqliteCreateTable* stmt, const QString& oldName, const QString& theNewName);

        bool handleName(const QString& oldName, QString& valueToUpdate);
        static bool handleName(const QString& oldName, const QString& theNewName, QString& valueToUpdate);
        bool handleIndexedColumns(const QList<SqliteOrderBy*>& columnsToUpdate);
        bool handleIndexedColumnsInitial(SqliteOrderBy* col, bool& modified);
        bool handleIndexedColumnsInitial(SqliteIndexedColumn* col, bool& modified);
        bool handleColumnNames(QStringList& columnsToUpdate);
        bool handleColumnTokens(TokenList& columnsToUpdate);
        bool handleUpdateColumns(SqliteUpdate* update);
        QStringList handleUpdateColumns(const QStringList& colNames, bool& modified);
        QString handleUpdateColumn(const QString& colName, bool& modified);
        QList<SqliteCreateTable::Column*> getColumnsToCopyData(SqliteCreateTablePtr newCreateTable);

        template <class T>
        bool handleIndexedColumns(QList<T*>& columnsToUpdate)
        {
            bool modified = false;
            QString lowerName;
            QString colName;
            QMutableListIterator<T*> it(columnsToUpdate);
            while (it.hasNext())
            {
                T* idxCol = it.next();
                if (handleIndexedColumnsInitial(idxCol, modified))
                    continue;

                colName = idxCol->getColumnName();

                // If column was modified, assign new name
                lowerName = colName.toLower();
                if (tableColMap.contains(lowerName))
                {
                    idxCol->setColumnName(tableColMap[lowerName]);
                    modified = true;
                    continue;
                }

                // It wasn't modified, but it's not on existing columns list? Remove it.
                if (indexOf(existingColumns, colName, Qt::CaseInsensitive) == -1)
                {
                    it.remove();
                    modified = true;
                }
            }
            return modified;
        }

        Db* db = nullptr;

        /**
         * @brief Database name. The "main" is default.
         * Other databases (temp, attached...) are not supported at the moment.
         */
        QString database;

        /**
         * @brief Current table name (after renaming)
         */
        QString table;

        /**
         * @brief Initial table name, before any renaming.
         */
        QString originalTable;

        /**
         * @brief createTable Original DDL.
         */
        SqliteCreateTablePtr createTable;

        /**
         * @brief Statements to be executed to make changes real.
         */
        QStringList sqls;

        QStringList warnings;
        QStringList errors;

        QString newName;
        QStringList existingColumns;
        QHash<QString, QString> tableColMap;
        QHash<QString, QString> triggerNameToDdlMap;
        QStringList tablesHandledForFk;
        QStringList modifiedTables;
        QStringList modifiedIndexes;
        QStringList modifiedTriggers;
        QStringList modifiedViews;
        QStringList usedTempTableNames;
};


#endif // TABLEMODIFIER_H