LCOV - code coverage report
Current view: top level - libgnucash/engine - qof-backend.hpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 70.0 % 10 7
Test Date: 2025-03-30 14:51:15 Functions: 62.5 % 8 5
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /********************************************************************\
       2                 :             :  * qof-backend.hpp Declare QofBackend class                         *
       3                 :             :  * Copyright 2016 John Ralls <jralls@ceridwen.us>                   *
       4                 :             :  *                                                                  *
       5                 :             :  * This program is free software; you can redistribute it and/or    *
       6                 :             :  * modify it under the terms of the GNU General Public License as   *
       7                 :             :  * published by the Free Software Foundation; either version 2 of   *
       8                 :             :  * the License, or (at your option) any later version.              *
       9                 :             :  *                                                                  *
      10                 :             :  * This program is distributed in the hope that it will be useful,  *
      11                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      12                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      13                 :             :  * GNU General Public License for more details.                     *
      14                 :             :  *                                                                  *
      15                 :             :  * You should have received a copy of the GNU General Public License*
      16                 :             :  * along with this program; if not, contact:                        *
      17                 :             :  *                                                                  *
      18                 :             :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      19                 :             :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      20                 :             :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      21                 :             :  *                                                                  *
      22                 :             : \********************************************************************/
      23                 :             : /** @addtogroup Object
      24                 :             :     @{ */
      25                 :             : /** @addtogroup Object_Private
      26                 :             :     Private interfaces, not meant to be used by applications.
      27                 :             :     @{ */
      28                 :             : /** @name  Backend_Private
      29                 :             :    Pseudo-object defining how the engine can interact with different
      30                 :             :    back-ends (which may be SQL databases, or network interfaces to
      31                 :             :    remote QOF servers. File-io is just one type of backend).
      32                 :             : 
      33                 :             :    The callbacks will be called at the appropriate times during
      34                 :             :    a book session to allow the backend to store the data as needed.
      35                 :             : 
      36                 :             :    @file qofbackend-p.h
      37                 :             :    @brief private api for data storage backend
      38                 :             :    @author Copyright (c) 2000,2001,2004 Linas Vepstas <linas@linas.org>
      39                 :             :    @author Copyright (c) 2005 Neil Williams <linux@codehelp.co.uk>
      40                 :             : @{ */
      41                 :             : 
      42                 :             : #ifndef __QOF_BACKEND_HPP__
      43                 :             : #define __QOF_BACKEND_HPP__
      44                 :             : 
      45                 :             : #include "qofbackend.h"
      46                 :             : #include "qofbook.h"
      47                 :             : #include "qofquery.h"
      48                 :             : #include "qofsession.h"
      49                 :             : #include <gmodule.h>
      50                 :             : 
      51                 :             : #include "qofinstance-p.h"
      52                 :             : #include <string>
      53                 :             : #include <algorithm>
      54                 :             : #include <vector>
      55                 :             : /* NOTE: The following comments were musings by the original developer about how
      56                 :             :  * some additional API might work. The compile/free/run_query functions were
      57                 :             :  * implemented for the DBI backend but never put into use; the rest were never
      58                 :             :  * implemented. They're here as something to consider if we ever decide to
      59                 :             :  * implement them.
      60                 :             :  *
      61                 :             :  * The compile_query() method compiles a QOF query object into
      62                 :             :  *    a backend-specific data structure and returns the compiled
      63                 :             :  *    query. For an SQL backend, the contents of the query object
      64                 :             :  *    need to be turned into a corresponding SQL query statement, and
      65                 :             :  *    sent to the database for evaluation.
      66                 :             :  *
      67                 :             :  * The free_query() method frees the data structure returned from
      68                 :             :  *    compile_query()
      69                 :             :  *
      70                 :             :  * The run_query() callback takes a compiled query (generated by
      71                 :             :  *    compile_query) and runs the query in across the backend,
      72                 :             :  *    inserting the responses into the engine. The database will
      73                 :             :  *    return a set of splits and transactions and this callback needs
      74                 :             :  *    to poke these into the account-group hierarchy held by the query
      75                 :             :  *    object.
      76                 :             :  *
      77                 :             :  *    For a network-communications backend, essentially the same is
      78                 :             :  *    done, except that this routine would convert the query to wire
      79                 :             :  *    protocol, get an answer from the remote server, and push that
      80                 :             :  *    into the account-group object.
      81                 :             :  *
      82                 :             :  *    The returned list of entities can be used to build a local
      83                 :             :  *    cache of the matching data.  This will allow the QOF client to
      84                 :             :  *    continue functioning even when disconnected from the server:
      85                 :             :  *    this is because it will have its local cache of data from which to work.
      86                 :             :  *
      87                 :             :  * The events_pending() routines should return true if there are
      88                 :             :  *    external events which need to be processed to bring the
      89                 :             :  *    engine up to date with the backend.
      90                 :             :  *
      91                 :             :  * The process_events() routine should process any events indicated
      92                 :             :  *    by the events_pending() routine. It should return TRUE if
      93                 :             :  *    the engine was changed while engine events were suspended.
      94                 :             :  *
      95                 :             :  * For support of book partitioning, use special "Book"  begin_edit()
      96                 :             :  *    and commit_edit() QOF_ID types.
      97                 :             :  *
      98                 :             :  *    Call the book begin() at the beginning of a book partitioning.  A
      99                 :             :  *    'partitioning' is the splitting off of a chunk of the current
     100                 :             :  *    book into a second book by means of a query.  Every transaction
     101                 :             :  *    in that query is to be moved ('transferred') to the second book
     102                 :             :  *    from the existing book.  The argument of this routine is a
     103                 :             :  *    pointer to the second book, where the results of the query
     104                 :             :  *    should go.
     105                 :             :  *
     106                 :             :  *    Call the book commit() to complete the book partitioning.
     107                 :             :  *
     108                 :             :  *    After the begin(), there will be a call to run_query(), followed
     109                 :             :  *    probably by a string of object calls, and completed by commit().
     110                 :             :  *    It should be explicitly understood that the results of that
     111                 :             :  *    run_query() precisely constitute the set of objects that are to
     112                 :             :  *    be moved between the initial and the new book. This specification
     113                 :             :  *    can be used by a clever backend to avoid excess data movement
     114                 :             :  *    between the server and the QOF client, as explained below.
     115                 :             :  *
     116                 :             :  *    There are several possible ways in which a backend may choose to
     117                 :             :  *    implement the book splitting process.  A 'file-type' backend may
     118                 :             :  *    choose to ignore this call, and the subsequent query, and simply
     119                 :             :  *    write out the new book to a file when the commit() call is made.
     120                 :             :  *    By that point, the engine will have performed all of the
     121                 :             :  *    nitty-gritty of moving transactions from one book to the other.
     122                 :             :  *
     123                 :             :  *    A 'database-type' backend has several interesting choices.  One
     124                 :             :  *    simple choice is to simply perform the run_query() as it
     125                 :             :  *    normally would, and likewise treat the object edits as usual.
     126                 :             :  *    In this scenario, the commit() is more or less a no-op.
     127                 :             :  *    This implementation has a drawback, however: the run_query() may
     128                 :             :  *    cause the transfer of a <b>huge</b> amount of data between the backend
     129                 :             :  *    and the engine.  For a large dataset, this is quite undesirable.
     130                 :             :  *    In addition, there are risks associated with the loss of network
     131                 :             :  *    connectivity during the transfer; thus a partition might terminate
     132                 :             :  *    half-finished, in some indeterminate state, due to network errors.
     133                 :             :  *    It might be difficult to recover from such errors: the engine does
     134                 :             :  *    not take any special safety measures during the transfer.
     135                 :             :  *
     136                 :             :  *    Thus, for a large database, an alternate implementation
     137                 :             :  *    might be to use the run_query() call as an opportunity to
     138                 :             :  *    transfer entities between the two books in the database,
     139                 :             :  *    and not actually return any new data to the engine.  In
     140                 :             :  *    this scenario, the engine will attempt to transfer those
     141                 :             :  *    entities that it does know about.  It does not, however,
     142                 :             :  *    need to know about all the other entities that also would
     143                 :             :  *    be transferred over.  In this way, a backend could perform
     144                 :             :  *    a mass transfer of entities between books without having
     145                 :             :  *    to actually move much (or any) data to the engine.
     146                 :             :  *
     147                 :             :  * To support configuration options from the frontend, the backend
     148                 :             :  *    can be passed a KvpFrame - according to the allowed options
     149                 :             :  *    for that backend, using load_config(). Configuration can be
     150                 :             :  *    updated at any point - it is up to the frontend to load the
     151                 :             :  *    data in time for whatever the backend needs to do. e.g. an
     152                 :             :  *    option to save a new book in a compressed format need not be
     153                 :             :  *    loaded until the backend is about to save. If the configuration
     154                 :             :  *    is updated by the user, the frontend should call load_config
     155                 :             :  *    again to update the backend.
     156                 :             :  *
     157                 :             :  *    Backends are responsible for ensuring that any supported
     158                 :             :  *    configuration options are initialised to usable values.
     159                 :             :  *    This should be done in the function called from backend_new.
     160                 :             :  */
     161                 :             : 
     162                 :             : 
     163                 :             : typedef enum
     164                 :             : {
     165                 :             :     LOAD_TYPE_INITIAL_LOAD,
     166                 :             :     LOAD_TYPE_LOAD_ALL
     167                 :             : } QofBackendLoadType;
     168                 :             : 
     169                 :             : using GModuleVec = std::vector<GModule*>;
     170                 :             : struct QofBackend
     171                 :             : {
     172                 :             : public:
     173                 :             :     /* For reasons that aren't a bit clear, using the default constructor
     174                 :             :      * sometimes initializes m_last_err incorrectly with Xcode8 and a 32-bit
     175                 :             :      * build unless the initialization is stepped-through in a debugger.
     176                 :             :      */
     177                 :          40 :     QofBackend() :
     178                 :          80 :         m_percentage{nullptr}, m_fullpath{}, m_last_err{ERR_BACKEND_NO_ERR},
     179                 :          40 :         m_error_msg{} {}
     180                 :             :     QofBackend(const QofBackend&) = delete;
     181                 :             :     QofBackend(const QofBackend&&) = delete;
     182                 :          19 :     virtual ~QofBackend() = default;
     183                 :             : /**
     184                 :             :  *    Open the file or connect to the server.
     185                 :             :  *    @param session The QofSession that will control the backend.
     186                 :             :  *    @param new_uri The location of the data store that the backend will use.
     187                 :             :  *    @param mode The session open mode. See qof_session_begin().
     188                 :             :  */
     189                 :             :     virtual void session_begin(QofSession *session, const char* new_uri,
     190                 :             :                                SessionOpenMode mode) = 0;
     191                 :             :     virtual void session_end() = 0;
     192                 :             : /**
     193                 :             :  *    Load the minimal set of application data needed for the application to be
     194                 :             :  *    operable at initial startup.  It is assumed that the application will
     195                 :             :  *    perform a 'run_query()' to obtain any additional data that it needs.  For
     196                 :             :  *    file-based backends, it is acceptable for the backend to return all data
     197                 :             :  *    at load time; for SQL-based backends, it is acceptable for the backend to
     198                 :             :  *    return no data.
     199                 :             :  *
     200                 :             :  *    Thus, for example, the old GnuCash postgres backend returned the account
     201                 :             :  *    tree, all currencies, and the pricedb, as these were needed at startup.
     202                 :             :  *    It did not have to return any transactions whatsoever, as these were
     203                 :             :  *    obtained at a later stage when a user opened a register, resulting in a
     204                 :             :  *    query being sent to the backend. The current DBI backend on the other hand
     205                 :             :  *    loads the entire database into memory.
     206                 :             :  *
     207                 :             :  *    (Its OK to send over entities at this point, but one should
     208                 :             :  *    be careful of the network load; also, its possible that whatever
     209                 :             :  *    is sent is not what the user wanted anyway, which is why its
     210                 :             :  *    better to wait for the query).
     211                 :             :  */
     212                 :             :     virtual void load (QofBook*, QofBackendLoadType) = 0;
     213                 :             : /**
     214                 :             :  *    Called when the engine is about to make a change to a data structure. It
     215                 :             :  *    could provide an advisory lock on data, but no backend does this.
     216                 :             :  */
     217                 :       13969 :     virtual void begin(QofInstance*) {}
     218                 :             : /**
     219                 :             :  *    Commits the changes from the engine to the backend data storage.
     220                 :             :  */
     221                 :             :     virtual void commit (QofInstance*);
     222                 :             : /**
     223                 :             :  *    Revert changes in the engine and unlock the backend.
     224                 :             :  */
     225                 :           0 :     virtual void rollback(QofInstance*) {}
     226                 :             : /**
     227                 :             :  *    Synchronizes the engine contents to the backend.
     228                 :             :  *    This should done by using version numbers (hack alert -- the engine
     229                 :             :  *    does not currently contain version numbers).
     230                 :             :  *    If the engine contents are newer than what is in the backend, the
     231                 :             :  *    data is stored to the backend. If the engine contents are older,
     232                 :             :  *    then the engine contents are updated.
     233                 :             :  *
     234                 :             :  *    Note that this sync operation is only meant to apply to the
     235                 :             :  *    current contents of the engine. This routine is not intended
     236                 :             :  *    to be used to fetch entity data from the backend.
     237                 :             :  *
     238                 :             :  *    File based backends tend to use sync as if it was called dump.
     239                 :             :  *    Data is written out into the backend, overwriting the previous
     240                 :             :  *    data. Database backends should implement a more intelligent
     241                 :             :  *    solution.
     242                 :             :  */
     243                 :             :     virtual void sync(QofBook *) = 0;
     244                 :             : /** Perform a sync in a way that prevents data loss on a DBI backend.
     245                 :             :  */
     246                 :             :     virtual void safe_sync(QofBook *) = 0;
     247                 :             : /**   Extract the chart of accounts from the current database and create a new
     248                 :             :  *   database with it. Implemented only in the XML backend at present.
     249                 :             :  */
     250                 :           0 :     virtual void export_coa(QofBook *) {}
     251                 :             : /** Set the error value only if there isn't already an error already.
     252                 :             :  */
     253                 :             :     void set_error(QofBackendError err);
     254                 :             : /** Retrieve the currently-stored error and clear it.
     255                 :             :  */
     256                 :             :     QofBackendError get_error();
     257                 :             : /** Report if there is an error.
     258                 :             :  */
     259                 :             :     bool check_error();
     260                 :             : /** Set a descriptive message that can be displayed to the user when there's an
     261                 :             :  * error.
     262                 :             :  */
     263                 :             :     void set_message(std::string&&);
     264                 :             : /** Retrieve and clear the stored error message.
     265                 :             :  */
     266                 :             :     const std::string&& get_message();
     267                 :             : /** Store and retrieve a backend-specific function for determining the progress
     268                 :             :  * in completing a long operation, for use with a progress meter.
     269                 :             :  */
     270                 :          35 :     void set_percentage(QofBePercentageFunc pctfn) { m_percentage = pctfn; }
     271                 :          25 :     QofBePercentageFunc get_percentage() { return m_percentage; }
     272                 :             : /** Retrieve the backend's storage URI.
     273                 :             :  */
     274                 :           0 :     const std::string& get_uri() { return m_fullpath; }
     275                 :             : /**
     276                 :             :  * Class methods for dynamically loading the several backends and for freeing
     277                 :             :  * them at shutdown.
     278                 :             :  */
     279                 :             :     static bool register_backend(const char*, const char*);
     280                 :             :     static void release_backends();
     281                 :             : protected:
     282                 :             :     QofBePercentageFunc m_percentage;
     283                 :             :     /** Each backend resolves a fully-qualified file path.
     284                 :             :      * This holds the filepath and communicates it to the frontends.
     285                 :             :      */
     286                 :             :     std::string m_fullpath;
     287                 :             : private:
     288                 :             :     static GModuleVec c_be_registry;
     289                 :             :     QofBackendError m_last_err;
     290                 :             :     std::string m_error_msg;
     291                 :             : };
     292                 :             : 
     293                 :             : /* @} */
     294                 :             : /* @} */
     295                 :             : /* @} */
     296                 :             : 
     297                 :             : #endif /* __QOF_BACKEND_HPP__ */
        

Generated by: LCOV version 2.0-1