GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-transaction-sql.c
Go to the documentation of this file.
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 [email protected] *
20 \********************************************************************/
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 "gnc-lot.h"
40 #include "engine-helpers.h"
41 
42 #include "gnc-backend-sql.h"
43 #include "gnc-transaction-sql.h"
44 #include "gnc-commodity.h"
45 #include "gnc-commodity-sql.h"
46 #include "gnc-slots-sql.h"
47 
48 #include "gnc-engine.h"
49 
50 #include "escape.h"
51 
52 #ifdef S_SPLINT_S
53 #include "splint-defs.h"
54 #endif
55 
56 #define SIMPLE_QUERY_COMPILATION 1
57 #define LOAD_TRANSACTIONS_AS_NEEDED 0
58 
59 static QofLogModule log_module = G_LOG_DOMAIN;
60 
61 #define TRANSACTION_TABLE "transactions"
62 #define TX_TABLE_VERSION 3
63 #define SPLIT_TABLE "splits"
64 #define SPLIT_TABLE_VERSION 4
65 
66 typedef struct
67 {
68  /*@ dependent @*/ GncSqlBackend* be;
69  /*@ dependent @*/
70  const GncGUID* guid;
71  gboolean is_ok;
72 } split_info_t;
73 
74 #define TX_MAX_NUM_LEN 2048
75 #define TX_MAX_DESCRIPTION_LEN 2048
76 
77 static const GncSqlColumnTableEntry tx_col_table[] =
78 {
79  /*@ -full_init_block @*/
80  { "guid", CT_GUID, 0, COL_NNUL | COL_PKEY, "guid" },
81  { "currency_guid", CT_COMMODITYREF, 0, COL_NNUL, "currency" },
82  { "num", CT_STRING, TX_MAX_NUM_LEN, COL_NNUL, "num" },
83  { "post_date", CT_TIMESPEC, 0, 0, "post-date" },
84  { "enter_date", CT_TIMESPEC, 0, 0, "enter-date" },
85  { "description", CT_STRING, TX_MAX_DESCRIPTION_LEN, 0, "description" },
86  { NULL }
87  /*@ +full_init_block @*/
88 };
89 
90 static /*@ dependent @*//*@ null @*/ gpointer get_split_reconcile_state( gpointer pObject );
91 static void set_split_reconcile_state( gpointer pObject, /*@ null @*/ gpointer pValue );
92 static void set_split_lot( gpointer pObject, /*@ null @*/ gpointer pLot );
93 
94 #define SPLIT_MAX_MEMO_LEN 2048
95 #define SPLIT_MAX_ACTION_LEN 2048
96 
97 static const GncSqlColumnTableEntry split_col_table[] =
98 {
99  /*@ -full_init_block @*/
100  { "guid", CT_GUID, 0, COL_NNUL | COL_PKEY, "guid" },
101  { "tx_guid", CT_TXREF, 0, COL_NNUL, "transaction" },
102  { "account_guid", CT_ACCOUNTREF, 0, COL_NNUL, "account" },
103  { "memo", CT_STRING, SPLIT_MAX_MEMO_LEN, COL_NNUL, "memo" },
104  { "action", CT_STRING, SPLIT_MAX_ACTION_LEN, COL_NNUL, "action" },
105  {
106  "reconcile_state", CT_STRING, 1, COL_NNUL, NULL, NULL,
107  (QofAccessFunc)get_split_reconcile_state, set_split_reconcile_state
108  },
109  { "reconcile_date", CT_TIMESPEC, 0, 0, "reconcile-date" },
110  { "value", CT_NUMERIC, 0, COL_NNUL, "value" },
111  { "quantity", CT_NUMERIC, 0, COL_NNUL, "amount" },
112  {
113  "lot_guid", CT_LOTREF, 0, 0, NULL, NULL,
114  (QofAccessFunc)xaccSplitGetLot, set_split_lot
115  },
116  { NULL }
117  /*@ +full_init_block @*/
118 };
119 
120 static const GncSqlColumnTableEntry post_date_col_table[] =
121 {
122  /*@ -full_init_block @*/
123  { "post_date", CT_TIMESPEC, 0, 0, "post-date" },
124  { NULL }
125  /*@ +full_init_block @*/
126 };
127 
128 static const GncSqlColumnTableEntry account_guid_col_table[] =
129 {
130  /*@ -full_init_block @*/
131  { "account_guid", CT_ACCOUNTREF, 0, COL_NNUL, "account" },
132  { NULL }
133  /*@ +full_init_block @*/
134 };
135 
136 static const GncSqlColumnTableEntry tx_guid_col_table[] =
137 {
138  /*@ -full_init_block @*/
139  { "tx_guid", CT_GUID, 0, 0, "guid" },
140  { NULL }
141  /*@ +full_init_block @*/
142 };
143 
144 /* ================================================================= */
145 
146 static /*@ dependent @*//*@ null @*/ gpointer
147 get_split_reconcile_state( gpointer pObject )
148 {
149  static gchar c[2];
150 
151  g_return_val_if_fail( pObject != NULL, NULL );
152  g_return_val_if_fail( GNC_IS_SPLIT(pObject), NULL );
153 
154  c[0] = xaccSplitGetReconcile( GNC_SPLIT(pObject) );
155  c[1] = '\0';
156  return (gpointer)c;
157 }
158 
159 static void
160 set_split_reconcile_state( gpointer pObject, /*@ null @*/ gpointer pValue )
161 {
162  const gchar* s = (const gchar*)pValue;
163 
164  g_return_if_fail( pObject != NULL );
165  g_return_if_fail( GNC_IS_SPLIT(pObject) );
166  g_return_if_fail( pValue != NULL );
167 
168  xaccSplitSetReconcile( GNC_SPLIT(pObject), s[0] );
169 }
170 
171 static void
172 set_split_lot( gpointer pObject, /*@ null @*/ gpointer pLot )
173 {
174  GNCLot* lot;
175  Split* split;
176 
177  g_return_if_fail( pObject != NULL );
178  g_return_if_fail( GNC_IS_SPLIT(pObject) );
179 
180  if ( pLot == NULL ) return;
181 
182  g_return_if_fail( GNC_IS_LOT(pLot) );
183 
184  split = GNC_SPLIT(pObject);
185  lot = GNC_LOT(pLot);
186  gnc_lot_add_split( lot, split );
187 }
188 
189 static /*@ null @*/ Split*
190 load_single_split( GncSqlBackend* be, GncSqlRow* row )
191 {
192  const GncGUID* guid;
193  GncGUID split_guid;
194  Split* pSplit = NULL;
195  gboolean bad_guid = FALSE;
196 
197  g_return_val_if_fail( be != NULL, NULL );
198  g_return_val_if_fail( row != NULL, NULL );
199 
200  guid = gnc_sql_load_guid( be, row );
201  if ( guid == NULL ) return NULL;
202  if (guid_equal(guid, guid_null()))
203  {
204  PWARN("Bad GUID, creating new");
205  bad_guid = TRUE;
206  split_guid = guid_new_return();
207  }
208  else
209  {
210  split_guid = *guid;
211  pSplit = xaccSplitLookup( &split_guid, be->book );
212  }
213 
214  if ( pSplit == NULL )
215  {
216  pSplit = xaccMallocSplit( be->book );
217  }
218 
219  /* If the split is dirty, don't overwrite it */
220  if ( !qof_instance_is_dirty( QOF_INSTANCE(pSplit) ) )
221  {
222  gnc_sql_load_object( be, row, GNC_ID_SPLIT, pSplit, split_col_table );
223  }
224 
225  /*# -ifempty */
226  if (pSplit != xaccSplitLookup( &split_guid, be->book ))
227  {
228  gchar guidstr[GUID_ENCODING_LENGTH+1];
229  guid_to_string_buff(qof_instance_get_guid(pSplit), guidstr);
230  PERR("A malformed split with id %s was found in the dataset.", guidstr);
232  pSplit = NULL;
233  }
234  return pSplit;
235 }
236 
237 static void
238 load_splits_for_tx_list( GncSqlBackend* be, GList* list )
239 {
240  GString* sql;
241  GncSqlResult* result;
242 
243  g_return_if_fail( be != NULL );
244 
245  if ( list == NULL ) return;
246 
247  sql = g_string_sized_new( 40 + (GUID_ENCODING_LENGTH + 3) * g_list_length( list ) );
248  g_string_append_printf( sql, "SELECT * FROM %s WHERE %s IN (", SPLIT_TABLE, tx_guid_col_table[0].col_name );
249  (void)gnc_sql_append_guid_list_to_sql( sql, list, G_MAXUINT );
250  (void)g_string_append( sql, ")" );
251 
252  // Execute the query and load the splits
253  result = gnc_sql_execute_select_sql( be, sql->str );
254  if ( result != NULL )
255  {
256  GList* split_list = NULL;
257  GncSqlRow* row;
258 
259  row = gnc_sql_result_get_first_row( result );
260  while ( row != NULL )
261  {
262  Split* s;
263  s = load_single_split( be, row );
264  if ( s != NULL )
265  {
266  split_list = g_list_prepend( split_list, s );
267  }
268  row = gnc_sql_result_get_next_row( result );
269  }
270 
271  if ( split_list != NULL )
272  {
273  gnc_sql_slots_load_for_list( be, split_list );
274  g_list_free( split_list );
275  }
276 
277  gnc_sql_result_dispose( result );
278  }
279  (void)g_string_free( sql, TRUE );
280 }
281 
282 static /*@ null @*/ Transaction*
283 load_single_tx( GncSqlBackend* be, GncSqlRow* row )
284 {
285  const GncGUID* guid;
286  GncGUID tx_guid;
287  Transaction* pTx;
288 
289  g_return_val_if_fail( be != NULL, NULL );
290  g_return_val_if_fail( row != NULL, NULL );
291 
292  guid = gnc_sql_load_guid( be, row );
293  if ( guid == NULL ) return NULL;
294  tx_guid = *guid;
295 
296  // Don't overwrite the transaction if it's already been loaded (and possibly modified).
297  pTx = xaccTransLookup( &tx_guid, be->book );
298  if ( pTx != NULL )
299  {
300  return NULL;
301  }
302 
303  pTx = xaccMallocTransaction( be->book );
304  xaccTransBeginEdit( pTx );
305  gnc_sql_load_object( be, row, GNC_ID_TRANS, pTx, tx_col_table );
306 
307  if (pTx != xaccTransLookup( &tx_guid, be->book ))
308  {
309  gchar guidstr[GUID_ENCODING_LENGTH+1];
311  PERR("A malformed transaction with id %s was found in the dataset.", guidstr);
313  pTx = NULL;
314  }
315 
316  return pTx;
317 }
318 
325 typedef struct
326 {
327  /*@ dependent @*/ Account* acc;
328  gnc_numeric start_bal;
329  gnc_numeric end_bal;
330  gnc_numeric start_cleared_bal;
331  gnc_numeric end_cleared_bal;
332  gnc_numeric start_reconciled_bal;
333  gnc_numeric end_reconciled_bal;
335 
343 static void
344 query_transactions( GncSqlBackend* be, GncSqlStatement* stmt )
345 {
346  GncSqlResult* result;
347 
348  g_return_if_fail( be != NULL );
349  g_return_if_fail( stmt != NULL );
350 
351  result = gnc_sql_execute_select_statement( be, stmt );
352  if ( result != NULL )
353  {
354  GList* tx_list = NULL;
355  GList* node;
356  GncSqlRow* row;
357  Transaction* tx;
358 #if LOAD_TRANSACTIONS_AS_NEEDED
359  GSList* bal_list = NULL;
360  GSList* nextbal;
361  Account* root = gnc_book_get_root_account( be->book );
362 
364  xaccAccountBeginEdit( root );
365 
366  // Save the start/ending balances (balance, cleared and reconciled) for
367  // every account.
368  gnc_account_foreach_descendant( gnc_book_get_root_account( be->primary_book ),
369  save_account_balances,
370  &bal_list );
371 #endif
372 
373  // Load the transactions
374  row = gnc_sql_result_get_first_row( result );
375  while ( row != NULL )
376  {
377  tx = load_single_tx( be, row );
378  if ( tx != NULL )
379  {
380  tx_list = g_list_prepend( tx_list, tx );
381  }
382  row = gnc_sql_result_get_next_row( result );
383  }
384  gnc_sql_result_dispose( result );
385 
386  // Load all splits and slots for the transactions
387  if ( tx_list != NULL )
388  {
389  gnc_sql_slots_load_for_list( be, tx_list );
390  load_splits_for_tx_list( be, tx_list );
391  }
392 
393  // Commit all of the transactions
394  for ( node = tx_list; node != NULL; node = node->next )
395  {
396  Transaction* pTx = GNC_TRANSACTION(node->data);
397  xaccTransCommitEdit( pTx );
398  }
399  g_list_free( tx_list );
400 
401 #if LOAD_TRANSACTIONS_AS_NEEDED
402  // Update the account balances based on the loaded splits. If the end
403  // balance has changed, update the start balance so that the end
404  // balance is the same as it was before the splits were loaded.
405  // Repeat for cleared and reconciled balances.
406  for ( nextbal = bal_list; nextbal != NULL; nextbal = nextbal->next )
407  {
408  full_acct_balances_t* balns = (full_acct_balances_t*)nextbal->data;
409  gnc_numeric* pnew_end_bal;
410  gnc_numeric* pnew_end_c_bal;
411  gnc_numeric* pnew_end_r_bal;
412  gnc_numeric adj;
413 
414  g_object_get( balns->acc,
415  "end-balance", &pnew_end_bal,
416  "end-cleared-balance", &pnew_end_c_bal,
417  "end-reconciled-balance", &pnew_end_r_bal,
418  NULL );
419 
420  qof_instance_increase_editlevel (balns-acc);
421  if ( !gnc_numeric_eq( *pnew_end_bal, balns->end_bal ) )
422  {
423  adj = gnc_numeric_sub( balns->end_bal, *pnew_end_bal,
425  balns->start_bal = gnc_numeric_add( balns->start_bal, adj,
427  g_object_set( balns->acc, "start-balance", &balns->start_bal, NULL );
428  qof_instance_decrease_editlevel (balns-acc);
429  }
430  if ( !gnc_numeric_eq( *pnew_end_c_bal, balns->end_cleared_bal ) )
431  {
432  adj = gnc_numeric_sub( balns->end_cleared_bal, *pnew_end_c_bal,
434  balns->start_cleared_bal = gnc_numeric_add( balns->start_cleared_bal, adj,
436  g_object_set( balns->acc, "start-cleared-balance", &balns->start_cleared_bal, NULL );
437  }
438  if ( !gnc_numeric_eq( *pnew_end_r_bal, balns->end_reconciled_bal ) )
439  {
440  adj = gnc_numeric_sub( balns->end_reconciled_bal, *pnew_end_r_bal,
442  balns->start_reconciled_bal = gnc_numeric_add( balns->start_reconciled_bal, adj,
444  g_object_set( balns->acc, "start-reconciled-balance", &balns->start_reconciled_bal, NULL );
445  }
446  qof_instance_decrease_editlevel (balns-acc);
447  xaccAccountRecomputeBalance( balns->acc );
448  g_free( pnew_end_bal );
449  g_free( pnew_end_c_bal );
450  g_free( pnew_end_r_bal );
451  g_free( balns );
452  }
453  if ( bal_list != NULL )
454  {
455  g_slist_free( bal_list );
456  }
457 
458  xaccAccountCommitEdit( root );
460 #endif
461  }
462 }
463 
464 /* ================================================================= */
470 static void
471 create_transaction_tables( GncSqlBackend* be )
472 {
473  gint version;
474  gboolean ok;
475 
476  g_return_if_fail( be != NULL );
477 
478  version = gnc_sql_get_table_version( be, TRANSACTION_TABLE );
479  if ( version == 0 )
480  {
481  (void)gnc_sql_create_table( be, TRANSACTION_TABLE, TX_TABLE_VERSION, tx_col_table );
482  ok = gnc_sql_create_index( be, "tx_post_date_index", TRANSACTION_TABLE, post_date_col_table );
483  if ( !ok )
484  {
485  PERR( "Unable to create index\n" );
486  }
487  }
488  else if ( version < TX_TABLE_VERSION )
489  {
490  /* Upgrade:
491  1->2: 64 bit int handling
492  2->3: allow dates to be NULL
493  */
494  gnc_sql_upgrade_table( be, TRANSACTION_TABLE, tx_col_table );
495  (void)gnc_sql_set_table_version( be, TRANSACTION_TABLE, TX_TABLE_VERSION );
496  PINFO("Transactions table upgraded from version %d to version %d\n", version, TX_TABLE_VERSION);
497  }
498 
499  version = gnc_sql_get_table_version( be, SPLIT_TABLE );
500  if ( version == 0 )
501  {
502  (void)gnc_sql_create_table( be, SPLIT_TABLE, SPLIT_TABLE_VERSION, split_col_table );
503  ok = gnc_sql_create_index( be, "splits_tx_guid_index", SPLIT_TABLE, tx_guid_col_table );
504  if ( !ok )
505  {
506  PERR( "Unable to create index\n" );
507  }
508  ok = gnc_sql_create_index( be, "splits_account_guid_index", SPLIT_TABLE, account_guid_col_table );
509  if ( !ok )
510  {
511  PERR( "Unable to create index\n" );
512  }
513  }
514  else if ( version < SPLIT_TABLE_VERSION )
515  {
516 
517  /* Upgrade:
518  1->2: 64 bit int handling
519  3->4: Split reconcile date can be NULL */
520  gnc_sql_upgrade_table( be, SPLIT_TABLE, split_col_table );
521  ok = gnc_sql_create_index( be, "splits_tx_guid_index", SPLIT_TABLE, tx_guid_col_table );
522  if ( !ok )
523  {
524  PERR( "Unable to create index\n" );
525  }
526  ok = gnc_sql_create_index( be, "splits_account_guid_index", SPLIT_TABLE, account_guid_col_table );
527  if ( !ok )
528  {
529  PERR( "Unable to create index\n" );
530  }
531  (void)gnc_sql_set_table_version( be, SPLIT_TABLE, SPLIT_TABLE_VERSION );
532  PINFO("Splits table upgraded from version %d to version %d\n", version, SPLIT_TABLE_VERSION);
533  }
534 }
535 /* ================================================================= */
542 static void
543 delete_split_slots_cb( gpointer data, gpointer user_data )
544 {
545  split_info_t* split_info = (split_info_t*)user_data;
546  Split* pSplit = GNC_SPLIT(data);
547 
548  g_return_if_fail( data != NULL );
549  g_return_if_fail( GNC_IS_SPLIT(data) );
550  g_return_if_fail( user_data != NULL );
551 
552  if ( split_info->is_ok )
553  {
554  split_info->is_ok = gnc_sql_slots_delete( split_info->be,
555  qof_instance_get_guid( QOF_INSTANCE(pSplit) ) );
556  }
557 }
558 
566 static gboolean
567 delete_splits( GncSqlBackend* be, Transaction* pTx )
568 {
569  split_info_t split_info;
570 
571  g_return_val_if_fail( be != NULL, FALSE );
572  g_return_val_if_fail( pTx != NULL, FALSE );
573 
574  if ( !gnc_sql_do_db_operation( be, OP_DB_DELETE, SPLIT_TABLE,
575  SPLIT_TABLE, pTx, tx_guid_col_table ) )
576  {
577  return FALSE;
578  }
579  split_info.be = be;
580  split_info.is_ok = TRUE;
581 
582  g_list_foreach( xaccTransGetSplitList( pTx ), delete_split_slots_cb, &split_info );
583 
584  return split_info.is_ok;
585 }
586 
594 static gboolean
595 commit_split( GncSqlBackend* be, QofInstance* inst )
596 {
597  gint op;
598  gboolean is_infant;
599  gboolean is_ok;
600  GncGUID *guid = (GncGUID*)qof_instance_get_guid(inst);
601 
602  g_return_val_if_fail( inst != NULL, FALSE );
603  g_return_val_if_fail( be != NULL, FALSE );
604 
605  is_infant = qof_instance_get_infant( inst );
606  if ( qof_instance_get_destroying( inst ) )
607  {
608  op = OP_DB_DELETE;
609  }
610  else if ( be->is_pristine_db || is_infant )
611  {
612  op = OP_DB_INSERT;
613  }
614  else
615  {
616  op = OP_DB_UPDATE;
617  }
618 
619  if (guid_equal (guid, guid_null ()))
620  {
621  *guid = guid_new_return ();
622  qof_instance_set_guid (inst, guid);
623  }
624 
625  is_ok = gnc_sql_do_db_operation( be, op, SPLIT_TABLE, GNC_ID_SPLIT,
626  inst, split_col_table );
627 
628  if ( is_ok && !qof_instance_get_destroying (inst))
629  {
630  is_ok = gnc_sql_slots_save( be,
631  guid,
632  is_infant,
633  qof_instance_get_slots( inst ) );
634  }
635 
636  return is_ok;
637 }
638 
639 static void
640 save_split_cb( gpointer data, gpointer user_data )
641 {
642  split_info_t* split_info = (split_info_t*)user_data;
643  Split* pSplit = GNC_SPLIT(data);
644 
645  g_return_if_fail( data != NULL );
646  g_return_if_fail( GNC_IS_SPLIT(data) );
647  g_return_if_fail( user_data != NULL );
648 
649  if ( split_info->is_ok )
650  {
651  split_info->is_ok = commit_split( split_info->be, QOF_INSTANCE(pSplit) );
652  }
653 }
654 
655 static gboolean
656 save_splits( GncSqlBackend* be, const GncGUID* tx_guid, SplitList* pSplitList )
657 {
658  split_info_t split_info;
659 
660  g_return_val_if_fail( be != NULL, FALSE );
661  g_return_val_if_fail( tx_guid != NULL, FALSE );
662  g_return_val_if_fail( pSplitList != NULL, FALSE );
663 
664  split_info.be = be;
665  split_info.guid = tx_guid;
666  split_info.is_ok = TRUE;
667  g_list_foreach( pSplitList, save_split_cb, &split_info );
668 
669  return split_info.is_ok;
670 }
671 
672 static gboolean
673 save_transaction( GncSqlBackend* be, Transaction* pTx, gboolean do_save_splits )
674 {
675  const GncGUID* guid;
676  gint op;
677  gboolean is_infant;
678  QofInstance* inst;
679  gboolean is_ok = TRUE;
680  gchar* err = NULL;
681 
682  g_return_val_if_fail( be != NULL, FALSE );
683  g_return_val_if_fail( pTx != NULL, FALSE );
684 
685  inst = QOF_INSTANCE(pTx);
686  is_infant = qof_instance_get_infant( inst );
687  if ( qof_instance_get_destroying( inst ) )
688  {
689  op = OP_DB_DELETE;
690  }
691  else if ( be->is_pristine_db || is_infant )
692  {
693  op = OP_DB_INSERT;
694  }
695  else
696  {
697  op = OP_DB_UPDATE;
698  }
699 
700  if ( op != OP_DB_DELETE )
701  {
702  gnc_commodity *commodity = xaccTransGetCurrency( pTx );
703  // Ensure the commodity is in the db
704  is_ok = gnc_sql_save_commodity( be, commodity );
705  if ( ! is_ok )
706  {
707  err = "Commodity save failed: Probably an invalid or missing currency";
709  }
710  }
711 
712  if ( is_ok )
713  {
714  is_ok = gnc_sql_do_db_operation( be, op, TRANSACTION_TABLE, GNC_ID_TRANS, pTx, tx_col_table );
715  if ( ! is_ok )
716  {
717  err = "Transaction header save failed. Check trace log for SQL errors";
718  }
719  }
720 
721  if ( is_ok )
722  {
723  // Commit slots and splits
724  guid = qof_instance_get_guid( inst );
725  if ( !qof_instance_get_destroying(inst) )
726  {
727  is_ok = gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) );
728  if ( ! is_ok )
729  {
730  err = "Slots save failed. Check trace log for SQL errors";
731  }
732  if ( is_ok && do_save_splits )
733  {
734  is_ok = save_splits( be, guid, xaccTransGetSplitList( pTx ) );
735  if ( ! is_ok )
736  {
737  err = "Split save failed. Check trace log for SQL errors";
738  }
739  }
740  }
741  else
742  {
743  is_ok = gnc_sql_slots_delete( be, guid );
744  if ( ! is_ok )
745  {
746  err = "Slots delete failed. Check trace log for SQL errors";
747  }
748  if ( is_ok )
749  {
750  is_ok = delete_splits( be, pTx );
751  if ( ! is_ok )
752  {
753  err = "Split delete failed. Check trace log for SQL errors";
754  }
755  }
756  }
757  }
758  if (! is_ok )
759  {
760  G_GNUC_UNUSED gchar *message1 = "Transaction %s dated %s in account %s not saved due to %s.%s";
761  G_GNUC_UNUSED gchar *message2 = "\nDatabase may be corrupted, check your data carefully.";
762  Split* split = xaccTransGetSplit( pTx, 0);
763  Account *acc = xaccSplitGetAccount( split );
764  /* FIXME: This needs to be implemented
765  qof_error_format_secondary_text( GTK_MESSAGE_DIALOG( msg ),
766  message1,
767  xaccTransGetDescription( pTx ),
768  qof_print_date( xaccTransGetDate( pTx ) ),
769  xaccAccountGetName( acc ),
770  err,
771  message2 );
772  */
773  PERR( "Transaction %s dated %s in account %s not saved due to %s.\n",
776  xaccAccountGetName( acc ),
777  err );
778  }
779  return is_ok;
780 }
781 
782 gboolean
784 {
785  g_return_val_if_fail( be != NULL, FALSE );
786  g_return_val_if_fail( inst != NULL, FALSE );
787  g_return_val_if_fail( GNC_IS_TRANS(inst), FALSE );
788 
789  return save_transaction( be, GNC_TRANS(inst), /* do_save_splits */TRUE );
790 }
791 
792 static gboolean
793 commit_transaction( GncSqlBackend* be, QofInstance* inst )
794 {
795  g_return_val_if_fail( be != NULL, FALSE );
796  g_return_val_if_fail( inst != NULL, FALSE );
797  g_return_val_if_fail( GNC_IS_TRANS(inst), FALSE );
798 
799  return save_transaction( be, GNC_TRANS(inst), /* do_save_splits */FALSE );
800 }
801 
802 /* ================================================================= */
810 {
811  const GncGUID* guid;
812  gchar guid_buf[GUID_ENCODING_LENGTH+1];
813  gchar* query_sql;
814  GncSqlStatement* stmt;
815 
816  g_return_if_fail( be != NULL );
817  g_return_if_fail( account != NULL );
818 
819  guid = qof_instance_get_guid( QOF_INSTANCE(account) );
820  (void)guid_to_string_buff( guid, guid_buf );
821  query_sql = g_strdup_printf(
822  "SELECT DISTINCT t.* FROM %s AS t, %s AS s WHERE s.tx_guid=t.guid AND s.account_guid ='%s'",
823  TRANSACTION_TABLE, SPLIT_TABLE, guid_buf );
824  stmt = gnc_sql_create_statement_from_sql( be, query_sql );
825  g_free( query_sql );
826  if ( stmt != NULL )
827  {
828  query_transactions( be, stmt );
829  gnc_sql_statement_dispose( stmt );
830  }
831 }
832 
840 {
841  gchar* query_sql;
842  GncSqlStatement* stmt;
843 
844  g_return_if_fail( be != NULL );
845 
846  query_sql = g_strdup_printf( "SELECT * FROM %s", TRANSACTION_TABLE );
847  stmt = gnc_sql_create_statement_from_sql( be, query_sql );
848  g_free( query_sql );
849  if ( stmt != NULL )
850  {
851  query_transactions( be, stmt );
852  gnc_sql_statement_dispose( stmt );
853  }
854 }
855 
856 static void
857 convert_query_comparison_to_sql( QofQueryPredData* pPredData, gboolean isInverted, GString* sql )
858 {
859  if ( pPredData->how == QOF_COMPARE_LT
860  || ( isInverted && pPredData->how == QOF_COMPARE_GTE ) )
861  {
862  g_string_append( sql, "<" );
863  }
864  else if ( pPredData->how == QOF_COMPARE_LTE
865  || ( isInverted && pPredData->how == QOF_COMPARE_GT ) )
866  {
867  g_string_append( sql, "<=" );
868  }
869  else if ( pPredData->how == QOF_COMPARE_EQUAL
870  || ( isInverted && pPredData->how == QOF_COMPARE_NEQ ) )
871  {
872  g_string_append( sql, "=" );
873  }
874  else if ( pPredData->how == QOF_COMPARE_GT
875  || ( isInverted && pPredData->how == QOF_COMPARE_LTE ) )
876  {
877  g_string_append( sql, ">" );
878  }
879  else if ( pPredData->how == QOF_COMPARE_GTE
880  || ( isInverted && pPredData->how == QOF_COMPARE_LT ) )
881  {
882  g_string_append( sql, ">=" );
883  }
884  else if ( pPredData->how == QOF_COMPARE_NEQ
885  || ( isInverted && pPredData->how == QOF_COMPARE_EQUAL ) )
886  {
887  g_string_append( sql, "~=" );
888  }
889  else
890  {
891  PERR( "Unknown comparison type\n" );
892  g_string_append( sql, "??" );
893  }
894 }
895 
896 static void
897 convert_query_term_to_sql( const GncSqlBackend* be, const gchar* fieldName, QofQueryTerm* pTerm, GString* sql )
898 {
899  QofQueryPredData* pPredData;
900  gboolean isInverted;
901 
902  g_return_if_fail( pTerm != NULL );
903  g_return_if_fail( sql != NULL );
904 
905  pPredData = qof_query_term_get_pred_data( pTerm );
906  isInverted = qof_query_term_is_inverted( pTerm );
907 
908  if ( g_strcmp0( pPredData->type_name, QOF_TYPE_GUID ) == 0 )
909  {
910  query_guid_t guid_data = (query_guid_t)pPredData;
911  GList* guid_entry;
912 
913  g_string_append( sql, "(" );
914  g_string_append( sql, fieldName );
915 
916  switch ( guid_data->options )
917  {
918  case QOF_GUID_MATCH_ANY:
919  if ( isInverted ) g_string_append( sql, " NOT IN (" );
920  else g_string_append( sql, " IN (" );
921  break;
922 
923  case QOF_GUID_MATCH_NONE:
924  if ( isInverted ) g_string_append( sql, " IN (" );
925  else g_string_append( sql, " NOT IN (" );
926  break;
927 
928  default:
929  PERR( "Unexpected GncGUID match type: %d\n", guid_data->options );
930  }
931 
932  for ( guid_entry = guid_data->guids; guid_entry != NULL; guid_entry = guid_entry->next )
933  {
934  gchar guid_buf[GUID_ENCODING_LENGTH+1];
935 
936  if ( guid_entry != guid_data->guids ) g_string_append( sql, "," );
937  (void)guid_to_string_buff( guid_entry->data, guid_buf );
938  g_string_append_printf( sql, "'%s'", guid_buf );
939  }
940  g_string_append( sql, "))" );
941 
942  }
943  else if ( g_strcmp0( pPredData->type_name, QOF_TYPE_CHAR ) == 0 )
944  {
945  query_char_t char_data = (query_char_t)pPredData;
946  int i;
947 
948  if ( isInverted )
949  {
950  g_string_append( sql, "NOT(" );
951  }
952  if ( char_data->options == QOF_CHAR_MATCH_NONE )
953  {
954  g_string_append( sql, "NOT " );
955  }
956  g_string_append( sql, "(" );
957  for ( i = 0; char_data->char_list[i] != '\0'; i++ )
958  {
959  if ( i != 0 )
960  {
961  g_string_append( sql, " OR " );
962  }
963  g_string_append( sql, fieldName );
964  g_string_append( sql, " = '" );
965  g_string_append_c( sql, char_data->char_list[i] );
966  g_string_append( sql, "'" );
967  }
968  g_string_append( sql, ") " );
969  if ( isInverted )
970  {
971  g_string_append( sql, ") " );
972  }
973 
974  }
975  else if ( g_strcmp0( pPredData->type_name, QOF_TYPE_STRING ) == 0 )
976  {
977  query_string_t string_data = (query_string_t)pPredData;
978  sqlEscape* escape = sqlEscape_new();
979 
980  if ( isInverted )
981  {
982  g_string_append( sql, "NOT(" );
983  }
984  if ( pPredData->how == QOF_COMPARE_NEQ )
985  {
986  g_string_append( sql, "NOT(" );
987  }
988  g_string_append( sql, fieldName );
989  if ( string_data->is_regex || string_data->options == QOF_STRING_MATCH_CASEINSENSITIVE )
990  {
991  PWARN( "String is_regex || option = QOF_STRING_MATCH_INSENSITIVE\n" );
992  }
993 // g_string_append( sql, " ~" );
994 // } else {
995  g_string_append( sql, " =" );
996 // }
997 // if( string_data->options == QOF_STRING_MATCH_CASEINSENSITIVE ) {
998 // g_string_append( sql, "*" );
999 // }
1000  g_string_append( sql, "'" );
1001  g_string_append( sql, sqlEscapeString( escape, string_data->matchstring ) );
1002  g_string_append( sql, "'" );
1003  if ( pPredData->how == QOF_COMPARE_NEQ )
1004  {
1005  g_string_append( sql, ")" );
1006  }
1007  if ( isInverted )
1008  {
1009  g_string_append( sql, ")" );
1010  }
1011  sqlEscape_destroy( escape );
1012 
1013  }
1014  else
1015  {
1016  g_string_append( sql, "(" );
1017  g_string_append( sql, fieldName );
1018  convert_query_comparison_to_sql( pPredData, isInverted, sql );
1019 
1020  if ( strcmp( pPredData->type_name, QOF_TYPE_NUMERIC ) == 0 )
1021  {
1022  query_numeric_t pData = (query_numeric_t)pPredData;
1023  double d = gnc_numeric_to_double( pData->amount );
1024 
1025  g_string_append_printf( sql, "%f", d );
1026 
1027  }
1028  else if ( g_strcmp0( pPredData->type_name, QOF_TYPE_DATE ) == 0 )
1029  {
1030  query_date_t date_data = (query_date_t)pPredData;
1031  gchar* datebuf;
1032 
1033  datebuf = gnc_sql_convert_timespec_to_string( be, date_data->date );
1034  g_string_append_printf( sql, "'%s'", datebuf );
1035 
1036  }
1037  else if ( strcmp( pPredData->type_name, QOF_TYPE_INT32 ) == 0 )
1038  {
1039  query_int32_t pData = (query_int32_t)pPredData;
1040 
1041  g_string_append_printf( sql, "%d", pData->val );
1042 
1043  }
1044  else if ( strcmp( pPredData->type_name, QOF_TYPE_INT64 ) == 0 )
1045  {
1046  query_int64_t pData = (query_int64_t)pPredData;
1047 
1048  g_string_append_printf( sql, "%" G_GINT64_FORMAT, pData->val );
1049 
1050  }
1051  else if ( strcmp( pPredData->type_name, QOF_TYPE_DOUBLE ) == 0 )
1052  {
1053  query_double_t pData = (query_double_t)pPredData;
1054 
1055  g_string_append_printf( sql, "%f", pData->val );
1056 
1057  }
1058  else if ( strcmp( pPredData->type_name, QOF_TYPE_BOOLEAN ) == 0 )
1059  {
1060  query_boolean_t pData = (query_boolean_t)pPredData;
1061 
1062  g_string_append_printf( sql, "%d", pData->val );
1063 
1064  }
1065  else
1066  {
1067  PERR( "Unknown query predicate type: %s\n", pPredData->type_name );
1068  }
1069 
1070  g_string_append( sql, ")" );
1071  }
1072 }
1073 
1074 typedef struct
1075 {
1076  GncSqlStatement* stmt;
1077  gboolean has_been_run;
1079 
1080 #define TX_GUID_CHECK 0
1081 
1082 G_GNUC_UNUSED static /*@ null @*/ gpointer
1083 compile_split_query( GncSqlBackend* be, QofQuery* query )
1084 {
1085  split_query_info_t* query_info = NULL;
1086  gchar* query_sql;
1087 
1088  g_return_val_if_fail( be != NULL, NULL );
1089  g_return_val_if_fail( query != NULL, NULL );
1090 
1091  query_info = g_malloc( (gsize)sizeof(split_query_info_t) );
1092  g_assert( query_info != NULL );
1093  query_info->has_been_run = FALSE;
1094 
1095  if ( qof_query_has_terms( query ) )
1096  {
1097  GList* orterms = qof_query_get_terms( query );
1098  GList* orTerm;
1099  GString* sql = g_string_new( "" );
1100  gboolean need_OR = FALSE;
1101 
1102  for ( orTerm = orterms; orTerm != NULL; orTerm = orTerm->next )
1103  {
1104  GList* andterms = (GList*)orTerm->data;
1105  GList* andTerm;
1106  gboolean need_AND = FALSE;
1107 #if TX_GUID_CHECK
1108  gboolean has_tx_guid_check = FALSE;
1109 #endif
1110  if ( need_OR )
1111  {
1112  g_string_append( sql, " OR " );
1113  }
1114  g_string_append( sql, "(" );
1115  for ( andTerm = andterms; andTerm != NULL; andTerm = andTerm->next )
1116  {
1117  QofQueryTerm* term;
1118  GSList* paramPath;
1119  gboolean unknownPath = FALSE;
1120 
1121  term = (QofQueryTerm*)andTerm->data;
1122  paramPath = qof_query_term_get_param_path( term );
1123 
1124  if ( strcmp( paramPath->data, QOF_PARAM_BOOK ) == 0 ) continue;
1125 
1126 #if SIMPLE_QUERY_COMPILATION
1127  if ( strcmp( paramPath->data, SPLIT_ACCOUNT ) != 0
1128  || strcmp( paramPath->next->data, QOF_PARAM_GUID ) != 0 ) continue;
1129 #endif
1130 
1131  if ( need_AND ) g_string_append( sql, " AND " );
1132 
1133  if ( strcmp( paramPath->data, SPLIT_ACCOUNT ) == 0
1134  && strcmp( paramPath->next->data, QOF_PARAM_GUID ) == 0 )
1135  {
1136  convert_query_term_to_sql( be, "s.account_guid", term, sql );
1137 #if SIMPLE_QUERY_COMPILATION
1138  goto done_compiling_query;
1139 #endif
1140 
1141  }
1142  else if ( strcmp( paramPath->data, SPLIT_RECONCILE ) == 0 )
1143  {
1144  convert_query_term_to_sql( be, "s.reconcile_state", term, sql );
1145 
1146  }
1147  else if ( strcmp( paramPath->data, SPLIT_TRANS ) == 0 )
1148  {
1149 #if TX_GUID_CHECK
1150  if ( !has_tx_guid_check )
1151  {
1152  g_string_append( sql, "(splits.tx_guid = transactions.guid) AND " );
1153  has_tx_guid_check = TRUE;
1154  }
1155 #endif
1156  if ( strcmp( paramPath->next->data, TRANS_DATE_POSTED ) == 0 )
1157  {
1158  convert_query_term_to_sql( be, "t.post_date", term, sql );
1159  }
1160  else if ( strcmp( paramPath->next->data, TRANS_DESCRIPTION ) == 0 )
1161  {
1162  convert_query_term_to_sql( be, "t.description", term, sql );
1163  }
1164  else
1165  {
1166  unknownPath = TRUE;
1167  }
1168 
1169  }
1170  else if ( strcmp( paramPath->data, SPLIT_VALUE ) == 0 )
1171  {
1172  convert_query_term_to_sql( be, "s.value_num/s.value_denom", term, sql );
1173 
1174  }
1175  else
1176  {
1177  unknownPath = TRUE;
1178  }
1179 
1180  if ( unknownPath )
1181  {
1182  GString* name = g_string_new( (gchar*)paramPath->data );
1183  while ( paramPath->next != NULL )
1184  {
1185  g_string_append( name, "." );
1186  g_string_append( name, paramPath->next->data );
1187  paramPath = paramPath->next;
1188  }
1189  PERR( "Unknown SPLIT query field: %s\n", name->str );
1190  g_string_free( name, TRUE );
1191  }
1192  need_AND = TRUE;
1193  }
1194 
1195  /* If the last char in the string is a '(', then for some reason, there were
1196  no terms added to the SQL. If so, remove it and ignore the OR term. */
1197  if ( sql->str[sql->len-1] == '(' )
1198  {
1199  g_string_truncate( sql, sql->len - 1 );
1200  need_OR = FALSE;
1201  }
1202  else
1203  {
1204  g_string_append( sql, ")" );
1205  need_OR = TRUE;
1206  }
1207  }
1208 
1209 #if SIMPLE_QUERY_COMPILATION
1210 done_compiling_query:
1211 #endif
1212  if ( sql->len != 0 )
1213  {
1214 #if SIMPLE_QUERY_COMPILATION
1215  g_string_append( sql, ")" );
1216 #endif
1217  query_sql = g_strdup_printf(
1218  "SELECT DISTINCT t.* FROM %s AS t, %s AS s WHERE s.tx_guid=t.guid AND %s",
1219  TRANSACTION_TABLE, SPLIT_TABLE, sql->str );
1220  }
1221  else
1222  {
1223  query_sql = g_strdup_printf( "SELECT * FROM %s", TRANSACTION_TABLE );
1224  }
1225  query_info->stmt = gnc_sql_create_statement_from_sql( be, query_sql );
1226 
1227  g_string_free( sql, TRUE );
1228  g_free( query_sql );
1229 
1230  }
1231  else
1232  {
1233  query_sql = g_strdup_printf( "SELECT * FROM %s", TRANSACTION_TABLE );
1234  query_info->stmt = gnc_sql_create_statement_from_sql( be, query_sql );
1235  g_free( query_sql );
1236  }
1237 
1238  return query_info;
1239 }
1240 
1241 G_GNUC_UNUSED static void
1242 run_split_query( GncSqlBackend* be, gpointer pQuery )
1243 {
1244  split_query_info_t* query_info = (split_query_info_t*)pQuery;
1245 
1246  g_return_if_fail( be != NULL );
1247  g_return_if_fail( pQuery != NULL );
1248 
1249  if ( !query_info->has_been_run )
1250  {
1251  query_transactions( be, query_info->stmt );
1252  query_info->has_been_run = TRUE;
1253  gnc_sql_statement_dispose( query_info->stmt );
1254  query_info->stmt = NULL;
1255  }
1256 }
1257 
1258 G_GNUC_UNUSED static void
1259 free_split_query( GncSqlBackend* be, gpointer pQuery )
1260 {
1261  g_return_if_fail( be != NULL );
1262  g_return_if_fail( pQuery != NULL );
1263 
1264  g_free( pQuery );
1265 }
1266 
1267 /* ----------------------------------------------------------------- */
1268 typedef struct
1269 {
1270  /*@ dependent @*/ const GncSqlBackend* be;
1271  /*@ dependent @*/
1272  Account* acct;
1273  char reconcile_state;
1274  gnc_numeric balance;
1276 
1277 static void
1278 set_acct_bal_account_from_guid( gpointer pObject, gpointer pValue )
1279 {
1281  const GncGUID* guid = (const GncGUID*)pValue;
1282 
1283  g_return_if_fail( pObject != NULL );
1284  g_return_if_fail( pValue != NULL );
1285 
1286  bal->acct = xaccAccountLookup( guid, bal->be->book );
1287 }
1288 
1289 static void
1290 set_acct_bal_reconcile_state( gpointer pObject, gpointer pValue )
1291 {
1293  const gchar* s = (const gchar*)pValue;
1294 
1295  g_return_if_fail( pObject != NULL );
1296  g_return_if_fail( pValue != NULL );
1297 
1298  bal->reconcile_state = s[0];
1299 }
1300 
1301 static void
1302 set_acct_bal_balance( gpointer pObject, gnc_numeric value )
1303 {
1305 
1306  g_return_if_fail( pObject != NULL );
1307 
1308  bal->balance = value;
1309 }
1310 
1311 static const GncSqlColumnTableEntry acct_balances_col_table[] =
1312 {
1313  /*@ -full_init_block @*/
1314  { "account_guid", CT_GUID, 0, 0, NULL, NULL, NULL, (QofSetterFunc)set_acct_bal_account_from_guid },
1315  { "reconcile_state", CT_STRING, 1, 0, NULL, NULL, NULL, (QofSetterFunc)set_acct_bal_reconcile_state },
1316  { "quantity", CT_NUMERIC, 0, 0, NULL, NULL, NULL, (QofSetterFunc)set_acct_bal_balance },
1317  { NULL }
1318  /*@ +full_init_block @*/
1319 };
1320 
1321 G_GNUC_UNUSED static /*@ null @*/ single_acct_balance_t*
1322 load_single_acct_balances( const GncSqlBackend* be, GncSqlRow* row )
1323 {
1324  single_acct_balance_t* bal = NULL;
1325 
1326  g_return_val_if_fail( be != NULL, NULL );
1327  g_return_val_if_fail( row != NULL, NULL );
1328 
1329  bal = g_malloc( (gsize)sizeof(single_acct_balance_t) );
1330  g_assert( bal != NULL );
1331 
1332  bal->be = be;
1333  gnc_sql_load_object( be, row, NULL, bal, acct_balances_col_table );
1334 
1335  return bal;
1336 }
1337 
1338 /*@ null @*/ GSList*
1340 {
1341 #if LOAD_TRANSACTIONS_AS_NEEDED
1342  GncSqlResult* result;
1343  GncSqlStatement* stmt;
1344  gchar* buf;
1345  GSList* bal_slist = NULL;
1346 
1347  g_return_val_if_fail( be != NULL, NULL );
1348 
1349  buf = g_strdup_printf( "SELECT account_guid, reconcile_state, sum(quantity_num) as quantity_num, quantity_denom FROM %s GROUP BY account_guid, reconcile_state, quantity_denom ORDER BY account_guid, reconcile_state",
1350  SPLIT_TABLE );
1351  stmt = gnc_sql_create_statement_from_sql( be, buf );
1352  g_assert( stmt != NULL );
1353  g_free( buf );
1354  result = gnc_sql_execute_select_statement( be, stmt );
1355  gnc_sql_statement_dispose( stmt );
1356  if ( result != NULL )
1357  {
1358  GncSqlRow* row;
1359  acct_balances_t* bal = NULL;
1360 
1361  row = gnc_sql_result_get_first_row( result );
1362  while ( row != NULL )
1363  {
1364  single_acct_balance_t* single_bal;
1365 
1366  // Get the next reconcile state balance and merge with other balances
1367  single_bal = load_single_acct_balances( be, row );
1368  if ( single_bal != NULL )
1369  {
1370  if ( bal != NULL && bal->acct != single_bal->acct )
1371  {
1372  bal->cleared_balance = gnc_numeric_add( bal->cleared_balance, bal->reconciled_balance,
1374  bal->balance = gnc_numeric_add( bal->balance, bal->cleared_balance,
1376  bal_slist = g_slist_append( bal_slist, bal );
1377  bal = NULL;
1378  }
1379  if ( bal == NULL )
1380  {
1381  bal = g_malloc( (gsize)sizeof(acct_balances_t) );
1382  g_assert( bal != NULL );
1383 
1384  bal->acct = single_bal->acct;
1385  bal->balance = gnc_numeric_zero();
1386  bal->cleared_balance = gnc_numeric_zero();
1387  bal->reconciled_balance = gnc_numeric_zero();
1388  }
1389  if ( single_bal->reconcile_state == 'n' )
1390  {
1391  bal->balance = gnc_numeric_add( bal->balance, single_bal->balance,
1393  }
1394  else if ( single_bal->reconcile_state == 'c' )
1395  {
1396  bal->cleared_balance = gnc_numeric_add( bal->cleared_balance, single_bal->balance,
1398  }
1399  else if ( single_bal->reconcile_state == 'y' )
1400  {
1401  bal->reconciled_balance = gnc_numeric_add( bal->reconciled_balance, single_bal->balance,
1403  }
1404  g_free( single_bal );
1405  }
1406  row = gnc_sql_result_get_next_row( result );
1407  }
1408 
1409  // Add the final balance
1410  if ( bal != NULL )
1411  {
1412  bal->cleared_balance = gnc_numeric_add( bal->cleared_balance, bal->reconciled_balance,
1414  bal->balance = gnc_numeric_add( bal->balance, bal->cleared_balance,
1416  bal_slist = g_slist_append( bal_slist, bal );
1417  }
1418  gnc_sql_result_dispose( result );
1419  }
1420 
1421  return bal_slist;
1422 #else
1423  return NULL;
1424 #endif
1425 }
1426 
1427 /* ----------------------------------------------------------------- */
1428 static void
1429 load_tx_guid( const GncSqlBackend* be, GncSqlRow* row,
1430  /*@ null @*/ QofSetterFunc setter, gpointer pObject,
1431  const GncSqlColumnTableEntry* table_row )
1432 {
1433  const GValue* val;
1434  GncGUID guid;
1435  Transaction* tx;
1436  const gchar* guid_str;
1437 
1438  g_return_if_fail( be != NULL );
1439  g_return_if_fail( row != NULL );
1440  g_return_if_fail( pObject != NULL );
1441  g_return_if_fail( table_row != NULL );
1442 
1443  val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
1444  g_assert( val != NULL );
1445  guid_str = g_value_get_string(val);
1446  if ( guid_str != NULL )
1447  {
1448  (void)string_to_guid( guid_str, &guid );
1449  tx = xaccTransLookup( &guid, be->book );
1450 
1451  // If the transaction is not found, try loading it
1452  if ( tx == NULL )
1453  {
1454  gchar* buf;
1455  GncSqlStatement* stmt;
1456 
1457  buf = g_strdup_printf( "SELECT * FROM %s WHERE guid='%s'",
1458  TRANSACTION_TABLE, guid_str );
1460  g_free( buf );
1461  query_transactions( (GncSqlBackend*)be, stmt );
1462  tx = xaccTransLookup( &guid, be->book );
1463  }
1464 
1465  if ( tx != NULL )
1466  {
1467  if ( table_row->gobj_param_name != NULL )
1468  {
1469  qof_instance_increase_editlevel (pObject);
1470  g_object_set( pObject, table_row->gobj_param_name, tx, NULL );
1471  qof_instance_decrease_editlevel (pObject);
1472  }
1473  else
1474  {
1475  g_return_if_fail( setter != NULL );
1476  (*setter)( pObject, (const gpointer)tx );
1477  }
1478  }
1479  }
1480 }
1481 
1482 static GncSqlColumnTypeHandler tx_guid_handler
1483 = { load_tx_guid,
1487  };
1488 /* ================================================================= */
1489 void
1490 gnc_sql_init_transaction_handler( void )
1491 {
1492  static GncSqlObjectBackend be_data_tx =
1493  {
1494  GNC_SQL_BACKEND_VERSION,
1495  GNC_ID_TRANS,
1496  commit_transaction, /* commit */
1497 #if LOAD_TRANSACTIONS_AS_NEEDED
1498  NULL, /* initial load */
1499 #else
1501 #endif
1502  create_transaction_tables, /* create tables */
1503  NULL, /* compile_query */
1504  NULL, /* run_query */
1505  NULL, /* free_query */
1506  NULL /* write */
1507  };
1508  static GncSqlObjectBackend be_data_split =
1509  {
1510  GNC_SQL_BACKEND_VERSION,
1511  GNC_ID_SPLIT,
1512  commit_split, /* commit */
1513  NULL, /* initial_load */
1514  NULL, /* create tables */
1515 #if LOAD_TRANSACTIONS_AS_NEEDED
1516  compile_split_query,
1517  run_split_query,
1518  free_split_query,
1519 #else
1520  NULL, /* compile_query */
1521  NULL, /* run_query */
1522  NULL, /* free_query */
1523 #endif
1524  NULL /* write */
1525  };
1526 
1527  (void)qof_object_register_backend( GNC_ID_TRANS, GNC_SQL_BACKEND, &be_data_tx );
1528  (void)qof_object_register_backend( GNC_ID_SPLIT, GNC_SQL_BACKEND, &be_data_split );
1529 
1530  gnc_sql_register_col_type_handler( CT_TXREF, &tx_guid_handler );
1531 }
1532 
1533 /* ========================== END OF FILE ===================== */
gboolean qof_object_register_backend(QofIdTypeConst type_name, const char *backend_name, gpointer be_data)
Transaction * xaccMallocTransaction(QofBook *book)
Definition: Transaction.c:513
void qof_backend_set_error(QofBackend *be, QofBackendError err)
const GncGUID * qof_instance_get_guid(gconstpointer)
Split * xaccTransGetSplit(const Transaction *trans, int i)
Definition: Transaction.c:2144
gint gnc_sql_get_table_version(const GncSqlBackend *be, const gchar *table_name)
time64 xaccTransGetDate(const Transaction *trans)
Definition: Transaction.c:2215
#define qof_instance_is_dirty
Definition: qofinstance.h:165
void gnc_account_foreach_descendant(const Account *acc, AccountCb thunk, gpointer user_data)
Definition: Account.c:2958
void gnc_sql_upgrade_table(GncSqlBackend *be, const gchar *table_name, const GncSqlColumnTableEntry *col_table)
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
#define PINFO(format, args...)
Definition: qoflog.h:249
load and save data to SQL
void gnc_sql_add_colname_to_list(const GncSqlColumnTableEntry *table_row, GList **pList)
load and save accounts data to SQL
#define COL_NNUL
GncGUID guid_new_return(void)
gboolean qof_instance_get_destroying(gconstpointer ptr)
gboolean string_to_guid(const gchar *string, GncGUID *guid)
char xaccSplitGetReconcile(const Split *split)
Definition: Split.c:1980
gboolean gnc_sql_create_table(GncSqlBackend *be, const gchar *table_name, gint table_version, const GncSqlColumnTableEntry *col_table)
void gnc_lot_add_split(GNCLot *lot, Split *split)
Definition: gnc-lot.c:569
load and save data to SQL
void gnc_sql_register_col_type_handler(const gchar *colType, const GncSqlColumnTypeHandler *handler)
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
void xaccSplitSetReconcile(Split *split, char recn)
Definition: Split.c:1826
QofBackend be
void gnc_sql_add_objectref_guid_col_info_to_list(const GncSqlBackend *be, const GncSqlColumnTableEntry *table_row, GList **pList)
gchar * guid_to_string_buff(const GncGUID *guid, gchar *buff)
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
Definition: qofclass.h:177
gboolean gnc_sql_save_transaction(GncSqlBackend *be, QofInstance *inst)
#define PERR(format, args...)
Definition: qoflog.h:237
#define QOF_PARAM_BOOK
Definition: qofquery.h:109
struct _QofQuery QofQuery
Definition: qofquery.h:90
QofBook * book
Definition: guid.h:65
#define COL_PKEY
gboolean gnc_sql_create_index(const GncSqlBackend *be, const gchar *index_name, const gchar *table_name, const GncSqlColumnTableEntry *col_table)
#define PWARN(format, args...)
Definition: qoflog.h:243
const GncGUID * gnc_sql_load_guid(const GncSqlBackend *be, GncSqlRow *row)
Transaction * xaccTransLookup(const GncGUID *guid, QofBook *book)
Definition: Transaction.c:1024
char * qof_print_date(time64 secs)
gdouble gnc_numeric_to_double(gnc_numeric n)
GList SplitList
Definition: gnc-engine.h:203
Split * xaccSplitLookup(const GncGUID *guid, QofBook *book)
Definition: Split.c:1104
Definition: escape.c:42
Account handling public routines.
guint gnc_sql_append_guid_list_to_sql(GString *sql, GList *list, guint maxCount)
gboolean gnc_sql_set_table_version(GncSqlBackend *be, const gchar *table_name, gint version)
gboolean guid_equal(const GncGUID *guid_1, const GncGUID *guid_2)
void gnc_sql_add_gvalue_objectref_guid_to_slist(const GncSqlBackend *be, QofIdTypeConst obj_name, const gpointer pObject, const GncSqlColumnTableEntry *table_row, GSList **pList)
#define GUID_ENCODING_LENGTH
Definition: guid.h:74
void gnc_sql_slots_load_for_list(GncSqlBackend *be, GList *list)
void xaccAccountRecomputeBalance(Account *acc)
Definition: Account.c:2058
load and save data to SQL
void gnc_sql_transaction_load_tx_for_account(GncSqlBackend *be, Account *account)
const char * xaccTransGetDescription(const Transaction *trans)
Definition: Transaction.c:2184
GSList * gnc_sql_get_account_balances_slist(GncSqlBackend *be)
void xaccTransCommitEdit(Transaction *trans)
Definition: Transaction.c:1579
void xaccTransBeginEdit(Transaction *trans)
Definition: Transaction.c:1380
GncSqlResult * gnc_sql_execute_select_statement(GncSqlBackend *be, GncSqlStatement *stmt)
gboolean gnc_numeric_eq(gnc_numeric a, gnc_numeric b)
All type declarations for the whole Gnucash engine.
void gnc_sql_load_object(const GncSqlBackend *be, GncSqlRow *row, QofIdTypeConst obj_name, gpointer pObject, const GncSqlColumnTableEntry *table)
const gchar * gobj_param_name
Split * xaccMallocSplit(QofBook *book)
Definition: Split.c:582
void gnc_sql_transaction_load_all_tx(GncSqlBackend *be)
gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
gboolean gnc_sql_slots_delete(GncSqlBackend *be, const GncGUID *guid)
Definition: SplitP.h:71
void qof_event_suspend(void)
Suspend all engine events.
void xaccAccountBeginEdit(Account *acc)
Definition: Account.c:1280
Account * xaccSplitGetAccount(const Split *s)
Definition: Split.c:968
const GncGUID * guid_null(void)
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Definition: Transaction.c:1348
void qof_event_resume(void)
gboolean is_pristine_db
void(* QofSetterFunc)(gpointer, gpointer)
Definition: qofclass.h:184
int qof_query_has_terms(QofQuery *q)
gboolean gnc_sql_do_db_operation(GncSqlBackend *be, E_DB_OPERATION op, const gchar *table_name, QofIdTypeConst obj_name, gpointer pObject, const GncSqlColumnTableEntry *table)
GncSqlStatement * gnc_sql_create_statement_from_sql(GncSqlBackend *be, const gchar *sql)
const char * xaccAccountGetName(const Account *acc)
Definition: Account.c:3031
#define GNC_DENOM_AUTO
Definition: gnc-numeric.h:246
API for Transactions and Splits (journal entries)
void xaccAccountCommitEdit(Account *acc)
Definition: Account.c:1321
SplitList * xaccTransGetSplitList(const Transaction *trans)
Definition: Transaction.c:2164
Commodity handling public routines.
gboolean gnc_sql_slots_save(GncSqlBackend *be, const GncGUID *guid, gboolean is_infant, KvpFrame *pFrame)
GncSqlResult * gnc_sql_execute_select_sql(GncSqlBackend *be, const gchar *sql)
GNCLot * xaccSplitGetLot(const Split *split)
Definition: Split.c:1953
const gchar * QofLogModule
Definition: qofid.h:89
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
Definition: Account.c:1827
gchar * gnc_sql_convert_timespec_to_string(const GncSqlBackend *be, Timespec ts)