File indexing completed on 2025-01-19 03:53:33
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2007-06-07 0007 * Description : Database engine abstract database backend 0008 * 0009 * SPDX-FileCopyrightText: 2007-2010 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0010 * SPDX-FileCopyrightText: 2010-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #ifndef DIGIKAM_DB_ENGINE_BACKEND_H 0017 #define DIGIKAM_DB_ENGINE_BACKEND_H 0018 0019 // Qt includes 0020 0021 #include <QMap> 0022 #include <QObject> 0023 #include <QString> 0024 #include <QStringList> 0025 #include <QSqlQuery> 0026 #include <QSqlError> 0027 #include <QRecursiveMutex> 0028 0029 // Local includes 0030 0031 #include "digikam_export.h" 0032 #include "dbengineparameters.h" 0033 #include "dbenginesqlquery.h" 0034 0035 namespace Digikam 0036 { 0037 0038 class DbEngineConfigSettings; 0039 class BdEngineBackendPrivate; 0040 class DbEngineErrorHandler; 0041 0042 class DIGIKAM_EXPORT DbEngineLocking 0043 { 0044 public: 0045 0046 explicit DbEngineLocking(); 0047 0048 public: 0049 0050 QRecursiveMutex mutex; 0051 int lockCount; 0052 }; 0053 0054 // ----------------------------------------------------------------- 0055 0056 class DIGIKAM_EXPORT BdEngineBackend : public QObject 0057 { 0058 Q_OBJECT 0059 0060 public: 0061 0062 enum QueryStateEnum 0063 { 0064 /** 0065 * No errors occurred while executing the query. 0066 */ 0067 NoErrors, 0068 0069 /** 0070 * An SQLError has occurred while executing the query. 0071 */ 0072 SQLError, 0073 0074 /** 0075 * An connection error has occurred while executing the query. 0076 */ 0077 ConnectionError 0078 }; 0079 0080 enum Status 0081 { 0082 /** 0083 * The database is not available, because it has not been 0084 * opened yet or because of an error condition. 0085 */ 0086 Unavailable, 0087 0088 /** 0089 * The database is open. It has not been verified that 0090 * the schema is up to date. 0091 * This status is sufficient for use in a context where it 0092 * can be assumed that the necessary schema check has been carried out 0093 * by a master process. 0094 */ 0095 Open, 0096 0097 /** 0098 * The database is open, and it has been verified that the schema is up to 0099 * date, or the schema has been updated. 0100 */ 0101 OpenSchemaChecked 0102 }; 0103 0104 enum QueryOperationStatus 0105 { 0106 ExecuteNormal, 0107 Wait, 0108 AbortQueries 0109 }; 0110 0111 enum DbType 0112 { 0113 SQLite, 0114 MySQL 0115 }; 0116 0117 public: 0118 0119 /** 0120 * Creates a database backend. The backend name is an arbitrary string that 0121 * shall be unique for this backend object. 0122 * It will be used to create unique connection names per backend and thread. 0123 */ 0124 explicit BdEngineBackend(const QString& backendName, DbEngineLocking* const locking); 0125 BdEngineBackend(const QString& backendName, DbEngineLocking* const locking, BdEngineBackendPrivate& dd); 0126 ~BdEngineBackend() override; 0127 0128 /** 0129 * Checks if the parameters can be used for this database backend. 0130 */ 0131 bool isCompatible(const DbEngineParameters& parameters); 0132 0133 /** 0134 * Check or set WAL mode for SQLite database if enabled in settings. 0135 * @returns true the WAL mode is confirmed enabled. 0136 */ 0137 bool checkOrSetWALMode(); 0138 0139 /** 0140 * Open the database connection. 0141 * @returns true on success 0142 */ 0143 bool open(const DbEngineParameters& parameters); 0144 0145 /** 0146 * Close the database connection. 0147 * Shall only be called from the thread that called open(). 0148 */ 0149 void close(); 0150 0151 public: 0152 0153 class QueryState 0154 { 0155 public: 0156 0157 QueryState() 0158 : value(BdEngineBackend::NoErrors) 0159 { 0160 } 0161 0162 explicit QueryState(const QueryStateEnum value) // krazy:exclude=explicit 0163 : value(value) 0164 { 0165 } 0166 0167 operator QueryStateEnum() const 0168 { 0169 return value; 0170 } 0171 0172 operator bool() const 0173 { 0174 return (value == BdEngineBackend::NoErrors); 0175 } 0176 0177 private: 0178 0179 QueryStateEnum value; 0180 }; 0181 0182 public: 0183 0184 /** 0185 * Returns the current status of the database backend 0186 */ 0187 Status status() const; 0188 0189 bool isOpen() const 0190 { 0191 return (status() > Unavailable); 0192 } 0193 0194 bool isReady() const 0195 { 0196 return (status() == OpenSchemaChecked); 0197 } 0198 0199 /** 0200 * Add a DbEngineErrorHandler. This object must be created in the main thread. 0201 * If a database error occurs, this object can handle problem solving and user interaction. 0202 */ 0203 void setDbEngineErrorHandler(DbEngineErrorHandler* const handler); 0204 0205 /** 0206 * Return config read from XML, 0207 * corresponding to this backend's database type. 0208 */ 0209 DbEngineConfigSettings configElement() const; 0210 0211 /** 0212 * Return the database type. 0213 */ 0214 DbType databaseType() const; 0215 0216 /** 0217 * Returns a database action with name, specified in actionName, 0218 * for the current database. 0219 */ 0220 DbEngineAction getDBAction(const QString& actionName) const; 0221 0222 /** 0223 * Performs the database action on the current database. 0224 * Queries by the specified parameters mustn't have named parameters. 0225 * The result values (if any) are stored within the values list. 0226 */ 0227 QueryState execDBAction(const DbEngineAction& action, QList<QVariant>* const values = nullptr, QVariant* const lastInsertId = nullptr); 0228 0229 QueryState execDBAction(const QString& action, QList<QVariant>* const values = nullptr, QVariant* const lastInsertId = nullptr); 0230 0231 /** 0232 * Performs the database action on the current database. 0233 * Queries by the specified parameters can have named parameters which are 0234 * substituted with values from the bindingMap parameter. 0235 * The result values (if any) are stored within the values list. 0236 */ 0237 QueryState execDBAction(const DbEngineAction& action, const QMap<QString, QVariant>& bindingMap, 0238 QList<QVariant>* const values = nullptr, QVariant* const lastInsertId = nullptr); 0239 0240 QueryState execDBAction(const QString& action, const QMap<QString, QVariant>& bindingMap, 0241 QList<QVariant>* const values = nullptr, QVariant* const lastInsertId = nullptr); 0242 0243 /** 0244 * Performs a special DBAction that is usually needed to "INSERT or UPDATE" entries in a table. 0245 * The corresponding DBAction must contain exactly the named parameters :id, :fieldValueList, 0246 * :fieldList and :valueList. 0247 * You pass the value to be bound to the ":id" field, then two lists of the same size: 0248 * The first containing the field names, the second one 0249 * containing the values as QVariants ready for binding. 0250 */ 0251 QueryState execUpsertDBAction(const DbEngineAction& action, const QVariant& id, 0252 const QStringList& fieldNames, const QList<QVariant>& values); 0253 QueryState execUpsertDBAction(const QString& action, const QVariant& id, 0254 const QStringList& fieldNames, const QList<QVariant>& values); 0255 0256 /** 0257 * Performs the database action on the current database. 0258 * Queries by the specified parameters can have named parameters which are 0259 * substituted with values from the bindingMap parameter. 0260 * The result values (if any) are stored within the values list. 0261 * This method returns the last query, which is used to handle special cases. 0262 */ 0263 QSqlQuery execDBActionQuery(const DbEngineAction& action, const QMap<QString, QVariant>& bindingMap); 0264 0265 QSqlQuery execDBActionQuery(const QString& action, const QMap<QString, QVariant>& bindingMap); 0266 0267 /** 0268 * Executes the SQL statement, and write the returned data into the values list. 0269 * If you are not interested in the returned data, set values to 0. 0270 * Methods are provided for up to four bound values (positional binding), or for a list of bound values. 0271 * If you want the last inserted id (and your query is suitable), set lastInsertId to the address of a QVariant. 0272 * Additionally, methods are provided for prepared statements. 0273 */ 0274 QueryState execSql(const QString& sql, 0275 QList<QVariant>* const values = nullptr, 0276 QVariant* const lastInsertId = nullptr); 0277 QueryState execSql(const QString& sql, 0278 const QVariant& boundValue1, 0279 QList<QVariant>* const values = nullptr, 0280 QVariant* const lastInsertId = nullptr); 0281 QueryState execSql(const QString& sql, 0282 const QVariant& boundValue1, 0283 const QVariant& boundValue2, 0284 QList<QVariant>* const values = nullptr, 0285 QVariant* const lastInsertId = nullptr); 0286 QueryState execSql(const QString& sql, 0287 const QVariant& boundValue1, 0288 const QVariant& boundValue2, 0289 const QVariant& boundValue3, 0290 QList<QVariant>* const values = nullptr, 0291 QVariant* const lastInsertId = nullptr); 0292 QueryState execSql(const QString& sql, 0293 const QVariant& boundValue1, 0294 const QVariant& boundValue2, 0295 const QVariant& boundValue3, 0296 const QVariant& boundValue4, 0297 QList<QVariant>* const values = nullptr, 0298 QVariant* const lastInsertId = nullptr); 0299 QueryState execSql(const QString& sql, 0300 const QList<QVariant>& boundValues, 0301 QList<QVariant>* const values = nullptr, 0302 QVariant* const lastInsertId = nullptr); 0303 0304 QueryState execSql(DbEngineSqlQuery& preparedQuery, 0305 QList<QVariant>* const values = nullptr, 0306 QVariant* const lastInsertId = nullptr); 0307 QueryState execSql(DbEngineSqlQuery& preparedQuery, 0308 const QVariant& boundValue1, 0309 QList<QVariant>* const values = nullptr, 0310 QVariant* const lastInsertId = nullptr); 0311 QueryState execSql(DbEngineSqlQuery& preparedQuery, 0312 const QVariant& boundValue1, 0313 const QVariant& boundValue2, 0314 QList<QVariant>* const values = nullptr, 0315 QVariant* const lastInsertId = nullptr); 0316 QueryState execSql(DbEngineSqlQuery& preparedQuery, 0317 const QVariant& boundValue1, 0318 const QVariant& boundValue2, 0319 const QVariant& boundValue3, 0320 QList<QVariant>* const values = nullptr, 0321 QVariant* const lastInsertId = nullptr); 0322 QueryState execSql(DbEngineSqlQuery& preparedQuery, 0323 const QVariant& boundValue1, 0324 const QVariant& boundValue2, 0325 const QVariant& boundValue3, 0326 const QVariant& boundValue4, 0327 QList<QVariant>* const values = nullptr, 0328 QVariant* const lastInsertId = nullptr); 0329 QueryState execSql(DbEngineSqlQuery& preparedQuery, 0330 const QList<QVariant>& boundValues, 0331 QList<QVariant>* const values = nullptr, 0332 QVariant* const lastInsertId = nullptr); 0333 0334 /** 0335 * Checks if there was a connection error. If so BdEngineBackend::ConnectionError is returned. 0336 * If not, the values are extracted from the query and inserted in the values list, 0337 * the last insertion id is taken from the query 0338 * and BdEngineBackend::NoErrors is returned. 0339 */ 0340 QueryState handleQueryResult(DbEngineSqlQuery& query, 0341 QList<QVariant>* const values, 0342 QVariant* const lastInsertId); 0343 0344 /** 0345 * Method which accepts a map for named binding. 0346 * For special cases it's also possible to add a DbEngineActionType which wraps another 0347 * data object (also lists or maps) which can be used as field entry or as value 0348 * (where it's prepared with positional binding). See more on DbEngineActionType class. 0349 * If the wrapped data object is an instance of list, then the elements are 0350 * separated by comma. 0351 * If the wrapped data object is an instance of map, then the elements are 0352 * inserted in the following way: key1=value1, key2=value2,...,keyN=valueN. 0353 */ 0354 QueryState execSql(const QString& sql, 0355 const QMap<QString, QVariant>& bindingMap, 0356 QList<QVariant>* const values = nullptr, 0357 QVariant* const lastInsertId = nullptr); 0358 /** 0359 * Calls exec on the query, and handles debug output if something went wrong. 0360 * The query is not prepared, which can be fail in certain situations 0361 * (e.g. trigger statements on QMYSQL). 0362 */ 0363 QueryState execDirectSql(const QString& query); 0364 0365 /** 0366 * Calls exec on the query, and handles debug output if something went wrong. 0367 * The query is not prepared, which can be fail in certain situations 0368 * (e.g. trigger statements on QMYSQL). 0369 */ 0370 QueryState execDirectSqlWithResult(const QString& query, 0371 QList<QVariant>* const values = nullptr, 0372 QVariant* const lastInsertId = nullptr); 0373 0374 /** 0375 * Executes the statement and returns the query object. 0376 * Methods are provided for up to four bound values (positional binding), or for a list of bound values. 0377 */ 0378 DbEngineSqlQuery execQuery(const QString& sql); 0379 DbEngineSqlQuery execQuery(const QString& sql, 0380 const QVariant& boundValue1); 0381 DbEngineSqlQuery execQuery(const QString& sql, 0382 const QVariant& boundValue1, 0383 const QVariant& boundValue2); 0384 DbEngineSqlQuery execQuery(const QString& sql, 0385 const QVariant& boundValue1, 0386 const QVariant& boundValue2, 0387 const QVariant& boundValue3); 0388 DbEngineSqlQuery execQuery(const QString& sql, 0389 const QVariant& boundValue1, 0390 const QVariant& boundValue2, 0391 const QVariant& boundValue3, 0392 const QVariant& boundValue4); 0393 DbEngineSqlQuery execQuery(const QString& sql, 0394 const QList<QVariant>& boundValues); 0395 0396 /** 0397 * Binds the values and executes the prepared query. 0398 */ 0399 void execQuery(DbEngineSqlQuery& preparedQuery, 0400 const QVariant& boundValue1); 0401 void execQuery(DbEngineSqlQuery& preparedQuery, 0402 const QVariant& boundValue1, 0403 const QVariant& boundValue2); 0404 void execQuery(DbEngineSqlQuery& preparedQuery, 0405 const QVariant& boundValue1, 0406 const QVariant& boundValue2, 0407 const QVariant& boundValue3); 0408 void execQuery(DbEngineSqlQuery& preparedQuery, 0409 const QVariant& boundValue1, 0410 const QVariant& boundValue2, 0411 const QVariant& boundValue3, 0412 const QVariant& boundValue4); 0413 void execQuery(DbEngineSqlQuery& preparedQuery, 0414 const QList<QVariant>& boundValues); 0415 0416 /** 0417 * Method which accept a hashmap with key, values which are used for named binding 0418 */ 0419 DbEngineSqlQuery execQuery(const QString& sql, 0420 const QMap<QString, QVariant>& bindingMap); 0421 0422 /** 0423 * Calls exec/execBatch on the query, and handles debug output if something went wrong 0424 */ 0425 bool exec(DbEngineSqlQuery& query); 0426 bool execBatch(DbEngineSqlQuery& query); 0427 0428 /** 0429 * Creates a query object prepared with the statement, waiting for bound values 0430 */ 0431 DbEngineSqlQuery prepareQuery(const QString& sql); 0432 /** 0433 * Creates an empty query object waiting for the statement 0434 */ 0435 DbEngineSqlQuery getQuery(); 0436 /** 0437 * Creates a faithful copy of the passed query, with the current db connection. 0438 */ 0439 DbEngineSqlQuery copyQuery(const DbEngineSqlQuery& old); 0440 0441 /** 0442 * Called with a failed query. Handles certain known errors and debug output. 0443 * If it returns true, reexecute the query; if it returns false, return it as failed. 0444 * Pass the number of retries already done for this query to help with some decisions. 0445 */ 0446 bool queryErrorHandling(DbEngineSqlQuery& query, int retries); 0447 bool transactionErrorHandling(const QSqlError& lastError, int retries); 0448 0449 /** 0450 * Called when an attempted connection to the database failed. 0451 * If it returns true, retry; if it returns false, bail out. 0452 * Pass the number of connection retries to help with some decisions. 0453 */ 0454 bool connectionErrorHandling(int retries); 0455 0456 /** 0457 * Reads data of returned result set into a list which is returned. 0458 * The read process is column wise, which means 0459 * all data elements of a row is read, then the resultset is switched 0460 * to the next row. 0461 */ 0462 QList<QVariant> readToList(DbEngineSqlQuery& query); 0463 0464 /** 0465 * Begin a database transaction 0466 */ 0467 BdEngineBackend::QueryState beginTransaction(); 0468 /** 0469 * Commit the current database transaction 0470 */ 0471 BdEngineBackend::QueryState commitTransaction(); 0472 /** 0473 * Rollback the current database transaction 0474 */ 0475 void rollbackTransaction(); 0476 0477 /** 0478 * Returns if the database is in a different thread in a transaction. 0479 * Note that a transaction does not require holding CoreDbAccess. 0480 * Note that this does not give information about other processes 0481 * locking the database. 0482 */ 0483 bool isInTransaction() const; 0484 0485 /** 0486 * Returns a list with the names of tables in the database. 0487 */ 0488 QStringList tables(); 0489 0490 /** 0491 * Returns a description of the last error that occurred on this database. 0492 * Use CoreDbAccess::lastError for errors presented to the user. 0493 * This error will be included in that message. 0494 * It may be empty. 0495 */ 0496 QString lastError(); 0497 0498 /** 0499 * Returns the last error that occurred on this database. 0500 * Use CoreDbAccess::lastError for errors presented to the user. 0501 * It may be empty. 0502 */ 0503 QSqlError lastSQLError(); 0504 0505 /** 0506 * Returns the maximum number of bound parameters allowed per query. 0507 * This value depends on the database engine. 0508 */ 0509 int maximumBoundValues() const; 0510 0511 /** 0512 * Enables or disables FOREIGN_KEY_CHECKS for the database. 0513 * This function depends on the database engine. 0514 */ 0515 void setForeignKeyChecks(bool check); 0516 0517 /* 0518 Qt SQL driver supported features 0519 SQLITE3: 0520 BLOB 0521 Transactions 0522 Unicode 0523 LastInsertId 0524 PreparedQueries 0525 PositionalPlaceholders 0526 SimpleLocking 0527 MySQL: 0528 Transactions (3.?) 0529 QuerySize 0530 BLOB 0531 LastInsertId 0532 Unicode 0533 PreparedQueries (4.1) 0534 PositionalPlaceholders (4.1) 0535 Postgresql: 0536 Transactions 0537 QuerySize 0538 LastInsertId 0539 */ 0540 0541 protected: 0542 0543 BdEngineBackendPrivate* const d_ptr; 0544 0545 private: 0546 0547 Q_DECLARE_PRIVATE(BdEngineBackend) 0548 0549 // Disable 0550 BdEngineBackend(QObject*) = delete; 0551 }; 0552 0553 } // namespace Digikam 0554 0555 Q_DECLARE_METATYPE(QSqlError) 0556 0557 #endif // DIGIKAM_DB_ENGINE_BACKEND_H