LCOV - code coverage report
Current view: top level - libgnucash/backend/sql - gnc-tax-table-sql.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 73.3 % 172 126
Test Date: 2026-11-02 18:19:43 Functions: 85.7 % 21 18
Legend: Lines:     hit not hit

            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 ===================== */
        

Generated by: LCOV version 2.0-1