mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-12-01 21:19:16 -06:00
4e25bbfc43
- __attribute__((unused)) for C - [[maybe_unused]] for cpp
483 lines
14 KiB
C++
483 lines
14 KiB
C++
/********************************************************************
|
|
* gnc-budget-sql.c: load and save data to SQL *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU General Public License as *
|
|
* published by the Free Software Foundation; either version 2 of *
|
|
* the License, or (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License*
|
|
* along with this program; if not, contact: *
|
|
* *
|
|
* Free Software Foundation Voice: +1-617-542-5942 *
|
|
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
|
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
|
\********************************************************************/
|
|
/** @file gnc-budget-sql.c
|
|
* @brief load and save data to SQL
|
|
* @author Copyright (c) 2006-2008 Phil Longstaff <plongstaff@rogers.com>
|
|
*
|
|
* This file implements the top-level QofBackend API for saving/
|
|
* restoring data to/from an SQL db
|
|
*/
|
|
#include <guid.hpp>
|
|
#include <config.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#include "qof.h"
|
|
#include "Recurrence.h"
|
|
#include "gnc-budget.h"
|
|
|
|
#if defined( S_SPLINT_S )
|
|
#include "splint-defs.h"
|
|
#endif
|
|
|
|
#include "gnc-sql-connection.hpp"
|
|
#include "gnc-sql-backend.hpp"
|
|
#include "gnc-sql-object-backend.hpp"
|
|
#include "gnc-sql-column-table-entry.hpp"
|
|
#include "gnc-budget-sql.h"
|
|
#include "gnc-slots-sql.h"
|
|
#include "gnc-recurrence-sql.h"
|
|
|
|
#define BUDGET_TABLE "budgets"
|
|
#define TABLE_VERSION 1
|
|
#define AMOUNTS_TABLE "budget_amounts"
|
|
#define AMOUNTS_TABLE_VERSION 1
|
|
|
|
[[maybe_unused]] static QofLogModule log_module = G_LOG_DOMAIN;
|
|
|
|
#define BUDGET_MAX_NAME_LEN 2048
|
|
#define BUDGET_MAX_DESCRIPTION_LEN 2048
|
|
|
|
static const EntryVec col_table
|
|
{
|
|
gnc_sql_make_table_entry<CT_GUID>(
|
|
"guid", 0, COL_NNUL | COL_PKEY, "guid"),
|
|
gnc_sql_make_table_entry<CT_STRING>(
|
|
"name", BUDGET_MAX_NAME_LEN, COL_NNUL, "name"),
|
|
gnc_sql_make_table_entry<CT_STRING>(
|
|
"description", BUDGET_MAX_DESCRIPTION_LEN, 0, "description"),
|
|
gnc_sql_make_table_entry<CT_INT>(
|
|
"num_periods", 0, COL_NNUL, "num_periods"),
|
|
};
|
|
|
|
static QofInstance* get_budget (gpointer pObj);
|
|
static void set_budget (gpointer pObj, gpointer val);
|
|
static QofInstance* get_account (gpointer pObj);
|
|
static void set_account (gpointer pObj, gpointer val);
|
|
static gint get_period_num (gpointer pObj);
|
|
static void set_period_num (gpointer pObj, gpointer val);
|
|
static gnc_numeric get_amount (gpointer pObj);
|
|
static void set_amount (gpointer pObj, gnc_numeric value);
|
|
|
|
GncSqlBudgetBackend::GncSqlBudgetBackend() :
|
|
GncSqlObjectBackend(TABLE_VERSION, GNC_ID_BUDGET,
|
|
BUDGET_TABLE, col_table) {}
|
|
|
|
typedef struct
|
|
{
|
|
GncBudget* budget;
|
|
Account* account;
|
|
guint period_num;
|
|
} budget_amount_info_t;
|
|
|
|
static const EntryVec budget_amounts_col_table
|
|
{
|
|
gnc_sql_make_table_entry<CT_INT>(
|
|
"id", 0, COL_NNUL | COL_PKEY | COL_AUTOINC),
|
|
gnc_sql_make_table_entry<CT_BUDGETREF>("budget_guid", 0, COL_NNUL,
|
|
(QofAccessFunc)get_budget,
|
|
(QofSetterFunc)set_budget),
|
|
gnc_sql_make_table_entry<CT_ACCOUNTREF>("account_guid", 0, COL_NNUL,
|
|
(QofAccessFunc)get_account,
|
|
(QofSetterFunc)set_account),
|
|
gnc_sql_make_table_entry<CT_INT>("period_num", 0, COL_NNUL,
|
|
(QofAccessFunc)get_period_num,
|
|
(QofSetterFunc)set_period_num),
|
|
gnc_sql_make_table_entry<CT_NUMERIC>("amount", 0, COL_NNUL,
|
|
(QofAccessFunc)get_amount,
|
|
(QofSetterFunc)set_amount),
|
|
};
|
|
|
|
/* ================================================================= */
|
|
static QofInstance*
|
|
get_budget (gpointer pObj)
|
|
{
|
|
budget_amount_info_t* info = (budget_amount_info_t*)pObj;
|
|
|
|
g_return_val_if_fail (pObj != NULL, NULL);
|
|
|
|
return QOF_INSTANCE (info->budget);
|
|
}
|
|
|
|
static void
|
|
set_budget (gpointer pObj, gpointer val)
|
|
{
|
|
}
|
|
|
|
static QofInstance*
|
|
get_account (gpointer pObj)
|
|
{
|
|
budget_amount_info_t* info = (budget_amount_info_t*)pObj;
|
|
|
|
g_return_val_if_fail (pObj != NULL, NULL);
|
|
|
|
return QOF_INSTANCE (info->account);
|
|
}
|
|
|
|
static void
|
|
set_account (gpointer pObj, gpointer val)
|
|
{
|
|
budget_amount_info_t* info = (budget_amount_info_t*)pObj;
|
|
|
|
g_return_if_fail (pObj != NULL);
|
|
g_return_if_fail (val != NULL);
|
|
g_return_if_fail (GNC_IS_ACCOUNT (val));
|
|
|
|
info->account = GNC_ACCOUNT (val);
|
|
}
|
|
|
|
static gint
|
|
get_period_num (gpointer pObj)
|
|
{
|
|
budget_amount_info_t* info = (budget_amount_info_t*)pObj;
|
|
|
|
g_return_val_if_fail (pObj != NULL, 0);
|
|
|
|
return info->period_num;
|
|
}
|
|
|
|
static void
|
|
set_period_num (gpointer pObj, gpointer val)
|
|
{
|
|
budget_amount_info_t* info = (budget_amount_info_t*)pObj;
|
|
|
|
g_return_if_fail (pObj != NULL);
|
|
|
|
info->period_num = GPOINTER_TO_UINT (val);
|
|
}
|
|
|
|
static gnc_numeric
|
|
get_amount (gpointer pObj)
|
|
{
|
|
budget_amount_info_t* info = (budget_amount_info_t*)pObj;
|
|
|
|
g_return_val_if_fail (pObj != NULL, gnc_numeric_zero ());
|
|
|
|
return gnc_budget_get_account_period_value (info->budget, info->account,
|
|
info->period_num);
|
|
}
|
|
|
|
static void
|
|
set_amount (gpointer pObj, gnc_numeric value)
|
|
{
|
|
budget_amount_info_t* info = (budget_amount_info_t*)pObj;
|
|
|
|
g_return_if_fail (pObj != NULL);
|
|
|
|
gnc_budget_set_account_period_value (info->budget, info->account,
|
|
info->period_num, value);
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
/**
|
|
* Loads the budget amounts for a budget.
|
|
*
|
|
* @param sql_be SQL backend
|
|
* @param budget Budget
|
|
*/
|
|
static void
|
|
load_budget_amounts (GncSqlBackend* sql_be, GncBudget* budget)
|
|
{
|
|
gchar guid_buf[GUID_ENCODING_LENGTH + 1];
|
|
|
|
g_return_if_fail (sql_be != NULL);
|
|
g_return_if_fail (budget != NULL);
|
|
|
|
(void)guid_to_string_buff (qof_instance_get_guid (QOF_INSTANCE (budget)),
|
|
guid_buf);
|
|
auto sql = g_strdup_printf ("SELECT * FROM %s WHERE budget_guid='%s'",
|
|
AMOUNTS_TABLE, guid_buf);
|
|
auto stmt = sql_be->create_statement_from_sql(sql);
|
|
g_free (sql);
|
|
if (stmt != nullptr)
|
|
{
|
|
auto result = sql_be->execute_select_statement(stmt);
|
|
budget_amount_info_t info = { budget, NULL, 0 };
|
|
|
|
for (auto row : *result)
|
|
gnc_sql_load_object (sql_be, row, NULL, &info, budget_amounts_col_table);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes the budget amounts for a budget.
|
|
*
|
|
* @param sql_be SQL backend
|
|
* @param budget Budget
|
|
*/
|
|
static gboolean
|
|
delete_budget_amounts (GncSqlBackend* sql_be, GncBudget* budget)
|
|
{
|
|
gchar guid_buf[GUID_ENCODING_LENGTH + 1];
|
|
|
|
g_return_val_if_fail (sql_be != NULL, FALSE);
|
|
g_return_val_if_fail (budget != NULL, FALSE);
|
|
|
|
(void)guid_to_string_buff (qof_instance_get_guid (QOF_INSTANCE (budget)),
|
|
guid_buf);
|
|
std::stringstream sql;
|
|
sql << "DELETE FROM " << AMOUNTS_TABLE << " WHERE budget_guid='"<<
|
|
guid_buf << "'";
|
|
auto stmt = sql_be->create_statement_from_sql(sql.str());
|
|
sql_be->execute_nonselect_statement(stmt);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Saves the budget amounts for a budget.
|
|
*
|
|
* @param sql_be SQL backend
|
|
* @param budget Budget
|
|
*/
|
|
static gboolean
|
|
save_budget_amounts (GncSqlBackend* sql_be, GncBudget* budget)
|
|
{
|
|
GList* descendants;
|
|
GList* node;
|
|
budget_amount_info_t info;
|
|
guint num_periods;
|
|
gboolean is_ok = TRUE;
|
|
|
|
g_return_val_if_fail (sql_be != NULL, FALSE);
|
|
g_return_val_if_fail (budget != NULL, FALSE);
|
|
|
|
// Delete the amounts, then save
|
|
delete_budget_amounts (sql_be, budget);
|
|
|
|
info.budget = budget;
|
|
num_periods = gnc_budget_get_num_periods (budget);
|
|
descendants = gnc_account_get_descendants (gnc_book_get_root_account (
|
|
sql_be->book()));
|
|
for (node = descendants; node != NULL && is_ok; node = g_list_next (node))
|
|
{
|
|
guint i;
|
|
|
|
info.account = GNC_ACCOUNT (node->data);
|
|
for (i = 0; i < num_periods && is_ok; i++)
|
|
{
|
|
if (gnc_budget_is_account_period_value_set (budget, info.account, i))
|
|
{
|
|
info.period_num = i;
|
|
is_ok = sql_be->do_db_operation(OP_DB_INSERT, AMOUNTS_TABLE,
|
|
"", &info,
|
|
budget_amounts_col_table);
|
|
}
|
|
}
|
|
}
|
|
g_list_free (descendants);
|
|
|
|
return is_ok;
|
|
}
|
|
/*----------------------------------------------------------------*/
|
|
static GncBudget*
|
|
load_single_budget (GncSqlBackend* sql_be, GncSqlRow& row)
|
|
{
|
|
const GncGUID* guid;
|
|
GncBudget* pBudget = NULL;
|
|
Recurrence* r;
|
|
|
|
g_return_val_if_fail (sql_be != NULL, NULL);
|
|
|
|
guid = gnc_sql_load_guid (sql_be, row);
|
|
if (guid != NULL)
|
|
{
|
|
pBudget = gnc_budget_lookup (guid, sql_be->book());
|
|
}
|
|
if (pBudget == NULL)
|
|
{
|
|
pBudget = gnc_budget_new (sql_be->book());
|
|
}
|
|
|
|
gnc_budget_begin_edit (pBudget);
|
|
gnc_sql_load_object (sql_be, row, GNC_ID_BUDGET, pBudget, col_table);
|
|
load_budget_amounts (sql_be, pBudget);
|
|
r = gnc_sql_recurrence_load (sql_be, gnc_budget_get_guid (pBudget));
|
|
if (r != NULL)
|
|
{
|
|
gnc_budget_set_recurrence (pBudget, r);
|
|
g_free (r);
|
|
}
|
|
gnc_budget_commit_edit (pBudget);
|
|
|
|
return pBudget;
|
|
}
|
|
|
|
void
|
|
GncSqlBudgetBackend::load_all (GncSqlBackend* sql_be)
|
|
{
|
|
g_return_if_fail (sql_be != NULL);
|
|
|
|
std::string sql("SELECT * FROM " BUDGET_TABLE);
|
|
auto stmt = sql_be->create_statement_from_sql(sql);
|
|
auto result = sql_be->execute_select_statement(stmt);
|
|
for (auto row : *result)
|
|
load_single_budget (sql_be, row);
|
|
|
|
std::string pkey(col_table[0]->name());
|
|
sql = "SELECT DISTINCT ";
|
|
sql += pkey + " FROM " BUDGET_TABLE;
|
|
gnc_sql_slots_load_for_sql_subquery (sql_be, sql,
|
|
(BookLookupFn)gnc_budget_lookup);
|
|
}
|
|
|
|
/* ================================================================= */
|
|
void
|
|
GncSqlBudgetBackend::create_tables (GncSqlBackend* sql_be)
|
|
{
|
|
gint version;
|
|
|
|
g_return_if_fail (sql_be != NULL);
|
|
|
|
version = sql_be->get_table_version( BUDGET_TABLE);
|
|
if (version == 0)
|
|
{
|
|
(void)sql_be->create_table(BUDGET_TABLE, TABLE_VERSION, col_table);
|
|
}
|
|
|
|
version = sql_be->get_table_version( AMOUNTS_TABLE);
|
|
if (version == 0)
|
|
{
|
|
(void)sql_be->create_table(AMOUNTS_TABLE, AMOUNTS_TABLE_VERSION,
|
|
budget_amounts_col_table);
|
|
}
|
|
}
|
|
|
|
/* ================================================================= */
|
|
bool
|
|
GncSqlBudgetBackend::commit (GncSqlBackend* sql_be, QofInstance* inst)
|
|
{
|
|
GncBudget* pBudget = GNC_BUDGET (inst);
|
|
const GncGUID* guid;
|
|
E_DB_OPERATION op;
|
|
gboolean is_infant;
|
|
gboolean is_ok;
|
|
|
|
g_return_val_if_fail (sql_be != NULL, FALSE);
|
|
g_return_val_if_fail (inst != NULL, FALSE);
|
|
g_return_val_if_fail (GNC_IS_BUDGET (inst), FALSE);
|
|
|
|
is_infant = qof_instance_get_infant (inst);
|
|
if (qof_instance_get_destroying (inst))
|
|
{
|
|
op = OP_DB_DELETE;
|
|
}
|
|
else if (sql_be->pristine() || is_infant)
|
|
{
|
|
op = OP_DB_INSERT;
|
|
}
|
|
else
|
|
{
|
|
op = OP_DB_UPDATE;
|
|
}
|
|
is_ok = sql_be->do_db_operation(op, BUDGET_TABLE, GNC_ID_BUDGET, pBudget,
|
|
col_table);
|
|
|
|
// Now, commit any slots and recurrence
|
|
if (is_ok)
|
|
{
|
|
guid = qof_instance_get_guid (inst);
|
|
if (!qof_instance_get_destroying (inst))
|
|
{
|
|
is_ok = save_budget_amounts (sql_be, pBudget);
|
|
if (is_ok)
|
|
{
|
|
is_ok = gnc_sql_recurrence_save (sql_be, guid,
|
|
gnc_budget_get_recurrence (pBudget));
|
|
}
|
|
if (is_ok)
|
|
{
|
|
is_ok = gnc_sql_slots_save (sql_be, guid, is_infant, inst);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
is_ok = delete_budget_amounts (sql_be, pBudget);
|
|
if (is_ok)
|
|
{
|
|
is_ok = gnc_sql_recurrence_delete (sql_be, guid);
|
|
}
|
|
if (is_ok)
|
|
{
|
|
(void)gnc_sql_slots_delete (sql_be, guid);
|
|
}
|
|
}
|
|
}
|
|
|
|
return is_ok;
|
|
}
|
|
|
|
static void
|
|
do_save (QofInstance* inst, gpointer data)
|
|
{
|
|
write_objects_t* s = (write_objects_t*)data;
|
|
|
|
if (s->is_ok)
|
|
{
|
|
s->is_ok = s->obe->commit (s->be, inst);
|
|
}
|
|
}
|
|
|
|
bool
|
|
GncSqlBudgetBackend::write (GncSqlBackend* sql_be)
|
|
{
|
|
write_objects_t data;
|
|
|
|
g_return_val_if_fail (sql_be != NULL, FALSE);
|
|
|
|
data.be = sql_be;
|
|
data.is_ok = TRUE;
|
|
data.obe = this;
|
|
qof_collection_foreach (qof_book_get_collection (sql_be->book(), GNC_ID_BUDGET),
|
|
(QofInstanceForeachCB)do_save, &data);
|
|
|
|
return data.is_ok;
|
|
}
|
|
|
|
/* ================================================================= */
|
|
template<> void
|
|
GncSqlColumnTableEntryImpl<CT_BUDGETREF>::load (const GncSqlBackend* sql_be,
|
|
GncSqlRow& row,
|
|
QofIdTypeConst obj_name,
|
|
gpointer pObject) const noexcept
|
|
{
|
|
load_from_guid_ref(row, obj_name, pObject,
|
|
[sql_be](GncGUID* g){
|
|
return gnc_budget_lookup (g, sql_be->book());
|
|
});
|
|
}
|
|
|
|
template<> void
|
|
GncSqlColumnTableEntryImpl<CT_BUDGETREF>::add_to_table(ColVec& vec) const noexcept
|
|
{
|
|
add_objectref_guid_to_table(vec);
|
|
}
|
|
|
|
template<> void
|
|
GncSqlColumnTableEntryImpl<CT_BUDGETREF>::add_to_query(QofIdTypeConst obj_name,
|
|
const gpointer pObject,
|
|
PairVec& vec) const noexcept
|
|
{
|
|
add_objectref_guid_to_query(obj_name, pObject, vec);
|
|
}
|
|
|
|
/* ========================== END OF FILE ===================== */
|