OwnSql: Some refactoring

Use QByteArray for the query so we don't have to convert twice.
Automatically finish the query when the DB close, so we don't
have to call reset
This commit is contained in:
Olivier Goffart 2018-03-14 11:50:55 +01:00 committed by Roeland Jago Douma
parent b309a1c8c4
commit 518fb90757
No known key found for this signature in database
GPG key ID: F941078878347C0C
4 changed files with 75 additions and 89 deletions

View file

@ -49,6 +49,12 @@ SqlDatabase::SqlDatabase()
{
}
SqlDatabase::~SqlDatabase()
{
close();
}
bool SqlDatabase::isOpen()
{
return _db != 0;
@ -184,6 +190,9 @@ QString SqlDatabase::error() const
void SqlDatabase::close()
{
if (_db) {
foreach (auto q, _queries) {
q->finish();
}
SQLITE_DO(sqlite3_close(_db));
if (_errId != SQLITE_OK)
qCWarning(lcSql) << "Closing database failed" << _error;
@ -217,9 +226,8 @@ sqlite3 *SqlDatabase::sqliteDb()
/* =========================================================================================== */
SqlQuery::SqlQuery(SqlDatabase &db)
: _db(db.sqliteDb())
, _stmt(0)
, _errId(0)
: _sqldb(&db)
, _db(db.sqliteDb())
{
}
@ -228,20 +236,21 @@ SqlQuery::~SqlQuery()
if (_stmt) {
finish();
}
if (_sqldb) {
_sqldb->_queries.removeAll(this);
}
}
SqlQuery::SqlQuery(const QString &sql, SqlDatabase &db)
: _db(db.sqliteDb())
, _stmt(0)
, _errId(0)
SqlQuery::SqlQuery(const QByteArray &sql, SqlDatabase &db)
: _sqldb(&db)
, _db(db.sqliteDb())
{
prepare(sql);
}
int SqlQuery::prepare(const QString &sql, bool allow_failure)
int SqlQuery::prepare(const QByteArray &sql, bool allow_failure)
{
QString s(sql);
_sql = s.trimmed();
_sql = sql.trimmed();
if (_stmt) {
finish();
}
@ -249,7 +258,7 @@ int SqlQuery::prepare(const QString &sql, bool allow_failure)
int n = 0;
int rc;
do {
rc = sqlite3_prepare_v2(_db, _sql.toUtf8().constData(), -1, &_stmt, 0);
rc = sqlite3_prepare_v2(_db, _sql.constData(), -1, &_stmt, 0);
if ((rc == SQLITE_BUSY) || (rc == SQLITE_LOCKED)) {
n++;
OCC::Utility::usleep(SQLITE_SLEEP_TIME_USEC);
@ -266,14 +275,21 @@ int SqlQuery::prepare(const QString &sql, bool allow_failure)
return _errId;
}
// There is no overloads to QByteArray::startWith that takes Qt::CaseInsensitive
template <int N>
static bool startsWithInsensitive(const QByteArray &a, const char (&cal)[N])
{
return a.size() >= N - 1 && qstrnicmp(a.constData(), cal, N - 1) == 0;
}
bool SqlQuery::isSelect()
{
return (!_sql.isEmpty() && _sql.startsWith("SELECT", Qt::CaseInsensitive));
return startsWithInsensitive(_sql, "SELECT");
}
bool SqlQuery::isPragma()
{
return (!_sql.isEmpty() && _sql.startsWith("PRAGMA", Qt::CaseInsensitive));
return startsWithInsensitive(_sql, "PRAGMA");
}
bool SqlQuery::exec()

View file

@ -29,6 +29,8 @@ struct sqlite3_stmt;
namespace OCC {
class SqlQuery;
/**
* @brief The SqlDatabase class
* @ingroup libsync
@ -38,6 +40,7 @@ class OCSYNC_EXPORT SqlDatabase
Q_DISABLE_COPY(SqlDatabase)
public:
explicit SqlDatabase();
~SqlDatabase();
bool isOpen();
bool openOrCreateReadWrite(const QString &filename);
@ -62,6 +65,9 @@ private:
sqlite3 *_db;
QString _error; // last error string
int _errId;
friend class SqlQuery;
QVector<SqlQuery *> _queries;
};
/**
@ -73,7 +79,7 @@ class OCSYNC_EXPORT SqlQuery
Q_DISABLE_COPY(SqlQuery)
public:
explicit SqlQuery(SqlDatabase &db);
explicit SqlQuery(const QString &sql, SqlDatabase &db);
explicit SqlQuery(const QByteArray &sql, SqlDatabase &db);
~SqlQuery();
QString error() const;
@ -82,7 +88,6 @@ public:
/// Checks whether the value at the given column index is NULL
bool nullValue(int index);
QString stringValue(int index);
int intValue(int index);
quint64 int64Value(int index);
@ -91,7 +96,7 @@ public:
bool isSelect();
bool isPragma();
bool exec();
int prepare(const QString &sql, bool allow_failure = false);
int prepare(const QByteArray &sql, bool allow_failure = false);
bool next();
void bindValue(int pos, const QVariant &value);
QString lastQuery() const;
@ -100,11 +105,12 @@ public:
void finish();
private:
sqlite3 *_db;
sqlite3_stmt *_stmt;
SqlDatabase *_sqldb = nullptr;
sqlite3 *_db = nullptr;
sqlite3_stmt *_stmt = nullptr;
QString _error;
int _errId;
QString _sql;
QByteArray _sql;
};
} // namespace OCC

View file

@ -65,7 +65,7 @@ static void fillFileRecordFromGetQuery(SyncJournalFileRecord &rec, SqlQuery &que
rec._e2eMangledName = query.baValue(10);
}
static QString defaultJournalMode(const QString &dbPath)
static QByteArray defaultJournalMode(const QString &dbPath)
{
#if defined(Q_OS_WIN)
// See #2693: Some exFAT file systems seem unable to cope with the
@ -95,7 +95,7 @@ SyncJournalDb::SyncJournalDb(const QString &dbFilePath, QObject *parent)
, _metadataTableIsEmpty(false)
{
// Allow forcing the journal mode for debugging
static QString envJournalMode = QString::fromLocal8Bit(qgetenv("OWNCLOUD_SQLITE_JOURNAL_MODE"));
static QByteArray envJournalMode = qgetenv("OWNCLOUD_SQLITE_JOURNAL_MODE");
_journalMode = envJournalMode;
if (_journalMode.isEmpty()) {
_journalMode = defaultJournalMode(_dbFile);
@ -314,7 +314,7 @@ bool SyncJournalDb::checkConnect()
qCInfo(lcDb) << "sqlite3 version" << pragma1.stringValue(0);
}
pragma1.prepare(QString("PRAGMA journal_mode=%1;").arg(_journalMode));
pragma1.prepare("PRAGMA journal_mode=" + _journalMode + ";");
if (!pragma1.exec()) {
return sqlFail("Set PRAGMA journal_mode", pragma1);
} else {
@ -323,9 +323,9 @@ bool SyncJournalDb::checkConnect()
}
// For debugging purposes, allow temp_store to be set
static QString env_temp_store = QString::fromLocal8Bit(qgetenv("OWNCLOUD_SQLITE_TEMP_STORE"));
static QByteArray env_temp_store = qgetenv("OWNCLOUD_SQLITE_TEMP_STORE");
if (!env_temp_store.isEmpty()) {
pragma1.prepare(QString("PRAGMA temp_store = %1;").arg(env_temp_store));
pragma1.prepare("PRAGMA temp_store = " + env_temp_store + ";");
if (!pragma1.exec()) {
return sqlFail("Set PRAGMA temp_store", pragma1);
}
@ -678,12 +678,12 @@ bool SyncJournalDb::checkConnect()
return sqlFail("prepare _deleteFileRecordRecursively", *_deleteFileRecordRecursively);
}
QString sql("SELECT lastTryEtag, lastTryModtime, retrycount, errorstring, lastTryTime, ignoreDuration, renameTarget, errorCategory "
"FROM blacklist WHERE path=?1");
QByteArray sql("SELECT lastTryEtag, lastTryModtime, retrycount, errorstring, lastTryTime, ignoreDuration, renameTarget, errorCategory "
"FROM blacklist WHERE path=?1");
if (Utility::fsCasePreserving()) {
// if the file system is case preserving we have to check the blacklist
// case insensitively
sql += QLatin1String(" COLLATE NOCASE");
sql += " COLLATE NOCASE";
}
_getErrorBlacklistQuery.reset(new SqlQuery(_db));
if (_getErrorBlacklistQuery->prepare(sql)) {
@ -771,36 +771,6 @@ void SyncJournalDb::close()
commitTransaction();
_getFileRecordQuery.reset(0);
_getFileRecordQueryByMangledName.reset(0);
_getFileRecordQueryByInode.reset(0);
_getFileRecordQueryByFileId.reset(0);
_getFilesBelowPathQuery.reset(0);
_getAllFilesQuery.reset(0);
_setFileRecordQuery.reset(0);
_setFileRecordChecksumQuery.reset(0);
_setFileRecordLocalMetadataQuery.reset(0);
_getDownloadInfoQuery.reset(0);
_setDownloadInfoQuery.reset(0);
_deleteDownloadInfoQuery.reset(0);
_getUploadInfoQuery.reset(0);
_setUploadInfoQuery.reset(0);
_deleteUploadInfoQuery.reset(0);
_deleteFileRecordPhash.reset(0);
_deleteFileRecordRecursively.reset(0);
_getErrorBlacklistQuery.reset(0);
_setErrorBlacklistQuery.reset(0);
_getSelectiveSyncListQuery.reset(0);
_getChecksumTypeIdQuery.reset(0);
_getChecksumTypeQuery.reset(0);
_insertChecksumTypeQuery.reset(0);
_getDataFingerprintQuery.reset(0);
_setDataFingerprintQuery1.reset(0);
_setDataFingerprintQuery2.reset(0);
_getConflictRecordQuery.reset(0);
_setConflictRecordQuery.reset(0);
_deleteConflictRecordQuery.reset(0);
_db.close();
_avoidReadFromDbOnNextSyncFilter.clear();
_metadataTableIsEmpty = false;
@ -818,7 +788,8 @@ bool SyncJournalDb::updateDatabaseStructure()
bool SyncJournalDb::updateMetadataTableStructure()
{
const QStringList columns = tableColumns("metadata");
auto columns = tableColumns("metadata");
bool re = true;
// check if the file_id column is there and create it if not
@ -826,7 +797,7 @@ bool SyncJournalDb::updateMetadataTableStructure()
return false;
}
if (columns.indexOf(QLatin1String("fileid")) == -1) {
if (columns.indexOf("fileid") == -1) {
SqlQuery query(_db);
query.prepare("ALTER TABLE metadata ADD COLUMN fileid VARCHAR(128);");
if (!query.exec()) {
@ -841,7 +812,7 @@ bool SyncJournalDb::updateMetadataTableStructure()
}
commitInternal("update database structure: add fileid col");
}
if (columns.indexOf(QLatin1String("remotePerm")) == -1) {
if (columns.indexOf("remotePerm") == -1) {
SqlQuery query(_db);
query.prepare("ALTER TABLE metadata ADD COLUMN remotePerm VARCHAR(128);");
if (!query.exec()) {
@ -850,7 +821,7 @@ bool SyncJournalDb::updateMetadataTableStructure()
}
commitInternal("update database structure (remotePerm)");
}
if (columns.indexOf(QLatin1String("filesize")) == -1) {
if (columns.indexOf("filesize") == -1) {
SqlQuery query(_db);
query.prepare("ALTER TABLE metadata ADD COLUMN filesize BIGINT;");
if (!query.exec()) {
@ -880,7 +851,7 @@ bool SyncJournalDb::updateMetadataTableStructure()
commitInternal("update database structure: add path index");
}
if (columns.indexOf(QLatin1String("ignoredChildrenRemote")) == -1) {
if (columns.indexOf("ignoredChildrenRemote") == -1) {
SqlQuery query(_db);
query.prepare("ALTER TABLE metadata ADD COLUMN ignoredChildrenRemote INT;");
if (!query.exec()) {
@ -890,7 +861,7 @@ bool SyncJournalDb::updateMetadataTableStructure()
commitInternal("update database structure: add ignoredChildrenRemote col");
}
if (columns.indexOf(QLatin1String("contentChecksum")) == -1) {
if (columns.indexOf("contentChecksum") == -1) {
SqlQuery query(_db);
query.prepare("ALTER TABLE metadata ADD COLUMN contentChecksum TEXT;");
if (!query.exec()) {
@ -899,7 +870,7 @@ bool SyncJournalDb::updateMetadataTableStructure()
}
commitInternal("update database structure: add contentChecksum col");
}
if (columns.indexOf(QLatin1String("contentChecksumTypeId")) == -1) {
if (columns.indexOf("contentChecksumTypeId") == -1) {
SqlQuery query(_db);
query.prepare("ALTER TABLE metadata ADD COLUMN contentChecksumTypeId INTEGER;");
if (!query.exec()) {
@ -909,7 +880,7 @@ bool SyncJournalDb::updateMetadataTableStructure()
commitInternal("update database structure: add contentChecksumTypeId col");
}
if (!columns.contains(QLatin1String("e2eMangledName"))) {
if (!columns.contains("e2eMangledName")) {
SqlQuery query(_db);
query.prepare("ALTER TABLE metadata ADD COLUMN e2eMangledName TEXT;");
if (!query.exec()) {
@ -935,7 +906,7 @@ bool SyncJournalDb::updateMetadataTableStructure()
bool SyncJournalDb::updateErrorBlacklistTableStructure()
{
QStringList columns = tableColumns("blacklist");
auto columns = tableColumns("blacklist");
bool re = true;
// check if the file_id column is there and create it if not
@ -943,7 +914,7 @@ bool SyncJournalDb::updateErrorBlacklistTableStructure()
return false;
}
if (columns.indexOf(QLatin1String("lastTryTime")) == -1) {
if (columns.indexOf("lastTryTime") == -1) {
SqlQuery query(_db);
query.prepare("ALTER TABLE blacklist ADD COLUMN lastTryTime INTEGER(8);");
if (!query.exec()) {
@ -957,7 +928,7 @@ bool SyncJournalDb::updateErrorBlacklistTableStructure()
}
commitInternal("update database structure: add lastTryTime, ignoreDuration cols");
}
if (columns.indexOf(QLatin1String("renameTarget")) == -1) {
if (columns.indexOf("renameTarget") == -1) {
SqlQuery query(_db);
query.prepare("ALTER TABLE blacklist ADD COLUMN renameTarget VARCHAR(4096);");
if (!query.exec()) {
@ -967,7 +938,7 @@ bool SyncJournalDb::updateErrorBlacklistTableStructure()
commitInternal("update database structure: add renameTarget col");
}
if (columns.indexOf(QLatin1String("errorCategory")) == -1) {
if (columns.indexOf("errorCategory") == -1) {
SqlQuery query(_db);
query.prepare("ALTER TABLE blacklist ADD COLUMN errorCategory INTEGER(8);");
if (!query.exec()) {
@ -987,26 +958,20 @@ bool SyncJournalDb::updateErrorBlacklistTableStructure()
return re;
}
QStringList SyncJournalDb::tableColumns(const QString &table)
QVector<QByteArray> SyncJournalDb::tableColumns(const QByteArray &table)
{
QStringList columns;
if (!table.isEmpty()) {
if (checkConnect()) {
QString q = QString("PRAGMA table_info('%1');").arg(table);
SqlQuery query(_db);
query.prepare(q);
if (!query.exec()) {
return columns;
}
while (query.next()) {
columns.append(query.stringValue(1));
}
}
QVector<QByteArray> columns;
if (!checkConnect()) {
return columns;
}
SqlQuery query("PRAGMA table_info('" + table + "');", _db);
if (!query.exec()) {
return columns;
}
while (query.next()) {
columns.append(query.baValue(1));
}
qCDebug(lcDb) << "Columns in the current journal: " << columns;
return columns;
}

View file

@ -248,7 +248,7 @@ private:
void commitInternal(const QString &context, bool startTrans = true);
void startTransaction();
void commitTransaction();
QStringList tableColumns(const QString &table);
QVector<QByteArray> tableColumns(const QByteArray &table);
bool checkConnect();
// Same as forceRemoteDiscoveryNextSync but without acquiring the lock
@ -265,7 +265,6 @@ private:
int _transaction;
bool _metadataTableIsEmpty;
// NOTE! when adding a query, don't forget to reset it in SyncJournalDb::close
QScopedPointer<SqlQuery> _getFileRecordQuery;
QScopedPointer<SqlQuery> _getFileRecordQueryByMangledName;
QScopedPointer<SqlQuery> _getFileRecordQueryByInode;
@ -309,7 +308,7 @@ private:
* Typically WAL initially, but may be set to other modes via environment
* variable, for specific filesystems, or when WAL fails in a particular way.
*/
QString _journalMode;
QByteArray _journalMode;
};
bool OCSYNC_EXPORT