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 }