GnuCash  5.6-150-g038405b370+
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 
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 GncSqlTaxTableBackend::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 
132 using TaxTblParentGuidVec = std::vector<TaxTblParentGuidPtr>;
133 
134 static gpointer
135 get_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 
144 static void
145 set_obj_guid (gpointer pObject, gpointer pValue)
146 {
147  // Nowhere to put the GncGUID
148 }
149 
150 static gpointer
151 bt_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 
174 static void
175 tt_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 
198 static void
199 tt_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 
209 static void
210 load_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 
221 static void
222 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  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 
244 static void
245 load_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 
283 void
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,
313  [&](TaxTblParentGuidPtr s)
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 /* ================================================================= */
333 void
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 /* ================================================================= */
371 static gboolean
372 delete_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 
385 static gboolean
386 save_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 
408 bool
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);
424  if (qof_instance_get_destroying (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 /* ================================================================= */
465 static void
466 save_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 
476 bool
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 /* ================================================================= */
488 template<> 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 
500 template<> void
502 {
503  add_objectref_guid_to_table(vec);
504 }
505 
506 template<> 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 ===================== */
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 create_table(const std::string &table_name, const EntryVec &col_table) const noexcept
Creates a table in the database.
bool set_table_version(const std::string &table_name, uint_t version) noexcept
Registers the version for a table.
GncSqlResultPtr execute_select_statement(const GncSqlStatementPtr &stmt) const noexcept
Executes an SQL SELECT statement and returns the result rows.
const GncGUID * qof_instance_get_guid(gconstpointer inst)
Return the GncGUID of this instance.
void create_tables(GncSqlBackend *) override
Conditionally create or update a database table from m_col_table.
bool write(GncSqlBackend *) override
Write all objects of m_type_name to the database.
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
const gchar * QofIdTypeConst
QofIdTypeConst declaration.
Definition: qofid.h:82
load and save accounts data to SQL
gboolean qof_instance_get_destroying(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object is about to be destroyed.
gboolean gnc_sql_slots_save(GncSqlBackend *sql_be, const GncGUID *guid, gboolean is_infant, QofInstance *inst)
gnc_sql_slots_save - Saves slots for an object to the db.
void add_to_query(QofIdTypeConst obj_name, void *pObject, PairVec &vec) const noexcept override
Add a pair of the table column heading and object&#39;s value&#39;s string representation to a PairVec; used ...
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:173
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition: qofclass.h:185
bool commit(GncSqlBackend *sql_be, QofInstance *inst) override
UPDATE/INSERT a single instance of m_type_name into the database.
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.
Row of SQL Query results.
void load_all(GncSqlBackend *) override
Load all objects of m_type in the database into memory.
void upgrade_table(const std::string &table_name, const EntryVec &col_table) noexcept
Upgrades a table to a new structure.
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition: guid.h:84
void qof_object_foreach(QofIdTypeConst type_name, QofBook *book, QofInstanceForeachCB cb, gpointer user_data)
Invoke the callback &#39;cb&#39; on every instance ov a particular object type.
Definition: qofobject.cpp:185
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition: qofclass.h:178
Encapsulates per-class table schema with functions to load, create a table, commit a changed front-en...
Data-passing struct for callbacks to qof_object_foreach() used in GncSqlObjectBackend::write().
gboolean gnc_sql_slots_delete(GncSqlBackend *sql_be, const GncGUID *guid)
gnc_sql_slots_delete - Deletes slots for an object from the db.
void gnc_sql_slots_load(GncSqlBackend *sql_be, QofInstance *inst)
Loads slots for an object from the db.
void add_to_table(ColVec &vec) const noexcept override
Add a GncSqlColumnInfo structure for the column type to a ColVec.
Business Entry Interface.
The type used to store guids in C.
Definition: guid.h:75
uint_t get_table_version(const std::string &table_name) const noexcept
Returns the version number for a DB table.
modtime is the internal date of the last modtime See libgnucash/engine/TaxTableBillTermImmutability.txt for an explanation of the following Code that handles refcount, parent, child, invisible and children is identical to that in ::GncBillTerm
Main SQL backend structure.
load and save tax table data to SQL