Branch data Line data Source code
1 : : /********************************************************************
2 : : * gnc-budget-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-budget-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 <guid.hpp>
29 : : #include <config.h>
30 : :
31 : : #include <glib.h>
32 : :
33 : : #include "qof.h"
34 : : #include "Recurrence.h"
35 : : #include "gnc-budget.h"
36 : :
37 : : #if defined( S_SPLINT_S )
38 : : #include "splint-defs.h"
39 : : #endif
40 : :
41 : : #include "gnc-sql-connection.hpp"
42 : : #include "gnc-sql-backend.hpp"
43 : : #include "gnc-sql-object-backend.hpp"
44 : : #include "gnc-sql-column-table-entry.hpp"
45 : : #include "gnc-budget-sql.h"
46 : : #include "gnc-slots-sql.h"
47 : : #include "gnc-recurrence-sql.h"
48 : :
49 : : #define BUDGET_TABLE "budgets"
50 : : #define TABLE_VERSION 1
51 : : #define AMOUNTS_TABLE "budget_amounts"
52 : : #define AMOUNTS_TABLE_VERSION 1
53 : :
54 : : [[maybe_unused]] static QofLogModule log_module = G_LOG_DOMAIN;
55 : :
56 : : #define BUDGET_MAX_NAME_LEN 2048
57 : : #define BUDGET_MAX_DESCRIPTION_LEN 2048
58 : :
59 : : static const EntryVec col_table
60 : : {
61 : : gnc_sql_make_table_entry<CT_GUID>(
62 : : "guid", 0, COL_NNUL | COL_PKEY, "guid"),
63 : : gnc_sql_make_table_entry<CT_STRING>(
64 : : "name", BUDGET_MAX_NAME_LEN, COL_NNUL, "name"),
65 : : gnc_sql_make_table_entry<CT_STRING>(
66 : : "description", BUDGET_MAX_DESCRIPTION_LEN, 0, "description"),
67 : : gnc_sql_make_table_entry<CT_INT>(
68 : : "num_periods", 0, COL_NNUL, "num_periods"),
69 : : };
70 : :
71 : : static QofInstance* get_budget (gpointer pObj);
72 : : static void set_budget (gpointer pObj, gpointer val);
73 : : static QofInstance* get_account (gpointer pObj);
74 : : static void set_account (gpointer pObj, gpointer val);
75 : : static gint get_period_num (gpointer pObj);
76 : : static void set_period_num (gpointer pObj, gpointer val);
77 : : static gnc_numeric get_amount (gpointer pObj);
78 : : static void set_amount (gpointer pObj, gnc_numeric value);
79 : :
80 : 10 : GncSqlBudgetBackend::GncSqlBudgetBackend() :
81 : : GncSqlObjectBackend(TABLE_VERSION, GNC_ID_BUDGET,
82 : 50 : BUDGET_TABLE, col_table) {}
83 : :
84 : : typedef struct
85 : : {
86 : : GncBudget* budget;
87 : : Account* account;
88 : : guint period_num;
89 : : } budget_amount_info_t;
90 : :
91 : : static const EntryVec budget_amounts_col_table
92 : : {
93 : : gnc_sql_make_table_entry<CT_INT>(
94 : : "id", 0, COL_NNUL | COL_PKEY | COL_AUTOINC),
95 : : gnc_sql_make_table_entry<CT_BUDGETREF>("budget_guid", 0, COL_NNUL,
96 : : (QofAccessFunc)get_budget,
97 : : (QofSetterFunc)set_budget),
98 : : gnc_sql_make_table_entry<CT_ACCOUNTREF>("account_guid", 0, COL_NNUL,
99 : : (QofAccessFunc)get_account,
100 : : (QofSetterFunc)set_account),
101 : : gnc_sql_make_table_entry<CT_INT>("period_num", 0, COL_NNUL,
102 : : (QofAccessFunc)get_period_num,
103 : : (QofSetterFunc)set_period_num),
104 : : gnc_sql_make_table_entry<CT_NUMERIC>("amount", 0, COL_NNUL,
105 : : (QofAccessFunc)get_amount,
106 : : (QofSetterFunc)set_amount),
107 : : };
108 : :
109 : : /* ================================================================= */
110 : : static QofInstance*
111 : 0 : get_budget (gpointer pObj)
112 : : {
113 : 0 : budget_amount_info_t* info = (budget_amount_info_t*)pObj;
114 : :
115 : 0 : g_return_val_if_fail (pObj != NULL, NULL);
116 : :
117 : 0 : return QOF_INSTANCE (info->budget);
118 : : }
119 : :
120 : : static void
121 : 0 : set_budget (gpointer pObj, gpointer val)
122 : : {
123 : 0 : }
124 : :
125 : : static QofInstance*
126 : 0 : get_account (gpointer pObj)
127 : : {
128 : 0 : budget_amount_info_t* info = (budget_amount_info_t*)pObj;
129 : :
130 : 0 : g_return_val_if_fail (pObj != NULL, NULL);
131 : :
132 : 0 : return QOF_INSTANCE (info->account);
133 : : }
134 : :
135 : : static void
136 : 0 : set_account (gpointer pObj, gpointer val)
137 : : {
138 : 0 : budget_amount_info_t* info = (budget_amount_info_t*)pObj;
139 : :
140 : 0 : g_return_if_fail (pObj != NULL);
141 : 0 : g_return_if_fail (val != NULL);
142 : 0 : g_return_if_fail (GNC_IS_ACCOUNT (val));
143 : :
144 : 0 : info->account = GNC_ACCOUNT (val);
145 : : }
146 : :
147 : : static gint
148 : 0 : get_period_num (gpointer pObj)
149 : : {
150 : 0 : budget_amount_info_t* info = (budget_amount_info_t*)pObj;
151 : :
152 : 0 : g_return_val_if_fail (pObj != NULL, 0);
153 : :
154 : 0 : return info->period_num;
155 : : }
156 : :
157 : : static void
158 : 0 : set_period_num (gpointer pObj, gpointer val)
159 : : {
160 : 0 : budget_amount_info_t* info = (budget_amount_info_t*)pObj;
161 : :
162 : 0 : g_return_if_fail (pObj != NULL);
163 : :
164 : 0 : info->period_num = GPOINTER_TO_UINT (val);
165 : : }
166 : :
167 : : static gnc_numeric
168 : 0 : get_amount (gpointer pObj)
169 : : {
170 : 0 : budget_amount_info_t* info = (budget_amount_info_t*)pObj;
171 : :
172 : 0 : g_return_val_if_fail (pObj != NULL, gnc_numeric_zero ());
173 : :
174 : 0 : return gnc_budget_get_account_period_value (info->budget, info->account,
175 : 0 : info->period_num);
176 : : }
177 : :
178 : : static void
179 : 0 : set_amount (gpointer pObj, gnc_numeric value)
180 : : {
181 : 0 : budget_amount_info_t* info = (budget_amount_info_t*)pObj;
182 : :
183 : 0 : g_return_if_fail (pObj != NULL);
184 : :
185 : 0 : gnc_budget_set_account_period_value (info->budget, info->account,
186 : : info->period_num, value);
187 : : }
188 : :
189 : : /*----------------------------------------------------------------*/
190 : : /**
191 : : * Loads the budget amounts for a budget.
192 : : *
193 : : * @param sql_be SQL backend
194 : : * @param budget Budget
195 : : */
196 : : static void
197 : 1 : load_budget_amounts (GncSqlBackend* sql_be, GncBudget* budget)
198 : : {
199 : : gchar guid_buf[GUID_ENCODING_LENGTH + 1];
200 : :
201 : 1 : g_return_if_fail (sql_be != NULL);
202 : 1 : g_return_if_fail (budget != NULL);
203 : :
204 : 1 : (void)guid_to_string_buff (qof_instance_get_guid (QOF_INSTANCE (budget)),
205 : : guid_buf);
206 : 1 : auto sql = g_strdup_printf ("SELECT * FROM %s WHERE budget_guid='%s'",
207 : : AMOUNTS_TABLE, guid_buf);
208 : 2 : auto stmt = sql_be->create_statement_from_sql(sql);
209 : 1 : g_free (sql);
210 : 1 : if (stmt != nullptr)
211 : : {
212 : 1 : auto result = sql_be->execute_select_statement(stmt);
213 : 1 : budget_amount_info_t info = { budget, NULL, 0 };
214 : :
215 : 1 : for (auto row : *result)
216 : 1 : gnc_sql_load_object (sql_be, row, NULL, &info, budget_amounts_col_table);
217 : : }
218 : 1 : }
219 : :
220 : : /**
221 : : * Deletes the budget amounts for a budget.
222 : : *
223 : : * @param sql_be SQL backend
224 : : * @param budget Budget
225 : : */
226 : : static gboolean
227 : 1 : delete_budget_amounts (GncSqlBackend* sql_be, GncBudget* budget)
228 : : {
229 : : gchar guid_buf[GUID_ENCODING_LENGTH + 1];
230 : :
231 : 1 : g_return_val_if_fail (sql_be != NULL, FALSE);
232 : 1 : g_return_val_if_fail (budget != NULL, FALSE);
233 : :
234 : 1 : (void)guid_to_string_buff (qof_instance_get_guid (QOF_INSTANCE (budget)),
235 : : guid_buf);
236 : 1 : std::stringstream sql;
237 : : sql << "DELETE FROM " << AMOUNTS_TABLE << " WHERE budget_guid='"<<
238 : 1 : guid_buf << "'";
239 : 1 : auto stmt = sql_be->create_statement_from_sql(sql.str());
240 : 1 : sql_be->execute_nonselect_statement(stmt);
241 : :
242 : 1 : return true;
243 : 1 : }
244 : :
245 : : /**
246 : : * Saves the budget amounts for a budget.
247 : : *
248 : : * @param sql_be SQL backend
249 : : * @param budget Budget
250 : : */
251 : : static gboolean
252 : 1 : save_budget_amounts (GncSqlBackend* sql_be, GncBudget* budget)
253 : : {
254 : : GList* descendants;
255 : : GList* node;
256 : : budget_amount_info_t info;
257 : : guint num_periods;
258 : 1 : gboolean is_ok = TRUE;
259 : :
260 : 1 : g_return_val_if_fail (sql_be != NULL, FALSE);
261 : 1 : g_return_val_if_fail (budget != NULL, FALSE);
262 : :
263 : : // Delete the amounts, then save
264 : 1 : delete_budget_amounts (sql_be, budget);
265 : :
266 : 1 : info.budget = budget;
267 : 1 : num_periods = gnc_budget_get_num_periods (budget);
268 : 1 : descendants = gnc_account_get_descendants (gnc_book_get_root_account (
269 : : sql_be->book()));
270 : 14 : for (node = descendants; node != NULL && is_ok; node = g_list_next (node))
271 : : {
272 : : guint i;
273 : :
274 : 13 : info.account = GNC_ACCOUNT (node->data);
275 : 169 : for (i = 0; i < num_periods && is_ok; i++)
276 : : {
277 : 156 : if (gnc_budget_is_account_period_value_set (budget, info.account, i))
278 : : {
279 : 0 : info.period_num = i;
280 : 0 : is_ok = sql_be->do_db_operation(OP_DB_INSERT, AMOUNTS_TABLE,
281 : : "", &info,
282 : : budget_amounts_col_table);
283 : : }
284 : : }
285 : : }
286 : 1 : g_list_free (descendants);
287 : :
288 : 1 : return is_ok;
289 : : }
290 : : /*----------------------------------------------------------------*/
291 : : static GncBudget*
292 : 1 : load_single_budget (GncSqlBackend* sql_be, GncSqlRow& row)
293 : : {
294 : : const GncGUID* guid;
295 : 1 : GncBudget* pBudget = NULL;
296 : : Recurrence* r;
297 : :
298 : 1 : g_return_val_if_fail (sql_be != NULL, NULL);
299 : :
300 : 1 : guid = gnc_sql_load_guid (sql_be, row);
301 : 1 : if (guid != NULL)
302 : : {
303 : 1 : pBudget = gnc_budget_lookup (guid, sql_be->book());
304 : : }
305 : 1 : if (pBudget == NULL)
306 : : {
307 : 1 : pBudget = gnc_budget_new (sql_be->book());
308 : : }
309 : :
310 : 1 : gnc_budget_begin_edit (pBudget);
311 : 1 : gnc_sql_load_object (sql_be, row, GNC_ID_BUDGET, pBudget, col_table);
312 : 1 : load_budget_amounts (sql_be, pBudget);
313 : 1 : r = gnc_sql_recurrence_load (sql_be, gnc_budget_get_guid (pBudget));
314 : 1 : if (r != NULL)
315 : : {
316 : 1 : gnc_budget_set_recurrence (pBudget, r);
317 : 1 : g_free (r);
318 : : }
319 : 1 : gnc_budget_commit_edit (pBudget);
320 : :
321 : 1 : return pBudget;
322 : : }
323 : :
324 : : void
325 : 5 : GncSqlBudgetBackend::load_all (GncSqlBackend* sql_be)
326 : : {
327 : 5 : g_return_if_fail (sql_be != NULL);
328 : :
329 : 5 : std::string sql("SELECT * FROM " BUDGET_TABLE);
330 : 5 : auto stmt = sql_be->create_statement_from_sql(sql);
331 : 5 : auto result = sql_be->execute_select_statement(stmt);
332 : 6 : for (auto row : *result)
333 : 6 : load_single_budget (sql_be, row);
334 : :
335 : 5 : std::string pkey(col_table[0]->name());
336 : 5 : sql = "SELECT DISTINCT ";
337 : 5 : sql += pkey + " FROM " BUDGET_TABLE;
338 : 5 : gnc_sql_slots_load_for_sql_subquery (sql_be, sql,
339 : : (BookLookupFn)gnc_budget_lookup);
340 : 5 : }
341 : :
342 : : /* ================================================================= */
343 : : void
344 : 10 : GncSqlBudgetBackend::create_tables (GncSqlBackend* sql_be)
345 : : {
346 : : gint version;
347 : :
348 : 10 : g_return_if_fail (sql_be != NULL);
349 : :
350 : 20 : version = sql_be->get_table_version( BUDGET_TABLE);
351 : 10 : if (version == 0)
352 : : {
353 : 15 : (void)sql_be->create_table(BUDGET_TABLE, TABLE_VERSION, col_table);
354 : : }
355 : :
356 : 20 : version = sql_be->get_table_version( AMOUNTS_TABLE);
357 : 10 : if (version == 0)
358 : : {
359 : 15 : (void)sql_be->create_table(AMOUNTS_TABLE, AMOUNTS_TABLE_VERSION,
360 : : budget_amounts_col_table);
361 : : }
362 : : }
363 : :
364 : : /* ================================================================= */
365 : : bool
366 : 1 : GncSqlBudgetBackend::commit (GncSqlBackend* sql_be, QofInstance* inst)
367 : : {
368 : 1 : GncBudget* pBudget = GNC_BUDGET (inst);
369 : : const GncGUID* guid;
370 : : E_DB_OPERATION op;
371 : : gboolean is_infant;
372 : : gboolean is_ok;
373 : :
374 : 1 : g_return_val_if_fail (sql_be != NULL, FALSE);
375 : 1 : g_return_val_if_fail (inst != NULL, FALSE);
376 : 1 : g_return_val_if_fail (GNC_IS_BUDGET (inst), FALSE);
377 : :
378 : 1 : is_infant = qof_instance_get_infant (inst);
379 : 1 : if (qof_instance_get_destroying (inst))
380 : : {
381 : 0 : op = OP_DB_DELETE;
382 : : }
383 : 1 : else if (sql_be->pristine() || is_infant)
384 : : {
385 : 1 : op = OP_DB_INSERT;
386 : : }
387 : : else
388 : : {
389 : 0 : op = OP_DB_UPDATE;
390 : : }
391 : 1 : is_ok = sql_be->do_db_operation(op, BUDGET_TABLE, GNC_ID_BUDGET, pBudget,
392 : : col_table);
393 : :
394 : : // Now, commit any slots and recurrence
395 : 1 : if (is_ok)
396 : : {
397 : 1 : guid = qof_instance_get_guid (inst);
398 : 1 : if (!qof_instance_get_destroying (inst))
399 : : {
400 : 1 : is_ok = save_budget_amounts (sql_be, pBudget);
401 : 1 : if (is_ok)
402 : : {
403 : 1 : is_ok = gnc_sql_recurrence_save (sql_be, guid,
404 : : gnc_budget_get_recurrence (pBudget));
405 : : }
406 : 1 : if (is_ok)
407 : : {
408 : 1 : is_ok = gnc_sql_slots_save (sql_be, guid, is_infant, inst);
409 : : }
410 : : }
411 : : else
412 : : {
413 : 0 : is_ok = delete_budget_amounts (sql_be, pBudget);
414 : 0 : if (is_ok)
415 : : {
416 : 0 : is_ok = gnc_sql_recurrence_delete (sql_be, guid);
417 : : }
418 : 0 : if (is_ok)
419 : : {
420 : 0 : (void)gnc_sql_slots_delete (sql_be, guid);
421 : : }
422 : : }
423 : : }
424 : :
425 : 1 : return is_ok;
426 : : }
427 : :
428 : : static void
429 : 1 : do_save (QofInstance* inst, gpointer data)
430 : : {
431 : 1 : write_objects_t* s = (write_objects_t*)data;
432 : :
433 : 1 : if (s->is_ok)
434 : : {
435 : 1 : s->is_ok = s->obe->commit (s->be, inst);
436 : : }
437 : 1 : }
438 : :
439 : : bool
440 : 5 : GncSqlBudgetBackend::write (GncSqlBackend* sql_be)
441 : : {
442 : 5 : write_objects_t data;
443 : :
444 : 5 : g_return_val_if_fail (sql_be != NULL, FALSE);
445 : :
446 : 5 : data.be = sql_be;
447 : 5 : data.is_ok = TRUE;
448 : 5 : data.obe = this;
449 : 5 : qof_collection_foreach (qof_book_get_collection (sql_be->book(), GNC_ID_BUDGET),
450 : : (QofInstanceForeachCB)do_save, &data);
451 : :
452 : 5 : return data.is_ok;
453 : : }
454 : :
455 : : /* ================================================================= */
456 : : template<> void
457 : 0 : GncSqlColumnTableEntryImpl<CT_BUDGETREF>::load (const GncSqlBackend* sql_be,
458 : : GncSqlRow& row,
459 : : QofIdTypeConst obj_name,
460 : : gpointer pObject) const noexcept
461 : : {
462 : 0 : load_from_guid_ref(row, obj_name, pObject,
463 : 0 : [sql_be](GncGUID* g){
464 : 0 : return gnc_budget_lookup (g, sql_be->book());
465 : : });
466 : 0 : }
467 : :
468 : : template<> void
469 : 5 : GncSqlColumnTableEntryImpl<CT_BUDGETREF>::add_to_table(ColVec& vec) const noexcept
470 : : {
471 : 5 : add_objectref_guid_to_table(vec);
472 : 5 : }
473 : :
474 : : template<> void
475 : 0 : GncSqlColumnTableEntryImpl<CT_BUDGETREF>::add_to_query(QofIdTypeConst obj_name,
476 : : const gpointer pObject,
477 : : PairVec& vec) const noexcept
478 : : {
479 : 0 : add_objectref_guid_to_query(obj_name, pObject, vec);
480 : 0 : }
481 : :
482 : : /* ========================== END OF FILE ===================== */
|