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__ */
|