GnuCash c935c2f+
Loading...
Searching...
No Matches
gnc-tax-table-sql.cpp
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
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
53static QofLogModule log_module = G_LOG_DOMAIN;
54
55typedef struct
56{
57 GncSqlBackend* be;
58 const GncGUID* guid;
60
61static gpointer get_obj_guid (gpointer pObject, const QofParam* param);
62static void set_obj_guid (gpointer pObject, gpointer pValue);
63static gpointer bt_get_parent (gpointer pObject);
64static void tt_set_parent (gpointer pObject, gpointer pValue);
65static 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
72static 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
85static 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
94static 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
113a column other than the primary key */
114static 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
120GncSqlTaxTableBackend::GncSqlTaxTableBackend() :
121 GncSqlObjectBackend(TT_TABLE_VERSION, GNC_ID_TAXTABLE,
122 TT_TABLE_NAME, tt_col_table) {}
123
125{
126 GncTaxTable* tt;
127 GncGUID guid;
128 bool have_guid;
129};
130
132using TaxTblParentGuidVec = std::vector<TaxTblParentGuidPtr>;
133
134static gpointer
135get_obj_guid (gpointer pObject, const QofParam* param)
136{
137 guid_info_t* pInfo = (guid_info_t*)pObject;
138
139 g_return_val_if_fail (pInfo != NULL, NULL);
140
141 return (gpointer)pInfo->guid;
142}
143
144static void
145set_obj_guid (gpointer pObject, gpointer pValue)
146{
147 // Nowhere to put the GncGUID
148}
149
150static gpointer
151bt_get_parent (gpointer pObject)
152{
153 const GncTaxTable* tt;
154 const GncTaxTable* pParent;
155 const GncGUID* parent_guid;
156
157 g_return_val_if_fail (pObject != NULL, NULL);
158 g_return_val_if_fail (GNC_IS_TAXTABLE (pObject), NULL);
159
160 tt = GNC_TAXTABLE (pObject);
161 pParent = gncTaxTableGetParent (tt);
162 if (pParent == NULL)
163 {
164 parent_guid = NULL;
165 }
166 else
167 {
168 parent_guid = qof_instance_get_guid (QOF_INSTANCE (pParent));
169 }
170
171 return (gpointer)parent_guid;
172}
173
174static void
175tt_set_parent (gpointer data, gpointer value)
176{
177 GncTaxTable* tt;
178 GncTaxTable* parent;
179 QofBook* pBook;
180 GncGUID* guid = (GncGUID*)value;
181
182 g_return_if_fail (data != NULL);
183 g_return_if_fail (GNC_IS_TAXTABLE (data));
184
185 tt = GNC_TAXTABLE (data);
186 pBook = qof_instance_get_book (QOF_INSTANCE (tt));
187 if (guid != NULL)
188 {
189 parent = gncTaxTableLookup (pBook, guid);
190 if (parent != NULL)
191 {
192 gncTaxTableSetParent (tt, parent);
193 gncTaxTableSetChild (parent, tt);
194 }
195 }
196}
197
198static void
199tt_set_parent_guid (gpointer pObject, gpointer pValue)
200{
201 g_return_if_fail (pObject != NULL);
202 g_return_if_fail (pValue != NULL);
203
204 auto s = static_cast<TaxTblParentGuidPtr>(pObject);
205 s->guid = *static_cast<GncGUID*>(pValue);
206 s->have_guid = true;
207}
208
209static void
210load_single_ttentry (GncSqlBackend* sql_be, GncSqlRow& row, GncTaxTable* tt)
211{
212 GncTaxTableEntry* e = gncTaxTableEntryCreate ();
213
214 g_return_if_fail (sql_be != NULL);
215 g_return_if_fail (tt != NULL);
216
217 gnc_sql_load_object (sql_be, row, GNC_ID_TAXTABLE, e, ttentries_col_table);
218 gncTaxTableAddEntry (tt, e);
219}
220
221static void
222load_taxtable_entries (GncSqlBackend* sql_be, GncTaxTable* tt)
223{
224 gchar guid_buf[GUID_ENCODING_LENGTH + 1];
225 GValue value;
226 gchar* buf;
227
228 g_return_if_fail (sql_be != NULL);
229 g_return_if_fail (tt != NULL);
230
231 guid_to_string_buff (qof_instance_get_guid (QOF_INSTANCE (tt)), guid_buf);
232 memset (&value, 0, sizeof (GValue));
233 g_value_init (&value, G_TYPE_STRING);
234 g_value_set_string (&value, guid_buf);
235 buf = g_strdup_printf ("SELECT * FROM %s WHERE taxtable='%s'",
236 TTENTRIES_TABLE_NAME, guid_buf);
237 auto stmt = sql_be->create_statement_from_sql (buf);
238 g_free (buf);
239 auto result = sql_be->execute_select_statement(stmt);
240 for (auto row : *result)
241 load_single_ttentry (sql_be, row, tt);
242}
243
244static void
245load_single_taxtable (GncSqlBackend* sql_be, GncSqlRow& row,
246 TaxTblParentGuidVec& l_tt_needing_parents)
247{
248 const GncGUID* guid;
249 GncTaxTable* tt;
250
251 g_return_if_fail (sql_be != NULL);
252
253 guid = gnc_sql_load_guid (sql_be, row);
254 tt = gncTaxTableLookup (sql_be->book(), guid);
255 if (tt == nullptr)
256 {
257 tt = gncTaxTableCreate (sql_be->book());
258 }
259 gnc_sql_load_object (sql_be, row, GNC_ID_TAXTABLE, tt, tt_col_table);
260 gnc_sql_slots_load (sql_be, QOF_INSTANCE (tt));
261 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 if (gncTaxTableGetParent (tt) == NULL)
268 {
270
271 s.tt = tt;
272 s.have_guid = false;
273 gnc_sql_load_object (sql_be, row, GNC_ID_TAXTABLE, &s,
274 tt_parent_col_table);
275 if (s.have_guid)
276 l_tt_needing_parents.push_back(new TaxTblParentGuid(s));
277
278 }
279
280 qof_instance_mark_clean (QOF_INSTANCE (tt));
281}
282
283void
285{
286 g_return_if_fail (sql_be != NULL);
287
288 /* First time, create the query */
289 std::stringstream sql;
290 sql << "SELECT * FROM " << TT_TABLE_NAME;
291 auto stmt = sql_be->create_statement_from_sql(sql.str());
292 auto result = sql_be->execute_select_statement(stmt);
293 TaxTblParentGuidVec tt_needing_parents;
294
295 for (auto row : *result)
296 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 if (!tt_needing_parents.empty())
304 {
305 bool progress_made = true;
306 std::reverse(tt_needing_parents.begin(),
307 tt_needing_parents.end());
308 auto end = tt_needing_parents.end();
309 while (progress_made)
310 {
311 progress_made = false;
312 end = std::remove_if(tt_needing_parents.begin(), end,
314 {
315 auto pBook = qof_instance_get_book (QOF_INSTANCE (s->tt));
316 auto parent = gncTaxTableLookup (pBook,
317 &s->guid);
318 if (parent != nullptr)
319 {
320 tt_set_parent (s->tt, &s->guid);
321 progress_made = true;
322 delete s;
323 return true;
324 }
325 return false;
326 });
327
328 }
329 }
330}
331
332/* ================================================================= */
333void
335{
336 gint version;
337
338 g_return_if_fail (sql_be != NULL);
339
340 version = sql_be->get_table_version( TT_TABLE_NAME);
341 if (version == 0)
342 {
343 sql_be->create_table(TT_TABLE_NAME, TT_TABLE_VERSION, tt_col_table);
344 }
345 else if (version < m_version)
346 {
347 /* Upgrade 64 bit int handling */
348 sql_be->upgrade_table(TT_TABLE_NAME, tt_col_table);
349 sql_be->set_table_version (TT_TABLE_NAME, TT_TABLE_VERSION);
350 PINFO ("Taxtables table upgraded from version 1 to version %d\n",
351 TT_TABLE_VERSION);
352 }
353
354 version = sql_be->get_table_version( TTENTRIES_TABLE_NAME);
355 if (version == 0)
356 {
357 sql_be->create_table(TTENTRIES_TABLE_NAME, TTENTRIES_TABLE_VERSION,
358 ttentries_col_table);
359 }
360 else if (version < TTENTRIES_TABLE_VERSION)
361 {
362 /* Upgrade 64 bit int handling */
363 sql_be->upgrade_table(TTENTRIES_TABLE_NAME, ttentries_col_table);
364 sql_be->set_table_version (TTENTRIES_TABLE_NAME, TTENTRIES_TABLE_VERSION);
365 PINFO ("Taxtable entries table upgraded from version 1 to version %d\n",
366 TTENTRIES_TABLE_VERSION);
367 }
368}
369
370/* ================================================================= */
371static gboolean
372delete_all_tt_entries (GncSqlBackend* sql_be, const GncGUID* guid)
373{
374 guid_info_t guid_info;
375
376 g_return_val_if_fail (sql_be != NULL, FALSE);
377 g_return_val_if_fail (guid != NULL, FALSE);
378
379 guid_info.be = sql_be;
380 guid_info.guid = guid;
381 return sql_be->do_db_operation(OP_DB_DELETE, TTENTRIES_TABLE_NAME,
382 TTENTRIES_TABLE_NAME, &guid_info, guid_col_table);
383}
384
385static gboolean
386save_tt_entries (GncSqlBackend* sql_be, const GncGUID* guid, GList* entries)
387{
388 GList* entry;
389 gboolean is_ok;
390
391 g_return_val_if_fail (sql_be != NULL, FALSE);
392 g_return_val_if_fail (guid != NULL, FALSE);
393
394 /* First, delete the old entries for this object */
395 is_ok = delete_all_tt_entries (sql_be, guid);
396
397 for (entry = entries; entry != NULL && is_ok; entry = entry->next)
398 {
399 GncTaxTableEntry* e = (GncTaxTableEntry*)entry->data;
400 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 return is_ok;
406}
407
408bool
410{
411 GncTaxTable* tt;
412 const GncGUID* guid;
413 E_DB_OPERATION op;
414 gboolean is_infant;
415 gboolean is_ok;
416
417 g_return_val_if_fail (inst != NULL, FALSE);
418 g_return_val_if_fail (GNC_IS_TAXTABLE (inst), FALSE);
419 g_return_val_if_fail (sql_be != NULL, FALSE);
420
421 tt = GNC_TAXTABLE (inst);
422
423 is_infant = qof_instance_get_infant (inst);
425 {
426 op = OP_DB_DELETE;
427 }
428 else if (sql_be->pristine() || is_infant)
429 {
430 op = OP_DB_INSERT;
431 }
432 else
433 {
434 op = OP_DB_UPDATE;
435 }
436 is_ok = sql_be->do_db_operation(op, TT_TABLE_NAME, GNC_ID_TAXTABLE, tt,
437 tt_col_table);
438
439 if (is_ok)
440 {
441 // Now, commit or delete any slots and tax table entries
442 guid = qof_instance_get_guid (inst);
443 if (!qof_instance_get_destroying (inst))
444 {
445 is_ok = gnc_sql_slots_save (sql_be, guid, is_infant, inst);
446 if (is_ok)
447 {
448 is_ok = save_tt_entries (sql_be, guid, gncTaxTableGetEntries (tt));
449 }
450 }
451 else
452 {
453 is_ok = gnc_sql_slots_delete (sql_be, guid);
454 if (is_ok)
455 {
456 is_ok = delete_all_tt_entries (sql_be, guid);
457 }
458 }
459 }
460
461 return is_ok;
462}
463
464/* ================================================================= */
465static void
466save_next_taxtable (QofInstance* inst, gpointer data)
467{
468 auto s = reinterpret_cast<write_objects_t*>(data);
469
470 if (s->is_ok)
471 {
472 s->commit (inst);
473 }
474}
475
476bool
478{
479 g_return_val_if_fail (sql_be != NULL, FALSE);
480 write_objects_t data{sql_be, true, this};
481
482 qof_object_foreach (GNC_ID_TAXTABLE, sql_be->book(), save_next_taxtable, &data);
483
484 return data.is_ok;
485}
486
487/* ================================================================= */
488template<> void
490 GncSqlRow& row,
491 QofIdTypeConst obj_name,
492 gpointer pObject) const noexcept
493{
494 load_from_guid_ref(row, obj_name, pObject,
495 [sql_be](GncGUID* g){
496 return gncTaxTableLookup(sql_be->book(), g);
497 });
498}
499
500template<> void
502{
503 add_objectref_guid_to_table(vec);
504}
505
506template<> void
508 const gpointer pObject,
509 PairVec& vec) const noexcept
510{
511 add_objectref_guid_to_query(obj_name, pObject, vec);
512}
513
514/* ========================== END OF FILE ===================== */
Main SQL backend structure.
uint_t get_table_version(const std::string &table_name) const noexcept
Returns the version number for a DB table.
bool create_table(const std::string &table_name, const EntryVec &col_table) const noexcept
Creates a table in the database.
GncSqlResultPtr execute_select_statement(const GncSqlStatementPtr &stmt) const noexcept
Executes an SQL SELECT statement and returns the result rows.
void upgrade_table(const std::string &table_name, const EntryVec &col_table) noexcept
Upgrades a table to a new structure.
bool do_db_operation(E_DB_OPERATION op, const char *table_name, QofIdTypeConst obj_name, gpointer pObject, const EntryVec &table) const noexcept
Performs an operation on the database.
bool set_table_version(const std::string &table_name, uint_t version) noexcept
Registers the version for a table.
void add_to_query(QofIdTypeConst obj_name, void *pObject, PairVec &vec) const noexcept override
Add a pair of the table column heading and object's value's string representation to a PairVec; used ...
void add_to_table(ColVec &vec) const noexcept override
Add a GncSqlColumnInfo structure for the column type to a ColVec.
void load(const GncSqlBackend *sql_be, GncSqlRow &row, QofIdTypeConst obj_name, void *pObject) const noexcept override
Load a value into an object from the database row.
Encapsulates per-class table schema with functions to load, create a table, commit a changed front-en...
Row of SQL Query results.
void load_all(GncSqlBackend *) override
Load all objects of m_type in the database into memory.
bool write(GncSqlBackend *) override
Write all objects of m_type_name to the database.
void create_tables(GncSqlBackend *) override
Conditionally create or update a database table from m_col_table.
bool commit(GncSqlBackend *sql_be, QofInstance *inst) override
UPDATE/INSERT a single instance of m_type_name into the database.
load and save accounts data to SQL
load and save tax table data to SQL
Business Entry Interface.
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition qofclass.h:123
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition qofclass.h:130
const gchar * QofIdTypeConst
QofIdTypeConst declaration.
Definition qofid.h:82
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition guid.h:84
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition guid.cpp:208
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
const GncGUID * qof_instance_get_guid(gconstpointer inst)
Return the GncGUID of this instance.
gboolean qof_instance_get_destroying(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object is about to be destroyed.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
void qof_object_foreach(QofIdTypeConst type_name, QofBook *book, QofInstanceForeachCB cb, gpointer user_data)
Invoke the callback 'cb' on every instance ov a particular object type.
The type used to store guids in C.
Definition guid.h:75
modtime is the internal date of the last modtime See libgnucash/engine/TaxTableBillTermImmutability....
QofBook reference.
Definition qofbook-p.hpp:47
Data-passing struct for callbacks to qof_object_foreach() used in GncSqlObjectBackend::write().