File indexing completed on 2024-11-24 04:53:13
0001 /* Copyright (C) 2006 - 2014 Jan Kundrát <jkt@flaska.net> 0002 0003 This file is part of the Trojita Qt IMAP e-mail client, 0004 http://trojita.flaska.net/ 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License as 0008 published by the Free Software Foundation; either version 2 of 0009 the License or (at your option) version 3 or any later version 0010 accepted by the membership of KDE e.V. (or its successor approved 0011 by the membership of KDE e.V.), which shall act as a proxy 0012 defined in Section 14 of version 3 of the license. 0013 0014 This program is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0017 GNU General Public License for more details. 0018 0019 You should have received a copy of the GNU General Public License 0020 along with this program. If not, see <http://www.gnu.org/licenses/>. 0021 */ 0022 0023 #ifndef IMAP_MODEL_SQLCACHE_H 0024 #define IMAP_MODEL_SQLCACHE_H 0025 0026 #include <memory> 0027 #include <QSqlDatabase> 0028 #include <QSqlQuery> 0029 #include "Cache.h" 0030 0031 class QTimer; 0032 0033 /** @short Namespace for IMAP interaction */ 0034 namespace Imap 0035 { 0036 0037 /** @short Classes for handling of mailboxes and connections */ 0038 namespace Mailbox 0039 { 0040 0041 /** @short Wrapper around the braindead API of QSqlDatabase for auto-removing connections 0042 0043 Because the QSqlDatabase really wants to operate over a global namespace of DB connections, it's important to remove them 0044 when they are no longer needed. However, this removal (QSqlDatabase::removeDatabase) should run after all queries are gone, 0045 otherwise there's an ugly warning on stderr about queries which are going to cease to work. 0046 */ 0047 struct DbConnectionCleanup 0048 { 0049 ~DbConnectionCleanup(); 0050 QString name; 0051 }; 0052 0053 /** @short A cache implementation using an sqlite database for the underlying storage 0054 0055 This class should not be used on its own, as it simply puts everything into a database. 0056 This is clearly a suboptimal way to store large binary data, like e-mail attachments. The 0057 purpose of this class is therefore to serve as one of a few caching backends, which are 0058 subsequently used by an intelligent cache manager. 0059 0060 The database layout is aimed at a regular desktop or an embedded device. It certainly is 0061 not meant as a proper database design -- we bundle several columns together when we know 0062 that the API will only access them as a tuple, we use a proprietary compression on them 0063 et cetera. In short, the layout of the database is supposed to act as a quick and dumb 0064 cache and is certainly *not* meant to be accessed by third-party applications. Please, do 0065 consider it an opaque format. 0066 0067 Some ideas for improvements: 0068 - Don't store full string mailbox names in each table, use another table for it 0069 - Merge uid_mapping with mailbox_sync_state, and also msg_metadata with flags 0070 - Serious embedded users might consider putting the database into a compressed filesystem, 0071 or using on-the-fly compression via sqlite's VFS subsystem 0072 0073 */ 0074 class SQLCache : public AbstractCache 0075 { 0076 public: 0077 SQLCache(); 0078 virtual ~SQLCache(); 0079 0080 QList<MailboxMetadata> childMailboxes(const QString &mailbox) const override; 0081 bool childMailboxesFresh(const QString &mailbox) const override; 0082 void setChildMailboxes(const QString &mailbox, const QList<MailboxMetadata> &data) override; 0083 0084 SyncState mailboxSyncState(const QString &mailbox) const override; 0085 void setMailboxSyncState(const QString &mailbox, const SyncState &state) override; 0086 0087 void setUidMapping(const QString &mailbox, const Imap::Uids &seqToUid) override; 0088 void clearUidMapping(const QString &mailbox) override; 0089 Imap::Uids uidMapping(const QString &mailbox) const override; 0090 0091 void clearAllMessages(const QString &mailbox) override; 0092 void clearMessage(const QString mailbox, const uint uid) override; 0093 0094 MessageDataBundle messageMetadata(const QString &mailbox, uint uid) const override; 0095 void setMessageMetadata(const QString &mailbox, const uint uid, const MessageDataBundle &metadata) override; 0096 0097 QStringList msgFlags(const QString &mailbox, const uint uid) const override; 0098 void setMsgFlags(const QString &mailbox, const uint uid, const QStringList &flags) override; 0099 0100 QByteArray messagePart(const QString &mailbox, const uint uid, const QByteArray &partId) const override; 0101 void setMsgPart(const QString &mailbox, const uint uid, const QByteArray &partId, const QByteArray &data) override; 0102 void forgetMessagePart(const QString &mailbox, const uint uid, const QByteArray &partId) override; 0103 0104 QVector<Imap::Responses::ThreadingNode> messageThreading(const QString &mailbox) override; 0105 void setMessageThreading(const QString &mailbox, const QVector<Imap::Responses::ThreadingNode> &threading) override; 0106 0107 /** @short Open a connection to the cache */ 0108 bool open(const QString &name, const QString &fileName); 0109 0110 void setRenewalThreshold(const int days) override; 0111 0112 private: 0113 /** @short Broadcast an error from the SQL query */ 0114 void emitError(const QString &message, const QSqlQuery &query) const; 0115 /** @short Broadcast an error from the SQL "database" */ 0116 void emitError(const QString &message, const QSqlDatabase &database) const; 0117 /** @short Broadcast a generic error */ 0118 void emitError(const QString &message) const; 0119 0120 /** @short Blindly create all tables */ 0121 bool createTables(); 0122 /** @short Initialize the prepared queries */ 0123 bool prepareQueries(); 0124 0125 /** @short We're about to touch the DB, so it might be a good time to start a transaction */ 0126 void touchingDB(); 0127 0128 /** @short Initialize the database */ 0129 void init(); 0130 0131 static QString mailboxName(const QString &mailbox); 0132 0133 private slots: 0134 /** @short We haven't committed for a while */ 0135 void timeToCommit(); 0136 0137 private: 0138 // this needs to go before all QSqlDatabase/QSqlQuery instances for proper destruction order 0139 DbConnectionCleanup m_cleanup; 0140 0141 QSqlDatabase db; 0142 0143 mutable QSqlQuery queryChildMailboxes; 0144 mutable QSqlQuery queryChildMailboxesFresh; 0145 mutable QSqlQuery queryRemoveChildMailboxes; 0146 mutable QSqlQuery querySetChildMailboxes; 0147 mutable QSqlQuery queryMailboxSyncState; 0148 mutable QSqlQuery querySetMailboxSyncState; 0149 mutable QSqlQuery queryUidMapping; 0150 mutable QSqlQuery querySetUidMapping; 0151 mutable QSqlQuery queryClearUidMapping; 0152 mutable QSqlQuery queryMessageMetadata; 0153 mutable QSqlQuery queryAccessMessageMetadata; 0154 mutable QSqlQuery querySetMessageMetadata; 0155 mutable QSqlQuery queryMessageFlags; 0156 mutable QSqlQuery querySetMessageFlags; 0157 mutable QSqlQuery queryClearAllMessages1; 0158 mutable QSqlQuery queryClearAllMessages2; 0159 mutable QSqlQuery queryClearAllMessages3; 0160 mutable QSqlQuery queryClearAllMessages4; 0161 mutable QSqlQuery queryClearMessage1; 0162 mutable QSqlQuery queryClearMessage2; 0163 mutable QSqlQuery queryClearMessage3; 0164 mutable QSqlQuery queryMessagePart; 0165 mutable QSqlQuery querySetMessagePart; 0166 mutable QSqlQuery queryForgetMessagePart; 0167 mutable QSqlQuery queryMessageThreading; 0168 mutable QSqlQuery querySetMessageThreading; 0169 0170 std::unique_ptr<QTimer> delayedCommit; 0171 std::unique_ptr<QTimer> tooMuchTimeWithoutCommit; 0172 bool inTransaction; 0173 0174 /** @short A point in time against which the "last accessed on" data is computed */ 0175 static QDate accessingThresholdDate; 0176 0177 /** @short Update the "last accessed on" each time we are making an access *and* the difference is greater than X days 0178 0179 To disable updating of the DB accesses, set to zero. 0180 */ 0181 int m_updateAccessIfOlder; 0182 }; 0183 0184 } 0185 0186 } 0187 0188 #endif /* IMAP_MODEL_SQLCACHE_H */