mirror of https://github.com/qTox/qTox.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
115 lines
4.5 KiB
115 lines
4.5 KiB
#ifndef RAWDATABASE_H |
|
#define RAWDATABASE_H |
|
|
|
#include <QString> |
|
#include <QByteArray> |
|
#include <QThread> |
|
#include <QQueue> |
|
#include <QVector> |
|
#include <QPair> |
|
#include <QMutex> |
|
#include <QVariant> |
|
#include <memory> |
|
#include <atomic> |
|
|
|
struct sqlite3; |
|
struct sqlite3_stmt; |
|
|
|
/// Implements a low level RAII interface to a SQLCipher (SQlite3) database |
|
/// Thread-safe, does all database operations on a worker thread |
|
/// The queries must not contain transaction commands (BEGIN/COMMIT/...) or the behavior is undefined |
|
class RawDatabase : QObject |
|
{ |
|
Q_OBJECT |
|
|
|
public: |
|
/// A query to be executed by the database. Can be composed of one or more SQL statements in the query, |
|
/// optional BLOB parameters to be bound, and callbacks fired when the query is executed |
|
/// Calling any database method from a query callback is undefined behavior |
|
class Query |
|
{ |
|
public: |
|
Query(QString query, QVector<QByteArray> blobs = {}, std::function<void(int64_t)> insertCallback={}) |
|
: query{query.toUtf8()}, blobs{blobs}, insertCallback{insertCallback} {} |
|
Query(QString query, std::function<void(int64_t)> insertCallback) |
|
: query{query.toUtf8()}, insertCallback{insertCallback} {} |
|
Query(QString query, std::function<void(const QVector<QVariant>&)> rowCallback) |
|
: query{query.toUtf8()}, rowCallback{rowCallback} {} |
|
Query() = default; |
|
private: |
|
QByteArray query; ///< UTF-8 query string |
|
QVector<QByteArray> blobs; ///< Bound data blobs |
|
std::function<void(int64_t)> insertCallback; ///< Called after execution with the last insert rowid |
|
std::function<void(const QVector<QVariant>&)> rowCallback; ///< Called during execution for each row |
|
QVector<sqlite3_stmt*> statements; ///< Statements to be compiled from the query |
|
|
|
friend class RawDatabase; |
|
}; |
|
|
|
public: |
|
/// Tries to open a database |
|
/// If password is empty, the database will be opened unencrypted |
|
/// Otherwise we will use toxencryptsave to derive a key and encrypt the database |
|
RawDatabase(const QString& path, const QString& password); |
|
~RawDatabase(); |
|
bool isOpen(); ///< Returns true if the database was opened successfully |
|
/// Executes a SQL transaction synchronously. |
|
/// Returns whether the transaction was successful. |
|
bool execNow(const QString& statement); |
|
bool execNow(const Query& statement); |
|
bool execNow(const QVector<Query>& statements); |
|
/// Executes a SQL transaction asynchronously. |
|
void execLater(const QString& statement); |
|
void execLater(const Query& statement); |
|
void execLater(const QVector<Query>& statements); |
|
/// Waits until all the pending transactions are executed |
|
void sync(); |
|
|
|
public slots: |
|
/// Changes the database password, encrypting or decrypting if necessary |
|
/// If password is empty, the database will be decrypted |
|
/// Will process all transactions before changing the password |
|
bool setPassword(const QString& password); |
|
/// Moves the database file on disk to match the new path |
|
/// /// Will process all transactions before renaming |
|
bool rename(const QString& newPath); |
|
|
|
protected slots: |
|
/// Tries to open the database with the given (possibly empty) key |
|
bool open(const QString& path, const QString& hexKey = {}); |
|
/// Closes the database and free its associated resources |
|
void close(); |
|
/// Implements the actual processing of pending transactions |
|
/// Unqueues, compiles, binds and executes queries, then notifies of results |
|
/// MUST only be called from the worker thread |
|
void process(); |
|
|
|
protected: |
|
/// Derives a 256bit key from the password and returns it hex-encoded |
|
static QString deriveKey(QString password); |
|
/// Extracts a variant from one column of a result row depending on the column type |
|
static QVariant extractData(sqlite3_stmt* stmt, int col); |
|
|
|
private: |
|
/// SQL transactions to be processed |
|
/// A transaction is made of queries, which can have bound BLOBs |
|
struct Transaction |
|
{ |
|
QVector<Query> queries; |
|
/// If not a nullptr, the result of the transaction will be set |
|
std::atomic_bool* success = nullptr; |
|
/// If not a nullptr, will be set to true when the transaction has been executed |
|
std::atomic_bool* done = nullptr; |
|
}; |
|
|
|
private: |
|
sqlite3* sqlite; |
|
std::unique_ptr<QThread> workerThread; |
|
QQueue<Transaction> pendingTransactions; |
|
/// Protects pendingTransactions |
|
QMutex transactionsMutex; |
|
QString path; |
|
QString currentHexKey; |
|
}; |
|
|
|
#endif // RAWDATABASE_H
|
|
|