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