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