Line data Source code
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 : \********************************************************************/
21 : /** @file gnc-recurrence-sql.c
22 : * @brief load and save data to SQL
23 : * @author Copyright (c) 2006-2008 Phil Longstaff <plongstaff@rogers.com>
24 : *
25 : * This file implements the top-level QofBackend API for saving/
26 : * restoring data to/from an SQL db
27 : */
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 :
46 : G_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 :
54 : typedef struct
55 : {
56 : GncSqlBackend* be;
57 : const GncGUID* guid;
58 : Recurrence* pRecurrence;
59 : } recurrence_info_t;
60 :
61 : static gpointer get_obj_guid (gpointer pObject);
62 : static void set_obj_guid (void);
63 : static gint get_recurrence_mult (gpointer pObject);
64 : static void set_recurrence_mult (gpointer pObject, gint value);
65 : static gpointer get_recurrence_period_type (gpointer pObject);
66 : static void set_recurrence_period_type (gpointer pObject, gpointer pValue);
67 : static gpointer get_recurrence_weekend_adjust (gpointer pObject);
68 : static void set_recurrence_weekend_adjust (gpointer pObject, gpointer pValue);
69 : static gpointer get_recurrence_period_start (gpointer pObject);
70 : static void set_recurrence_period_start (gpointer pObject, gpointer pValue);
71 :
72 : static 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
97 : a column other than the primary key */
98 : static 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 */
106 : static 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 :
112 : /**
113 : * Recurrences are neither loadable nor committable. Note that the default
114 : * write() implementation is also a no-op.
115 : */
116 15 : GncSqlRecurrenceBackend::GncSqlRecurrenceBackend() :
117 75 : GncSqlObjectBackend(TABLE_VERSION, GNC_ID_ACCOUNT, TABLE_NAME, col_table) {}
118 :
119 : /* ================================================================= */
120 :
121 : static gpointer
122 8 : get_obj_guid (gpointer pObject)
123 : {
124 8 : recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
125 :
126 8 : g_return_val_if_fail (pObject != NULL, NULL);
127 :
128 8 : return (gpointer)pInfo->guid;
129 : }
130 :
131 : static void
132 4 : set_obj_guid (void)
133 : {
134 : // Nowhere to put the GncGUID
135 4 : }
136 :
137 : static gint
138 4 : get_recurrence_mult (gpointer pObject)
139 : {
140 4 : recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
141 :
142 4 : g_return_val_if_fail (pObject != NULL, 0);
143 4 : g_return_val_if_fail (pInfo->pRecurrence != NULL, 0);
144 :
145 4 : return (gint)pInfo->pRecurrence->mult;
146 : }
147 :
148 : static void
149 4 : set_recurrence_mult (gpointer pObject, gint value)
150 : {
151 4 : recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
152 :
153 4 : g_return_if_fail (pObject != NULL);
154 4 : g_return_if_fail (pInfo->pRecurrence != NULL);
155 :
156 4 : pInfo->pRecurrence->mult = (guint16)value;
157 : }
158 :
159 : static gpointer
160 4 : get_recurrence_period_type (gpointer pObject)
161 : {
162 4 : recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
163 :
164 4 : g_return_val_if_fail (pObject != NULL, NULL);
165 4 : g_return_val_if_fail (pInfo->pRecurrence != NULL, NULL);
166 :
167 4 : return (gpointer)recurrencePeriodTypeToString (
168 8 : recurrenceGetPeriodType (pInfo->pRecurrence));
169 : }
170 :
171 : static void
172 4 : set_recurrence_period_type (gpointer pObject, gpointer pValue)
173 : {
174 4 : recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
175 :
176 4 : g_return_if_fail (pObject != NULL);
177 4 : g_return_if_fail (pInfo->pRecurrence != NULL);
178 4 : g_return_if_fail (pValue != NULL);
179 :
180 4 : pInfo->pRecurrence->ptype = recurrencePeriodTypeFromString ((gchar*)pValue);
181 : }
182 :
183 : static gpointer
184 4 : get_recurrence_weekend_adjust (gpointer pObject)
185 : {
186 4 : recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
187 :
188 4 : g_return_val_if_fail (pObject != NULL, NULL);
189 4 : g_return_val_if_fail (pInfo->pRecurrence != NULL, NULL);
190 :
191 4 : return (gpointer)recurrenceWeekendAdjustToString (
192 8 : recurrenceGetWeekendAdjust (pInfo->pRecurrence));
193 : }
194 :
195 : static void
196 4 : set_recurrence_weekend_adjust (gpointer pObject, gpointer pValue)
197 : {
198 4 : recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
199 :
200 4 : g_return_if_fail (pObject != NULL);
201 4 : g_return_if_fail (pInfo->pRecurrence != NULL);
202 4 : g_return_if_fail (pValue != NULL);
203 :
204 4 : pInfo->pRecurrence->wadj = recurrenceWeekendAdjustFromString ((gchar*)pValue);
205 : }
206 :
207 : static gpointer
208 4 : get_recurrence_period_start (gpointer pObject)
209 : {
210 4 : recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
211 : static GDate date;
212 :
213 4 : g_return_val_if_fail (pObject != NULL, NULL);
214 4 : g_return_val_if_fail (pInfo->pRecurrence != NULL, NULL);
215 :
216 4 : date = recurrenceGetDate (pInfo->pRecurrence);
217 4 : return (gpointer)&date;
218 : }
219 :
220 : static void
221 4 : set_recurrence_period_start (gpointer pObject, gpointer pValue)
222 : {
223 4 : recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
224 4 : GDate* date = (GDate*)pValue;
225 :
226 4 : g_return_if_fail (pObject != NULL);
227 4 : g_return_if_fail (pInfo->pRecurrence != NULL);
228 4 : g_return_if_fail (pValue != NULL);
229 :
230 4 : pInfo->pRecurrence->start = *date;
231 : }
232 :
233 : /* ================================================================= */
234 :
235 : gboolean
236 1 : gnc_sql_recurrence_save (GncSqlBackend* sql_be, const GncGUID* guid,
237 : const Recurrence* r)
238 : {
239 : recurrence_info_t recurrence_info;
240 :
241 1 : g_return_val_if_fail (sql_be != NULL, FALSE);
242 1 : g_return_val_if_fail (guid != NULL, FALSE);
243 1 : g_return_val_if_fail (r != NULL, FALSE);
244 :
245 1 : (void)gnc_sql_recurrence_delete (sql_be, guid);
246 :
247 1 : recurrence_info.be = sql_be;
248 1 : recurrence_info.guid = guid;
249 1 : recurrence_info.pRecurrence = (Recurrence*)r;
250 1 : return sql_be->do_db_operation(OP_DB_INSERT, TABLE_NAME,
251 1 : TABLE_NAME, &recurrence_info, col_table);
252 : }
253 :
254 : void
255 3 : gnc_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 3 : g_return_if_fail (sql_be != NULL);
262 3 : g_return_if_fail (guid != NULL);
263 :
264 3 : (void)gnc_sql_recurrence_delete (sql_be, guid);
265 :
266 3 : recurrence_info.be = sql_be;
267 3 : recurrence_info.guid = guid;
268 9 : for (l = schedule; l != NULL; l = g_list_next (l))
269 : {
270 3 : recurrence_info.pRecurrence = (Recurrence*)l->data;
271 3 : (void)sql_be->do_db_operation(OP_DB_INSERT, TABLE_NAME,
272 : TABLE_NAME, &recurrence_info, col_table);
273 : }
274 : }
275 :
276 : gboolean
277 4 : gnc_sql_recurrence_delete (GncSqlBackend* sql_be, const GncGUID* guid)
278 : {
279 : recurrence_info_t recurrence_info;
280 :
281 4 : g_return_val_if_fail (sql_be != NULL, FALSE);
282 4 : g_return_val_if_fail (guid != NULL, FALSE);
283 :
284 4 : recurrence_info.be = sql_be;
285 4 : recurrence_info.guid = guid;
286 4 : return sql_be->do_db_operation(OP_DB_DELETE, TABLE_NAME,
287 4 : TABLE_NAME, &recurrence_info, guid_col_table);
288 : }
289 :
290 : static void
291 4 : load_recurrence (GncSqlBackend* sql_be, GncSqlRow& row, Recurrence* r)
292 : {
293 : recurrence_info_t recurrence_info;
294 :
295 4 : g_return_if_fail (sql_be != NULL);
296 4 : g_return_if_fail (r != NULL);
297 :
298 4 : recurrence_info.be = sql_be;
299 4 : recurrence_info.pRecurrence = r;
300 :
301 4 : gnc_sql_load_object (sql_be, row, TABLE_NAME, &recurrence_info, col_table);
302 : }
303 :
304 : static GncSqlResultPtr
305 4 : gnc_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 4 : g_return_val_if_fail (sql_be != NULL, NULL);
311 4 : g_return_val_if_fail (guid != NULL, NULL);
312 :
313 4 : (void)guid_to_string_buff (guid, guid_buf);
314 4 : buf = g_strdup_printf ("SELECT * FROM %s WHERE obj_guid='%s'", TABLE_NAME,
315 : guid_buf);
316 8 : auto stmt = sql_be->create_statement_from_sql (buf);
317 4 : g_free (buf);
318 4 : auto result = sql_be->execute_select_statement(stmt);
319 4 : return result;
320 4 : }
321 :
322 : Recurrence*
323 1 : gnc_sql_recurrence_load (GncSqlBackend* sql_be, const GncGUID* guid)
324 : {
325 1 : Recurrence* r = NULL;
326 :
327 1 : g_return_val_if_fail (sql_be != NULL, NULL);
328 1 : g_return_val_if_fail (guid != NULL, NULL);
329 :
330 1 : auto result = gnc_sql_set_recurrences_from_db (sql_be, guid);
331 1 : auto row = result->begin();
332 1 : if (row == nullptr)
333 : {
334 0 : g_warning ("No recurrences found");
335 0 : return r;
336 : }
337 1 : r = g_new0 (Recurrence, 1);
338 1 : g_assert (r != NULL);
339 1 : load_recurrence (sql_be, *(result->begin()), r);
340 :
341 1 : if (++row != nullptr)
342 0 : g_warning ("More than 1 recurrence found: first one used");
343 :
344 1 : return r;
345 1 : }
346 :
347 : GList*
348 3 : gnc_sql_recurrence_load_list (GncSqlBackend* sql_be, const GncGUID* guid)
349 : {
350 3 : GList* list = NULL;
351 :
352 3 : g_return_val_if_fail (sql_be != NULL, NULL);
353 3 : g_return_val_if_fail (guid != NULL, NULL);
354 :
355 3 : auto result = gnc_sql_set_recurrences_from_db (sql_be, guid);
356 9 : for (auto row : *result)
357 : {
358 3 : Recurrence* pRecurrence = g_new0 (Recurrence, 1);
359 3 : g_assert (pRecurrence != NULL);
360 3 : load_recurrence (sql_be, row, pRecurrence);
361 3 : list = g_list_append (list, pRecurrence);
362 6 : }
363 :
364 3 : return list;
365 : }
366 :
367 : /* ================================================================= */
368 : static void
369 0 : upgrade_recurrence_table_1_2 (GncSqlBackend* sql_be)
370 : {
371 : /* Step 1: add field, but allow it to be null */
372 0 : gboolean ok = sql_be->add_columns_to_table(TABLE_NAME,
373 0 : weekend_adjust_col_table);
374 0 : if (!ok)
375 : {
376 0 : PERR ("Unable to add recurrence_weekend_adjust column\n");
377 0 : return;
378 : }
379 :
380 : /* Step 2: insert a default value in the newly created column */
381 : {
382 0 : const gchar* weekend_adj_str = recurrenceWeekendAdjustToString (WEEKEND_ADJ_NONE);
383 0 : std::stringstream sql;
384 : sql << "UPDATE " << TABLE_NAME << " SET " <<
385 0 : weekend_adjust_col_table[0]->name() << "='" <<
386 0 : weekend_adj_str << "'";
387 0 : auto stmt = sql_be->create_statement_from_sql(sql.str());
388 0 : sql_be->execute_nonselect_statement(stmt);
389 0 : }
390 :
391 : /* Step 3: rewrite the table, requiring the weekend_adj column to be non-null */
392 0 : sql_be->upgrade_table(TABLE_NAME, col_table);
393 :
394 : }
395 :
396 : void
397 13 : GncSqlRecurrenceBackend::create_tables (GncSqlBackend* sql_be)
398 : {
399 : gint version;
400 :
401 13 : g_return_if_fail (sql_be != NULL);
402 :
403 26 : version = sql_be->get_table_version( TABLE_NAME);
404 13 : if (version == 0)
405 : {
406 18 : (void)sql_be->create_table(TABLE_NAME, TABLE_VERSION, col_table);
407 : }
408 7 : else if (version < TABLE_VERSION)
409 : {
410 : /* Upgrade:
411 : 1->2: Add recurrence_weekend_adjust field (mandatory, non-null field)
412 : */
413 0 : if (version < m_version)
414 : {
415 0 : upgrade_recurrence_table_1_2 (sql_be);
416 : }
417 0 : sql_be->set_table_version (TABLE_NAME, TABLE_VERSION);
418 0 : PINFO ("Recurrence table upgraded from version %d to version %d\n", version,
419 : TABLE_VERSION);
420 : }
421 : }
422 :
423 : /* ========================== END OF FILE ===================== */
|