gnucash/libgnucash/backend/sql/gnc-bill-term-sql.cpp

358 lines
12 KiB
C++

/********************************************************************\
* gnc-bill-term-sql.c -- billing term sql backend *
* *
* 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-bill-term-sql.c
* @brief load and save address data to SQL
* @author Copyright (c) 2007-2008 Phil Longstaff <plongstaff@rogers.com>
*
* This file implements the top-level QofBackend API for saving/
* restoring data to/from an SQL database
*/
#include <guid.hpp>
extern "C"
{
#include <config.h>
#include <glib.h>
#include <stdlib.h>
#include <string.h>
#include "gncBillTermP.h"
#include "gncInvoice.h"
#include "qof.h"
}
#include <string>
#include <vector>
#include <algorithm>
#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-slots-sql.h"
#include "gnc-bill-term-sql.h"
#define _GNC_MOD_NAME GNC_ID_BILLTERM
static QofLogModule log_module = G_LOG_DOMAIN;
#define MAX_NAME_LEN 2048
#define MAX_DESCRIPTION_LEN 2048
#define MAX_TYPE_LEN 2048
static void set_invisible (gpointer data, gboolean value);
static gpointer bt_get_parent (gpointer data);
static void bt_set_parent (gpointer data, gpointer value);
static void bt_set_parent_guid (gpointer data, gpointer value);
#define TABLE_NAME "billterms"
#define TABLE_VERSION 2
static 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", MAX_NAME_LEN, COL_NNUL, "name"),
gnc_sql_make_table_entry<CT_STRING>("description", MAX_DESCRIPTION_LEN,
COL_NNUL, GNC_BILLTERM_DESC,
true),
gnc_sql_make_table_entry<CT_INT>("refcount", 0, COL_NNUL,
(QofAccessFunc)gncBillTermGetRefcount,
(QofSetterFunc)gncBillTermSetRefcount),
gnc_sql_make_table_entry<CT_BOOLEAN>("invisible", 0, COL_NNUL,
(QofAccessFunc)gncBillTermGetInvisible,
(QofSetterFunc)set_invisible),
gnc_sql_make_table_entry<CT_GUID>("parent", 0, 0,
(QofAccessFunc)bt_get_parent,
(QofSetterFunc)bt_set_parent),
gnc_sql_make_table_entry<CT_STRING>("type", MAX_TYPE_LEN, COL_NNUL,
GNC_BILLTERM_TYPE, true),
gnc_sql_make_table_entry<CT_INT>("duedays", 0, 0, GNC_BILLTERM_DUEDAYS,
true),
gnc_sql_make_table_entry<CT_INT>("discountdays", 0, 0,
GNC_BILLTERM_DISCDAYS, true),
gnc_sql_make_table_entry<CT_NUMERIC>("discount", 0, 0,
GNC_BILLTERM_DISCOUNT, true),
gnc_sql_make_table_entry<CT_INT>("cutoff", 0, 0, GNC_BILLTERM_CUTOFF,
true),
};
static EntryVec billterm_parent_col_table
{
gnc_sql_make_table_entry<CT_GUID>("parent", 0, 0, nullptr,
bt_set_parent_guid),
};
GncSqlBillTermBackend::GncSqlBillTermBackend() :
GncSqlObjectBackend(GNC_SQL_BACKEND_VERSION, GNC_ID_BILLTERM,
TABLE_NAME, col_table) {}
struct BillTermParentGuid
{
GncBillTerm* billterm;
GncGUID guid;
bool have_guid;
};
using BillTermParentGuidPtr = BillTermParentGuid*;
using BillTermParentGuidVec = std::vector<BillTermParentGuidPtr>;
static void
set_invisible (gpointer data, gboolean value)
{
GncBillTerm* term = GNC_BILLTERM (data);
g_return_if_fail (term != NULL);
if (value)
{
gncBillTermMakeInvisible (term);
}
}
static gpointer
bt_get_parent (gpointer pObject)
{
const GncBillTerm* billterm;
const GncBillTerm* pParent;
const GncGUID* parent_guid;
g_return_val_if_fail (pObject != NULL, NULL);
g_return_val_if_fail (GNC_IS_BILLTERM (pObject), NULL);
billterm = GNC_BILLTERM (pObject);
pParent = gncBillTermGetParent (billterm);
if (pParent == NULL)
{
parent_guid = NULL;
}
else
{
parent_guid = qof_instance_get_guid (QOF_INSTANCE (pParent));
}
return (gpointer)parent_guid;
}
static void
bt_set_parent (gpointer data, gpointer value)
{
GncBillTerm* billterm;
GncBillTerm* parent;
QofBook* pBook;
GncGUID* guid = (GncGUID*)value;
g_return_if_fail (data != NULL);
g_return_if_fail (GNC_IS_BILLTERM (data));
billterm = GNC_BILLTERM (data);
pBook = qof_instance_get_book (QOF_INSTANCE (billterm));
if (guid != NULL)
{
parent = gncBillTermLookup (pBook, guid);
if (parent != NULL)
{
gncBillTermSetParent (billterm, parent);
gncBillTermSetChild (parent, billterm);
}
}
}
static void
bt_set_parent_guid (gpointer pObject, gpointer pValue)
{
g_return_if_fail (pObject != NULL);
g_return_if_fail (pValue != NULL);
auto s = static_cast<BillTermParentGuidPtr>(pObject);
s->guid = *static_cast<GncGUID*>(pValue);
s->have_guid = true;
}
static GncBillTerm*
load_single_billterm (GncSqlBackend* sql_be, GncSqlRow& row,
BillTermParentGuidVec& l_billterms_needing_parents)
{
g_return_val_if_fail (sql_be != NULL, NULL);
auto guid = gnc_sql_load_guid (sql_be, row);
auto pBillTerm = gncBillTermLookup (sql_be->book(), guid);
if (pBillTerm == nullptr)
{
pBillTerm = gncBillTermCreate (sql_be->book());
}
gnc_sql_load_object (sql_be, row, GNC_ID_BILLTERM, pBillTerm, col_table);
/* If the billterm doesn't have a parent, it might be because it hasn't been
loaded yet. If so, add this billterm to the list of billterms with no
parent, along with the parent GncGUID so that after they are all loaded,
the parents can be fixed up. */
if (gncBillTermGetParent (pBillTerm) == NULL)
{
BillTermParentGuid s;
s.billterm = pBillTerm;
s.have_guid = false;
gnc_sql_load_object (sql_be, row, GNC_ID_BILLTERM, &s,
billterm_parent_col_table);
if (s.have_guid)
l_billterms_needing_parents.push_back(new BillTermParentGuid(s));
}
qof_instance_mark_clean (QOF_INSTANCE (pBillTerm));
return pBillTerm;
}
void
GncSqlBillTermBackend::load_all (GncSqlBackend* sql_be)
{
g_return_if_fail (sql_be != NULL);
std::stringstream sql;
sql << "SELECT * FROM " << TABLE_NAME;
auto stmt = sql_be->create_statement_from_sql(sql.str());
auto result = sql_be->execute_select_statement(stmt);
InstanceVec instances;
BillTermParentGuidVec l_billterms_needing_parents;
for (auto row : *result)
{
auto pBillTerm =
load_single_billterm (sql_be, row, l_billterms_needing_parents);
if (pBillTerm != nullptr)
instances.push_back(QOF_INSTANCE(pBillTerm));
}
delete result;
if (!instances.empty())
gnc_sql_slots_load_for_instancevec (sql_be, instances);
/* While there are items on the list of billterms needing parents,
try to see if the parent has now been loaded. Theory says that if
items are removed from the front and added to the back if the
parent is still not available, then eventually, the list will
shrink to size 0. */
if (!l_billterms_needing_parents.empty())
{
bool progress_made = true;
std::reverse(l_billterms_needing_parents.begin(),
l_billterms_needing_parents.end());
auto end = l_billterms_needing_parents.end();
while (progress_made)
{
progress_made = false;
end = std::remove_if(l_billterms_needing_parents.begin(), end,
[&](BillTermParentGuidPtr s)
{
auto pBook = qof_instance_get_book (QOF_INSTANCE (s->billterm));
auto parent = gncBillTermLookup (pBook,
&s->guid);
if (parent != nullptr)
{
gncBillTermSetParent (s->billterm, parent);
gncBillTermSetChild (parent, s->billterm);
progress_made = true;
delete s;
return true;
}
return false;
});
}
}
}
/* ================================================================= */
static void
do_save_billterm (QofInstance* inst, void* p2)
{
auto data = static_cast<write_objects_t*>(p2);
data->commit(inst);
}
bool
GncSqlBillTermBackend::write (GncSqlBackend* sql_be)
{
g_return_val_if_fail (sql_be != NULL, FALSE);
write_objects_t data {sql_be, true, this};
qof_object_foreach (GNC_ID_BILLTERM, sql_be->book(), do_save_billterm, &data);
return data.is_ok;
}
/* ================================================================= */
void
GncSqlBillTermBackend::create_tables (GncSqlBackend* sql_be)
{
gint version;
g_return_if_fail (sql_be != NULL);
version = sql_be->get_table_version( TABLE_NAME);
if (version == 0)
{
sql_be->create_table(TABLE_NAME, TABLE_VERSION, col_table);
}
else if (version == 1)
{
/* Upgrade 64 bit int handling */
sql_be->upgrade_table(TABLE_NAME, col_table);
sql_be->set_table_version (TABLE_NAME, TABLE_VERSION);
PINFO ("Billterms table upgraded from version 1 to version %d\n",
TABLE_VERSION);
}
}
/* ================================================================= */
template<> void
GncSqlColumnTableEntryImpl<CT_BILLTERMREF>::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 gncBillTermLookup(sql_be->book(), g);
});
}
template<> void
GncSqlColumnTableEntryImpl<CT_BILLTERMREF>::add_to_table(ColVec& vec) const noexcept
{
add_objectref_guid_to_table(vec);
}
template<> void
GncSqlColumnTableEntryImpl<CT_BILLTERMREF>::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 ===================== */