diff options
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/impl')
| -rw-r--r-- | SQLiteStudio3/coreSQLiteStudio/impl/dbattacherimpl.cpp | 178 | ||||
| -rw-r--r-- | SQLiteStudio3/coreSQLiteStudio/impl/dbattacherimpl.h | 94 |
2 files changed, 272 insertions, 0 deletions
diff --git a/SQLiteStudio3/coreSQLiteStudio/impl/dbattacherimpl.cpp b/SQLiteStudio3/coreSQLiteStudio/impl/dbattacherimpl.cpp new file mode 100644 index 0000000..75d94eb --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/impl/dbattacherimpl.cpp @@ -0,0 +1,178 @@ +#include "dbattacherimpl.h" +#include "db/db.h" +#include "services/dbmanager.h" +#include "parser/parser.h" +#include "services/notifymanager.h" +#include "common/utils_sql.h" +#include <QDebug> + +DbAttacherImpl::DbAttacherImpl(Db* db) +{ + this->db = db; + dialect = db->getDialect(); +} + +bool DbAttacherImpl::attachDatabases(const QString& query) +{ + Parser parser(dialect); + if (!parser.parse(query)) + return false; + + queries = parser.getQueries(); + return attachDatabases(); +} + +bool DbAttacherImpl::attachDatabases(const QList<SqliteQueryPtr>& queries) +{ + this->queries = queries; + return attachDatabases(); +} + +bool DbAttacherImpl::attachDatabases(SqliteQueryPtr query) +{ + queries.clear(); + queries << query; + return attachDatabases(); +} + +void DbAttacherImpl::detachDatabases() +{ + detachAttached(); +} + +bool DbAttacherImpl::attachDatabases() +{ + dbNameToAttach.clear(); + + prepareNameToDbMap(); + + TokenList dbTokens = getDbTokens(); + QHash<QString,TokenList> groupedDbTokens = groupDbTokens(dbTokens); + + if (!attachAllDbs(groupedDbTokens)) + return false; + + QHash<TokenPtr,TokenPtr> tokenMapping = getTokenMapping(dbTokens); + replaceTokensInQueries(tokenMapping); + + return true; +} + +TokenList DbAttacherImpl::getDbTokens() +{ + TokenList dbTokens; + foreach (SqliteQueryPtr query, queries) + dbTokens += query->getContextDatabaseTokens(); + + return dbTokens; +} + +void DbAttacherImpl::detachAttached() +{ + foreach (const QString& dbName, dbNameToAttach.leftValues()) + db->detach(nameToDbMap[dbName]); + + dbNameToAttach.clear(); + nameToDbMap.clear(); +} + +void DbAttacherImpl::prepareNameToDbMap() +{ + foreach (Db* db, DBLIST->getValidDbList()) + nameToDbMap[db->getName()] = db; +} + +QHash<QString, TokenList> DbAttacherImpl::groupDbTokens(const TokenList& dbTokens) +{ + // Filter out tokens of unknown databases and group results by name + QHash<QString,TokenList> groupedDbTokens; + QString strippedName; + foreach (TokenPtr token, dbTokens) + { + strippedName = stripObjName(token->value, dialect); + if (!nameToDbMap.contains(strippedName, Qt::CaseInsensitive)) + continue; + + groupedDbTokens[strippedName] += token; + } + return groupedDbTokens; +} + +bool DbAttacherImpl::attachAllDbs(const QHash<QString, TokenList>& groupedDbTokens) +{ + QString attachName; + foreach (const QString& dbName, groupedDbTokens.keys()) + { + attachName = db->attach(nameToDbMap[dbName]); + if (attachName.isNull()) + { + notifyError(QObject::tr("Could not attach database %1: %2").arg(dbName).arg(db->getErrorText())); + detachAttached(); + return false; + } + + dbNameToAttach.insert(dbName, attachName); + } + return true; +} + +QHash<TokenPtr, TokenPtr> DbAttacherImpl::getTokenMapping(const TokenList& dbTokens) +{ + QHash<TokenPtr, TokenPtr> tokenMapping; + QString strippedName; + TokenPtr dstToken; + foreach (TokenPtr srcToken, dbTokens) + { + strippedName = stripObjName(srcToken->value, dialect); + if (strippedName.compare("main", Qt::CaseInsensitive) == 0 || + strippedName.compare("temp", Qt::CaseInsensitive) == 0) + continue; + + if (!dbNameToAttach.containsLeft(strippedName, Qt::CaseInsensitive)) + { + qCritical() << "DB token to be replaced, but it's not on nameToAlias map! Token value:" << strippedName + << "and nameToAlias map has keys:" << dbNameToAttach.leftValues(); + continue; + } + dstToken = TokenPtr::create(dbNameToAttach.valueByLeft(strippedName, Qt::CaseInsensitive)); + tokenMapping[srcToken] = dstToken; + } + return tokenMapping; +} + +void DbAttacherImpl::replaceTokensInQueries(const QHash<TokenPtr, TokenPtr>& tokenMapping) +{ + int idx; + QHashIterator<TokenPtr,TokenPtr> it(tokenMapping); + while (it.hasNext()) + { + it.next(); + foreach (SqliteQueryPtr query, queries) + { + idx = query->tokens.indexOf(it.key()); + if (idx < 0) + continue; // token not in this query, most likely in other query + + query->tokens.replace(idx, it.value()); + } + } +} + +BiStrHash DbAttacherImpl::getDbNameToAttach() const +{ + return dbNameToAttach; +} + +QString DbAttacherImpl::getQuery() const +{ + QStringList queryStrings; + foreach (SqliteQueryPtr query, queries) + queryStrings << query->detokenize(); + + return queryStrings.join(";"); +} + +DbAttacher* DbAttacherDefaultFactory::create(Db* db) +{ + return new DbAttacherImpl(db); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/impl/dbattacherimpl.h b/SQLiteStudio3/coreSQLiteStudio/impl/dbattacherimpl.h new file mode 100644 index 0000000..49307c6 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/impl/dbattacherimpl.h @@ -0,0 +1,94 @@ +#ifndef DBATTACHERIMPL_H +#define DBATTACHERIMPL_H + +#include "dbattacher.h" + +class DbAttacherImpl : public DbAttacher +{ + public: + /** + * @brief Creates attacher with the given database as the main. + * @param db Database that the query will be executed on. + */ + DbAttacherImpl(Db* db); + + bool attachDatabases(const QString& query); + bool attachDatabases(const QList<SqliteQueryPtr>& queries); + bool attachDatabases(SqliteQueryPtr query); + void detachDatabases(); + BiStrHash getDbNameToAttach() const; + QString getQuery() const; + + private: + /** + * @brief Does the actual job, after having all input queries as parsed objects. + * @return true on success, false on failure. + */ + bool attachDatabases(); + + /** + * @brief Finds tokens representing databases in the query. + * @return List of tokens. Some tokens have non-printable value (spaces, etc), others are database names. + */ + TokenList getDbTokens(); + + /** + * @brief Detaches all databases currently attached by the attacher. + * + * Also clears names mappings. + */ + void detachAttached(); + + /** + * @brief Generates mapping of database name to its Db object for all registered databases. + */ + void prepareNameToDbMap(); + + /** + * @brief Groups tokens by the name of database they refer to. + * @param dbTokens Tokens representing databases in the query. + * + * This method is used to learn if some database is used more than once in the query, + * so we attach it only once, then replace all tokens referring to it by the attach name. + */ + QHash<QString,TokenList> groupDbTokens(const TokenList& dbTokens); + + /** + * @brief Tries to attach all required databases. + * @param groupedDbTokens Database tokens grouped by database name, as returned from groupDbTokens(). + * @return true on success, false on any problem. + * + * Major problem that can happen is when "<tt>ATTACH 'path to file'</tt>" fails for any reason. In that case + * detachAttached() is called and false is returned. + */ + bool attachAllDbs(const QHash<QString,TokenList>& groupedDbTokens); + + /** + * @brief Creates token-to-token replace map to update the query. + * @param dbTokens Tokens representing databases in the query. + * @return Mapping to be used when replacing tokens in the query. + */ + QHash<TokenPtr, TokenPtr> getTokenMapping(const TokenList& dbTokens); + + /** + * @brief Replaces tokens in the query. + * @param tokenMapping Map of tokens to replace. + * + * Replacing takes place in token lists of each query in the queries member. + */ + void replaceTokensInQueries(const QHash<TokenPtr, TokenPtr>& tokenMapping); + + QList<SqliteQueryPtr> queries; + Db* db = nullptr; + Dialect dialect; + BiStrHash dbNameToAttach; + StrHash<Db*> nameToDbMap; +}; + +class DbAttacherDefaultFactory : public DbAttacherFactory +{ + public: + DbAttacher* create(Db* db); +}; + +#endif // DBATTACHERIMPL_H |
