From 7167ce41b61d2ba2cdb526777a4233eb84a3b66a Mon Sep 17 00:00:00 2001 From: Unit 193 Date: Sat, 6 Dec 2014 17:33:25 -0500 Subject: Imported Upstream version 2.99.6 --- .../coreSQLiteStudio/impl/dbattacherimpl.cpp | 178 +++++++++++++++++++++ .../coreSQLiteStudio/impl/dbattacherimpl.h | 94 +++++++++++ 2 files changed, 272 insertions(+) create mode 100644 SQLiteStudio3/coreSQLiteStudio/impl/dbattacherimpl.cpp create mode 100644 SQLiteStudio3/coreSQLiteStudio/impl/dbattacherimpl.h (limited to 'SQLiteStudio3/coreSQLiteStudio/impl') 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 + +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& 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 groupedDbTokens = groupDbTokens(dbTokens); + + if (!attachAllDbs(groupedDbTokens)) + return false; + + QHash 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 DbAttacherImpl::groupDbTokens(const TokenList& dbTokens) +{ + // Filter out tokens of unknown databases and group results by name + QHash 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& 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 DbAttacherImpl::getTokenMapping(const TokenList& dbTokens) +{ + QHash 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& tokenMapping) +{ + int idx; + QHashIterator 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& 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 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 "ATTACH 'path to file'" fails for any reason. In that case + * detachAttached() is called and false is returned. + */ + bool attachAllDbs(const QHash& 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 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& tokenMapping); + + QList queries; + Db* db = nullptr; + Dialect dialect; + BiStrHash dbNameToAttach; + StrHash nameToDbMap; +}; + +class DbAttacherDefaultFactory : public DbAttacherFactory +{ + public: + DbAttacher* create(Db* db); +}; + +#endif // DBATTACHERIMPL_H -- cgit v1.2.3