1 #include "queryexecutorreplaceviews.h"
2 #include "parser/ast/sqlitecreateview.h"
3 #include "schemaresolver.h"
4 #include <QDebug>
5 
~QueryExecutorReplaceViews()6 QueryExecutorReplaceViews::~QueryExecutorReplaceViews()
7 {
8     if (schemaResolver)
9     {
10         delete schemaResolver;
11         schemaResolver = nullptr;
12     }
13 }
14 
exec()15 bool QueryExecutorReplaceViews::exec()
16 {
17     SqliteSelectPtr select = getSelect();
18     if (!select || select->explain)
19         return true;
20 
21     if (select->coreSelects.size() > 1)
22         return true;
23 
24     if (select->coreSelects.first()->distinctKw)
25         return true;
26 
27     replaceViews(select.data());
28     select->rebuildTokens();
29     updateQueries();
30 
31     return true;
32 }
33 
init()34 void QueryExecutorReplaceViews::init()
35 {
36     if (!schemaResolver)
37         schemaResolver = new SchemaResolver(db);
38 }
39 
getViews(const QString & database)40 QStringList QueryExecutorReplaceViews::getViews(const QString& database)
41 {
42     QString dbName = database.isNull() ? "main" : database.toLower();
43     if (views.contains(dbName))
44         return views[dbName];
45 
46     views[dbName] = schemaResolver->getViews(database);
47     return views[dbName];
48 }
49 
getView(const QString & database,const QString & viewName)50 SqliteCreateViewPtr QueryExecutorReplaceViews::getView(const QString& database, const QString& viewName)
51 {
52     View view(database, viewName);
53     if (viewStatements.contains(view))
54         return viewStatements[view];
55 
56     SqliteQueryPtr query = schemaResolver->getParsedObject(database, viewName, SchemaResolver::VIEW);
57     if (!query)
58         return SqliteCreateViewPtr();
59 
60     SqliteCreateViewPtr viewPtr = query.dynamicCast<SqliteCreateView>();
61     if (!viewPtr)
62         return SqliteCreateViewPtr();
63 
64     viewStatements[view] = viewPtr;
65     return viewPtr;
66 }
67 
replaceViews(SqliteSelect * select)68 void QueryExecutorReplaceViews::replaceViews(SqliteSelect* select)
69 {
70     SqliteSelect::Core* core = select->coreSelects.first();
71 
72     QStringList viewsInDatabase;
73     SqliteCreateViewPtr view;
74 
75     QList<SqliteSelect::Core::SingleSource*> sources = core->getAllTypedStatements<SqliteSelect::Core::SingleSource>();
76 
77     QList<SqliteSelect::Core::SingleSource*> viewSources;
78     QSet<SqliteStatement*> parents;
79     for (SqliteSelect::Core::SingleSource* src : sources)
80     {
81         if (src->table.isNull())
82             continue;
83 
84         viewsInDatabase = getViews(src->database);
85         if (!viewsInDatabase.contains(src->table, Qt::CaseInsensitive))
86             continue;
87 
88         parents << src->parentStatement();
89         viewSources << src;
90     }
91 
92     if (parents.size() > 1)
93     {
94         // Multi-level views (view selecting from view, selecting from view...).
95         // Such constructs build up easily to huge, non-optimized queries.
96         // For performance reasons, we won't expand such views.
97         qDebug() << "Multi-level views. Skipping view expanding feature of query executor. Some columns won't be editable due to that. Number of different view parents:"
98                  << parents.size();
99         return;
100     }
101 
102     for (SqliteSelect::Core::SingleSource* src : viewSources)
103     {
104         view = getView(src->database, src->table);
105         if (!view)
106         {
107             qWarning() << "Object" << src->database << "." << src->table
108                        << "was identified to be a view, but could not get it's parsed representation.";
109             continue;
110         }
111 
112         QString alias = src->alias.isNull() ? view->view : src->alias;
113 
114         src->select = view->select;
115         src->alias = alias;
116         src->database = QString();
117         src->table = QString();
118 
119         replaceViews(src->select);
120     }
121 }
122 
qHash(const QueryExecutorReplaceViews::View & view)123 uint qHash(const QueryExecutorReplaceViews::View& view)
124 {
125     return qHash(view.database + "." + view.view);
126 }
127 
View(const QString & database,const QString & view)128 QueryExecutorReplaceViews::View::View(const QString& database, const QString& view) :
129     database(database), view(view)
130 {
131 }
132 
operator ==(const QueryExecutorReplaceViews::View & other) const133 int QueryExecutorReplaceViews::View::operator==(const QueryExecutorReplaceViews::View& other) const
134 {
135     return database == other.database && view == other.view;
136 }
137