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: 2026-11-02 18:19:43 Functions: 62.5 % 8 5
Legend: Lines:     hit not hit

            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           47 :     QofBackend() :
     178           94 :         m_percentage{nullptr}, m_fullpath{}, m_last_err{ERR_BACKEND_NO_ERR},
     179           47 :         m_error_msg{} {}
     180              :     QofBackend(const QofBackend&) = delete;
     181              :     QofBackend(const QofBackend&&) = delete;
     182           31 :     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        10283 :     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           39 :     void set_percentage(QofBePercentageFunc pctfn) { m_percentage = pctfn; }
     271           26 :     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