aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/schemaresolver.h
blob: c7b457f624ae40a4112103b47dd76aaf86cd2e16 (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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
#ifndef SCHEMARESOLVER_H
#define SCHEMARESOLVER_H

#include "parser/parser.h"
#include "parser/ast/sqlitequerytype.h"
#include "parser/ast/sqlitecreatetable.h"
#include "parser/ast/sqlitecreateindex.h"
#include "parser/ast/sqlitecreateview.h"
#include "parser/ast/sqlitecreatetrigger.h"
#include "coreSQLiteStudio_global.h"
#include "common/utils_sql.h"
#include "selectresolver.h"
#include "db/sqlresultsrow.h"
#include "db/sqlquery.h"
#include "db/db.h"
#include "common/strhash.h"
#include "common/expiringcache.h"
#include "parser/ast/sqlitequerytype.h"
#include <QStringList>

class SqliteCreateTable;

// TODO add cache

class API_EXPORT SchemaResolver
{
    public:
        enum ObjectType
        {
            TABLE,
            INDEX,
            TRIGGER,
            VIEW,
            ANY
        };

        struct ObjectDetails
        {
            ObjectType type;
            QString ddl;
        };

        struct TableListItem
        {
            enum Type
            {
                TABLE,
                VIRTUAL_TABLE,
                SHADOW_TABLE,
                VIEW,
                UNKNOWN
            };

            Type type;
            QString name;
        };

        struct ObjectCacheKey
        {
            enum Type
            {
                OBJECT_NAMES,
                OBJECT_DETAILS,
                TABLE_LIST_ITEM,
                OBJECT_DDL
            };

            ObjectCacheKey(Type type, Db* db, const QString& value1 = QString(), const QString& value2 = QString(), const QString& value3 = QString());
            ObjectCacheKey(Type type, Db* db, bool skipSystemObj, const QString& value1 = QString(), const QString& value2 = QString(), const QString& value3 = QString());

            Type type;
            Db* db;
            bool skipSystemObj;
            QString value1;
            QString value2;
            QString value3;
        };

        explicit SchemaResolver(Db* db);
        virtual ~SchemaResolver();

        QStringList getTables(const QString& database = QString());
        QStringList getIndexes(const QString& database = QString());
        QStringList getTriggers(const QString& database = QString());
        QStringList getViews(const QString& database = QString());
        StrHash<QStringList> getGroupedIndexes(const QString& database = QString());
        StrHash<QStringList> getGroupedTriggers(const QString& database = QString());
        QSet<QString> getDatabases();
        QStringList getObjects(const QString& type);
        QStringList getObjects(const QString& database, const QString& type);
        QStringList getAllObjects();
        QStringList getAllObjects(const QString& database);
        QString getUniqueName(const QString& database, const QString& namePrefix, const QStringList& forbiddenNames = QStringList());
        QString getUniqueName(const QString& namePrefix = QString(), const QStringList& forbiddenNames = QStringList());
        QStringList getFkReferencingTables(const QString& table);
        QStringList getFkReferencingTables(const QString& database, const QString& table);
        QStringList getFkReferencedTables(const QString& table);
        QStringList getFkReferencedTables(const QString& database, const QString& table);

        QStringList getIndexesForTable(const QString& database, const QString& table);
        QStringList getIndexesForTable(const QString& table);
        QStringList getTriggersForTable(const QString& database, const QString& table);
        QStringList getTriggersForTable(const QString& table);
        QStringList getTriggersForView(const QString& database, const QString& view);
        QStringList getTriggersForView(const QString& view);
        QStringList getViewsForTable(const QString& database, const QString& table);
        QStringList getViewsForTable(const QString& table);
        QStringList getIndexDdlsForTable(const QString& database, const QString& table);
        QStringList getIndexDdlsForTable(const QString& table);
        QStringList getTriggerDdlsForTableOrView(const QString& database, const QString& table);
        QStringList getTriggerDdlsForTableOrView(const QString& table);

        QList<TableListItem> getAllTableListItems();
        QList<TableListItem> getAllTableListItems(const QString& database);

        StrHash<ObjectDetails> getAllObjectDetails();
        StrHash<ObjectDetails> getAllObjectDetails(const QString& database);

        QList<SqliteCreateIndexPtr> getParsedIndexesForTable(const QString& database, const QString& table);
        QList<SqliteCreateIndexPtr> getParsedIndexesForTable(const QString& table);
        QList<SqliteCreateTriggerPtr> getParsedTriggersForTable(const QString& database, const QString& table, bool includeContentReferences = false);
        QList<SqliteCreateTriggerPtr> getParsedTriggersForTable(const QString& table, bool includeContentReferences = false);
        QList<SqliteCreateTriggerPtr> getParsedTriggersForView(const QString& database, const QString& view, bool includeContentReferences = false);
        QList<SqliteCreateTriggerPtr> getParsedTriggersForView(const QString& view, bool includeContentReferences = false);
        QList<SqliteCreateViewPtr> getParsedViewsForTable(const QString& database, const QString& table);
        QList<SqliteCreateViewPtr> getParsedViewsForTable(const QString& table);

        bool isWithoutRowIdTable(const QString& table);
        bool isWithoutRowIdTable(const QString& database, const QString& table);
        bool isVirtualTable(const QString& database, const QString& table);
        bool isVirtualTable(const QString& table);
        SqliteCreateTablePtr resolveVirtualTableAsRegularTable(const QString& table);
        SqliteCreateTablePtr resolveVirtualTableAsRegularTable(const QString& database, const QString& table);

        QStringList getWithoutRowIdTableColumns(const QString& table);
        QStringList getWithoutRowIdTableColumns(const QString& database, const QString& table);

        /**
         * @brief getTableColumns Get column names for a table.
         * @param table Table to query.
         * @param onlyReal If true, the method will skip columns that are not real (like GENERATED columns).
         * @return List of column names of the table.
         * This does something similar to getting list of columns with
         * PRAGMA table_info(), but the pragma in Sqlite2 doesn't support
         * queries for attached databases, therefore this method is provided
         * to make this possible. It finds table's DDL and parses it,
         * then extracts list of columns from parsing results.
         */
        QStringList getTableColumns(const QString& table, bool onlyReal = false);

        /**
         * @brief getTableColumns Get column names for a table.
         * @param database Attached database name.
         * @param table Table to query.
         * @param onlyReal If true, the method will skip columns that are not real (like GENERATED columns).
         * @return List of column names of the table.
         * @overload
         */
        QStringList getTableColumns(const QString& database, const QString& table, bool onlyReal = false);

        StrHash<DataType> getTableColumnDataTypesByName(const QString& table);
        StrHash<DataType> getTableColumnDataTypesByName(const QString& database, const QString& table);
        QList<DataType> getTableColumnDataTypes(const QString& table, int expectedNumberOfTypes = -1);
        QList<DataType> getTableColumnDataTypes(const QString& database, const QString& table, int expectedNumberOfTypes = -1);

        StrHash<QStringList> getAllTableColumns(const QString& database = QString());

        QStringList getViewColumns(const QString& view);
        QStringList getViewColumns(const QString& database, const QString& view);
        QList<SelectResolver::Column> getViewColumnObjects(const QString& view);
        QList<SelectResolver::Column> getViewColumnObjects(const QString& database, const QString& view);

        QString getObjectDdl(const QString& name, ObjectType type);
        QString getObjectDdl(const QString& database, const QString& name, ObjectType type);

        QStringList getColumnsFromDdlUsingPragma(const QString& ddl);
        QStringList getColumnsUsingPragma(const QString& tableOrView);
        QStringList getColumnsUsingPragma(SqliteCreateTable* createTable);
        QStringList getColumnsUsingPragma(SqliteCreateView* createView);

        /**
         * @brief Parses given object's DDL.
         * @param name Name of the object in the database.
         * @return Parsed object, or null pointer if named object was not in the database, or parsing error occured.
         *
         * Returned query has to be deleted outside!
         */
        SqliteQueryPtr getParsedObject(const QString& name, ObjectType type);

        /**
         * @brief Parses given object's DDL.
         * @param database Database that the object is in (the attach name of the database).
         * @param name Name of the object in the database.
         * @return Parsed object, or null pointer if named object was not in the database, or parsing error occured.
         * @overload
         */
        SqliteQueryPtr getParsedObject(const QString& database, const QString& name, ObjectType type);

        StrHash<SqliteQueryPtr> getAllParsedObjects();
        StrHash<SqliteQueryPtr> getAllParsedObjects(const QString& database);
        StrHash<SqliteCreateTablePtr> getAllParsedTables();
        StrHash<SqliteCreateTablePtr> getAllParsedTables(const QString& database);
        StrHash<SqliteCreateIndexPtr> getAllParsedIndexes();
        StrHash<SqliteCreateIndexPtr> getAllParsedIndexes(const QString& database);
        StrHash<SqliteCreateTriggerPtr> getAllParsedTriggers();
        StrHash<SqliteCreateTriggerPtr> getAllParsedTriggers(const QString& database);
        StrHash<SqliteCreateViewPtr> getAllParsedViews();
        StrHash<SqliteCreateViewPtr> getAllParsedViews(const QString& database);

        QString getSqliteAutoIndexDdl(const QString& database, const QString& index);
        static QString getSqliteMasterDdl(bool temp = false);
        static ObjectType objectTypeFromQueryType(const SqliteQueryType& queryType);

        QStringList getCollations();

        bool getIgnoreSystemObjects() const;
        void setIgnoreSystemObjects(bool value);

        bool getNoDbLocking() const;
        void setNoDbLocking(bool value);

        QString normalizeCaseObjectName(const QString& name);
        QString normalizeCaseObjectName(const QString& database, const QString& name);

        static QString objectTypeToString(ObjectType type);
        static ObjectType stringToObjectType(const QString& type);
        static TableListItem::Type stringToTableListItemType(const QString& type);
        static void staticInit();

        static_char* USE_SCHEMA_CACHING = "useSchemaCaching";

    private:
        bool usesCache();
        SqliteQueryPtr getParsedDdl(const QString& ddl);
        SqliteCreateTablePtr virtualTableAsRegularTable(const QString& database, const QString& table);
        StrHash< QStringList> getGroupedObjects(const QString &database, const QStringList& inputList, SqliteQueryType type);
        bool isFilteredOut(const QString& value, const QString& type);
        void filterSystemIndexes(QStringList& indexes);
        QList<SqliteCreateTriggerPtr> getParsedTriggersForTableOrView(const QString& database, const QString& tableOrView, bool includeContentReferences);
        QString getObjectDdlWithDifficultName(const QString& dbName, const QString& lowerName, QString targetTable, ObjectType type);
        QString getObjectDdlWithSimpleName(const QString& dbName, const QString& lowerName, QString targetTable, ObjectType type);
        StrHash<QString> getIndexesWithTables(const QString& database = QString());
        QString normalizeCaseObjectNameByQuery(const QString& query, const QString& name);
        QStringList getObjectDdlsReferencingTableOrView(const QString& database, const QString& table, ObjectType type);

        template <class T>
        StrHash<QSharedPointer<T>> getAllParsedObjectsForType(const QString& database, const QString& type);

        Db* db = nullptr;
        Parser* parser = nullptr;
        bool ignoreSystemObjects = false;
        Db::Flags dbFlags;

        static ExpiringCache<ObjectCacheKey,QVariant> cache;
        static ExpiringCache<QString, QString> autoIndexDdlCache;
};

int qHash(const SchemaResolver::ObjectCacheKey& key);
int operator==(const SchemaResolver::ObjectCacheKey& k1, const SchemaResolver::ObjectCacheKey& k2);

template <class T>
StrHash<QSharedPointer<T>> SchemaResolver::getAllParsedObjectsForType(const QString& database, const QString& type)
{
     StrHash< QSharedPointer<T>> parsedObjects;

     QString dbName = getPrefixDb(database);

     SqlQueryPtr results;

     if (type.isNull())
         results = db->exec(QString("SELECT name, type, sql FROM %1.sqlite_master;").arg(dbName));
     else
         results = db->exec(QString("SELECT name, type, sql FROM %1.sqlite_master WHERE type = '%2';").arg(dbName, type));

     QString name;
     SqliteQueryPtr parsedObject;
     QSharedPointer<T> castedObject;
     for (SqlResultsRowPtr row : results->getAll())
     {
         name = row->value("name").toString();
         parsedObject = getParsedDdl(row->value("sql").toString());
         if (!parsedObject)
             continue;

         if (isFilteredOut(name, row->value("type").toString()))
             continue;

         castedObject = parsedObject.dynamicCast<T>();
         if (castedObject)
             parsedObjects[name] = castedObject;
     }

     return parsedObjects;
}

#endif // SCHEMARESOLVER_H