GnuCash c935c2f+
Loading...
Searching...
No Matches
gnc-budget-sql.cpp
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\********************************************************************/
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
59static 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
71static QofInstance* get_budget (gpointer pObj);
72static void set_budget (gpointer pObj, gpointer val);
73static QofInstance* get_account (gpointer pObj);
74static void set_account (gpointer pObj, gpointer val);
75static gint get_period_num (gpointer pObj);
76static void set_period_num (gpointer pObj, gpointer val);
77static gnc_numeric get_amount (gpointer pObj);
78static void set_amount (gpointer pObj, gnc_numeric value);
79
80GncSqlBudgetBackend::GncSqlBudgetBackend() :
81 GncSqlObjectBackend(TABLE_VERSION, GNC_ID_BUDGET,
82 BUDGET_TABLE, col_table) {}
83
84typedef struct
85{
86 GncBudget* budget;
87 Account* account;
88 guint period_num;
90
91static 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/* ================================================================= */
110static QofInstance*
111get_budget (gpointer pObj)
112{
114
115 g_return_val_if_fail (pObj != NULL, NULL);
116
117 return QOF_INSTANCE (info->budget);
118}
119
120static void
121set_budget (gpointer pObj, gpointer val)
122{
123}
124
125static QofInstance*
126get_account (gpointer pObj)
127{
129
130 g_return_val_if_fail (pObj != NULL, NULL);
131
132 return QOF_INSTANCE (info->account);
133}
134
135static void
136set_account (gpointer pObj, gpointer val)
137{
139
140 g_return_if_fail (pObj != NULL);
141 g_return_if_fail (val != NULL);
142 g_return_if_fail (GNC_IS_ACCOUNT (val));
143
144 info->account = GNC_ACCOUNT (val);
145}
146
147static gint
148get_period_num (gpointer pObj)
149{
151
152 g_return_val_if_fail (pObj != NULL, 0);
153
154 return info->period_num;
155}
156
157static void
158set_period_num (gpointer pObj, gpointer val)
159{
161
162 g_return_if_fail (pObj != NULL);
163
164 info->period_num = GPOINTER_TO_UINT (val);
165}
166
167static gnc_numeric
168get_amount (gpointer pObj)
169{
171
172 g_return_val_if_fail (pObj != NULL, gnc_numeric_zero ());
173
174 return gnc_budget_get_account_period_value (info->budget, info->account,
175 info->period_num);
176}
177
178static void
179set_amount (gpointer pObj, gnc_numeric value)
180{
182
183 g_return_if_fail (pObj != NULL);
184
185 gnc_budget_set_account_period_value (info->budget, info->account,
186 info->period_num, value);
187}
188
189/*----------------------------------------------------------------*/
196static void
197load_budget_amounts (GncSqlBackend* sql_be, GncBudget* budget)
198{
199 gchar guid_buf[GUID_ENCODING_LENGTH + 1];
200
201 g_return_if_fail (sql_be != NULL);
202 g_return_if_fail (budget != NULL);
203
204 (void)guid_to_string_buff (qof_instance_get_guid (QOF_INSTANCE (budget)),
205 guid_buf);
206 auto sql = g_strdup_printf ("SELECT * FROM %s WHERE budget_guid='%s'",
207 AMOUNTS_TABLE, guid_buf);
208 auto stmt = sql_be->create_statement_from_sql(sql);
209 g_free (sql);
210 if (stmt != nullptr)
211 {
212 auto result = sql_be->execute_select_statement(stmt);
213 budget_amount_info_t info = { budget, NULL, 0 };
214
215 for (auto row : *result)
216 gnc_sql_load_object (sql_be, row, NULL, &info, budget_amounts_col_table);
217 }
218}
219
226static gboolean
227delete_budget_amounts (GncSqlBackend* sql_be, GncBudget* budget)
228{
229 gchar guid_buf[GUID_ENCODING_LENGTH + 1];
230
231 g_return_val_if_fail (sql_be != NULL, FALSE);
232 g_return_val_if_fail (budget != NULL, FALSE);
233
234 (void)guid_to_string_buff (qof_instance_get_guid (QOF_INSTANCE (budget)),
235 guid_buf);
236 std::stringstream sql;
237 sql << "DELETE FROM " << AMOUNTS_TABLE << " WHERE budget_guid='"<<
238 guid_buf << "'";
239 auto stmt = sql_be->create_statement_from_sql(sql.str());
240 sql_be->execute_nonselect_statement(stmt);
241
242 return true;
243}
244
251static gboolean
252save_budget_amounts (GncSqlBackend* sql_be, GncBudget* budget)
253{
254 GList* descendants;
255 GList* node;
257 guint num_periods;
258 gboolean is_ok = TRUE;
259
260 g_return_val_if_fail (sql_be != NULL, FALSE);
261 g_return_val_if_fail (budget != NULL, FALSE);
262
263 // Delete the amounts, then save
264 delete_budget_amounts (sql_be, budget);
265
266 info.budget = budget;
267 num_periods = gnc_budget_get_num_periods (budget);
268 descendants = gnc_account_get_descendants (gnc_book_get_root_account (
269 sql_be->book()));
270 for (node = descendants; node != NULL && is_ok; node = g_list_next (node))
271 {
272 guint i;
273
274 info.account = GNC_ACCOUNT (node->data);
275 for (i = 0; i < num_periods && is_ok; i++)
276 {
277 if (gnc_budget_is_account_period_value_set (budget, info.account, i))
278 {
279 info.period_num = i;
280 is_ok = sql_be->do_db_operation(OP_DB_INSERT, AMOUNTS_TABLE,
281 "", &info,
282 budget_amounts_col_table);
283 }
284 }
285 }
286 g_list_free (descendants);
287
288 return is_ok;
289}
290/*----------------------------------------------------------------*/
291static GncBudget*
292load_single_budget (GncSqlBackend* sql_be, GncSqlRow& row)
293{
294 const GncGUID* guid;
295 GncBudget* pBudget = NULL;
296 Recurrence* r;
297
298 g_return_val_if_fail (sql_be != NULL, NULL);
299
300 guid = gnc_sql_load_guid (sql_be, row);
301 if (guid != NULL)
302 {
303 pBudget = gnc_budget_lookup (guid, sql_be->book());
304 }
305 if (pBudget == NULL)
306 {
307 pBudget = gnc_budget_new (sql_be->book());
308 }
309
310 gnc_budget_begin_edit (pBudget);
311 gnc_sql_load_object (sql_be, row, GNC_ID_BUDGET, pBudget, col_table);
312 load_budget_amounts (sql_be, pBudget);
313 r = gnc_sql_recurrence_load (sql_be, gnc_budget_get_guid (pBudget));
314 if (r != NULL)
315 {
316 gnc_budget_set_recurrence (pBudget, r);
317 g_free (r);
318 }
319 gnc_budget_commit_edit (pBudget);
320
321 return pBudget;
322}
323
324void
326{
327 g_return_if_fail (sql_be != NULL);
328
329 std::string sql("SELECT * FROM " BUDGET_TABLE);
330 auto stmt = sql_be->create_statement_from_sql(sql);
331 auto result = sql_be->execute_select_statement(stmt);
332 for (auto row : *result)
333 load_single_budget (sql_be, row);
334
335 std::string pkey(col_table[0]->name());
336 sql = "SELECT DISTINCT ";
337 sql += pkey + " FROM " BUDGET_TABLE;
338 gnc_sql_slots_load_for_sql_subquery (sql_be, sql,
339 (BookLookupFn)gnc_budget_lookup);
340}
341
342/* ================================================================= */
343void
345{
346 gint version;
347
348 g_return_if_fail (sql_be != NULL);
349
350 version = sql_be->get_table_version( BUDGET_TABLE);
351 if (version == 0)
352 {
353 (void)sql_be->create_table(BUDGET_TABLE, TABLE_VERSION, col_table);
354 }
355
356 version = sql_be->get_table_version( AMOUNTS_TABLE);
357 if (version == 0)
358 {
359 (void)sql_be->create_table(AMOUNTS_TABLE, AMOUNTS_TABLE_VERSION,
360 budget_amounts_col_table);
361 }
362}
363
364/* ================================================================= */
365bool
367{
368 GncBudget* pBudget = GNC_BUDGET (inst);
369 const GncGUID* guid;
370 E_DB_OPERATION op;
371 gboolean is_infant;
372 gboolean is_ok;
373
374 g_return_val_if_fail (sql_be != NULL, FALSE);
375 g_return_val_if_fail (inst != NULL, FALSE);
376 g_return_val_if_fail (GNC_IS_BUDGET (inst), FALSE);
377
378 is_infant = qof_instance_get_infant (inst);
380 {
381 op = OP_DB_DELETE;
382 }
383 else if (sql_be->pristine() || is_infant)
384 {
385 op = OP_DB_INSERT;
386 }
387 else
388 {
389 op = OP_DB_UPDATE;
390 }
391 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 if (is_ok)
396 {
397 guid = qof_instance_get_guid (inst);
398 if (!qof_instance_get_destroying (inst))
399 {
400 is_ok = save_budget_amounts (sql_be, pBudget);
401 if (is_ok)
402 {
403 is_ok = gnc_sql_recurrence_save (sql_be, guid,
404 gnc_budget_get_recurrence (pBudget));
405 }
406 if (is_ok)
407 {
408 is_ok = gnc_sql_slots_save (sql_be, guid, is_infant, inst);
409 }
410 }
411 else
412 {
413 is_ok = delete_budget_amounts (sql_be, pBudget);
414 if (is_ok)
415 {
416 is_ok = gnc_sql_recurrence_delete (sql_be, guid);
417 }
418 if (is_ok)
419 {
420 (void)gnc_sql_slots_delete (sql_be, guid);
421 }
422 }
423 }
424
425 return is_ok;
426}
427
428static void
429do_save (QofInstance* inst, gpointer data)
430{
432
433 if (s->is_ok)
434 {
435 s->is_ok = s->obe->commit (s->be, inst);
436 }
437}
438
439bool
441{
442 write_objects_t data;
443
444 g_return_val_if_fail (sql_be != NULL, FALSE);
445
446 data.be = sql_be;
447 data.is_ok = TRUE;
448 data.obe = this;
449 qof_collection_foreach (qof_book_get_collection (sql_be->book(), GNC_ID_BUDGET),
450 (QofInstanceForeachCB)do_save, &data);
451
452 return data.is_ok;
453}
454
455/* ================================================================= */
456template<> void
458 GncSqlRow& row,
459 QofIdTypeConst obj_name,
460 gpointer pObject) const noexcept
461{
462 load_from_guid_ref(row, obj_name, pObject,
463 [sql_be](GncGUID* g){
464 return gnc_budget_lookup (g, sql_be->book());
465 });
466}
467
468template<> void
470{
471 add_objectref_guid_to_table(vec);
472}
473
474template<> void
476 const gpointer pObject,
477 PairVec& vec) const noexcept
478{
479 add_objectref_guid_to_query(obj_name, pObject, vec);
480}
481
482/* ========================== END OF FILE ===================== */
Main SQL backend structure.
uint_t get_table_version(const std::string &table_name) const noexcept
Returns the version number for a DB table.
bool create_table(const std::string &table_name, const EntryVec &col_table) const noexcept
Creates a table in the database.
GncSqlResultPtr execute_select_statement(const GncSqlStatementPtr &stmt) const noexcept
Executes an SQL SELECT statement and returns the result rows.
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.
void load_all(GncSqlBackend *) override
Load all objects of m_type in the database into memory.
void create_tables(GncSqlBackend *) override
Conditionally create or update a database table from m_col_table.
bool commit(GncSqlBackend *sql_be, QofInstance *inst) override
UPDATE/INSERT a single instance of m_type_name into the database.
bool write(GncSqlBackend *) override
Write all objects of m_type_name to the database.
void add_to_query(QofIdTypeConst obj_name, void *pObject, PairVec &vec) const noexcept override
Add a pair of the table column heading and object's value's string representation to a PairVec; used ...
void add_to_table(ColVec &vec) const noexcept override
Add a GncSqlColumnInfo structure for the column type to a ColVec.
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.
Encapsulates per-class table schema with functions to load, create a table, commit a changed front-en...
Row of SQL Query results.
load and save data to SQL
GnuCash Budgets.
load and save accounts data to SQL
load and save accounts data to SQL
GList * gnc_account_get_descendants(const Account *account)
This routine returns a flat list of all of the accounts that are descendants of the specified account...
Definition Account.cpp:3044
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition qofbook.cpp:521
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition qofclass.h:123
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition qofclass.h:130
void(* QofInstanceForeachCB)(QofInstance *, gpointer user_data)
Callback type for qof_collection_foreach.
Definition qofid.h:146
const gchar * QofIdTypeConst
QofIdTypeConst declaration.
Definition qofid.h:82
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition guid.h:84
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:208
const GncGUID * qof_instance_get_guid(gconstpointer inst)
Return the GncGUID of this instance.
gboolean qof_instance_get_destroying(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object is about to be destroyed.
GncBudget * gnc_budget_new(QofBook *book)
Creates and initializes a Budget.
STRUCTS.
The type used to store guids in C.
Definition guid.h:75
Data-passing struct for callbacks to qof_object_foreach() used in GncSqlObjectBackend::write().