#ifndef COMPLETIONHELPER_H #define COMPLETIONHELPER_H #include "expectedtoken.h" #include "schemaresolver.h" #include "selectresolver.h" #include "dialect.h" #include "completioncomparer.h" #include "parser/ast/sqliteselect.h" #include "parser/token.h" #include "db/db.h" #include "common/strhash.h" #include "common/table.h" #include #include class DbAttacher; class API_EXPORT CompletionHelper : public QObject { Q_OBJECT friend class CompletionComparer; public: struct API_EXPORT Results { QList filtered(); QList expectedTokens; QString partialToken; bool wrappedToken = false; }; CompletionHelper(const QString& sql, Db* db); CompletionHelper(const QString& sql, quint32 cursorPos, Db* db); ~CompletionHelper(); static void applyFilter(QList &results, const QString &filter); static void init(); Results getExpectedTokens(); DbAttacher* getDbAttacher() const; void setDbAttacher(DbAttacher* value); static bool enableLemonDebug; QString getCreateTriggerTable() const; void setCreateTriggerTable(const QString& value); private: enum class Context { NONE, SELECT_RESULT_COLUMN, SELECT_FROM, SELECT_WHERE, SELECT_GROUP_BY, SELECT_HAVING, SELECT_ORDER_BY, SELECT_LIMIT, UPDATE_COLUMN, UPDATE_WHERE, DELETE_WHERE, CREATE_TABLE, CREATE_TRIGGER, EXPR }; QList getExpectedTokens(TokenPtr token); ExpectedTokenPtr getExpectedToken(ExpectedToken::Type type); ExpectedTokenPtr getExpectedToken(ExpectedToken::Type type, const QString& value); ExpectedTokenPtr getExpectedToken(ExpectedToken::Type type, const QString& value, int priority); ExpectedTokenPtr getExpectedToken(ExpectedToken::Type type, const QString& value, const QString& contextInfo); ExpectedTokenPtr getExpectedToken(ExpectedToken::Type type, const QString& value, const QString& contextInfo, int priority); ExpectedTokenPtr getExpectedToken(ExpectedToken::Type type, const QString &value, const QString &contextInfo, const QString &label); ExpectedTokenPtr getExpectedToken(ExpectedToken::Type type, const QString &value, const QString &contextInfo, const QString &label, int priority); ExpectedTokenPtr getExpectedToken(ExpectedToken::Type type, const QString &value, const QString &contextInfo, const QString &label, const QString &prefix); ExpectedTokenPtr getExpectedToken(ExpectedToken::Type type, const QString &value, const QString &contextInfo, const QString &label, const QString &prefix, int priority); bool validatePreviousIdForGetObjects(QString* dbName = nullptr); QList getTables(); QList getIndexes(); QList getTriggers(); QList getViews(); QList getDatabases(); QList getObjects(ExpectedToken::Type type); QList getObjects(ExpectedToken::Type type, const QString& database); QList getColumns(); QList getColumnsNoPrefix(); QList getColumnsNoPrefix(const QString &column, const QStringList &tables); QList getColumns(const QString& prefixTable); QList getColumns(const QString& prefixDb, const QString& prefixTable); QList getFavoredColumns(const QList& resultsSoFar); QList getPragmas(Dialect dialect); QList getFunctions(Db* db); QList getCollations(); TokenPtr getPreviousDbOrTable(const TokenList& parsedTokens); void attachDatabases(); void detachDatabases(); QString translateDatabase(const QString& dbName); QString translateDatabaseBack(const QString& dbName); void collectOtherDatabases(); QString removeStartedToken(const QString& adjustedSql, QString &finalFilter, bool& wrappedFilter); void filterContextKeywords(QList &results, const TokenList& tokens); void filterOtherId(QList &results, const TokenList& tokens); void filterDuplicates(QList &results); bool isFilterType(Token::Type type); void parseFullSql(); bool tryToParse(Parser* parser, const QString& query); void sort(QList &results); void extractPreviousIdTokens(const TokenList& parsedTokens); void extractQueryAdditionalInfo(); void extractSelectAvailableColumnsAndTables(); bool extractSelectCore(); SqliteSelect::Core* extractSelectCore(SqliteQueryPtr query); void extractTableAliasMap(); void extractCreateTableColumns(); void detectSelectContext(); bool isInUpdateColumn(); bool isInUpdateWhere(); bool isInDeleteWhere(); bool isInCreateTable(); bool isInCreateTrigger(); bool isIn(SqliteQueryType queryType, const QString& tokenMapKey, const QString &prefixKeyword); bool isInExpr(); bool testQueryToken(int tokenPosition, Token::Type type, const QString& value, Qt::CaseSensitivity cs = Qt::CaseInsensitive); bool cursorAfterTokenMaps(SqliteStatement* stmt, const QStringList& mapNames); bool cursorBeforeTokenMaps(SqliteStatement* stmt, const QStringList& mapNames); template bool cursorFitsInCollection(const QList& collection) { if (collection.size() == 0) return false; T* firstStmt = collection.first(); T* lastStmt = collection.last(); int startIdx = -1; int endIdx = -1; if (firstStmt && firstStmt->tokens.size() > 0) startIdx = firstStmt->tokens.first()->start; if (lastStmt && lastStmt->tokens.size() > 0) endIdx = lastStmt->tokens.last()->end; if (startIdx < 0 || endIdx < 0) return false; return (cursorPosition >= startIdx && cursorPosition <= endIdx); } template bool cursorFitsInStatement(T* stmt) { if (!stmt || stmt->tokens.size() == 0) return false; int startIdx = stmt->tokens.first()->start; int endIdx = stmt->tokens.last()->end; return (cursorPosition >= startIdx && cursorPosition <= endIdx); } Context context = Context::NONE; Db* db = nullptr; qint64 cursorPosition; QString fullSql; TokenPtr previousId; TokenPtr twoIdsBack; TokenList queryTokens; SqliteQueryPtr parsedQuery; SqliteQueryPtr originalParsedQuery; SchemaResolver* schemaResolver = nullptr; SelectResolver* selectResolver = nullptr; DbAttacher* dbAttacher = nullptr; QString createTriggerTable; /** * @brief tableToAlias * This map maps real table name to its alias. Every table can be typed multiple times * with different aliases, therefore this map has a list on the right side. */ QHash tableToAlias; /** * @brief aliasToTable * Maps table alias to table's real name. */ //QHash aliasToTable; StrHash aliasToTable; /** * @brief currentSelectCore * The SqliteSelect::Core object that contains current cursor position. */ SqliteSelect::Core* currentSelectCore = nullptr; /** * @brief originalCurrentSelectCore * * The same as currentSelectCore, but relates to originalParsedQuery, not just parsedQuery. */ SqliteSelect::Core* originalCurrentSelectCore = nullptr; /** * @brief selectAvailableColumns * Available columns are columns that can be selected basing on what tables are mentioned in FROM clause. */ QList selectAvailableColumns; /** * @brief selectAvailableTables * Availble tables are tables mentioned in FROM clause. */ QSet selectAvailableTables; /** * @brief parentSelectCores * List of all parent select core objects in order: from direct parent, to the oldest parent. */ QList parentSelectCores; /** * @brief parentSelectAvailableColumns * List of all columns available in all tables mentioned in all parent select cores. */ QList parentSelectAvailableColumns; /** * @brief parentSelectAvailableTables * Same as parentSelectAvailableColumns, but for tables in FROM clauses. */ QSet parentSelectAvailableTables; /** * @brief favoredColumnNames * For some contexts there are favored column names, like for example CREATE TABLE * will favour column names specified so far in its definition. * Such columns will be added to completion results even they weren't result * of regular computation. */ QStringList favoredColumnNames; /** * @brief Context databases to look for objects in. * * If the query uses some other databases (not the currentone), then user might be interested * also in objects from those databases. This list contains those databases. */ QStringList otherDatabasesToLookupFor; }; #endif // COMPLETIONHELPER_H