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 <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 
66 static 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 
85 static 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 
97 static gpointer get_split_reconcile_state (gpointer pObject);
98 static void set_split_reconcile_state (gpointer pObject, gpointer pValue);
99 static void set_split_lot (gpointer pObject, gpointer pLot);
100 
101 #define SPLIT_MAX_MEMO_LEN 2048
102 #define SPLIT_MAX_ACTION_LEN 2048
103 
104 static 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 
126 static const EntryVec post_date_col_table
127 {
128  gnc_sql_make_table_entry<CT_TIME>("post_date", 0, 0, "post-date"),
129 };
130 
131 static const EntryVec account_guid_col_table
132 {
133  gnc_sql_make_table_entry<CT_ACCOUNTREF>("account_guid", 0, COL_NNUL,
134  "account"),
135 };
136 
137 static const EntryVec tx_guid_col_table
138 {
139  gnc_sql_make_table_entry<CT_GUID>("tx_guid", 0, 0, "guid"),
140 };
141 
142 GncSqlTransBackend::GncSqlTransBackend() :
143  GncSqlObjectBackend(TX_TABLE_VERSION, GNC_ID_TRANS,
144  TRANSACTION_TABLE, tx_col_table) {}
145 
146 GncSqlSplitBackend::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 
159 static gpointer
160 get_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 
172 static void
173 set_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 
184 static void
185 set_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 
202 static Split*
203 load_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 }
247 static void
248 load_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;
276  (BookLookupFn)xaccSplitLookup);
277 }
278 
279 static Transaction*
280 load_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 
318 typedef 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 
336 static void
337 query_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 /* ================================================================= */
408 void
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 }
441 void
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 /* ================================================================= */
488 static void
489 delete_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 
512 static gboolean
513 delete_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 
541 bool
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);
553  if (qof_instance_get_destroying (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 
584 bool
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);
596  if (qof_instance_get_destroying (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 /* ================================================================= */
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 
716 void
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 
729 typedef struct
730 {
731  GncSqlStatementPtr stmt;
732  gboolean has_been_run;
734 
735 /* ----------------------------------------------------------------- */
736 typedef struct
737 {
738  const GncSqlBackend* sql_be;
739  Account* acct;
740  char reconcile_state;
741  gnc_numeric balance;
743 
744 static void
745 set_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 
756 static void
757 set_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 
768 static void
769 set_acct_bal_balance (gpointer pObject, gnc_numeric value)
770 {
772 
773  g_return_if_fail (pObject != NULL);
774 
775  bal->balance = value;
776 }
777 
778 static 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 /* ----------------------------------------------------------------- */
789 template<> 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 
821 template<> void
823 {
824  add_objectref_guid_to_table(vec);
825 }
826 
827 template<> 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 ===================== */
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:1475
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:3239
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:1516
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:2032