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