GnuCash c935c2f+
Loading...
Searching...
No Matches
gnc-recurrence-sql.cpp
1/********************************************************************
2 * gnc-recurrence-sql.c: load and save data to SQL *
3 * *
4 * This program is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU General Public License as *
6 * published by the Free Software Foundation; either version 2 of *
7 * the License, or (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License*
15 * along with this program; if not, contact: *
16 * *
17 * Free Software Foundation Voice: +1-617-542-5942 *
18 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
19 * Boston, MA 02110-1301, USA gnu@gnu.org *
20\********************************************************************/
28#include <glib.h>
29
30#include <config.h>
31
32#include "qof.h"
33#include "gnc-engine.h"
34#include "Recurrence.h"
35
36#if defined( S_SPLINT_S )
37#include "splint-defs.h"
38#endif
39
40#include "gnc-sql-connection.hpp"
41#include "gnc-sql-backend.hpp"
42#include "gnc-sql-object-backend.hpp"
43#include "gnc-sql-column-table-entry.hpp"
44#include "gnc-recurrence-sql.h"
45
46G_GNUC_UNUSED static QofLogModule log_module = G_LOG_DOMAIN;
47
48#define TABLE_NAME "recurrences"
49#define TABLE_VERSION 2
50
51#define BUDGET_MAX_RECURRENCE_PERIOD_TYPE_LEN 2048
52#define BUDGET_MAX_RECURRENCE_WEEKEND_ADJUST_LEN 2048
53
54typedef struct
55{
56 GncSqlBackend* be;
57 const GncGUID* guid;
58 Recurrence* pRecurrence;
60
61static gpointer get_obj_guid (gpointer pObject);
62static void set_obj_guid (void);
63static gint get_recurrence_mult (gpointer pObject);
64static void set_recurrence_mult (gpointer pObject, gint value);
65static gpointer get_recurrence_period_type (gpointer pObject);
66static void set_recurrence_period_type (gpointer pObject, gpointer pValue);
67static gpointer get_recurrence_weekend_adjust (gpointer pObject);
68static void set_recurrence_weekend_adjust (gpointer pObject, gpointer pValue);
69static gpointer get_recurrence_period_start (gpointer pObject);
70static void set_recurrence_period_start (gpointer pObject, gpointer pValue);
71
72static const EntryVec col_table
73({
74 gnc_sql_make_table_entry<CT_INT>(
75 "id", 0, COL_PKEY | COL_NNUL | COL_AUTOINC),
76 gnc_sql_make_table_entry<CT_GUID>("obj_guid", 0, COL_NNUL,
77 (QofAccessFunc)get_obj_guid, (QofSetterFunc)set_obj_guid),
78 gnc_sql_make_table_entry<CT_INT>(
79 "recurrence_mult", 0, COL_NNUL,
80 (QofAccessFunc)get_recurrence_mult, (QofSetterFunc)set_recurrence_mult),
81 gnc_sql_make_table_entry<CT_STRING>(
82 "recurrence_period_type", BUDGET_MAX_RECURRENCE_PERIOD_TYPE_LEN,
83 COL_NNUL,
84 (QofAccessFunc)get_recurrence_period_type, set_recurrence_period_type),
85 gnc_sql_make_table_entry<CT_GDATE>(
86 "recurrence_period_start", 0, COL_NNUL,
87 (QofAccessFunc)get_recurrence_period_start,
88 set_recurrence_period_start),
89 gnc_sql_make_table_entry<CT_STRING>(
90 "recurrence_weekend_adjust", BUDGET_MAX_RECURRENCE_WEEKEND_ADJUST_LEN,
91 COL_NNUL,
92 (QofAccessFunc)get_recurrence_weekend_adjust,
93 set_recurrence_weekend_adjust)
94});
95
96/* Special column table because we need to be able to access the table by
97a column other than the primary key */
98static const EntryVec guid_col_table
99({
100 gnc_sql_make_table_entry<CT_GUID>("obj_guid", 0, 0,
101 (QofAccessFunc)get_obj_guid,
102 (QofSetterFunc)set_obj_guid)
103});
104
105/* Special column table used to upgrade table from version 1 to 2 */
106static const EntryVec weekend_adjust_col_table
107({
108 gnc_sql_make_table_entry<CT_STRING>(
109 "recurrence_weekend_adjust", BUDGET_MAX_RECURRENCE_WEEKEND_ADJUST_LEN, 0)
110});
111
117 GncSqlObjectBackend(TABLE_VERSION, GNC_ID_ACCOUNT, TABLE_NAME, col_table) {}
118
119/* ================================================================= */
120
121static gpointer
122get_obj_guid (gpointer pObject)
123{
124 recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
125
126 g_return_val_if_fail (pObject != NULL, NULL);
127
128 return (gpointer)pInfo->guid;
129}
130
131static void
132set_obj_guid (void)
133{
134 // Nowhere to put the GncGUID
135}
136
137static gint
138get_recurrence_mult (gpointer pObject)
139{
140 recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
141
142 g_return_val_if_fail (pObject != NULL, 0);
143 g_return_val_if_fail (pInfo->pRecurrence != NULL, 0);
144
145 return (gint)pInfo->pRecurrence->mult;
146}
147
148static void
149set_recurrence_mult (gpointer pObject, gint value)
150{
151 recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
152
153 g_return_if_fail (pObject != NULL);
154 g_return_if_fail (pInfo->pRecurrence != NULL);
155
156 pInfo->pRecurrence->mult = (guint16)value;
157}
158
159static gpointer
160get_recurrence_period_type (gpointer pObject)
161{
162 recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
163
164 g_return_val_if_fail (pObject != NULL, NULL);
165 g_return_val_if_fail (pInfo->pRecurrence != NULL, NULL);
166
167 return (gpointer)recurrencePeriodTypeToString (
168 recurrenceGetPeriodType (pInfo->pRecurrence));
169}
170
171static void
172set_recurrence_period_type (gpointer pObject, gpointer pValue)
173{
174 recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
175
176 g_return_if_fail (pObject != NULL);
177 g_return_if_fail (pInfo->pRecurrence != NULL);
178 g_return_if_fail (pValue != NULL);
179
180 pInfo->pRecurrence->ptype = recurrencePeriodTypeFromString ((gchar*)pValue);
181}
182
183static gpointer
184get_recurrence_weekend_adjust (gpointer pObject)
185{
186 recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
187
188 g_return_val_if_fail (pObject != NULL, NULL);
189 g_return_val_if_fail (pInfo->pRecurrence != NULL, NULL);
190
191 return (gpointer)recurrenceWeekendAdjustToString (
192 recurrenceGetWeekendAdjust (pInfo->pRecurrence));
193}
194
195static void
196set_recurrence_weekend_adjust (gpointer pObject, gpointer pValue)
197{
198 recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
199
200 g_return_if_fail (pObject != NULL);
201 g_return_if_fail (pInfo->pRecurrence != NULL);
202 g_return_if_fail (pValue != NULL);
203
204 pInfo->pRecurrence->wadj = recurrenceWeekendAdjustFromString ((gchar*)pValue);
205}
206
207static gpointer
208get_recurrence_period_start (gpointer pObject)
209{
210 recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
211 static GDate date;
212
213 g_return_val_if_fail (pObject != NULL, NULL);
214 g_return_val_if_fail (pInfo->pRecurrence != NULL, NULL);
215
216 date = recurrenceGetDate (pInfo->pRecurrence);
217 return (gpointer)&date;
218}
219
220static void
221set_recurrence_period_start (gpointer pObject, gpointer pValue)
222{
223 recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
224 GDate* date = (GDate*)pValue;
225
226 g_return_if_fail (pObject != NULL);
227 g_return_if_fail (pInfo->pRecurrence != NULL);
228 g_return_if_fail (pValue != NULL);
229
230 pInfo->pRecurrence->start = *date;
231}
232
233/* ================================================================= */
234
235gboolean
236gnc_sql_recurrence_save (GncSqlBackend* sql_be, const GncGUID* guid,
237 const Recurrence* r)
238{
239 recurrence_info_t recurrence_info;
240
241 g_return_val_if_fail (sql_be != NULL, FALSE);
242 g_return_val_if_fail (guid != NULL, FALSE);
243 g_return_val_if_fail (r != NULL, FALSE);
244
245 (void)gnc_sql_recurrence_delete (sql_be, guid);
246
247 recurrence_info.be = sql_be;
248 recurrence_info.guid = guid;
249 recurrence_info.pRecurrence = (Recurrence*)r;
250 return sql_be->do_db_operation(OP_DB_INSERT, TABLE_NAME,
251 TABLE_NAME, &recurrence_info, col_table);
252}
253
254void
255gnc_sql_recurrence_save_list (GncSqlBackend* sql_be, const GncGUID* guid,
256 GList* schedule)
257{
258 recurrence_info_t recurrence_info;
259 GList* l;
260
261 g_return_if_fail (sql_be != NULL);
262 g_return_if_fail (guid != NULL);
263
264 (void)gnc_sql_recurrence_delete (sql_be, guid);
265
266 recurrence_info.be = sql_be;
267 recurrence_info.guid = guid;
268 for (l = schedule; l != NULL; l = g_list_next (l))
269 {
270 recurrence_info.pRecurrence = (Recurrence*)l->data;
271 (void)sql_be->do_db_operation(OP_DB_INSERT, TABLE_NAME,
272 TABLE_NAME, &recurrence_info, col_table);
273 }
274}
275
276gboolean
277gnc_sql_recurrence_delete (GncSqlBackend* sql_be, const GncGUID* guid)
278{
279 recurrence_info_t recurrence_info;
280
281 g_return_val_if_fail (sql_be != NULL, FALSE);
282 g_return_val_if_fail (guid != NULL, FALSE);
283
284 recurrence_info.be = sql_be;
285 recurrence_info.guid = guid;
286 return sql_be->do_db_operation(OP_DB_DELETE, TABLE_NAME,
287 TABLE_NAME, &recurrence_info, guid_col_table);
288}
289
290static void
291load_recurrence (GncSqlBackend* sql_be, GncSqlRow& row, Recurrence* r)
292{
293 recurrence_info_t recurrence_info;
294
295 g_return_if_fail (sql_be != NULL);
296 g_return_if_fail (r != NULL);
297
298 recurrence_info.be = sql_be;
299 recurrence_info.pRecurrence = r;
300
301 gnc_sql_load_object (sql_be, row, TABLE_NAME, &recurrence_info, col_table);
302}
303
304static GncSqlResultPtr
305gnc_sql_set_recurrences_from_db (GncSqlBackend* sql_be, const GncGUID* guid)
306{
307 gchar* buf;
308 gchar guid_buf[GUID_ENCODING_LENGTH + 1];
309
310 g_return_val_if_fail (sql_be != NULL, NULL);
311 g_return_val_if_fail (guid != NULL, NULL);
312
313 (void)guid_to_string_buff (guid, guid_buf);
314 buf = g_strdup_printf ("SELECT * FROM %s WHERE obj_guid='%s'", TABLE_NAME,
315 guid_buf);
316 auto stmt = sql_be->create_statement_from_sql (buf);
317 g_free (buf);
318 auto result = sql_be->execute_select_statement(stmt);
319 return result;
320}
321
323gnc_sql_recurrence_load (GncSqlBackend* sql_be, const GncGUID* guid)
324{
325 Recurrence* r = NULL;
326
327 g_return_val_if_fail (sql_be != NULL, NULL);
328 g_return_val_if_fail (guid != NULL, NULL);
329
330 auto result = gnc_sql_set_recurrences_from_db (sql_be, guid);
331 auto row = result->begin();
332 if (row == nullptr)
333 {
334 g_warning ("No recurrences found");
335 return r;
336 }
337 r = g_new0 (Recurrence, 1);
338 g_assert (r != NULL);
339 load_recurrence (sql_be, *(result->begin()), r);
340
341 if (++row != nullptr)
342 g_warning ("More than 1 recurrence found: first one used");
343
344 return r;
345}
346
347GList*
348gnc_sql_recurrence_load_list (GncSqlBackend* sql_be, const GncGUID* guid)
349{
350 GList* list = NULL;
351
352 g_return_val_if_fail (sql_be != NULL, NULL);
353 g_return_val_if_fail (guid != NULL, NULL);
354
355 auto result = gnc_sql_set_recurrences_from_db (sql_be, guid);
356 for (auto row : *result)
357 {
358 Recurrence* pRecurrence = g_new0 (Recurrence, 1);
359 g_assert (pRecurrence != NULL);
360 load_recurrence (sql_be, row, pRecurrence);
361 list = g_list_append (list, pRecurrence);
362 }
363
364 return list;
365}
366
367/* ================================================================= */
368static void
369upgrade_recurrence_table_1_2 (GncSqlBackend* sql_be)
370{
371 /* Step 1: add field, but allow it to be null */
372 gboolean ok = sql_be->add_columns_to_table(TABLE_NAME,
373 weekend_adjust_col_table);
374 if (!ok)
375 {
376 PERR ("Unable to add recurrence_weekend_adjust column\n");
377 return;
378 }
379
380 /* Step 2: insert a default value in the newly created column */
381 {
382 const gchar* weekend_adj_str = recurrenceWeekendAdjustToString (WEEKEND_ADJ_NONE);
383 std::stringstream sql;
384 sql << "UPDATE " << TABLE_NAME << " SET " <<
385 weekend_adjust_col_table[0]->name() << "='" <<
386 weekend_adj_str << "'";
387 auto stmt = sql_be->create_statement_from_sql(sql.str());
388 sql_be->execute_nonselect_statement(stmt);
389 }
390
391 /* Step 3: rewrite the table, requiring the weekend_adj column to be non-null */
392 sql_be->upgrade_table(TABLE_NAME, col_table);
393
394}
395
396void
398{
399 gint version;
400
401 g_return_if_fail (sql_be != NULL);
402
403 version = sql_be->get_table_version( TABLE_NAME);
404 if (version == 0)
405 {
406 (void)sql_be->create_table(TABLE_NAME, TABLE_VERSION, col_table);
407 }
408 else if (version < TABLE_VERSION)
409 {
410 /* Upgrade:
411 1->2: Add recurrence_weekend_adjust field (mandatory, non-null field)
412 */
413 if (version < m_version)
414 {
415 upgrade_recurrence_table_1_2 (sql_be);
416 }
417 sql_be->set_table_version (TABLE_NAME, TABLE_VERSION);
418 PINFO ("Recurrence table upgraded from version %d to version %d\n", version,
419 TABLE_VERSION);
420 }
421}
422
423/* ========================== END OF FILE ===================== */
Main SQL backend structure.
uint_t get_table_version(const std::string &table_name) const noexcept
Returns the version number for a DB table.
bool add_columns_to_table(const std::string &table_name, const EntryVec &col_table) const noexcept
Adds one or more columns to an existing table.
bool create_table(const std::string &table_name, const EntryVec &col_table) const noexcept
Creates a table in the database.
GncSqlResultPtr execute_select_statement(const GncSqlStatementPtr &stmt) const noexcept
Executes an SQL SELECT statement and returns the result rows.
void upgrade_table(const std::string &table_name, const EntryVec &col_table) noexcept
Upgrades a table to a new structure.
bool do_db_operation(E_DB_OPERATION op, const char *table_name, QofIdTypeConst obj_name, gpointer pObject, const EntryVec &table) const noexcept
Performs an operation on the database.
bool set_table_version(const std::string &table_name, uint_t version) noexcept
Registers the version for a table.
Encapsulates per-class table schema with functions to load, create a table, commit a changed front-en...
GncSqlRecurrenceBackend()
Recurrences are neither loadable nor committable.
void create_tables(GncSqlBackend *) override
Conditionally create or update a database table from m_col_table.
Pure virtual class to iterate over a query result set.
Row of SQL Query results.
All type declarations for the whole Gnucash engine.
load and save accounts data to SQL
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition qofclass.h:123
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition qofclass.h:130
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition guid.h:84
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition guid.cpp:208
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define PERR(format, args...)
Log a serious error.
Definition qoflog.h:244
The type used to store guids in C.
Definition guid.h:75