aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/impl
diff options
context:
space:
mode:
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/impl')
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/impl/dbattacherimpl.cpp178
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/impl/dbattacherimpl.h94
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