1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2017-06-28
7  * Description : Similarity Database access wrapper.
8  *
9  * Copyright (C) 2007-2008 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
10  * Copyright (C) 2010-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
11  * Copyright (C)      2017 by Swati  Lodha   <swatilodha27 at gmail dot com>
12  * Copyright (C)      2018 by Mario Frank    <mario dot frank at uni minus potsdam dot de>
13  *
14  * This program is free software; you can redistribute it
15  * and/or modify it under the terms of the GNU General
16  * Public License as published by the Free Software Foundation;
17  * either version 2, or (at your option)
18  * any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * ============================================================ */
26 
27 #include "similaritydbaccess.h"
28 
29 // Qt includes
30 
31 #include <QMutex>
32 #include <QSqlDatabase>
33 
34 // KDE includes
35 
36 #include <klocalizedstring.h>
37 
38 // Local includes
39 
40 #include "digikam_debug.h"
41 #include "similaritydbbackend.h"
42 #include "similaritydb.h"
43 #include "similaritydbschemaupdater.h"
44 #include "dbengineparameters.h"
45 #include "dbengineaccess.h"
46 #include "dbengineerrorhandler.h"
47 
48 namespace Digikam
49 {
50 
51 class Q_DECL_HIDDEN SimilarityDbAccessStaticPriv
52 {
53 public:
54 
SimilarityDbAccessStaticPriv()55     SimilarityDbAccessStaticPriv()
56         : backend     (nullptr),
57           db          (nullptr),
58           initializing(false)
59     {
60     }
61 
~SimilarityDbAccessStaticPriv()62     ~SimilarityDbAccessStaticPriv()
63     {
64     };
65 
66     SimilarityDbBackend* backend;
67     SimilarityDb*        db;
68     DbEngineParameters   parameters;
69     DbEngineLocking      lock;
70     QString              lastError;
71 
72     bool                 initializing;
73 };
74 
75 SimilarityDbAccessStaticPriv* SimilarityDbAccess::d = nullptr;
76 
77 // -----------------------------------------------------------------------------
78 
79 class Q_DECL_HIDDEN SimilarityDbAccessMutexLocker : public QMutexLocker
80 {
81 public:
82 
SimilarityDbAccessMutexLocker(SimilarityDbAccessStaticPriv * const dd)83     explicit SimilarityDbAccessMutexLocker(SimilarityDbAccessStaticPriv* const dd)
84         : QMutexLocker(&dd->lock.mutex),
85           d           (dd)
86     {
87         d->lock.lockCount++;
88     }
89 
~SimilarityDbAccessMutexLocker()90     ~SimilarityDbAccessMutexLocker()
91     {
92         d->lock.lockCount--;
93     }
94 
95 public:
96 
97     SimilarityDbAccessStaticPriv* const d;
98 };
99 
100 // -----------------------------------------------------------------------------
101 
SimilarityDbAccess()102 SimilarityDbAccess::SimilarityDbAccess()
103 {
104     // You will want to call setParameters before constructing SimilarityDbAccess.
105 
106     Q_ASSERT(d);
107 
108     d->lock.mutex.lock();
109     d->lock.lockCount++;
110 
111     if (!d->backend->isOpen() && !d->initializing)
112     {
113         // avoid endless loops
114 
115         d->initializing = true;
116 
117         d->backend->open(d->parameters);
118 
119         d->initializing = false;
120     }
121 }
122 
~SimilarityDbAccess()123 SimilarityDbAccess::~SimilarityDbAccess()
124 {
125     d->lock.lockCount--;
126     d->lock.mutex.unlock();
127 }
128 
SimilarityDbAccess(bool)129 SimilarityDbAccess::SimilarityDbAccess(bool)
130 {
131     // private constructor, when mutex is locked and
132     // backend should not be checked
133 
134     d->lock.mutex.lock();
135     d->lock.lockCount++;
136 }
137 
db() const138 SimilarityDb* SimilarityDbAccess::db() const
139 {
140     return d->db;
141 }
142 
backend() const143 SimilarityDbBackend* SimilarityDbAccess::backend() const
144 {
145     return d->backend;
146 }
147 
parameters()148 DbEngineParameters SimilarityDbAccess::parameters()
149 {
150     if (d)
151     {
152         return d->parameters;
153     }
154 
155     return DbEngineParameters();
156 }
157 
isInitialized()158 bool SimilarityDbAccess::isInitialized()
159 {
160     return d;
161 }
162 
initDbEngineErrorHandler(DbEngineErrorHandler * const errorhandler)163 void SimilarityDbAccess::initDbEngineErrorHandler(DbEngineErrorHandler* const errorhandler)
164 {
165     if (!d)
166     {
167         d = new SimilarityDbAccessStaticPriv();
168     }
169 
170     //DbEngineErrorHandler* const errorhandler = new DbEngineGuiErrorHandler(d->parameters);
171 
172     d->backend->setDbEngineErrorHandler(errorhandler);
173 }
174 
setParameters(const DbEngineParameters & parameters)175 void SimilarityDbAccess::setParameters(const DbEngineParameters& parameters)
176 {
177     if (!d)
178     {
179         d = new SimilarityDbAccessStaticPriv();
180     }
181 
182     SimilarityDbAccessMutexLocker lock(d);
183 
184     if (d->parameters == parameters)
185     {
186         return;
187     }
188 
189     if (d->backend && d->backend->isOpen())
190     {
191         d->backend->close();
192     }
193 
194     // Kill the old database error handler
195 
196     if (d->backend)
197     {
198         d->backend->setDbEngineErrorHandler(nullptr);
199     }
200 
201     d->parameters = parameters;
202 
203     if (!d->backend || !d->backend->isCompatible(parameters))
204     {
205         delete d->db;
206         delete d->backend;
207         d->backend = new SimilarityDbBackend(&d->lock);
208         d->db      = new SimilarityDb(d->backend);
209     }
210 }
211 
checkReadyForUse(InitializationObserver * const observer)212 bool SimilarityDbAccess::checkReadyForUse(InitializationObserver* const observer)
213 {
214     if (!DbEngineAccess::checkReadyForUse(d->lastError))
215     {
216         return false;
217     }
218 
219     // create an object with private shortcut constructor
220 
221     SimilarityDbAccess access(false);
222 
223     if (!d->backend)
224     {
225         qCWarning(DIGIKAM_SIMILARITYDB_LOG) << "Similarity database: no database backend available in checkReadyForUse. "
226                                                 "Did you call setParameters before?";
227         return false;
228     }
229 
230     if (d->backend->isReady())
231     {
232         return true;
233     }
234 
235     if (!d->backend->isOpen())
236     {
237         if (!d->backend->open(d->parameters))
238         {
239             access.setLastError(i18n("Error opening database backend.\n%1",
240                                 d->backend->lastError()));
241             return false;
242         }
243     }
244 
245     // avoid endless loops (if called methods create new SimilarityDbAccess objects)
246 
247     d->initializing = true;
248 
249     // update schema
250 
251     SimilarityDbSchemaUpdater updater(&access);
252     updater.setObserver(observer);
253 
254     if (!d->backend->initSchema(&updater))
255     {
256         qCWarning(DIGIKAM_SIMILARITYDB_LOG) << "Similarity database: cannot process schema initialization";
257 
258         d->initializing = false;
259         return false;
260     }
261 
262     d->initializing = false;
263 
264     return d->backend->isReady();
265 }
266 
lastError() const267 QString SimilarityDbAccess::lastError() const
268 {
269     return d->lastError;
270 }
271 
setLastError(const QString & error)272 void SimilarityDbAccess::setLastError(const QString& error)
273 {
274     d->lastError = error;
275 }
276 
cleanUpDatabase()277 void SimilarityDbAccess::cleanUpDatabase()
278 {
279     if (d)
280     {
281         SimilarityDbAccessMutexLocker locker(d);
282 
283         if (d->backend)
284         {
285             d->backend->close();
286             delete d->db;
287             delete d->backend;
288         }
289     }
290 
291     delete d;
292     d = nullptr;
293 }
294 
295 } // namespace Digikam
296