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