GnuCash c935c2f+
Loading...
Searching...
No Matches
gnc-transaction-sql.cpp
1/********************************************************************
2 * gnc-transaction-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/gi18n.h>
32
33#include "qof.h"
34#include "qofquery-p.h"
35#include "qofquerycore-p.h"
36
37#include "Account.h"
38#include "Transaction.h"
39#include <TransactionP.hpp>
40#include <Scrub.h>
41#include "gnc-lot.h"
42#include "engine-helpers.h"
43#include "gnc-commodity.h"
44#include "gnc-engine.h"
45
46#ifdef S_SPLINT_S
47#include "splint-defs.h"
48#endif
49
50#include <string>
51#include <sstream>
52
53#include "escape.h"
54
55#include <gnc-datetime.hpp>
56#include "gnc-sql-connection.hpp"
57#include "gnc-sql-backend.hpp"
58#include "gnc-sql-object-backend.hpp"
59#include "gnc-sql-column-table-entry.hpp"
60#include "gnc-transaction-sql.h"
61#include "gnc-commodity-sql.h"
62#include "gnc-slots-sql.h"
63
64#define SIMPLE_QUERY_COMPILATION 1
65
66static QofLogModule log_module = G_LOG_DOMAIN;
67
68#define TRANSACTION_TABLE "transactions"
69#define TX_TABLE_VERSION 4
70#define SPLIT_TABLE "splits"
71#define SPLIT_TABLE_VERSION 5
72
74{
75 split_info_t () = default;
76 split_info_t (GncSqlBackend* sql_be, bool o,
77 GncSqlObjectBackend* e, const GncGUID* g):
78 write_objects_t(sql_be, o, e), guid{g} {}
79 const GncGUID* guid;
80};
81
82#define TX_MAX_NUM_LEN 2048
83#define TX_MAX_DESCRIPTION_LEN 2048
84
85static const EntryVec tx_col_table
86{
87 gnc_sql_make_table_entry<CT_GUID>("guid", 0, COL_NNUL | COL_PKEY, "guid"),
88 gnc_sql_make_table_entry<CT_COMMODITYREF>("currency_guid", 0, COL_NNUL,
89 "currency"),
90 gnc_sql_make_table_entry<CT_STRING>("num", TX_MAX_NUM_LEN, COL_NNUL, "num"),
91 gnc_sql_make_table_entry<CT_TIME>("post_date", 0, 0, "post-date"),
92 gnc_sql_make_table_entry<CT_TIME>("enter_date", 0, 0, "enter-date"),
93 gnc_sql_make_table_entry<CT_STRING>("description", TX_MAX_DESCRIPTION_LEN,
94 0, "description"),
95};
96
97static gpointer get_split_reconcile_state (gpointer pObject);
98static void set_split_reconcile_state (gpointer pObject, gpointer pValue);
99static void set_split_lot (gpointer pObject, gpointer pLot);
100
101#define SPLIT_MAX_MEMO_LEN 2048
102#define SPLIT_MAX_ACTION_LEN 2048
103
104static const EntryVec split_col_table
105{
106 gnc_sql_make_table_entry<CT_GUID>("guid", 0, COL_NNUL | COL_PKEY, "guid"),
107 gnc_sql_make_table_entry<CT_TXREF>("tx_guid", 0, COL_NNUL, "transaction"),
108 gnc_sql_make_table_entry<CT_ACCOUNTREF>("account_guid", 0, COL_NNUL,
109 "account"),
110 gnc_sql_make_table_entry<CT_STRING>("memo", SPLIT_MAX_MEMO_LEN, COL_NNUL,
111 "memo"),
112 gnc_sql_make_table_entry<CT_STRING>("action", SPLIT_MAX_ACTION_LEN,
113 COL_NNUL, "action"),
114 gnc_sql_make_table_entry<CT_STRING>("reconcile_state", 1, COL_NNUL,
115 (QofAccessFunc)get_split_reconcile_state,
116 set_split_reconcile_state),
117 gnc_sql_make_table_entry<CT_TIME>("reconcile_date", 0, 0,
118 "reconcile-date"),
119 gnc_sql_make_table_entry<CT_NUMERIC>("value", 0, COL_NNUL, "value"),
120 gnc_sql_make_table_entry<CT_NUMERIC>("quantity", 0, COL_NNUL, "amount"),
121 gnc_sql_make_table_entry<CT_LOTREF>("lot_guid", 0, 0,
123 set_split_lot),
124};
125
126static const EntryVec post_date_col_table
127{
128 gnc_sql_make_table_entry<CT_TIME>("post_date", 0, 0, "post-date"),
129};
130
131static const EntryVec account_guid_col_table
132{
133 gnc_sql_make_table_entry<CT_ACCOUNTREF>("account_guid", 0, COL_NNUL,
134 "account"),
135};
136
137static const EntryVec tx_guid_col_table
138{
139 gnc_sql_make_table_entry<CT_GUID>("tx_guid", 0, 0, "guid"),
140};
141
142GncSqlTransBackend::GncSqlTransBackend() :
143 GncSqlObjectBackend(TX_TABLE_VERSION, GNC_ID_TRANS,
144 TRANSACTION_TABLE, tx_col_table) {}
145
146GncSqlSplitBackend::GncSqlSplitBackend() :
147 GncSqlObjectBackend(SPLIT_TABLE_VERSION, GNC_ID_SPLIT,
148 SPLIT_TABLE, split_col_table) {}
149
150/* These functions exist but have not been tested.
151 #if LOAD_TRANSACTIONS_AS_NEEDED
152 compile_split_query,
153 run_split_query,
154 free_split_query,
155*/
156
157/* ================================================================= */
158
159static gpointer
160get_split_reconcile_state (gpointer pObject)
161{
162 static gchar c[2];
163
164 g_return_val_if_fail (pObject != NULL, NULL);
165 g_return_val_if_fail (GNC_IS_SPLIT (pObject), NULL);
166
167 c[0] = xaccSplitGetReconcile (GNC_SPLIT (pObject));
168 c[1] = '\0';
169 return (gpointer)c;
170}
171
172static void
173set_split_reconcile_state (gpointer pObject, gpointer pValue)
174{
175 const gchar* s = (const gchar*)pValue;
176
177 g_return_if_fail (pObject != NULL);
178 g_return_if_fail (GNC_IS_SPLIT (pObject));
179 g_return_if_fail (pValue != NULL);
180
181 xaccSplitSetReconcile (GNC_SPLIT (pObject), s[0]);
182}
183
184static void
185set_split_lot (gpointer pObject, gpointer pLot)
186{
187 GNCLot* lot;
188 Split* split;
189
190 g_return_if_fail (pObject != NULL);
191 g_return_if_fail (GNC_IS_SPLIT (pObject));
192
193 if (pLot == NULL) return;
194
195 g_return_if_fail (GNC_IS_LOT (pLot));
196
197 split = GNC_SPLIT (pObject);
198 lot = GNC_LOT (pLot);
199 gnc_lot_add_split (lot, split);
200}
201
202static Split*
203load_single_split (GncSqlBackend* sql_be, GncSqlRow& row)
204{
205 const GncGUID* guid;
206 GncGUID split_guid;
207 Split* pSplit = NULL;
208
209 g_return_val_if_fail (sql_be != NULL, NULL);
210
211 guid = gnc_sql_load_guid (sql_be, row);
212 if (guid == NULL) return NULL;
213 if (guid_equal (guid, guid_null ()))
214 {
215 PWARN ("Bad GUID, creating new");
216 split_guid = guid_new_return ();
217 }
218 else
219 {
220 split_guid = *guid;
221 pSplit = xaccSplitLookup (&split_guid, sql_be->book());
222 }
223
224 if (pSplit)
225 return pSplit; //Already loaded, nothing to do.
226
227 pSplit = xaccMallocSplit (sql_be->book());
228 gnc_sql_load_object (sql_be, row, GNC_ID_SPLIT, pSplit, split_col_table);
229
230 /*# -ifempty */
231 if (pSplit != xaccSplitLookup (&split_guid, sql_be->book()))
232 {
233 gchar guidstr[GUID_ENCODING_LENGTH + 1];
234 guid_to_string_buff (qof_instance_get_guid (pSplit), guidstr);
235 PERR ("A malformed split with id %s was found in the dataset.", guidstr);
237 pSplit = NULL;
238 }
239 if (!xaccSplitGetAccount(pSplit))
240 {
241 gchar guidstr[GUID_ENCODING_LENGTH + 1];
242 guid_to_string_buff (qof_instance_get_guid (pSplit), guidstr);
243 PERR("Split %s created with no account!", guidstr);
244 }
245 return pSplit;
246}
247static void
248load_splits_for_transactions (GncSqlBackend* sql_be, std::string selector)
249{
250 g_return_if_fail (sql_be != NULL);
251
252 const std::string spkey(split_col_table[0]->name());
253 const std::string sskey(tx_guid_col_table[0]->name());
254 const std::string tpkey(tx_col_table[0]->name());
255
256 std::string sql("SELECT ");
257 if (selector.empty())
258 {
259 sql += SPLIT_TABLE ".* FROM " SPLIT_TABLE " INNER JOIN "
260 TRANSACTION_TABLE " ON " SPLIT_TABLE "." + sskey + " = "
261 TRANSACTION_TABLE "." + tpkey;
262 selector = "(SELECT DISTINCT " + tpkey + " FROM " TRANSACTION_TABLE ")";
263 }
264 else
265 sql += " * FROM " SPLIT_TABLE " WHERE " + sskey + " IN " + selector;
266
267 // Execute the query and load the splits
268 auto stmt = sql_be->create_statement_from_sql(sql);
269 auto result = sql_be->execute_select_statement (stmt);
270
271 for (auto row : *result)
272 load_single_split (sql_be, row);
273 sql = "SELECT DISTINCT ";
274 sql += spkey + " FROM " SPLIT_TABLE " WHERE " + sskey + " IN " + selector;
275 gnc_sql_slots_load_for_sql_subquery(sql_be, sql,
276 (BookLookupFn)xaccSplitLookup);
277}
278
279static Transaction*
280load_single_tx (GncSqlBackend* sql_be, GncSqlRow& row)
281{
282 const GncGUID* guid;
283 GncGUID tx_guid;
284 Transaction* pTx;
285
286 g_return_val_if_fail (sql_be != NULL, NULL);
287
288 guid = gnc_sql_load_guid (sql_be, row);
289 if (guid == NULL) return NULL;
290 tx_guid = *guid;
291
292 pTx = xaccTransLookup (&tx_guid, sql_be->book());
293 if (pTx)
294 return nullptr; // Nothing to do.
295
296 pTx = xaccMallocTransaction (sql_be->book());
297 xaccTransBeginEdit (pTx);
298 gnc_sql_load_object (sql_be, row, GNC_ID_TRANS, pTx, tx_col_table);
299
300 if (pTx != xaccTransLookup (&tx_guid, sql_be->book()))
301 {
302 gchar guidstr[GUID_ENCODING_LENGTH + 1];
304 PERR ("A malformed transaction with id %s was found in the dataset.", guidstr);
306 pTx = NULL;
307 }
308
309 return pTx;
310}
311
318typedef struct
319{
320 Account* acc;
321 gnc_numeric start_bal;
322 gnc_numeric end_bal;
323 gnc_numeric start_cleared_bal;
324 gnc_numeric end_cleared_bal;
325 gnc_numeric start_reconciled_bal;
326 gnc_numeric end_reconciled_bal;
328
336static void
337query_transactions (GncSqlBackend* sql_be, std::string selector)
338{
339 g_return_if_fail (sql_be != NULL);
340
341 const std::string tpkey(tx_col_table[0]->name());
342 std::string sql("SELECT * FROM " TRANSACTION_TABLE);
343
344 if (!selector.empty() && selector[0] == '(')
345 sql += " WHERE " + tpkey + " IN " + selector;
346 else if (!selector.empty()) // plain condition
347 sql += " WHERE " + selector;
348 auto stmt = sql_be->create_statement_from_sql(sql);
349 auto result = sql_be->execute_select_statement(stmt);
350 if (result->begin() == result->end())
351 {
352 PINFO("Query %s returned no results", sql.c_str());
353 return;
354 }
355
356 Transaction* tx;
357
358 // Load the transactions
359 InstanceVec instances;
360 instances.reserve(result->size());
361 for (auto row : *result)
362 {
363 tx = load_single_tx (sql_be, row);
364 if (tx != nullptr)
365 {
367 instances.push_back(QOF_INSTANCE(tx));
368 }
369 }
370
371 // Load all splits and slots for the transactions
372 if (!instances.empty())
373 {
374 const std::string tpkey(tx_col_table[0]->name());
375 if (!selector.empty() && (selector[0] != '('))
376 {
377 auto tselector = std::string ("(SELECT DISTINCT ");
378 tselector += tpkey + " FROM " TRANSACTION_TABLE " WHERE " + selector + ")";
379 selector = tselector;
380 }
381
382 load_splits_for_transactions (sql_be, selector);
383
384 if (selector.empty())
385 {
386 selector = "SELECT DISTINCT ";
387 selector += tpkey + " FROM " TRANSACTION_TABLE;
388 }
389 gnc_sql_slots_load_for_sql_subquery (sql_be, selector,
390 (BookLookupFn)xaccTransLookup);
391 }
392
393 // Commit all of the transactions, but don't scrub because any
394 // scrubbing changes won't be written back to the database
395 xaccDisableDataScrubbing();
396 for (auto instance : instances)
397 xaccTransCommitEdit(GNC_TRANSACTION(instance));
398 xaccEnableDataScrubbing();
399}
400
401
402/* ================================================================= */
408void
410{
411 gint version;
412 gboolean ok;
413
414 g_return_if_fail (sql_be != NULL);
415
416 version = sql_be->get_table_version( m_table_name.c_str());
417 if (version == 0)
418 {
419 (void)sql_be->create_table(TRANSACTION_TABLE, TX_TABLE_VERSION,
420 tx_col_table);
421 ok = sql_be->create_index ("tx_post_date_index", TRANSACTION_TABLE,
422 post_date_col_table);
423 if (!ok)
424 {
425 PERR ("Unable to create index\n");
426 }
427 }
428 else if (version < m_version)
429 {
430 /* Upgrade:
431 1->2: 64 bit int handling
432 2->3: allow dates to be NULL
433 3->4: Use DATETIME instead of TIMESTAMP in MySQL
434 */
435 sql_be->upgrade_table(m_table_name.c_str(), tx_col_table);
436 sql_be->set_table_version (m_table_name.c_str(), m_version);
437 PINFO ("Transactions table upgraded from version %d to version %d\n",
438 version, m_version);
439 }
440}
441void
443{
444 g_return_if_fail (sql_be != nullptr);
445
446 auto version = sql_be->get_table_version( m_table_name.c_str());
447 if (version == 0)
448 {
449 (void)sql_be->create_table(m_table_name.c_str(),
450 m_version, m_col_table);
451 if (!sql_be->create_index("splits_tx_guid_index",
452 m_table_name.c_str(), tx_guid_col_table))
453 PERR ("Unable to create index\n");
454 if (!sql_be->create_index("splits_account_guid_index",
455 m_table_name.c_str(),
456 account_guid_col_table))
457 PERR ("Unable to create index\n");
458 }
459 else if (version < SPLIT_TABLE_VERSION)
460 {
461
462 /* Upgrade:
463 1->2: 64 bit int handling
464 3->4: Split reconcile date can be NULL
465 4->5: Use DATETIME instead of TIMESTAMP in MySQL
466 */
467 sql_be->upgrade_table(m_table_name.c_str(), split_col_table);
468 if (!sql_be->create_index("splits_tx_guid_index",
469 m_table_name.c_str(),
470 tx_guid_col_table))
471 PERR ("Unable to create index\n");
472 if (!sql_be->create_index("splits_account_guid_index",
473 m_table_name.c_str(),
474 account_guid_col_table))
475 PERR ("Unable to create index\n");
476 sql_be->set_table_version (m_table_name.c_str(), m_version);
477 PINFO ("Splits table upgraded from version %d to version %d\n", version,
478 m_version);
479 }
480}
481/* ================================================================= */
488static void
489delete_split_slots_cb (gpointer data, gpointer user_data)
490{
491 split_info_t* split_info = (split_info_t*)user_data;
492 Split* pSplit = GNC_SPLIT (data);
493
494 g_return_if_fail (data != NULL);
495 g_return_if_fail (GNC_IS_SPLIT (data));
496 g_return_if_fail (user_data != NULL);
497
498 if (split_info->is_ok)
499 {
500 split_info->is_ok = gnc_sql_slots_delete (split_info->be,
501 qof_instance_get_guid (QOF_INSTANCE (pSplit)));
502 }
503}
504
512static gboolean
513delete_splits (GncSqlBackend* sql_be, Transaction* pTx)
514{
515 split_info_t split_info;
516
517 g_return_val_if_fail (sql_be != NULL, FALSE);
518 g_return_val_if_fail (pTx != NULL, FALSE);
519
520 if (!sql_be->do_db_operation(OP_DB_DELETE, SPLIT_TABLE,
521 SPLIT_TABLE, pTx, tx_guid_col_table))
522 {
523 return FALSE;
524 }
525 split_info.be = sql_be;
526 split_info.is_ok = TRUE;
527
528 g_list_foreach (xaccTransGetSplitList (pTx), delete_split_slots_cb,
529 &split_info);
530
531 return split_info.is_ok;
532}
533
541bool
543{
544 E_DB_OPERATION op;
545 gboolean is_infant;
546 gboolean is_ok;
547 GncGUID* guid = (GncGUID*)qof_instance_get_guid (inst);
548
549 g_return_val_if_fail (inst != NULL, FALSE);
550 g_return_val_if_fail (sql_be != NULL, FALSE);
551
552 is_infant = qof_instance_get_infant (inst);
554 {
555 op = OP_DB_DELETE;
556 }
557 else if (sql_be->pristine() || is_infant)
558 {
559 op = OP_DB_INSERT;
560 }
561 else
562 {
563 op = OP_DB_UPDATE;
564 }
565
566 if (guid_equal (guid, guid_null ()))
567 {
568 *guid = guid_new_return ();
569 qof_instance_set_guid (inst, guid);
570 }
571
572 is_ok = sql_be->do_db_operation(op, SPLIT_TABLE, GNC_ID_SPLIT,
573 inst, split_col_table);
574
575 if (is_ok && !qof_instance_get_destroying (inst))
576 {
577 is_ok = gnc_sql_slots_save (sql_be, guid, is_infant, inst);
578 }
579
580 return is_ok;
581}
582
583
584bool
586{
587 E_DB_OPERATION op;
588 gboolean is_ok = TRUE;
589 const char* err = NULL;
590
591 g_return_val_if_fail (sql_be != NULL, FALSE);
592 g_return_val_if_fail (inst != NULL, FALSE);
593
594 auto pTx = GNC_TRANS(inst);
595 auto is_infant = qof_instance_get_infant (inst);
597 {
598 op = OP_DB_DELETE;
599 }
600 else if (sql_be->pristine() || is_infant)
601 {
602 op = OP_DB_INSERT;
603 }
604 else
605 {
606 op = OP_DB_UPDATE;
607 }
608
609 if (op != OP_DB_DELETE)
610 {
611 gnc_commodity* commodity = xaccTransGetCurrency (pTx);
612 // Ensure the commodity is in the db
613 is_ok = sql_be->save_commodity(commodity);
614 if (! is_ok)
615 {
616 err = "Commodity save failed: Probably an invalid or missing currency";
618 }
619 }
620
621 if (is_ok)
622 {
623 is_ok = sql_be->do_db_operation(op, TRANSACTION_TABLE, GNC_ID_TRANS,
624 pTx, tx_col_table);
625 if (! is_ok)
626 {
627 err = "Transaction header save failed. Check trace log for SQL errors";
628 }
629 }
630
631 if (is_ok)
632 {
633 // Commit slots
634 auto guid = qof_instance_get_guid (inst);
635 if (!qof_instance_get_destroying (inst))
636 {
637 is_ok = gnc_sql_slots_save (sql_be, guid, is_infant, inst);
638 if (! is_ok)
639 {
640 err = "Slots save failed. Check trace log for SQL errors";
641 }
642 }
643 else
644 {
645 is_ok = gnc_sql_slots_delete (sql_be, guid);
646 if (! is_ok)
647 {
648 err = "Slots delete failed. Check trace log for SQL errors";
649 }
650 if (is_ok)
651 {
652 is_ok = delete_splits (sql_be, pTx);
653 if (! is_ok)
654 {
655 err = "Split delete failed. Check trace log for SQL errors";
656 }
657 }
658 }
659 }
660 if (! is_ok)
661 {
662 Split* split = xaccTransGetSplit (pTx, 0);
663 Account* acc = xaccSplitGetAccount (split);
664 gchar *datestr = qof_print_date (xaccTransGetDate (pTx));
665 /* FIXME: This needs to be implemented
666 const char *message1 = "Transaction %s dated %s in account %s not saved due to %s.%s";
667 const char *message2 = "\nDatabase may be corrupted, check your data carefully.";
668 qof_error_format_secondary_text( GTK_MESSAGE_DIALOG( msg ),
669 message1,
670 xaccTransGetDescription( pTx ),
671 qof_print_date( xaccTransGetDate( pTx ) ),
672 xaccAccountGetName( acc ),
673 err,
674 message2 );
675 */
676 PERR ("Transaction %s dated %s in account %s not saved due to %s.\n",
677 xaccTransGetDescription (pTx), datestr, xaccAccountGetName (acc), err);
678 g_free (datestr);
679 }
680 return is_ok;
681}
682
683/* ================================================================= */
690void gnc_sql_transaction_load_tx_for_account (GncSqlBackend* sql_be,
691 Account* account)
692{
693 const GncGUID* guid;
694
695 g_return_if_fail (sql_be != NULL);
696 g_return_if_fail (account != NULL);
697
698 guid = qof_instance_get_guid (QOF_INSTANCE (account));
699
700 const std::string tpkey(tx_col_table[0]->name()); //guid
701 const std::string spkey(split_col_table[0]->name()); //guid
702 const std::string stkey(split_col_table[1]->name()); //txn_guid
703 const std::string sakey(split_col_table[2]->name()); //account_guid
704 std::string sql("(SELECT DISTINCT ");
705 sql += stkey + " FROM " SPLIT_TABLE " WHERE " + sakey + " = '";
706 sql += gnc::GUID(*guid).to_string() + "')";
707 query_transactions (sql_be, sql);
708}
709
716void
718{
719 g_return_if_fail (sql_be != NULL);
720
721 auto root = gnc_book_get_root_account (sql_be->book());
722 gnc_account_foreach_descendant(root, (AccountCb)xaccAccountBeginEdit,
723 nullptr);
724 query_transactions (sql_be, "");
725 gnc_account_foreach_descendant(root, (AccountCb)xaccAccountCommitEdit,
726 nullptr);
727}
728
729typedef struct
730{
731 GncSqlStatementPtr stmt;
732 gboolean has_been_run;
734
735/* ----------------------------------------------------------------- */
736typedef struct
737{
738 const GncSqlBackend* sql_be;
739 Account* acct;
740 char reconcile_state;
741 gnc_numeric balance;
743
744static void
745set_acct_bal_account_from_guid (gpointer pObject, gpointer pValue)
746{
748 const GncGUID* guid = (const GncGUID*)pValue;
749
750 g_return_if_fail (pObject != NULL);
751 g_return_if_fail (pValue != NULL);
752
753 bal->acct = xaccAccountLookup (guid, bal->sql_be->book());
754}
755
756static void
757set_acct_bal_reconcile_state (gpointer pObject, gpointer pValue)
758{
760 const gchar* s = (const gchar*)pValue;
761
762 g_return_if_fail (pObject != NULL);
763 g_return_if_fail (pValue != NULL);
764
765 bal->reconcile_state = s[0];
766}
767
768static void
769set_acct_bal_balance (gpointer pObject, gnc_numeric value)
770{
772
773 g_return_if_fail (pObject != NULL);
774
775 bal->balance = value;
776}
777
778static const EntryVec acct_balances_col_table
779{
780 gnc_sql_make_table_entry<CT_GUID>("account_guid", 0, 0, nullptr,
781 (QofSetterFunc)set_acct_bal_account_from_guid),
782 gnc_sql_make_table_entry<CT_STRING>("reconcile_state", 1, 0, nullptr,
783 (QofSetterFunc)set_acct_bal_reconcile_state),
784 gnc_sql_make_table_entry<CT_NUMERIC>("quantity", 0, 0, nullptr,
785 (QofSetterFunc)set_acct_bal_balance),
786};
787
788/* ----------------------------------------------------------------- */
789template<> void
791 GncSqlRow& row,
792 QofIdTypeConst obj_name,
793 gpointer pObject) const noexcept
794{
795 g_return_if_fail (sql_be != NULL);
796 g_return_if_fail (pObject != NULL);
797
798 auto val = row.get_string_at_col (m_col_name);
799 if (!val)
800 return;
801
802 GncGUID guid;
803 Transaction *tx = nullptr;
804 if (string_to_guid (val->c_str(), &guid))
805 tx = xaccTransLookup (&guid, sql_be->book());
806
807 // If the transaction is not found, try loading it
808 std::string tpkey(tx_col_table[0]->name());
809 if (tx == nullptr)
810 {
811 std::string sql = tpkey + " = '" + *val + "'";
812 query_transactions ((GncSqlBackend*)sql_be, sql);
813 tx = xaccTransLookup (&guid, sql_be->book());
814 }
815
816 if (tx != nullptr)
817 set_parameter (pObject, tx, get_setter(obj_name), m_gobj_param_name);
818
819}
820
821template<> void
823{
824 add_objectref_guid_to_table(vec);
825}
826
827template<> void
829 const gpointer pObject,
830 PairVec& vec) const noexcept
831{
832 add_objectref_guid_to_query(obj_name, pObject, vec);
833}
834
835/* ========================== END OF FILE ===================== */
Account handling public routines.
convert single-entry accounts to clean double-entry
API for Transactions and Splits (journal entries)
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_index(const std::string &index_name, const std::string &table_name, const EntryVec &col_table) const noexcept
Creates an index in the database.
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.
void upgrade_table(const std::string &table_name, const EntryVec &col_table) noexcept
Upgrades a table to a new structure.
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.
bool save_commodity(gnc_commodity *comm) noexcept
Ensure that a commodity referenced in another object is in fact saved in the database.
bool set_table_version(const std::string &table_name, uint_t version) noexcept
Registers the version for a table.
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...
const EntryVec & m_col_table
The front-end QofIdType.
Row of SQL Query results.
bool commit(GncSqlBackend *sql_be, QofInstance *inst) override
Commits a split to the database.
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.
void create_tables(GncSqlBackend *) override
Creates the transaction and split tables.
void load_all(GncSqlBackend *) override
Loads all transactions.
load and save data to SQL
Commodity handling public routines.
All type declarations for the whole Gnucash engine.
load and save accounts data to SQL
load and save data to SQL
const char * xaccAccountGetName(const Account *acc)
Get the account's name.
Definition Account.cpp:3289
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition Account.cpp:1516
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition Account.cpp:1475
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id,...
Definition Account.cpp:2050
void qof_backend_set_error(QofBackend *qof_be, QofBackendError err)
Set the error on the specified QofBackend.
@ ERR_BACKEND_DATA_CORRUPT
data in db is corrupt
Definition qofbackend.h:70
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
char * qof_print_date(time64 t)
Convenience; calls through to qof_print_date_dmy_buff().
Definition gnc-date.cpp:610
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
Transaction * xaccTransLookup(const GncGUID *guid, QofBook *book)
The xaccTransLookup() subroutine will return the transaction associated with the given id,...
Split * xaccTransGetSplit(const Transaction *trans, int i)
Return a pointer to the indexed split in this transaction's split list.
Transaction * xaccMallocTransaction(QofBook *book)
The xaccMallocTransaction() will malloc memory and initialize it.
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
const gchar * QofIdTypeConst
QofIdTypeConst declaration.
Definition qofid.h:82
const GncGUID * guid_null(void)
Returns a GncGUID which is guaranteed to never reference any entity.
Definition guid.cpp:165
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition guid.h:84
gboolean guid_equal(const GncGUID *guid_1, const GncGUID *guid_2)
Given two GUIDs, return TRUE if they are non-NULL and equal.
Definition guid.cpp:237
GncGUID guid_new_return(void)
Generate a new id.
Definition guid.cpp:193
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.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define PERR(format, args...)
Log a serious error.
Definition qoflog.h:244
#define PWARN(format, args...)
Log a warning.
Definition qoflog.h:250
void gnc_lot_add_split(GNCLot *lot, Split *split)
Adds a split to this lot.
Definition gnc-lot.cpp:579
void xaccTransScrubPostedDate(Transaction *trans)
Changes Transaction date_posted timestamps from 00:00 local to 11:00 UTC.
Definition Scrub.cpp:1543
void xaccSplitSetReconcile(Split *split, char recn)
Set the reconcile flag.
char xaccSplitGetReconcile(const Split *split)
Returns the value of the reconcile flag.
Split * xaccSplitLookup(const GncGUID *guid, QofBook *book)
The xaccSplitLookup() subroutine will return the split associated with the given id,...
Definition Split.cpp:1091
Split * xaccMallocSplit(QofBook *book)
Constructor.
GNCLot * xaccSplitGetLot(const Split *split)
Returns the pointer to the debited/credited Lot where this split belongs to, or NULL if it doesn't be...
Definition Split.cpp:1920
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
STRUCTS.
The type used to store guids in C.
Definition guid.h:75
Structure to hold start/end balances for each account.
Data-passing struct for callbacks to qof_object_foreach() used in GncSqlObjectBackend::write().