1 #include "sqlitestatement.h"
2 #include "../token.h"
3 #include "../lexer.h"
4 #include "common/unused.h"
5 #include <QDebug>
6 
SqliteStatement()7 SqliteStatement::SqliteStatement()
8 {
9 }
10 
SqliteStatement(const SqliteStatement & other)11 SqliteStatement::SqliteStatement(const SqliteStatement& other) :
12     QObject(), tokens(other.tokens), tokensMap(other.tokensMap)
13 {
14 
15 }
16 
~SqliteStatement()17 SqliteStatement::~SqliteStatement()
18 {
19 }
20 
detokenize()21 QString SqliteStatement::detokenize()
22 {
23     return tokens.detokenize();
24 }
25 
getContextColumns(bool checkParent,bool checkChilds)26 QStringList SqliteStatement::getContextColumns(bool checkParent, bool checkChilds)
27 {
28     return getContextColumns(this, checkParent, checkChilds);
29 }
30 
getContextTables(bool checkParent,bool checkChilds)31 QStringList SqliteStatement::getContextTables(bool checkParent, bool checkChilds)
32 {
33     return getContextTables(this, checkParent, checkChilds);
34 }
35 
getContextDatabases(bool checkParent,bool checkChilds)36 QStringList SqliteStatement::getContextDatabases(bool checkParent, bool checkChilds)
37 {
38     return getContextDatabases(this, checkParent, checkChilds);
39 }
40 
getContextColumnTokens(bool checkParent,bool checkChilds)41 TokenList SqliteStatement::getContextColumnTokens(bool checkParent, bool checkChilds)
42 {
43     return getContextColumnTokens(this, checkParent, checkChilds);
44 }
45 
getContextTableTokens(bool checkParent,bool checkChilds)46 TokenList SqliteStatement::getContextTableTokens(bool checkParent, bool checkChilds)
47 {
48     return getContextTableTokens(this, checkParent, checkChilds);
49 }
50 
getContextDatabaseTokens(bool checkParent,bool checkChilds)51 TokenList SqliteStatement::getContextDatabaseTokens(bool checkParent, bool checkChilds)
52 {
53     return getContextDatabaseTokens(this, checkParent, checkChilds);
54 }
55 
getContextFullObjects(bool checkParent,bool checkChilds)56 QList<SqliteStatement::FullObject> SqliteStatement::getContextFullObjects(bool checkParent, bool checkChilds)
57 {
58     QList<FullObject> fullObjects = getContextFullObjects(this, checkParent, checkChilds);
59 
60     FullObject fullObj;
61     QMutableListIterator<FullObject> it(fullObjects);
62     while (it.hasNext())
63     {
64         fullObj = it.next();
65         if (fullObj.type == SqliteStatement::FullObject::NONE)
66         {
67             qWarning() << "FullObject of type NONE!";
68             it.remove();
69             continue;
70         }
71 
72         if (fullObj.type != SqliteStatement::FullObject::DATABASE && !fullObj.object)
73         {
74             qWarning() << "No 'object' member in FullObject that is not of DATABASE type!";
75             it.remove();
76             continue;
77         }
78 
79         if (fullObj.type == SqliteStatement::FullObject::DATABASE && !fullObj.database)
80         {
81             qWarning() << "No 'database' member in FullObject that is of DATABASE type!";
82             it.remove();
83             continue;
84         }
85     }
86 
87     return fullObjects;
88 }
89 
detach()90 SqliteStatementPtr SqliteStatement::detach()
91 {
92     if (!parent())
93         qWarning() << "Detaching " << this << ", but there's no parent!";
94 
95     setParent(nullptr);
96     return SqliteStatementPtr(this);
97 }
98 
processPostParsing()99 void SqliteStatement::processPostParsing()
100 {
101     evaluatePostParsing();
102     for (SqliteStatement* stmt : childStatements())
103         stmt->processPostParsing();
104 }
105 
getContextColumns(SqliteStatement * caller,bool checkParent,bool checkChilds)106 QStringList SqliteStatement::getContextColumns(SqliteStatement *caller, bool checkParent, bool checkChilds)
107 {
108     QStringList results = getColumnsInStatement();
109     for (SqliteStatement* stmt : getContextStatements(caller, checkParent, checkChilds))
110         results += stmt->getContextColumns(this, checkParent, checkChilds);
111 
112     return results;
113 }
114 
getContextTables(SqliteStatement * caller,bool checkParent,bool checkChilds)115 QStringList SqliteStatement::getContextTables(SqliteStatement *caller, bool checkParent, bool checkChilds)
116 {
117     QStringList results = getTablesInStatement();
118     for (SqliteStatement* stmt : getContextStatements(caller, checkParent, checkChilds))
119         results += stmt->getContextTables(this, checkParent, checkChilds);
120 
121     return results;
122 }
123 
getContextDatabases(SqliteStatement * caller,bool checkParent,bool checkChilds)124 QStringList SqliteStatement::getContextDatabases(SqliteStatement *caller, bool checkParent, bool checkChilds)
125 {
126     QStringList results = getDatabasesInStatement();
127     for (SqliteStatement* stmt : getContextStatements(caller, checkParent, checkChilds))
128         results += stmt->getContextDatabases(this, checkParent, checkChilds);
129 
130     return results;
131 }
132 
getContextColumnTokens(SqliteStatement * caller,bool checkParent,bool checkChilds)133 TokenList SqliteStatement::getContextColumnTokens(SqliteStatement *caller, bool checkParent, bool checkChilds)
134 {
135     TokenList results = getColumnTokensInStatement();
136     for (SqliteStatement* stmt : getContextStatements(caller, checkParent, checkChilds))
137         results += stmt->getContextColumnTokens(this, checkParent, checkChilds);
138 
139     return results;
140 }
141 
getContextTableTokens(SqliteStatement * caller,bool checkParent,bool checkChilds)142 TokenList SqliteStatement::getContextTableTokens(SqliteStatement *caller, bool checkParent, bool checkChilds)
143 {
144     TokenList results = getTableTokensInStatement();
145     for (SqliteStatement* stmt : getContextStatements(caller, checkParent, checkChilds))
146         results += stmt->getContextTableTokens(this, checkParent, checkChilds);
147 
148     return results;
149 }
150 
getContextDatabaseTokens(SqliteStatement * caller,bool checkParent,bool checkChilds)151 TokenList SqliteStatement::getContextDatabaseTokens(SqliteStatement *caller, bool checkParent, bool checkChilds)
152 {
153     TokenList results = getDatabaseTokensInStatement();
154     for (SqliteStatement* stmt : getContextStatements(caller, checkParent, checkChilds))
155         results += stmt->getContextDatabaseTokens(this, checkParent, checkChilds);
156 
157     return results;
158 }
159 
getContextFullObjects(SqliteStatement * caller,bool checkParent,bool checkChilds)160 QList<SqliteStatement::FullObject> SqliteStatement::getContextFullObjects(SqliteStatement* caller, bool checkParent, bool checkChilds)
161 {
162     QList<SqliteStatement::FullObject> results = getFullObjectsInStatement();
163     for (SqliteStatement* stmt : getContextStatements(caller, checkParent, checkChilds))
164     {
165         stmt->setContextDbForFullObject(dbTokenForFullObjects);
166         results += stmt->getContextFullObjects(this, checkParent, checkChilds);
167     }
168 
169     return results;
170 }
171 
getColumnsInStatement()172 QStringList SqliteStatement::getColumnsInStatement()
173 {
174     return QStringList();
175 }
176 
getTablesInStatement()177 QStringList SqliteStatement::getTablesInStatement()
178 {
179     return QStringList();
180 }
181 
getDatabasesInStatement()182 QStringList SqliteStatement::getDatabasesInStatement()
183 {
184     return QStringList();
185 }
186 
getColumnTokensInStatement()187 TokenList SqliteStatement::getColumnTokensInStatement()
188 {
189     return TokenList();
190 }
191 
getTableTokensInStatement()192 TokenList SqliteStatement::getTableTokensInStatement()
193 {
194     return TokenList();
195 }
196 
getDatabaseTokensInStatement()197 TokenList SqliteStatement::getDatabaseTokensInStatement()
198 {
199     return TokenList();
200 }
201 
getFullObjectsInStatement()202 QList<SqliteStatement::FullObject> SqliteStatement::getFullObjectsInStatement()
203 {
204     return QList<SqliteStatement::FullObject>();
205 }
206 
rebuildTokensFromContents()207 TokenList SqliteStatement::rebuildTokensFromContents()
208 {
209     qCritical() << "called rebuildTokensFromContents() for SqliteStatement that has no implementation for it.";
210     return TokenList();
211 }
212 
evaluatePostParsing()213 void SqliteStatement::evaluatePostParsing()
214 {
215 }
216 
getContextStatements(SqliteStatement * caller,bool checkParent,bool checkChilds)217 QList<SqliteStatement *> SqliteStatement::getContextStatements(SqliteStatement *caller, bool checkParent, bool checkChilds)
218 {
219     QList<SqliteStatement *> results;
220 
221     SqliteStatement* stmt = parentStatement();
222     if (checkParent && stmt && stmt != caller)
223         results += stmt;
224 
225     if (checkChilds)
226     {
227         for (SqliteStatement* childStmt : childStatements())
228         {
229             if (childStmt == caller)
230                 continue;
231 
232             results += childStmt;
233         }
234     }
235 
236     return results;
237 }
238 
extractPrintableTokens(const TokenList & tokens,bool skipMeaningless)239 TokenList SqliteStatement::extractPrintableTokens(const TokenList &tokens, bool skipMeaningless)
240 {
241     TokenList list;
242     for (TokenPtr token : tokens)
243     {
244         switch (token->type)
245         {
246             case Token::OTHER:
247             case Token::STRING:
248             case Token::FLOAT:
249             case Token::INTEGER:
250             case Token::BIND_PARAM:
251             case Token::OPERATOR:
252             case Token::PAR_LEFT:
253             case Token::PAR_RIGHT:
254             case Token::BLOB:
255             case Token::KEYWORD:
256                 list << token;
257                 break;
258             case Token::COMMENT:
259             case Token::SPACE:
260                 if (!skipMeaningless)
261                     list << token;
262                 break;
263             default:
264                 break;
265         }
266     }
267     return list;
268 }
269 
getStrListFromValue(const QString & value)270 QStringList SqliteStatement::getStrListFromValue(const QString &value)
271 {
272     QStringList list;
273     if (!value.isNull())
274         list << value;
275 
276     return list;
277 }
278 
getTokenListFromNamedKey(const QString & tokensMapKey,int idx)279 TokenList SqliteStatement::getTokenListFromNamedKey(const QString &tokensMapKey, int idx)
280 {
281     TokenList list;
282     if (tokensMap.contains(tokensMapKey))
283     {
284         if (idx < 0)
285             list += extractPrintableTokens(tokensMap[tokensMapKey]);
286         else if (tokensMap[tokensMapKey].size() > idx)
287             list << extractPrintableTokens(tokensMap[tokensMapKey])[idx];
288     }
289     else
290         qCritical() << "No '" << tokensMapKey << "' in tokens map when asked for it in getTokenListFromNamedKey().";
291 
292     return list;
293 }
294 
getDbTokenFromFullname(const QString & tokensMapKey)295 TokenPtr SqliteStatement::getDbTokenFromFullname(const QString &tokensMapKey)
296 {
297     if (!tokensMap.contains(tokensMapKey))
298     {
299         qCritical() << "No '" << tokensMapKey << "' in tokens map when asked for it getDbTokenFromFullname().";
300         return TokenPtr();
301     }
302 
303     TokenList tokens = extractPrintableTokens(tokensMap[tokensMapKey]);
304 
305     if (tokens.size() == 3)
306         return tokens[0];
307     else if (tokens.size() == 1)
308         return TokenPtr();
309     else
310         qCritical() << "Expected 1 or 3 tokens in '" << tokensMapKey << "' in tokens map, but got" << tokens.size();
311 
312     return TokenPtr();
313 }
314 
getObjectTokenFromFullname(const QString & tokensMapKey)315 TokenPtr SqliteStatement::getObjectTokenFromFullname(const QString &tokensMapKey)
316 {
317     if (!tokensMap.contains(tokensMapKey))
318     {
319         qCritical() << "No '" << tokensMapKey << "' in tokens map when asked for it.";
320         return TokenPtr();
321     }
322 
323     TokenList tokens = extractPrintableTokens(tokensMap[tokensMapKey]);
324     if (tokens.size() == 3)
325         return tokens[2];
326     else if (tokens.size() == 1)
327         return tokens[0];
328     else
329         qCritical() << "Expected 1 or 3 tokens in '" << tokensMapKey << "' in tokens map, but got" << tokens.size();
330 
331     return TokenPtr();
332 }
333 
getDbTokenFromNmDbnm(const QString & tokensMapKey1,const QString & tokensMapKey2)334 TokenPtr SqliteStatement::getDbTokenFromNmDbnm(const QString &tokensMapKey1, const QString &tokensMapKey2)
335 {
336     if (!tokensMap.contains(tokensMapKey1))
337     {
338         qCritical() << "No '" << tokensMapKey1 << "' in tokens map when asked for it in getDbTokenFromNmDbnm().";
339         return TokenPtr();
340     }
341 
342     // It seems like checking tokensMapKey2 has no added value to the logic, while it prevents from reporting
343     // database token in case of: SELECT * FROM dbName.    <- the valid query with "minor" error
344     UNUSED(tokensMapKey2);
345 //    if (!tokensMap.contains(tokensMapKey2))
346 //    {
347 //        qCritical() << "No '" << tokensMapKey2 << "' in tokens map when asked for it in getDbTokenFromNmDbnm().";
348 //        return TokenPtr();
349 //    }
350 
351 //    if (tokensMap[tokensMapKey2].size() == 0)
352 //        return TokenPtr();
353 
354     TokenList listForKey1 = extractPrintableTokens(tokensMap[tokensMapKey1]);
355     TokenList listForKey2 = extractPrintableTokens(tokensMap[tokensMapKey2]);
356     if (!tokensMap.contains("DOT") && listForKey2.size() == 0)
357     {
358         // In this case the query is "SELECT * FROM test" and there is no database,
359         // but if there was a dot after the "test", then the "test" is a database name,
360         // so this block won't be executed. Instead the name of the database will be returned below.
361         return TokenPtr();
362     }
363 
364     return extractPrintableTokens(listForKey1)[0];
365 }
366 
getObjectTokenFromNmDbnm(const QString & tokensMapKey1,const QString & tokensMapKey2)367 TokenPtr SqliteStatement::getObjectTokenFromNmDbnm(const QString &tokensMapKey1, const QString &tokensMapKey2)
368 {
369     if (!tokensMap.contains(tokensMapKey1))
370     {
371         qCritical() << "No '" << tokensMapKey1 << "' in tokens map when asked for it in getObjectTokenFromNmDbnm().";
372         return TokenPtr();
373     }
374 
375     if (!tokensMap.contains(tokensMapKey2))
376     {
377         qCritical() << "No '" << tokensMapKey2 << "' in tokens map when asked for it in getObjectTokenFromNmDbnm().";
378         return TokenPtr();
379     }
380 
381     TokenList listForKey1 = extractPrintableTokens(tokensMap[tokensMapKey1]);
382     TokenList listForKey2 = extractPrintableTokens(tokensMap[tokensMapKey2]);
383     if (listForKey2.size() == 0)
384         return extractPrintableTokens(listForKey1)[0];
385 
386     return extractPrintableTokens(listForKey2)[1];
387 }
388 
getDbTokenListFromFullname(const QString & tokensMapKey)389 TokenList SqliteStatement::getDbTokenListFromFullname(const QString &tokensMapKey)
390 {
391     TokenList list;
392     TokenPtr token = getDbTokenFromFullname(tokensMapKey);
393     if (token)
394         list << token;
395 
396     return list;
397 }
398 
getObjectTokenListFromFullname(const QString & tokensMapKey)399 TokenList SqliteStatement::getObjectTokenListFromFullname(const QString &tokensMapKey)
400 {
401     TokenList list;
402     TokenPtr token = getObjectTokenFromFullname(tokensMapKey);
403     if (token)
404         list << token;
405 
406     return list;
407 }
408 
getDbTokenListFromNmDbnm(const QString & tokensMapKey1,const QString & tokensMapKey2)409 TokenList SqliteStatement::getDbTokenListFromNmDbnm(const QString &tokensMapKey1, const QString &tokensMapKey2)
410 {
411     TokenList list;
412     TokenPtr token = getDbTokenFromNmDbnm(tokensMapKey1, tokensMapKey2);
413     if (token)
414         list << token;
415 
416     return list;
417 }
418 
getObjectTokenListFromNmDbnm(const QString & tokensMapKey1,const QString & tokensMapKey2)419 TokenList SqliteStatement::getObjectTokenListFromNmDbnm(const QString &tokensMapKey1, const QString &tokensMapKey2)
420 {
421     TokenList list;
422     TokenPtr token = getObjectTokenFromNmDbnm(tokensMapKey1, tokensMapKey2);
423     if (token)
424         list << token;
425 
426     return list;
427 }
428 
getFullObjectFromFullname(SqliteStatement::FullObject::Type type,const QString & tokensMapKey)429 SqliteStatement::FullObject SqliteStatement::getFullObjectFromFullname(SqliteStatement::FullObject::Type type, const QString& tokensMapKey)
430 {
431     return getFullObject(type, getDbTokenFromFullname(tokensMapKey), getObjectTokenFromFullname(tokensMapKey));
432 }
433 
getFullObjectFromNmDbnm(SqliteStatement::FullObject::Type type,const QString & tokensMapKey1,const QString & tokensMapKey2)434 SqliteStatement::FullObject SqliteStatement::getFullObjectFromNmDbnm(SqliteStatement::FullObject::Type type, const QString& tokensMapKey1, const QString& tokensMapKey2)
435 {
436     return getFullObject(type, getDbTokenFromNmDbnm(tokensMapKey1, tokensMapKey2), getObjectTokenFromNmDbnm(tokensMapKey1, tokensMapKey2));
437 }
438 
getFullObject(SqliteStatement::FullObject::Type type,TokenPtr dbToken,TokenPtr objToken)439 SqliteStatement::FullObject SqliteStatement::getFullObject(SqliteStatement::FullObject::Type type, TokenPtr dbToken, TokenPtr objToken)
440 {
441     FullObject fullObj;
442     if (!objToken)
443         return fullObj;
444 
445     fullObj.database = dbToken;
446     fullObj.object = objToken;
447     fullObj.type = type;
448     return fullObj;
449 }
450 
setContextDbForFullObject(TokenPtr dbToken)451 void SqliteStatement::setContextDbForFullObject(TokenPtr dbToken)
452 {
453     dbTokenForFullObjects = dbToken;
454 }
455 
getFirstDbFullObject()456 SqliteStatement::FullObject SqliteStatement::getFirstDbFullObject()
457 {
458     TokenList dbTokens = getDatabaseTokensInStatement();
459     return getDbFullObject(dbTokens.size() > 0 ? dbTokens[0] : TokenPtr());
460 }
461 
getDbFullObject(TokenPtr dbToken)462 SqliteStatement::FullObject SqliteStatement::getDbFullObject(TokenPtr dbToken)
463 {
464     FullObject fullObj;
465     if (!dbToken)
466         return fullObj;
467 
468     fullObj.database = dbToken;
469     fullObj.type = FullObject::DATABASE;
470     return fullObj;
471 }
472 
getRange()473 Range SqliteStatement::getRange()
474 {
475     if (tokens.size() == 0)
476         return Range(0, 0);
477 
478     return Range(tokens.first()->start, tokens.last()->end);
479 }
480 
findStatementWithToken(TokenPtr token)481 SqliteStatement *SqliteStatement::findStatementWithToken(TokenPtr token)
482 {
483     SqliteStatement* stmtWithToken = nullptr;
484     for (SqliteStatement* stmt : childStatements())
485     {
486         stmtWithToken = stmt->findStatementWithToken(token);
487         if (stmtWithToken)
488             return stmtWithToken;
489     }
490 
491     if (tokens.contains(token))
492         return this;
493 
494     return nullptr;
495 }
496 
findStatementWithPosition(quint64 cursorPosition)497 SqliteStatement *SqliteStatement::findStatementWithPosition(quint64 cursorPosition)
498 {
499     TokenPtr token = tokens.atCursorPosition(cursorPosition);
500     if (!token)
501         return nullptr;
502 
503     return findStatementWithToken(token);
504 }
505 
parentStatement()506 SqliteStatement *SqliteStatement::parentStatement()
507 {
508     if (!parent())
509         return nullptr;
510 
511     return dynamic_cast<SqliteStatement*>(parent());
512 }
513 
childStatements()514 QList<SqliteStatement *> SqliteStatement::childStatements()
515 {
516     QList<SqliteStatement*> results;
517     for (QObject* obj : children())
518         results += dynamic_cast<SqliteStatement*>(obj);
519 
520     return results;
521 }
522 
rebuildTokens()523 void SqliteStatement::rebuildTokens()
524 {
525     tokens.clear();
526     tokensMap.clear();
527     tokens = rebuildTokensFromContents();
528     // TODO rebuild tokensMap as well
529     // It shouldn't be hard to write unit tests that parse a query, remembers it tokensMap, then rebuilds tokens from contents
530     // and then compare new tokens map with previous one. This way we should be able to get all maps correctly.
531 }
532 
attach(SqliteStatement * & memberForChild,SqliteStatement * childStatementToAttach)533 void SqliteStatement::attach(SqliteStatement*& memberForChild, SqliteStatement* childStatementToAttach)
534 {
535     memberForChild = childStatementToAttach;
536     childStatementToAttach->setParent(this);
537 }
538 
isValid() const539 bool SqliteStatement::FullObject::isValid() const
540 {
541     return (object != nullptr || (type == DATABASE && database != nullptr));
542 }
543