Branch data 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 : 10 : GncSqlRecurrenceBackend::GncSqlRecurrenceBackend() :
117 : 10 : 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 : 6 : 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 : 6 : 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 : 10 : GncSqlRecurrenceBackend::create_tables (GncSqlBackend* sql_be)
398 : : {
399 : : gint version;
400 : :
401 : 10 : g_return_if_fail (sql_be != NULL);
402 : :
403 : 10 : version = sql_be->get_table_version( TABLE_NAME);
404 : 10 : if (version == 0)
405 : : {
406 : 5 : (void)sql_be->create_table(TABLE_NAME, TABLE_VERSION, col_table);
407 : : }
408 : 5 : 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 ===================== */
|