File indexing completed on 2024-04-21 03:51:39

0001 /*
0002     This file is part of the KDE Baloo project.
0003     SPDX-FileCopyrightText: 2015 Vishesh Handa <vhanda@kde.org>
0004     SPDX-FileCopyrightText: 2016 Christoph Cullmann <cullmann@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-or-later
0007 */
0008 
0009 #include "database.h"
0010 #include "transaction.h"
0011 #include "postingdb.h"
0012 #include "documentdb.h"
0013 #include "documenturldb.h"
0014 #include "documentiddb.h"
0015 #include "positiondb.h"
0016 #include "documenttimedb.h"
0017 #include "documentdatadb.h"
0018 #include "mtimedb.h"
0019 
0020 #include "enginequery.h"
0021 
0022 #include "andpostingiterator.h"
0023 #include "orpostingiterator.h"
0024 #include "phraseanditerator.h"
0025 
0026 #include "writetransaction.h"
0027 #include "idutils.h"
0028 #include "fsutils.h"
0029 
0030 #include "enginedebug.h"
0031 
0032 // MSVC does not understand the inline assembly in valgrind.h
0033 // Defining NVALGRIND stubs out all definitions, so we can use
0034 // the macros without ifdef'ing these in place
0035 #if defined _MSC_VER && !defined NVALGRIND
0036 #define NVALGRIND 1
0037 #endif
0038 #include "valgrind.h"
0039 
0040 #include <QFile>
0041 #include <QFileInfo>
0042 #include <QDir>
0043 #include <QMutexLocker>
0044 
0045 using namespace Baloo;
0046 
0047 Database::Database(const QString& path)
0048     : m_path(path)
0049     , m_env(nullptr)
0050 {
0051 }
0052 
0053 Database::~Database()
0054 {
0055     // try only to close if we did open the DB successfully
0056     if (m_env) {
0057         mdb_env_close(m_env);
0058         m_env = nullptr;
0059     }
0060 }
0061 
0062 bool Database::open(OpenMode mode)
0063 {
0064     QMutexLocker locker(&m_mutex);
0065 
0066     // nop if already open!
0067     if (m_env) {
0068         return true;
0069     }
0070 
0071     QDir dir(m_path);
0072     if (!dir.exists()) {
0073         dir.mkpath(QStringLiteral("."));
0074         dir.refresh();
0075     }
0076     QFileInfo indexInfo(dir, QStringLiteral("index"));
0077 
0078     if ((mode != CreateDatabase) && !indexInfo.exists()) {
0079         return false;
0080     }
0081 
0082     if (mode == CreateDatabase) {
0083         if (!QFileInfo(dir.absolutePath()).permission(QFile::WriteOwner)) {
0084             qCCritical(ENGINE) << m_path << "does not have write permissions. Aborting";
0085             return false;
0086         }
0087 
0088         if (!indexInfo.exists()) {
0089             FSUtils::disableCoW(m_path);
0090         }
0091     }
0092 
0093     int rc = mdb_env_create(&m_env);
0094     if (rc) {
0095         m_env = nullptr;
0096         return false;
0097     }
0098 
0099     /**
0100      * maximal number of allowed named databases, must match number of databases we create below
0101      * each additional one leads to overhead
0102      */
0103     mdb_env_set_maxdbs(m_env, 12);
0104 
0105     /**
0106      * size limit for database == size limit of mmap
0107      * use 1 GB on 32-bit, use 256 GB on 64-bit
0108      * Valgrind by default (without recompiling) limits the mmap size:
0109      * <= 3.9: 32 GByte, 3.9 to 3.12: 64 GByte, 3.13: 128 GByte
0110      */
0111     size_t sizeInGByte = 256;
0112     if (sizeof(void*) == 4) {
0113         sizeInGByte = 1;
0114         qCWarning(ENGINE) << "Running on 32 bit arch, limiting DB mmap to" << sizeInGByte << "GByte";
0115     } else if (RUNNING_ON_VALGRIND) {
0116         // valgrind lacks a runtime version check, assume valgrind >= 3.9, and allow for some other mmaps
0117         sizeInGByte = 40;
0118         qCWarning(ENGINE) << "Valgrind detected, limiting DB mmap to" << sizeInGByte << "GByte";
0119     }
0120     const size_t maximalSizeInBytes = sizeInGByte * size_t(1024) * size_t(1024) * size_t(1024);
0121     mdb_env_set_mapsize(m_env, maximalSizeInBytes);
0122 
0123     // Set MDB environment flags
0124     auto mdbEnvFlags = MDB_NOSUBDIR | MDB_NOMEMINIT;
0125     if (mode == ReadOnlyDatabase) {
0126         mdbEnvFlags |= MDB_RDONLY;
0127     }
0128 
0129     // The directory needs to be created before opening the environment
0130     QByteArray arr = QFile::encodeName(indexInfo.absoluteFilePath());
0131     rc = mdb_env_open(m_env, arr.constData(), mdbEnvFlags, 0664);
0132     if (rc) {
0133         mdb_env_close(m_env);
0134         m_env = nullptr;
0135         return false;
0136     }
0137 
0138     rc = mdb_reader_check(m_env, nullptr);
0139 
0140     if (rc) {
0141         qCWarning(ENGINE) << "Database::open reader_check" << mdb_strerror(rc);
0142         mdb_env_close(m_env);
0143         m_env = nullptr;
0144         return false;
0145     }
0146 
0147     //
0148     // Individual Databases
0149     //
0150     MDB_txn* txn;
0151     if (mode != CreateDatabase) {
0152         int rc = mdb_txn_begin(m_env, nullptr, MDB_RDONLY, &txn);
0153         if (rc) {
0154             qCWarning(ENGINE) << "Database::transaction ro begin" << mdb_strerror(rc);
0155             mdb_env_close(m_env);
0156             m_env = nullptr;
0157             return false;
0158         }
0159 
0160         m_dbis.postingDbi = PostingDB::open(txn);
0161         m_dbis.positionDBi = PositionDB::open(txn);
0162 
0163         m_dbis.docTermsDbi = DocumentDB::open("docterms", txn);
0164         m_dbis.docFilenameTermsDbi = DocumentDB::open("docfilenameterms", txn);
0165         m_dbis.docXattrTermsDbi = DocumentDB::open("docxatrrterms", txn);
0166 
0167         m_dbis.idTreeDbi = IdTreeDB::open(txn);
0168         m_dbis.idFilenameDbi = IdFilenameDB::open(txn);
0169 
0170         m_dbis.docTimeDbi = DocumentTimeDB::open(txn);
0171         m_dbis.docDataDbi = DocumentDataDB::open(txn);
0172 
0173         m_dbis.contentIndexingDbi = DocumentIdDB::open("indexingleveldb", txn);
0174         m_dbis.failedIdDbi = DocumentIdDB::open("failediddb", txn);
0175 
0176         m_dbis.mtimeDbi = MTimeDB::open(txn);
0177 
0178         if (!m_dbis.isValid()) {
0179             qCWarning(ENGINE) << "dbis is invalid";
0180             mdb_txn_abort(txn);
0181             mdb_env_close(m_env);
0182             m_env = nullptr;
0183             return false;
0184         }
0185 
0186         rc = mdb_txn_commit(txn);
0187         if (rc) {
0188             qCWarning(ENGINE) << "Database::transaction ro commit" << mdb_strerror(rc);
0189             mdb_env_close(m_env);
0190             m_env = nullptr;
0191             return false;
0192         }
0193     } else {
0194         int rc = mdb_txn_begin(m_env, nullptr, 0, &txn);
0195         if (rc) {
0196             qCWarning(ENGINE) << "Database::transaction begin" << mdb_strerror(rc);
0197             mdb_env_close(m_env);
0198             m_env = nullptr;
0199             return false;
0200         }
0201 
0202         m_dbis.postingDbi = PostingDB::create(txn);
0203         m_dbis.positionDBi = PositionDB::create(txn);
0204 
0205         m_dbis.docTermsDbi = DocumentDB::create("docterms", txn);
0206         m_dbis.docFilenameTermsDbi = DocumentDB::create("docfilenameterms", txn);
0207         m_dbis.docXattrTermsDbi = DocumentDB::create("docxatrrterms", txn);
0208 
0209         m_dbis.idTreeDbi = IdTreeDB::create(txn);
0210         m_dbis.idFilenameDbi = IdFilenameDB::create(txn);
0211 
0212         m_dbis.docTimeDbi = DocumentTimeDB::create(txn);
0213         m_dbis.docDataDbi = DocumentDataDB::create(txn);
0214 
0215         m_dbis.contentIndexingDbi = DocumentIdDB::create("indexingleveldb", txn);
0216         m_dbis.failedIdDbi = DocumentIdDB::create("failediddb", txn);
0217 
0218         m_dbis.mtimeDbi = MTimeDB::create(txn);
0219 
0220         if (!m_dbis.isValid()) {
0221             qCWarning(ENGINE) << "dbis is invalid";
0222             mdb_txn_abort(txn);
0223             mdb_env_close(m_env);
0224             m_env = nullptr;
0225             return false;
0226         }
0227 
0228         rc = mdb_txn_commit(txn);
0229         if (rc) {
0230             qCWarning(ENGINE) << "Database::transaction commit" << mdb_strerror(rc);
0231             mdb_env_close(m_env);
0232             m_env = nullptr;
0233             return false;
0234         }
0235     }
0236 
0237     Q_ASSERT(m_env);
0238     return true;
0239 }
0240 
0241 bool Database::isOpen() const
0242 {
0243     QMutexLocker locker(&m_mutex);
0244     return m_env != nullptr;
0245 }
0246 
0247 QString Database::path() const
0248 {
0249     QMutexLocker locker(&m_mutex);
0250     return m_path;
0251 }
0252 
0253 bool Database::isAvailable() const
0254 {
0255     QMutexLocker locker(&m_mutex);
0256     return QFileInfo::exists(m_path + QStringLiteral("/index"));
0257 }