Branch data Line data Source code
1 : : /********************************************************************\
2 : : * gnc-tax-table-sql.c -- tax table sql implementation *
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 : : \********************************************************************/
22 : :
23 : : /** @file gnc-tax-table-sql.c
24 : : * @brief load and save address data to SQL
25 : : * @author Copyright (c) 2007-2008 Phil Longstaff <plongstaff@rogers.com>
26 : : *
27 : : * This file implements the top-level QofBackend API for saving/
28 : : * restoring data to/from an SQL database
29 : : */
30 : : #include <guid.hpp>
31 : : #include <config.h>
32 : :
33 : : #include <glib.h>
34 : : #include <stdlib.h>
35 : : #include <string.h>
36 : :
37 : : #include "gncEntry.h"
38 : : #include "gncTaxTableP.h"
39 : :
40 : : #include <string>
41 : : #include <vector>
42 : : #include <algorithm>
43 : :
44 : : #include "gnc-sql-connection.hpp"
45 : : #include "gnc-sql-backend.hpp"
46 : : #include "gnc-sql-object-backend.hpp"
47 : : #include "gnc-sql-column-table-entry.hpp"
48 : : #include "gnc-slots-sql.h"
49 : : #include "gnc-tax-table-sql.h"
50 : :
51 : : #define _GNC_MOD_NAME GNC_ID_TAXTABLE
52 : :
53 : : static QofLogModule log_module = G_LOG_DOMAIN;
54 : :
55 : : typedef struct
56 : : {
57 : : GncSqlBackend* be;
58 : : const GncGUID* guid;
59 : : } guid_info_t;
60 : :
61 : : static gpointer get_obj_guid (gpointer pObject, const QofParam* param);
62 : : static void set_obj_guid (gpointer pObject, gpointer pValue);
63 : : static gpointer bt_get_parent (gpointer pObject);
64 : : static void tt_set_parent (gpointer pObject, gpointer pValue);
65 : : static void tt_set_parent_guid (gpointer pObject, gpointer pValue);
66 : :
67 : : #define MAX_NAME_LEN 50
68 : :
69 : : #define TT_TABLE_NAME "taxtables"
70 : : #define TT_TABLE_VERSION 2
71 : :
72 : : static EntryVec tt_col_table
73 : : ({
74 : : gnc_sql_make_table_entry<CT_GUID>("guid", 0, COL_NNUL | COL_PKEY, "guid" ),
75 : : gnc_sql_make_table_entry<CT_STRING>("name", MAX_NAME_LEN, COL_NNUL, "name" ),
76 : : gnc_sql_make_table_entry<CT_INT64>("refcount", 0, COL_NNUL, "ref-count" ),
77 : : gnc_sql_make_table_entry<CT_BOOLEAN>("invisible", 0, COL_NNUL, "invisible" ),
78 : : /* gnc_sql_make_table_entry<CT_TAXTABLEREF>("child", 0, 0,
79 : : get_child, (QofSetterFunc)gncTaxTableSetChild ), */
80 : : gnc_sql_make_table_entry<CT_GUID>("parent", 0, 0,
81 : : (QofAccessFunc)bt_get_parent, tt_set_parent
82 : : ),
83 : : });
84 : :
85 : : static EntryVec tt_parent_col_table
86 : : ({
87 : : gnc_sql_make_table_entry<CT_GUID>("parent", 0, 0, nullptr,
88 : : tt_set_parent_guid ),
89 : : });
90 : :
91 : : #define TTENTRIES_TABLE_NAME "taxtable_entries"
92 : : #define TTENTRIES_TABLE_VERSION 3
93 : :
94 : : static EntryVec ttentries_col_table
95 : : ({
96 : : gnc_sql_make_table_entry<CT_INT>(
97 : : "id", 0, COL_PKEY | COL_NNUL | COL_AUTOINC),
98 : : gnc_sql_make_table_entry<CT_TAXTABLEREF>("taxtable", 0, COL_NNUL,
99 : : (QofAccessFunc)gncTaxTableEntryGetTable,
100 : : set_obj_guid),
101 : : gnc_sql_make_table_entry<CT_ACCOUNTREF>("account", 0, COL_NNUL,
102 : : (QofAccessFunc)gncTaxTableEntryGetAccount,
103 : : (QofSetterFunc)gncTaxTableEntrySetAccount),
104 : : gnc_sql_make_table_entry<CT_NUMERIC>("amount", 0, COL_NNUL,
105 : : (QofAccessFunc)gncTaxTableEntryGetAmount,
106 : : (QofSetterFunc)gncTaxTableEntrySetAmount),
107 : : gnc_sql_make_table_entry<CT_INT>("type", 0, COL_NNUL,
108 : : (QofAccessFunc)gncTaxTableEntryGetType,
109 : : (QofSetterFunc)gncTaxTableEntrySetType),
110 : : });
111 : :
112 : : /* Special column table because we need to be able to access the table by
113 : : a column other than the primary key */
114 : : static EntryVec guid_col_table
115 : : ({
116 : : gnc_sql_make_table_entry<CT_GUID>("taxtable", 0, 0,
117 : : get_obj_guid, set_obj_guid),
118 : : });
119 : :
120 : 10 : GncSqlTaxTableBackend::GncSqlTaxTableBackend() :
121 : : GncSqlObjectBackend(TT_TABLE_VERSION, GNC_ID_TAXTABLE,
122 : 10 : TT_TABLE_NAME, tt_col_table) {}
123 : :
124 : : struct TaxTblParentGuid
125 : : {
126 : : GncTaxTable* tt;
127 : : GncGUID guid;
128 : : bool have_guid;
129 : : };
130 : :
131 : : using TaxTblParentGuidPtr = TaxTblParentGuid*;
132 : : using TaxTblParentGuidVec = std::vector<TaxTblParentGuidPtr>;
133 : :
134 : : static gpointer
135 : 2 : get_obj_guid (gpointer pObject, const QofParam* param)
136 : : {
137 : 2 : guid_info_t* pInfo = (guid_info_t*)pObject;
138 : :
139 : 2 : g_return_val_if_fail (pInfo != NULL, NULL);
140 : :
141 : 2 : return (gpointer)pInfo->guid;
142 : : }
143 : :
144 : : static void
145 : 3 : set_obj_guid (gpointer pObject, gpointer pValue)
146 : : {
147 : : // Nowhere to put the GncGUID
148 : 3 : }
149 : :
150 : : static gpointer
151 : 2 : bt_get_parent (gpointer pObject)
152 : : {
153 : : const GncTaxTable* tt;
154 : : const GncTaxTable* pParent;
155 : : const GncGUID* parent_guid;
156 : :
157 : 2 : g_return_val_if_fail (pObject != NULL, NULL);
158 : 2 : g_return_val_if_fail (GNC_IS_TAXTABLE (pObject), NULL);
159 : :
160 : 2 : tt = GNC_TAXTABLE (pObject);
161 : 2 : pParent = gncTaxTableGetParent (tt);
162 : 2 : if (pParent == NULL)
163 : : {
164 : 2 : parent_guid = NULL;
165 : : }
166 : : else
167 : : {
168 : 0 : parent_guid = qof_instance_get_guid (QOF_INSTANCE (pParent));
169 : : }
170 : :
171 : 2 : return (gpointer)parent_guid;
172 : : }
173 : :
174 : : static void
175 : 0 : tt_set_parent (gpointer data, gpointer value)
176 : : {
177 : : GncTaxTable* tt;
178 : : GncTaxTable* parent;
179 : : QofBook* pBook;
180 : 0 : GncGUID* guid = (GncGUID*)value;
181 : :
182 : 0 : g_return_if_fail (data != NULL);
183 : 0 : g_return_if_fail (GNC_IS_TAXTABLE (data));
184 : :
185 : 0 : tt = GNC_TAXTABLE (data);
186 : 0 : pBook = qof_instance_get_book (QOF_INSTANCE (tt));
187 : 0 : if (guid != NULL)
188 : : {
189 : 0 : parent = gncTaxTableLookup (pBook, guid);
190 : 0 : if (parent != NULL)
191 : : {
192 : 0 : gncTaxTableSetParent (tt, parent);
193 : 0 : gncTaxTableSetChild (parent, tt);
194 : : }
195 : : }
196 : : }
197 : :
198 : : static void
199 : 0 : tt_set_parent_guid (gpointer pObject, gpointer pValue)
200 : : {
201 : 0 : g_return_if_fail (pObject != NULL);
202 : 0 : g_return_if_fail (pValue != NULL);
203 : :
204 : 0 : auto s = static_cast<TaxTblParentGuidPtr>(pObject);
205 : 0 : s->guid = *static_cast<GncGUID*>(pValue);
206 : 0 : s->have_guid = true;
207 : : }
208 : :
209 : : static void
210 : 3 : load_single_ttentry (GncSqlBackend* sql_be, GncSqlRow& row, GncTaxTable* tt)
211 : : {
212 : 3 : GncTaxTableEntry* e = gncTaxTableEntryCreate ();
213 : :
214 : 3 : g_return_if_fail (sql_be != NULL);
215 : 3 : g_return_if_fail (tt != NULL);
216 : :
217 : 3 : gnc_sql_load_object (sql_be, row, GNC_ID_TAXTABLE, e, ttentries_col_table);
218 : 3 : gncTaxTableAddEntry (tt, e);
219 : : }
220 : :
221 : : static void
222 : 2 : load_taxtable_entries (GncSqlBackend* sql_be, GncTaxTable* tt)
223 : : {
224 : : gchar guid_buf[GUID_ENCODING_LENGTH + 1];
225 : : GValue value;
226 : : gchar* buf;
227 : :
228 : 2 : g_return_if_fail (sql_be != NULL);
229 : 2 : g_return_if_fail (tt != NULL);
230 : :
231 : 2 : guid_to_string_buff (qof_instance_get_guid (QOF_INSTANCE (tt)), guid_buf);
232 : 2 : memset (&value, 0, sizeof (GValue));
233 : 2 : g_value_init (&value, G_TYPE_STRING);
234 : 2 : g_value_set_string (&value, guid_buf);
235 : 2 : buf = g_strdup_printf ("SELECT * FROM %s WHERE taxtable='%s'",
236 : : TTENTRIES_TABLE_NAME, guid_buf);
237 : 4 : auto stmt = sql_be->create_statement_from_sql (buf);
238 : 2 : g_free (buf);
239 : 2 : auto result = sql_be->execute_select_statement(stmt);
240 : 5 : for (auto row : *result)
241 : 5 : load_single_ttentry (sql_be, row, tt);
242 : 2 : }
243 : :
244 : : static void
245 : 2 : load_single_taxtable (GncSqlBackend* sql_be, GncSqlRow& row,
246 : : TaxTblParentGuidVec& l_tt_needing_parents)
247 : : {
248 : : const GncGUID* guid;
249 : : GncTaxTable* tt;
250 : :
251 : 2 : g_return_if_fail (sql_be != NULL);
252 : :
253 : 2 : guid = gnc_sql_load_guid (sql_be, row);
254 : 2 : tt = gncTaxTableLookup (sql_be->book(), guid);
255 : 2 : if (tt == nullptr)
256 : : {
257 : 2 : tt = gncTaxTableCreate (sql_be->book());
258 : : }
259 : 2 : gnc_sql_load_object (sql_be, row, GNC_ID_TAXTABLE, tt, tt_col_table);
260 : 2 : gnc_sql_slots_load (sql_be, QOF_INSTANCE (tt));
261 : 2 : load_taxtable_entries (sql_be, tt);
262 : :
263 : : /* If the tax table doesn't have a parent, it might be because it hasn't
264 : : been loaded yet. if so, add this tax table to the list of tax tables
265 : : with no parent, along with the parent GncGUID so that after they are all
266 : : loaded, the parents can be fixed up. */
267 : 2 : if (gncTaxTableGetParent (tt) == NULL)
268 : : {
269 : : TaxTblParentGuid s;
270 : :
271 : 2 : s.tt = tt;
272 : 2 : s.have_guid = false;
273 : 2 : gnc_sql_load_object (sql_be, row, GNC_ID_TAXTABLE, &s,
274 : : tt_parent_col_table);
275 : 2 : if (s.have_guid)
276 : 0 : l_tt_needing_parents.push_back(new TaxTblParentGuid(s));
277 : :
278 : : }
279 : :
280 : 2 : qof_instance_mark_clean (QOF_INSTANCE (tt));
281 : : }
282 : :
283 : : void
284 : 5 : GncSqlTaxTableBackend::load_all (GncSqlBackend* sql_be)
285 : : {
286 : 5 : g_return_if_fail (sql_be != NULL);
287 : :
288 : : /* First time, create the query */
289 : 5 : std::stringstream sql;
290 : 5 : sql << "SELECT * FROM " << TT_TABLE_NAME;
291 : 5 : auto stmt = sql_be->create_statement_from_sql(sql.str());
292 : 5 : auto result = sql_be->execute_select_statement(stmt);
293 : 5 : TaxTblParentGuidVec tt_needing_parents;
294 : :
295 : 7 : for (auto row : *result)
296 : 7 : load_single_taxtable (sql_be, row, tt_needing_parents);
297 : :
298 : : /* While there are items on the list of taxtables needing parents,
299 : : try to see if the parent has now been loaded. Theory says that if
300 : : items are removed from the front and added to the back if the
301 : : parent is still not available, then eventually, the list will
302 : : shrink to size 0. */
303 : 5 : if (!tt_needing_parents.empty())
304 : : {
305 : 0 : bool progress_made = true;
306 : 0 : std::reverse(tt_needing_parents.begin(),
307 : : tt_needing_parents.end());
308 : 0 : auto end = tt_needing_parents.end();
309 : 0 : while (progress_made)
310 : : {
311 : 0 : progress_made = false;
312 : 0 : end = std::remove_if(tt_needing_parents.begin(), end,
313 : 0 : [&](TaxTblParentGuidPtr s)
314 : : {
315 : 0 : auto pBook = qof_instance_get_book (QOF_INSTANCE (s->tt));
316 : 0 : auto parent = gncTaxTableLookup (pBook,
317 : 0 : &s->guid);
318 : 0 : if (parent != nullptr)
319 : : {
320 : 0 : tt_set_parent (s->tt, &s->guid);
321 : 0 : progress_made = true;
322 : 0 : delete s;
323 : 0 : return true;
324 : : }
325 : 0 : return false;
326 : : });
327 : :
328 : : }
329 : : }
330 : 5 : }
331 : :
332 : : /* ================================================================= */
333 : : void
334 : 10 : GncSqlTaxTableBackend::create_tables (GncSqlBackend* sql_be)
335 : : {
336 : : gint version;
337 : :
338 : 10 : g_return_if_fail (sql_be != NULL);
339 : :
340 : 10 : version = sql_be->get_table_version( TT_TABLE_NAME);
341 : 10 : if (version == 0)
342 : : {
343 : 5 : sql_be->create_table(TT_TABLE_NAME, TT_TABLE_VERSION, tt_col_table);
344 : : }
345 : 5 : else if (version < m_version)
346 : : {
347 : : /* Upgrade 64 bit int handling */
348 : 0 : sql_be->upgrade_table(TT_TABLE_NAME, tt_col_table);
349 : 0 : sql_be->set_table_version (TT_TABLE_NAME, TT_TABLE_VERSION);
350 : 0 : PINFO ("Taxtables table upgraded from version 1 to version %d\n",
351 : : TT_TABLE_VERSION);
352 : : }
353 : :
354 : 10 : version = sql_be->get_table_version( TTENTRIES_TABLE_NAME);
355 : 10 : if (version == 0)
356 : : {
357 : 5 : sql_be->create_table(TTENTRIES_TABLE_NAME, TTENTRIES_TABLE_VERSION,
358 : : ttentries_col_table);
359 : : }
360 : 5 : else if (version < TTENTRIES_TABLE_VERSION)
361 : : {
362 : : /* Upgrade 64 bit int handling */
363 : 0 : sql_be->upgrade_table(TTENTRIES_TABLE_NAME, ttentries_col_table);
364 : 0 : sql_be->set_table_version (TTENTRIES_TABLE_NAME, TTENTRIES_TABLE_VERSION);
365 : 0 : PINFO ("Taxtable entries table upgraded from version 1 to version %d\n",
366 : : TTENTRIES_TABLE_VERSION);
367 : : }
368 : : }
369 : :
370 : : /* ================================================================= */
371 : : static gboolean
372 : 2 : delete_all_tt_entries (GncSqlBackend* sql_be, const GncGUID* guid)
373 : : {
374 : : guid_info_t guid_info;
375 : :
376 : 2 : g_return_val_if_fail (sql_be != NULL, FALSE);
377 : 2 : g_return_val_if_fail (guid != NULL, FALSE);
378 : :
379 : 2 : guid_info.be = sql_be;
380 : 2 : guid_info.guid = guid;
381 : 2 : return sql_be->do_db_operation(OP_DB_DELETE, TTENTRIES_TABLE_NAME,
382 : 2 : TTENTRIES_TABLE_NAME, &guid_info, guid_col_table);
383 : : }
384 : :
385 : : static gboolean
386 : 2 : save_tt_entries (GncSqlBackend* sql_be, const GncGUID* guid, GList* entries)
387 : : {
388 : : GList* entry;
389 : : gboolean is_ok;
390 : :
391 : 2 : g_return_val_if_fail (sql_be != NULL, FALSE);
392 : 2 : g_return_val_if_fail (guid != NULL, FALSE);
393 : :
394 : : /* First, delete the old entries for this object */
395 : 2 : is_ok = delete_all_tt_entries (sql_be, guid);
396 : :
397 : 5 : for (entry = entries; entry != NULL && is_ok; entry = entry->next)
398 : : {
399 : 3 : GncTaxTableEntry* e = (GncTaxTableEntry*)entry->data;
400 : 3 : is_ok = sql_be->do_db_operation(OP_DB_INSERT, TTENTRIES_TABLE_NAME,
401 : : GNC_ID_TAXTABLE, e,
402 : : ttentries_col_table);
403 : : }
404 : :
405 : 2 : return is_ok;
406 : : }
407 : :
408 : : bool
409 : 2 : GncSqlTaxTableBackend::commit (GncSqlBackend* sql_be, QofInstance* inst)
410 : : {
411 : : GncTaxTable* tt;
412 : : const GncGUID* guid;
413 : : E_DB_OPERATION op;
414 : : gboolean is_infant;
415 : : gboolean is_ok;
416 : :
417 : 2 : g_return_val_if_fail (inst != NULL, FALSE);
418 : 2 : g_return_val_if_fail (GNC_IS_TAXTABLE (inst), FALSE);
419 : 2 : g_return_val_if_fail (sql_be != NULL, FALSE);
420 : :
421 : 2 : tt = GNC_TAXTABLE (inst);
422 : :
423 : 2 : is_infant = qof_instance_get_infant (inst);
424 : 2 : if (qof_instance_get_destroying (inst))
425 : : {
426 : 0 : op = OP_DB_DELETE;
427 : : }
428 : 2 : else if (sql_be->pristine() || is_infant)
429 : : {
430 : 2 : op = OP_DB_INSERT;
431 : : }
432 : : else
433 : : {
434 : 0 : op = OP_DB_UPDATE;
435 : : }
436 : 2 : is_ok = sql_be->do_db_operation(op, TT_TABLE_NAME, GNC_ID_TAXTABLE, tt,
437 : : tt_col_table);
438 : :
439 : 2 : if (is_ok)
440 : : {
441 : : // Now, commit or delete any slots and tax table entries
442 : 2 : guid = qof_instance_get_guid (inst);
443 : 2 : if (!qof_instance_get_destroying (inst))
444 : : {
445 : 2 : is_ok = gnc_sql_slots_save (sql_be, guid, is_infant, inst);
446 : 2 : if (is_ok)
447 : : {
448 : 2 : is_ok = save_tt_entries (sql_be, guid, gncTaxTableGetEntries (tt));
449 : : }
450 : : }
451 : : else
452 : : {
453 : 0 : is_ok = gnc_sql_slots_delete (sql_be, guid);
454 : 0 : if (is_ok)
455 : : {
456 : 0 : is_ok = delete_all_tt_entries (sql_be, guid);
457 : : }
458 : : }
459 : : }
460 : :
461 : 2 : return is_ok;
462 : : }
463 : :
464 : : /* ================================================================= */
465 : : static void
466 : 2 : save_next_taxtable (QofInstance* inst, gpointer data)
467 : : {
468 : 2 : auto s = reinterpret_cast<write_objects_t*>(data);
469 : :
470 : 2 : if (s->is_ok)
471 : : {
472 : 2 : s->commit (inst);
473 : : }
474 : 2 : }
475 : :
476 : : bool
477 : 5 : GncSqlTaxTableBackend::write (GncSqlBackend* sql_be)
478 : : {
479 : 5 : g_return_val_if_fail (sql_be != NULL, FALSE);
480 : 5 : write_objects_t data{sql_be, true, this};
481 : :
482 : 5 : qof_object_foreach (GNC_ID_TAXTABLE, sql_be->book(), save_next_taxtable, &data);
483 : :
484 : 5 : return data.is_ok;
485 : : }
486 : :
487 : : /* ================================================================= */
488 : : template<> void
489 : 7 : GncSqlColumnTableEntryImpl<CT_TAXTABLEREF>::load (const GncSqlBackend* sql_be,
490 : : GncSqlRow& row,
491 : : QofIdTypeConst obj_name,
492 : : gpointer pObject) const noexcept
493 : : {
494 : 7 : load_from_guid_ref(row, obj_name, pObject,
495 : 3 : [sql_be](GncGUID* g){
496 : 3 : return gncTaxTableLookup(sql_be->book(), g);
497 : : });
498 : 7 : }
499 : :
500 : : template<> void
501 : 25 : GncSqlColumnTableEntryImpl<CT_TAXTABLEREF>::add_to_table(ColVec& vec) const noexcept
502 : : {
503 : 25 : add_objectref_guid_to_table(vec);
504 : 25 : }
505 : :
506 : : template<> void
507 : 7 : GncSqlColumnTableEntryImpl<CT_TAXTABLEREF>::add_to_query(QofIdTypeConst obj_name,
508 : : const gpointer pObject,
509 : : PairVec& vec) const noexcept
510 : : {
511 : 7 : add_objectref_guid_to_query(obj_name, pObject, vec);
512 : 7 : }
513 : :
514 : : /* ========================== END OF FILE ===================== */
|