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 15 : GncSqlTaxTableBackend::GncSqlTaxTableBackend() :
121 30 : GncSqlObjectBackend(TT_TABLE_VERSION, GNC_ID_TAXTABLE,
122 75 : 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 7 : GncSqlTaxTableBackend::load_all (GncSqlBackend* sql_be)
285 : {
286 7 : g_return_if_fail (sql_be != NULL);
287 :
288 : /* First time, create the query */
289 7 : std::stringstream sql;
290 7 : sql << "SELECT * FROM " << TT_TABLE_NAME;
291 7 : auto stmt = sql_be->create_statement_from_sql(sql.str());
292 7 : auto result = sql_be->execute_select_statement(stmt);
293 7 : TaxTblParentGuidVec tt_needing_parents;
294 :
295 11 : for (auto row : *result)
296 9 : 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 7 : 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 7 : }
331 :
332 : /* ================================================================= */
333 : void
334 13 : GncSqlTaxTableBackend::create_tables (GncSqlBackend* sql_be)
335 : {
336 : gint version;
337 :
338 13 : g_return_if_fail (sql_be != NULL);
339 :
340 26 : version = sql_be->get_table_version( TT_TABLE_NAME);
341 13 : if (version == 0)
342 : {
343 18 : sql_be->create_table(TT_TABLE_NAME, TT_TABLE_VERSION, tt_col_table);
344 : }
345 7 : 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 26 : version = sql_be->get_table_version( TTENTRIES_TABLE_NAME);
355 13 : if (version == 0)
356 : {
357 18 : sql_be->create_table(TTENTRIES_TABLE_NAME, TTENTRIES_TABLE_VERSION,
358 : ttentries_col_table);
359 : }
360 7 : 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 6 : GncSqlTaxTableBackend::write (GncSqlBackend* sql_be)
478 : {
479 6 : g_return_val_if_fail (sql_be != NULL, FALSE);
480 6 : write_objects_t data{sql_be, true, this};
481 :
482 6 : qof_object_foreach (GNC_ID_TAXTABLE, sql_be->book(), save_next_taxtable, &data);
483 :
484 6 : 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 30 : GncSqlColumnTableEntryImpl<CT_TAXTABLEREF>::add_to_table(ColVec& vec) const noexcept
502 : {
503 30 : add_objectref_guid_to_table(vec);
504 30 : }
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 ===================== */
|