LCOV - code coverage report
Current view: top level - libgnucash/backend/sql - gnc-budget-sql.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 69.1 % 162 112
Test Date: 2026-11-02 18:19:43 Functions: 50.0 % 22 11
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /********************************************************************
       2              :  * gnc-budget-sql.c: load and save data to SQL                      *
       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              : /** @file gnc-budget-sql.c
      22              :  *  @brief load and save data to SQL
      23              :  *  @author Copyright (c) 2006-2008 Phil Longstaff <plongstaff@rogers.com>
      24              :  *
      25              :  * This file implements the top-level QofBackend API for saving/
      26              :  * restoring data to/from an SQL db
      27              :  */
      28              : #include <guid.hpp>
      29              : #include <config.h>
      30              : 
      31              : #include <glib.h>
      32              : 
      33              : #include "qof.h"
      34              : #include "Recurrence.h"
      35              : #include "gnc-budget.h"
      36              : 
      37              : #if defined( S_SPLINT_S )
      38              : #include "splint-defs.h"
      39              : #endif
      40              : 
      41              : #include "gnc-sql-connection.hpp"
      42              : #include "gnc-sql-backend.hpp"
      43              : #include "gnc-sql-object-backend.hpp"
      44              : #include "gnc-sql-column-table-entry.hpp"
      45              : #include "gnc-budget-sql.h"
      46              : #include "gnc-slots-sql.h"
      47              : #include "gnc-recurrence-sql.h"
      48              : 
      49              : #define BUDGET_TABLE "budgets"
      50              : #define TABLE_VERSION 1
      51              : #define AMOUNTS_TABLE "budget_amounts"
      52              : #define AMOUNTS_TABLE_VERSION 1
      53              : 
      54              : [[maybe_unused]] static QofLogModule log_module = G_LOG_DOMAIN;
      55              : 
      56              : #define BUDGET_MAX_NAME_LEN 2048
      57              : #define BUDGET_MAX_DESCRIPTION_LEN 2048
      58              : 
      59              : static const EntryVec col_table
      60              : {
      61              :     gnc_sql_make_table_entry<CT_GUID>(
      62              :         "guid", 0, COL_NNUL | COL_PKEY, "guid"),
      63              :     gnc_sql_make_table_entry<CT_STRING>(
      64              :         "name", BUDGET_MAX_NAME_LEN, COL_NNUL, "name"),
      65              :     gnc_sql_make_table_entry<CT_STRING>(
      66              :         "description", BUDGET_MAX_DESCRIPTION_LEN, 0, "description"),
      67              :     gnc_sql_make_table_entry<CT_INT>(
      68              :         "num_periods", 0, COL_NNUL, "num_periods"),
      69              : };
      70              : 
      71              : static  QofInstance* get_budget (gpointer pObj);
      72              : static void set_budget (gpointer pObj, gpointer val);
      73              : static  QofInstance* get_account (gpointer pObj);
      74              : static void set_account (gpointer pObj, gpointer val);
      75              : static gint get_period_num (gpointer pObj);
      76              : static void set_period_num (gpointer pObj, gpointer val);
      77              : static gnc_numeric get_amount (gpointer pObj);
      78              : static void set_amount (gpointer pObj, gnc_numeric value);
      79              : 
      80           15 : GncSqlBudgetBackend::GncSqlBudgetBackend() :
      81           30 :     GncSqlObjectBackend(TABLE_VERSION, GNC_ID_BUDGET,
      82           75 :                         BUDGET_TABLE, col_table) {}
      83              : 
      84              : typedef struct
      85              : {
      86              :     GncBudget* budget;
      87              :     Account* account;
      88              :     guint period_num;
      89              : } budget_amount_info_t;
      90              : 
      91              : static const EntryVec budget_amounts_col_table
      92              : {
      93              :     gnc_sql_make_table_entry<CT_INT>(
      94              :         "id", 0, COL_NNUL | COL_PKEY | COL_AUTOINC),
      95              :     gnc_sql_make_table_entry<CT_BUDGETREF>("budget_guid",  0, COL_NNUL,
      96              :                                            (QofAccessFunc)get_budget,
      97              :                                            (QofSetterFunc)set_budget),
      98              :     gnc_sql_make_table_entry<CT_ACCOUNTREF>("account_guid", 0, COL_NNUL,
      99              :                                             (QofAccessFunc)get_account,
     100              :                                             (QofSetterFunc)set_account),
     101              :     gnc_sql_make_table_entry<CT_INT>("period_num", 0, COL_NNUL,
     102              :                                      (QofAccessFunc)get_period_num,
     103              :                                      (QofSetterFunc)set_period_num),
     104              :     gnc_sql_make_table_entry<CT_NUMERIC>("amount", 0, COL_NNUL,
     105              :                                          (QofAccessFunc)get_amount,
     106              :                                          (QofSetterFunc)set_amount),
     107              : };
     108              : 
     109              : /* ================================================================= */
     110              : static QofInstance*
     111            0 : get_budget (gpointer pObj)
     112              : {
     113            0 :     budget_amount_info_t* info = (budget_amount_info_t*)pObj;
     114              : 
     115            0 :     g_return_val_if_fail (pObj != NULL, NULL);
     116              : 
     117            0 :     return QOF_INSTANCE (info->budget);
     118              : }
     119              : 
     120              : static void
     121            0 : set_budget (gpointer pObj, gpointer val)
     122              : {
     123            0 : }
     124              : 
     125              : static  QofInstance*
     126            0 : get_account (gpointer pObj)
     127              : {
     128            0 :     budget_amount_info_t* info = (budget_amount_info_t*)pObj;
     129              : 
     130            0 :     g_return_val_if_fail (pObj != NULL, NULL);
     131              : 
     132            0 :     return QOF_INSTANCE (info->account);
     133              : }
     134              : 
     135              : static void
     136            0 : set_account (gpointer pObj, gpointer val)
     137              : {
     138            0 :     budget_amount_info_t* info = (budget_amount_info_t*)pObj;
     139              : 
     140            0 :     g_return_if_fail (pObj != NULL);
     141            0 :     g_return_if_fail (val != NULL);
     142            0 :     g_return_if_fail (GNC_IS_ACCOUNT (val));
     143              : 
     144            0 :     info->account = GNC_ACCOUNT (val);
     145              : }
     146              : 
     147              : static gint
     148            0 : get_period_num (gpointer pObj)
     149              : {
     150            0 :     budget_amount_info_t* info = (budget_amount_info_t*)pObj;
     151              : 
     152            0 :     g_return_val_if_fail (pObj != NULL, 0);
     153              : 
     154            0 :     return info->period_num;
     155              : }
     156              : 
     157              : static void
     158            0 : set_period_num (gpointer pObj, gpointer val)
     159              : {
     160            0 :     budget_amount_info_t* info = (budget_amount_info_t*)pObj;
     161              : 
     162            0 :     g_return_if_fail (pObj != NULL);
     163              : 
     164            0 :     info->period_num = GPOINTER_TO_UINT (val);
     165              : }
     166              : 
     167              : static gnc_numeric
     168            0 : get_amount (gpointer pObj)
     169              : {
     170            0 :     budget_amount_info_t* info = (budget_amount_info_t*)pObj;
     171              : 
     172            0 :     g_return_val_if_fail (pObj != NULL, gnc_numeric_zero ());
     173              : 
     174            0 :     return gnc_budget_get_account_period_value (info->budget, info->account,
     175            0 :                                                 info->period_num);
     176              : }
     177              : 
     178              : static void
     179            0 : set_amount (gpointer pObj, gnc_numeric value)
     180              : {
     181            0 :     budget_amount_info_t* info = (budget_amount_info_t*)pObj;
     182              : 
     183            0 :     g_return_if_fail (pObj != NULL);
     184              : 
     185            0 :     gnc_budget_set_account_period_value (info->budget, info->account,
     186              :                                          info->period_num, value);
     187              : }
     188              : 
     189              : /*----------------------------------------------------------------*/
     190              : /**
     191              :  * Loads the budget amounts for a budget.
     192              :  *
     193              :  * @param sql_be SQL backend
     194              :  * @param budget Budget
     195              :  */
     196              : static void
     197            1 : load_budget_amounts (GncSqlBackend* sql_be, GncBudget* budget)
     198              : {
     199              :     gchar guid_buf[GUID_ENCODING_LENGTH + 1];
     200              : 
     201            1 :     g_return_if_fail (sql_be != NULL);
     202            1 :     g_return_if_fail (budget != NULL);
     203              : 
     204            1 :     (void)guid_to_string_buff (qof_instance_get_guid (QOF_INSTANCE (budget)),
     205              :                                guid_buf);
     206            1 :     auto sql = g_strdup_printf ("SELECT * FROM %s WHERE budget_guid='%s'",
     207              :                                 AMOUNTS_TABLE, guid_buf);
     208            2 :     auto stmt = sql_be->create_statement_from_sql(sql);
     209            1 :     g_free (sql);
     210            1 :     if (stmt != nullptr)
     211              :     {
     212            1 :         auto result = sql_be->execute_select_statement(stmt);
     213            1 :         budget_amount_info_t info = { budget, NULL, 0 };
     214              : 
     215            1 :         for (auto row : *result)
     216            1 :             gnc_sql_load_object (sql_be, row, NULL, &info, budget_amounts_col_table);
     217              :     }
     218            1 : }
     219              : 
     220              : /**
     221              :  * Deletes the budget amounts for a budget.
     222              :  *
     223              :  * @param sql_be SQL backend
     224              :  * @param budget Budget
     225              :  */
     226              : static gboolean
     227            1 : delete_budget_amounts (GncSqlBackend* sql_be, GncBudget* budget)
     228              : {
     229              :     gchar guid_buf[GUID_ENCODING_LENGTH + 1];
     230              : 
     231            1 :     g_return_val_if_fail (sql_be != NULL, FALSE);
     232            1 :     g_return_val_if_fail (budget != NULL, FALSE);
     233              : 
     234            1 :     (void)guid_to_string_buff (qof_instance_get_guid (QOF_INSTANCE (budget)),
     235              :                                guid_buf);
     236            1 :     std::stringstream sql;
     237              :     sql << "DELETE FROM " << AMOUNTS_TABLE << " WHERE budget_guid='"<<
     238            1 :         guid_buf << "'";
     239            1 :     auto stmt = sql_be->create_statement_from_sql(sql.str());
     240            1 :     sql_be->execute_nonselect_statement(stmt);
     241              : 
     242            1 :     return true;
     243            1 : }
     244              : 
     245              : /**
     246              :  * Saves the budget amounts for a budget.
     247              :  *
     248              :  * @param sql_be SQL backend
     249              :  * @param budget Budget
     250              :  */
     251              : static gboolean
     252            1 : save_budget_amounts (GncSqlBackend* sql_be, GncBudget* budget)
     253              : {
     254              :     GList* descendants;
     255              :     GList* node;
     256              :     budget_amount_info_t info;
     257              :     guint num_periods;
     258            1 :     gboolean is_ok = TRUE;
     259              : 
     260            1 :     g_return_val_if_fail (sql_be != NULL, FALSE);
     261            1 :     g_return_val_if_fail (budget != NULL, FALSE);
     262              : 
     263              :     // Delete the amounts, then save
     264            1 :     delete_budget_amounts (sql_be, budget);
     265              : 
     266            1 :     info.budget = budget;
     267            1 :     num_periods = gnc_budget_get_num_periods (budget);
     268            1 :     descendants = gnc_account_get_descendants (gnc_book_get_root_account (
     269              :                                                    sql_be->book()));
     270           27 :     for (node = descendants; node != NULL && is_ok; node = g_list_next (node))
     271              :     {
     272              :         guint i;
     273              : 
     274           13 :         info.account = GNC_ACCOUNT (node->data);
     275          169 :         for (i = 0; i < num_periods && is_ok; i++)
     276              :         {
     277          156 :             if (gnc_budget_is_account_period_value_set (budget, info.account, i))
     278              :             {
     279            0 :                 info.period_num = i;
     280            0 :                 is_ok = sql_be->do_db_operation(OP_DB_INSERT, AMOUNTS_TABLE,
     281              :                                                 "", &info,
     282              :                                                  budget_amounts_col_table);
     283              :             }
     284              :         }
     285              :     }
     286            1 :     g_list_free (descendants);
     287              : 
     288            1 :     return is_ok;
     289              : }
     290              : /*----------------------------------------------------------------*/
     291              : static  GncBudget*
     292            1 : load_single_budget (GncSqlBackend* sql_be, GncSqlRow& row)
     293              : {
     294              :     const GncGUID* guid;
     295            1 :     GncBudget* pBudget = NULL;
     296              :     Recurrence* r;
     297              : 
     298            1 :     g_return_val_if_fail (sql_be != NULL, NULL);
     299              : 
     300            1 :     guid = gnc_sql_load_guid (sql_be, row);
     301            1 :     if (guid != NULL)
     302              :     {
     303            1 :         pBudget = gnc_budget_lookup (guid, sql_be->book());
     304              :     }
     305            1 :     if (pBudget == NULL)
     306              :     {
     307            1 :         pBudget = gnc_budget_new (sql_be->book());
     308              :     }
     309              : 
     310            1 :     gnc_budget_begin_edit (pBudget);
     311            1 :     gnc_sql_load_object (sql_be, row, GNC_ID_BUDGET, pBudget, col_table);
     312            1 :     load_budget_amounts (sql_be, pBudget);
     313            1 :     r = gnc_sql_recurrence_load (sql_be, gnc_budget_get_guid (pBudget));
     314            1 :     if (r != NULL)
     315              :     {
     316            1 :         gnc_budget_set_recurrence (pBudget, r);
     317            1 :         g_free (r);
     318              :     }
     319            1 :     gnc_budget_commit_edit (pBudget);
     320              : 
     321            1 :     return pBudget;
     322              : }
     323              : 
     324              : void
     325            7 : GncSqlBudgetBackend::load_all (GncSqlBackend* sql_be)
     326              : {
     327            7 :     g_return_if_fail (sql_be != NULL);
     328              : 
     329            7 :     std::string sql("SELECT * FROM " BUDGET_TABLE);
     330            7 :     auto stmt = sql_be->create_statement_from_sql(sql);
     331            7 :     auto result = sql_be->execute_select_statement(stmt);
     332            8 :     for (auto row : *result)
     333            8 :         load_single_budget (sql_be, row);
     334              : 
     335            7 :     std::string pkey(col_table[0]->name());
     336            7 :     sql = "SELECT DISTINCT ";
     337            7 :     sql += pkey + " FROM " BUDGET_TABLE;
     338            7 :     gnc_sql_slots_load_for_sql_subquery (sql_be, sql,
     339              :                                          (BookLookupFn)gnc_budget_lookup);
     340            7 : }
     341              : 
     342              : /* ================================================================= */
     343              : void
     344           13 : GncSqlBudgetBackend::create_tables (GncSqlBackend* sql_be)
     345              : {
     346              :     gint version;
     347              : 
     348           13 :     g_return_if_fail (sql_be != NULL);
     349              : 
     350           26 :     version = sql_be->get_table_version( BUDGET_TABLE);
     351           13 :     if (version == 0)
     352              :     {
     353           18 :         (void)sql_be->create_table(BUDGET_TABLE, TABLE_VERSION, col_table);
     354              :     }
     355              : 
     356           26 :     version = sql_be->get_table_version( AMOUNTS_TABLE);
     357           13 :     if (version == 0)
     358              :     {
     359           18 :         (void)sql_be->create_table(AMOUNTS_TABLE, AMOUNTS_TABLE_VERSION,
     360              :                                     budget_amounts_col_table);
     361              :     }
     362              : }
     363              : 
     364              : /* ================================================================= */
     365              : bool
     366            1 : GncSqlBudgetBackend::commit (GncSqlBackend* sql_be, QofInstance* inst)
     367              : {
     368            1 :     GncBudget* pBudget = GNC_BUDGET (inst);
     369              :     const GncGUID* guid;
     370              :     E_DB_OPERATION op;
     371              :     gboolean is_infant;
     372              :     gboolean is_ok;
     373              : 
     374            1 :     g_return_val_if_fail (sql_be != NULL, FALSE);
     375            1 :     g_return_val_if_fail (inst != NULL, FALSE);
     376            1 :     g_return_val_if_fail (GNC_IS_BUDGET (inst), FALSE);
     377              : 
     378            1 :     is_infant = qof_instance_get_infant (inst);
     379            1 :     if (qof_instance_get_destroying (inst))
     380              :     {
     381            0 :         op = OP_DB_DELETE;
     382              :     }
     383            1 :     else if (sql_be->pristine() || is_infant)
     384              :     {
     385            1 :         op = OP_DB_INSERT;
     386              :     }
     387              :     else
     388              :     {
     389            0 :         op = OP_DB_UPDATE;
     390              :     }
     391            1 :     is_ok = sql_be->do_db_operation(op, BUDGET_TABLE, GNC_ID_BUDGET, pBudget,
     392              :                                     col_table);
     393              : 
     394              :     // Now, commit any slots and recurrence
     395            1 :     if (is_ok)
     396              :     {
     397            1 :         guid = qof_instance_get_guid (inst);
     398            1 :         if (!qof_instance_get_destroying (inst))
     399              :         {
     400            1 :             is_ok = save_budget_amounts (sql_be, pBudget);
     401            1 :             if (is_ok)
     402              :             {
     403            1 :                 is_ok = gnc_sql_recurrence_save (sql_be, guid,
     404              :                                                  gnc_budget_get_recurrence (pBudget));
     405              :             }
     406            1 :             if (is_ok)
     407              :             {
     408            1 :                 is_ok = gnc_sql_slots_save (sql_be, guid, is_infant, inst);
     409              :             }
     410              :         }
     411              :         else
     412              :         {
     413            0 :             is_ok = delete_budget_amounts (sql_be, pBudget);
     414            0 :             if (is_ok)
     415              :             {
     416            0 :                 is_ok = gnc_sql_recurrence_delete (sql_be, guid);
     417              :             }
     418            0 :             if (is_ok)
     419              :             {
     420            0 :                 (void)gnc_sql_slots_delete (sql_be, guid);
     421              :             }
     422              :         }
     423              :     }
     424              : 
     425            1 :     return is_ok;
     426              : }
     427              : 
     428              : static void
     429            1 : do_save (QofInstance* inst, gpointer data)
     430              : {
     431            1 :     write_objects_t* s = (write_objects_t*)data;
     432              : 
     433            1 :     if (s->is_ok)
     434              :     {
     435            1 :         s->is_ok = s->obe->commit (s->be, inst);
     436              :     }
     437            1 : }
     438              : 
     439              : bool
     440            6 : GncSqlBudgetBackend::write (GncSqlBackend* sql_be)
     441              : {
     442            6 :     write_objects_t data;
     443              : 
     444            6 :     g_return_val_if_fail (sql_be != NULL, FALSE);
     445              : 
     446            6 :     data.be = sql_be;
     447            6 :     data.is_ok = TRUE;
     448            6 :     data.obe = this;
     449            6 :     qof_collection_foreach (qof_book_get_collection (sql_be->book(), GNC_ID_BUDGET),
     450              :                             (QofInstanceForeachCB)do_save, &data);
     451              : 
     452            6 :     return data.is_ok;
     453              : }
     454              : 
     455              : /* ================================================================= */
     456              : template<> void
     457            0 : GncSqlColumnTableEntryImpl<CT_BUDGETREF>::load (const GncSqlBackend* sql_be,
     458              :                                                  GncSqlRow& row,
     459              :                                                  QofIdTypeConst obj_name,
     460              :                                                  gpointer pObject) const noexcept
     461              : {
     462            0 :     load_from_guid_ref(row, obj_name, pObject,
     463            0 :                        [sql_be](GncGUID* g){
     464            0 :                             return gnc_budget_lookup (g, sql_be->book());
     465              :                         });
     466            0 : }
     467              : 
     468              : template<> void
     469            6 : GncSqlColumnTableEntryImpl<CT_BUDGETREF>::add_to_table(ColVec& vec) const noexcept
     470              : {
     471            6 :     add_objectref_guid_to_table(vec);
     472            6 : }
     473              : 
     474              : template<> void
     475            0 : GncSqlColumnTableEntryImpl<CT_BUDGETREF>::add_to_query(QofIdTypeConst obj_name,
     476              :                                                     const gpointer pObject,
     477              :                                                     PairVec& vec) const noexcept
     478              : {
     479            0 :     add_objectref_guid_to_query(obj_name, pObject, vec);
     480            0 : }
     481              : 
     482              : /* ========================== END OF FILE ===================== */
        

Generated by: LCOV version 2.0-1