Akonadi Search

xapiandatabase.cpp
1/*
2 * SPDX-FileCopyrightText: 2014 Vishesh Handa <me@vhanda.in>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5 *
6 */
7
8#include "xapiandatabase.h"
9#include "xapiandocument.h"
10
11#include "akonadi_search_xapian_debug.h"
12#include <QDir>
13
14#if defined(HAVE_MALLOC_H)
15#include <malloc.h>
16#endif
17
18#include <chrono>
19#include <thread>
20
21using namespace Akonadi::Search;
22
23XapianDatabase::XapianDatabase(const QString &path, bool writeOnly)
24 : m_writeOnly(writeOnly)
25{
26 QDir().mkpath(path);
27 m_path = path.toStdString();
28
29 if (!writeOnly) {
30 try {
31 createWritableDb();
32 m_db = new Xapian::Database(m_path);
33 } catch (const Xapian::DatabaseError &err) {
34 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << "Serious Error: " << err.get_error_string();
35 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << err.get_msg().c_str() << err.get_context().c_str() << err.get_description().c_str();
36 }
37
38 // Possible errors - DatabaseLock error
39 // Corrupt and InvalidID error
40 } else {
41 m_wDb = createWritableDb();
42 }
43}
44
45XapianDatabase::~XapianDatabase()
46{
47 delete m_db;
48}
49
50void XapianDatabase::replaceDocument(uint id, const XapianDocument &doc)
51{
52 replaceDocument(id, doc.doc());
53}
54
55void XapianDatabase::replaceDocument(uint id, const Xapian::Document &doc)
56{
57 if (m_writeOnly) {
58 try {
59 m_wDb.replace_document(id, doc);
60 } catch (const Xapian::Error &) {
61 }
62 return;
63 }
64 m_docsToAdd << qMakePair(id, doc);
65}
66
67void XapianDatabase::deleteDocument(uint id)
68{
69 if (id == 0) {
70 return;
71 }
72
73 if (m_writeOnly) {
74 try {
75 m_wDb.delete_document(id);
76 } catch (const Xapian::Error &) {
77 }
78 return;
79 }
80 m_docsToRemove << id;
81}
82
84{
85 return !m_docsToAdd.isEmpty() || !m_docsToRemove.isEmpty();
86}
87
89{
90 if (m_writeOnly) {
91 try {
92 m_wDb.commit();
93 } catch (const Xapian::Error &err) {
94 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << err.get_error_string();
95 }
96 return;
97 }
98
99 if (!haveChanges()) {
100 return;
101 }
102
103 Xapian::WritableDatabase wdb = createWritableDb();
104
105 qCDebug(AKONADI_SEARCH_XAPIAN_LOG) << "Adding:" << m_docsToAdd.size() << "docs";
106 for (const DocIdPair &doc : std::as_const(m_docsToAdd)) {
107 try {
108 wdb.replace_document(doc.first, doc.second);
109 } catch (const Xapian::Error &) {
110 }
111 }
112
113 qCDebug(AKONADI_SEARCH_XAPIAN_LOG) << "Removing:" << m_docsToRemove.size() << "docs";
114 for (Xapian::docid id : std::as_const(m_docsToRemove)) {
115 try {
116 wdb.delete_document(id);
117 } catch (const Xapian::Error &) {
118 }
119 }
120
121 try {
122 wdb.commit();
123 m_db->reopen();
124 } catch (const Xapian::Error &err) {
125 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << err.get_error_string();
126 }
127 qCDebug(AKONADI_SEARCH_XAPIAN_LOG) << "Xapian Committed";
128
129 m_docsToAdd.clear();
130 m_docsToRemove.clear();
131
132#if defined(HAVE_MALLOC_TRIM)
133 malloc_trim(0);
134#endif
135}
136
137XapianDocument XapianDatabase::document(uint id)
138{
139 try {
140 Xapian::Document xdoc;
141 if (m_writeOnly) {
142 xdoc = m_wDb.get_document(id);
143 } else {
144 xdoc = m_db->get_document(id);
145 }
146 return XapianDocument(xdoc);
147 } catch (const Xapian::DatabaseModifiedError &) {
148 m_db->reopen();
149 return document(id);
150 } catch (const Xapian::Error &) {
151 return {};
152 }
153}
154
155Xapian::WritableDatabase XapianDatabase::createWritableDb()
156{
157 // We need to keep sleeping for a required amount, until we reach
158 // a threshold. That's when we decide to abort?
159 for (int i = 1; i <= 20; ++i) {
160 try {
161 Xapian::WritableDatabase wdb(m_path, Xapian::DB_CREATE_OR_OPEN);
162 return wdb;
163 } catch (const Xapian::DatabaseLockError &) {
164 std::this_thread::sleep_for(std::chrono::milliseconds(i * 50));
165 } catch (const Xapian::DatabaseModifiedError &) {
166 std::this_thread::sleep_for(std::chrono::milliseconds(i * 50));
167 } catch (const Xapian::DatabaseCreateError &err) {
168 qCDebug(AKONADI_SEARCH_XAPIAN_LOG) << err.get_error_string();
169 return {};
170 } catch (const Xapian::DatabaseCorruptError &err) {
171 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << "Database Corrupted - What did you do?";
172 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << err.get_error_string();
173 return {};
174 } catch (...) {
175 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << "Bananana Error";
176 return {};
177 }
178 }
179
180 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << "Could not obtain lock for Xapian Database. This is bad";
181 return {};
182}
bool haveChanges() const
Returns true if the XapianDatabase has changes which need to be committed.
XapianDatabase(const QString &path, bool writeOnly=false)
Create the Xapian db at path path.
void commit()
Commit all the pending changes.
This class is just a light wrapper over Xapian::Document which provides nice Qt apis.
Akonadi search infrastructure.
Definition core/query.h:21
bool mkpath(const QString &dirPath) const const
void clear()
bool isEmpty() const const
qsizetype size() const const
std::string toStdString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:10:52 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.