GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-account-sql.c
Go to the documentation of this file.
1 /********************************************************************
2  * gnc-account-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.h>
32 
33 #include "qof.h"
34 #include "Account.h"
35 #include "AccountP.h"
36 #include "gnc-commodity.h"
37 
38 #include "gnc-backend-sql.h"
39 
40 #include "gnc-account-sql.h"
41 #include "gnc-commodity-sql.h"
42 #include "gnc-slots-sql.h"
43 #include "gnc-transaction-sql.h"
44 
45 #if defined( S_SPLINT_S )
46 #include "splint-defs.h"
47 #endif
48 
49 static QofLogModule log_module = G_LOG_DOMAIN;
50 
51 #define TABLE_NAME "accounts"
52 #define TABLE_VERSION 1
53 
54 static /*@ null @*//*@ dependent @*/ gpointer get_parent( gpointer pObject );
55 static void set_parent( gpointer pObject, /*@ null @*/ gpointer pValue );
56 static void set_parent_guid( gpointer pObject, /*@ null @*/ gpointer pValue );
57 
58 #define ACCOUNT_MAX_NAME_LEN 2048
59 #define ACCOUNT_MAX_TYPE_LEN 2048
60 #define ACCOUNT_MAX_CODE_LEN 2048
61 #define ACCOUNT_MAX_DESCRIPTION_LEN 2048
62 
63 static const GncSqlColumnTableEntry col_table[] =
64 {
65  /*@ -full_init_block @*/
66  { "guid", CT_GUID, 0, COL_NNUL | COL_PKEY, "guid" },
67  { "name", CT_STRING, ACCOUNT_MAX_NAME_LEN, COL_NNUL, "name" },
68  { "account_type", CT_STRING, ACCOUNT_MAX_TYPE_LEN, COL_NNUL, NULL, ACCOUNT_TYPE_ },
69  { "commodity_guid", CT_COMMODITYREF, 0, 0, "commodity" },
70  { "commodity_scu", CT_INT, 0, COL_NNUL, "commodity-scu" },
71  { "non_std_scu", CT_BOOLEAN, 0, COL_NNUL, "non-std-scu" },
72  {
73  "parent_guid", CT_GUID, 0, 0, NULL, NULL,
74  (QofAccessFunc)get_parent, set_parent
75  },
76  { "code", CT_STRING, ACCOUNT_MAX_CODE_LEN, 0, "code" },
77  { "description", CT_STRING, ACCOUNT_MAX_DESCRIPTION_LEN, 0, "description" },
78  { "hidden", CT_BOOLEAN, 0, 0, "hidden" },
79  { "placeholder", CT_BOOLEAN, 0, 0, "placeholder" },
80  { NULL }
81  /*@ +full_init_block @*/
82 };
83 static GncSqlColumnTableEntry parent_col_table[] =
84 {
85  /*@ -full_init_block @*/
86  { "parent_guid", CT_GUID, 0, 0, NULL, NULL, NULL, set_parent_guid },
87  { NULL }
88  /*@ +full_init_block @*/
89 };
90 
91 typedef struct
92 {
93  /*@ dependent @*/ Account* pAccount;
94  GncGUID guid;
96 
97 /* ================================================================= */
98 
99 static /*@ null @*//*@ dependent @*/ gpointer
100 get_parent( gpointer pObject )
101 {
102  const Account* pAccount;
103  const Account* pParent;
104  const GncGUID* parent_guid;
105 
106  g_return_val_if_fail( pObject != NULL, NULL );
107  g_return_val_if_fail( GNC_IS_ACCOUNT(pObject), NULL );
108 
109  pAccount = GNC_ACCOUNT(pObject);
110  pParent = gnc_account_get_parent( pAccount );
111  if ( pParent == NULL )
112  {
113  parent_guid = NULL;
114  }
115  else
116  {
117  parent_guid = qof_instance_get_guid( QOF_INSTANCE(pParent) );
118  }
119 
120  return (gpointer)parent_guid;
121 }
122 
123 static void
124 set_parent( gpointer pObject, /*@ null @*/ gpointer pValue )
125 {
126  Account* pAccount;
127  QofBook* pBook;
128  GncGUID* guid = (GncGUID*)pValue;
129  Account* pParent;
130 
131  g_return_if_fail( pObject != NULL );
132  g_return_if_fail( GNC_IS_ACCOUNT(pObject) );
133 
134  pAccount = GNC_ACCOUNT(pObject);
135  pBook = qof_instance_get_book( QOF_INSTANCE(pAccount) );
136  if ( guid != NULL )
137  {
138  pParent = xaccAccountLookup( guid, pBook );
139  if ( pParent != NULL )
140  {
141  gnc_account_append_child( pParent, pAccount );
142  }
143  }
144 }
145 
146 static void
147 set_parent_guid( gpointer pObject, /*@ null @*/ gpointer pValue )
148 {
150  GncGUID* guid = (GncGUID*)pValue;
151 
152  g_return_if_fail( pObject != NULL );
153  g_return_if_fail( pValue != NULL );
154 
155  s->guid = *guid;
156 }
157 
158 static /*@ dependent @*//*@ null @*/ Account*
159 load_single_account( GncSqlBackend* be, GncSqlRow* row,
160  GList** l_accounts_needing_parents )
161 {
162  const GncGUID* guid;
163  Account* pAccount = NULL;
164 
165  g_return_val_if_fail( be != NULL, NULL );
166  g_return_val_if_fail( row != NULL, NULL );
167  g_return_val_if_fail( l_accounts_needing_parents != NULL, NULL );
168 
169  guid = gnc_sql_load_guid( be, row );
170  if ( guid != NULL )
171  {
172  pAccount = xaccAccountLookup( guid, be->book );
173  }
174  if ( pAccount == NULL )
175  {
176  pAccount = xaccMallocAccount( be->book );
177  }
178  xaccAccountBeginEdit( pAccount );
179  gnc_sql_load_object( be, row, GNC_ID_ACCOUNT, pAccount, col_table );
180  xaccAccountCommitEdit( pAccount );
181 
182  /* If we don't have a parent and this isn't the root account, it might be because the parent
183  account hasn't been loaded yet. Remember the account and its parent guid for later. */
184  if ( gnc_account_get_parent( pAccount ) == NULL
185  && pAccount != gnc_book_get_root_account( be->book ) )
186  {
187  account_parent_guid_struct* s = g_malloc( (gsize)sizeof(account_parent_guid_struct) );
188  g_assert( s != NULL );
189 
190  s->pAccount = pAccount;
191  gnc_sql_load_object( be, row, GNC_ID_ACCOUNT, s, parent_col_table );
192  *l_accounts_needing_parents = g_list_prepend( *l_accounts_needing_parents, s );
193  }
194 
195  return pAccount;
196 }
197 
198 static void
199 load_all_accounts( GncSqlBackend* be )
200 {
201  GncSqlStatement* stmt = NULL;
202  GncSqlResult* result;
203  QofBook* pBook;
204  GList* l_accounts_needing_parents = NULL;
205  GSList* bal_slist;
206  GSList* bal;
207 
208  g_return_if_fail( be != NULL );
209 
210  ENTER( "" );
211 
212  pBook = be->book;
213 
214  stmt = gnc_sql_create_select_statement( be, TABLE_NAME );
215  if ( stmt == NULL )
216  {
217  LEAVE( "stmt == NULL" );
218  return;
219  }
220  result = gnc_sql_execute_select_statement( be, stmt );
221  gnc_sql_statement_dispose( stmt );
222  if ( result != NULL )
223  {
224  GncSqlRow* row = gnc_sql_result_get_first_row( result );
225  gchar* sql;
226 
227  while ( row != NULL )
228  {
229  load_single_account( be, row, &l_accounts_needing_parents );
230  row = gnc_sql_result_get_next_row( result );
231  }
232  gnc_sql_result_dispose( result );
233 
234  sql = g_strdup_printf( "SELECT DISTINCT guid FROM %s", TABLE_NAME );
235  gnc_sql_slots_load_for_sql_subquery( be, sql, (BookLookupFn)xaccAccountLookup );
236  g_free( sql );
237 
238  /* While there are items on the list of accounts needing parents,
239  try to see if the parent has now been loaded. Theory says that if
240  items are removed from the front and added to the back if the
241  parent is still not available, then eventually, the list will
242  shrink to size 0. */
243  if ( l_accounts_needing_parents != NULL )
244  {
245  gboolean progress_made = TRUE;
246  Account* root;
247  Account* pParent;
248  GList* elem;
249 
250  while ( progress_made )
251  {
252  progress_made = FALSE;
253  for ( elem = l_accounts_needing_parents; elem != NULL; )
254  {
256  pParent = xaccAccountLookup( &s->guid, be->book );
257  if ( pParent != NULL )
258  {
259  GList* next_elem;
260 
261  gnc_account_append_child( pParent, s->pAccount );
262  next_elem = g_list_next( elem );
263  l_accounts_needing_parents = g_list_delete_link( l_accounts_needing_parents, elem );
264  g_free( s );
265  elem = next_elem;
266  progress_made = TRUE;
267  }
268  else
269  {
270  /* Can't be up in the for loop because the 'then' clause reads inside a node freed
271  by g_list_delete_link(). */
272  elem = g_list_next( elem );
273  }
274  }
275  }
276 
277  /* Any non-ROOT accounts left over must be parented by the root account */
278  root = gnc_book_get_root_account( pBook );
279  while ( l_accounts_needing_parents != NULL )
280  {
281  account_parent_guid_struct* s = (account_parent_guid_struct*)l_accounts_needing_parents->data;
282  if ( xaccAccountGetType( s->pAccount ) != ACCT_TYPE_ROOT )
283  {
284  gnc_account_append_child( root, s->pAccount );
285  }
286  g_free( s );
287  l_accounts_needing_parents = g_list_delete_link( l_accounts_needing_parents, l_accounts_needing_parents );
288  }
289  }
290 
291  /* Load starting balances */
292  bal_slist = gnc_sql_get_account_balances_slist( be );
293  for ( bal = bal_slist; bal != NULL; bal = bal->next )
294  {
295  acct_balances_t* balances = (acct_balances_t*)bal->data;
296 
297  qof_instance_increase_editlevel (balances->acct);
298  g_object_set( balances->acct,
299  "start-balance", &balances->balance,
300  "start-cleared-balance", &balances->cleared_balance,
301  "start-reconciled-balance", &balances->reconciled_balance,
302  NULL);
303 
304  qof_instance_decrease_editlevel (balances->acct);
305  }
306  if ( bal_slist != NULL )
307  {
308  g_slist_free( bal_slist );
309  }
310  }
311 
312  LEAVE( "" );
313 }
314 
315 /* ================================================================= */
316 static void
317 create_account_tables( GncSqlBackend* be )
318 {
319  gint version;
320 
321  g_return_if_fail( be != NULL );
322 
323  version = gnc_sql_get_table_version( be, TABLE_NAME );
324  if ( version == 0 )
325  {
326  (void)gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table );
327  }
328 }
329 
330 /* ================================================================= */
331 gboolean
332 gnc_sql_save_account( GncSqlBackend* be, QofInstance* inst )
333 {
334  Account* pAcc = GNC_ACCOUNT(inst);
335  const GncGUID* guid;
336  gboolean is_infant;
337  gboolean is_ok = FALSE;
338  gnc_commodity* commodity;
339  gint op;
340 
341  g_return_val_if_fail( be != NULL, FALSE );
342  g_return_val_if_fail( inst != NULL, FALSE );
343  g_return_val_if_fail( GNC_IS_ACCOUNT(inst), FALSE );
344 
345  ENTER( "inst=%p", inst );
346 
347  is_infant = qof_instance_get_infant( inst );
348 
349  // If there is no commodity yet, this might be because a new account name
350  // has been entered directly into the register and an account window will
351  // be opened. The account info is not complete yet, but the name has been
352  // set, triggering this commit
353  commodity = xaccAccountGetCommodity( pAcc );
354 
355  is_ok = TRUE;
356  if ( qof_instance_get_destroying( inst ) )
357  {
358  op = OP_DB_DELETE;
359  }
360  else if ( be->is_pristine_db || is_infant )
361  {
362  op = OP_DB_INSERT;
363  }
364  else
365  {
366  op = OP_DB_UPDATE;
367  }
368 
369  // If not deleting the account, ensure the commodity is in the db
370  if ( op != OP_DB_DELETE && commodity != NULL )
371  {
372  is_ok = gnc_sql_save_commodity( be, commodity );
373  }
374 
375  if ( is_ok )
376  {
377  is_ok = gnc_sql_do_db_operation( be, op, TABLE_NAME, GNC_ID_ACCOUNT, pAcc, col_table );
378  }
379 
380  if ( is_ok )
381  {
382  // Now, commit or delete any slots
383  guid = qof_instance_get_guid( inst );
384  if ( !qof_instance_get_destroying(inst) )
385  {
386  is_ok = gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) );
387  }
388  else
389  {
390  is_ok = gnc_sql_slots_delete( be, guid );
391  }
392  }
393 
394  LEAVE( "is_ok=%d", is_ok );
395 
396  return is_ok;
397 }
398 
399 /* ================================================================= */
400 static void
401 load_account_guid( const GncSqlBackend* be, GncSqlRow* row,
402  /*@ null @*/ QofSetterFunc setter, gpointer pObject,
403  const GncSqlColumnTableEntry* table_row )
404 {
405  const GValue* val;
406  GncGUID guid;
407  Account* account = NULL;
408 
409  g_return_if_fail( be != NULL );
410  g_return_if_fail( row != NULL );
411  g_return_if_fail( pObject != NULL );
412  g_return_if_fail( table_row != NULL );
413 
414  val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
415  if ( val != NULL && G_VALUE_HOLDS_STRING( val ) && g_value_get_string( val ) != NULL )
416  {
417  (void)string_to_guid( g_value_get_string( val ), &guid );
418  account = xaccAccountLookup( &guid, be->book );
419  if ( account != NULL )
420  {
421  if ( table_row->gobj_param_name != NULL )
422  {
423  qof_instance_increase_editlevel (pObject);
424  g_object_set( pObject, table_row->gobj_param_name, account, NULL );
425  qof_instance_decrease_editlevel (pObject);
426  }
427  else
428  {
429  g_return_if_fail( setter != NULL );
430  (*setter)( pObject, (const gpointer)account );
431  }
432  }
433  else
434  {
435  PWARN( "Account ref '%s' not found", g_value_get_string( val ) );
436  }
437  }
438 }
439 
440 static GncSqlColumnTypeHandler account_guid_handler
441 = { load_account_guid,
445  };
446 /* ================================================================= */
447 void
448 gnc_sql_init_account_handler( void )
449 {
450  static GncSqlObjectBackend be_data =
451  {
452  GNC_SQL_BACKEND_VERSION,
453  GNC_ID_ACCOUNT,
454  gnc_sql_save_account, /* commit */
455  load_all_accounts, /* initial_load */
456  create_account_tables, /* create_tables */
457  NULL, /* compile_query */
458  NULL, /* run_query */
459  NULL, /* free_query */
460  NULL /* write */
461  };
462 
463  (void)qof_object_register_backend( GNC_ID_ACCOUNT, GNC_SQL_BACKEND, &be_data );
464 
465  gnc_sql_register_col_type_handler( CT_ACCOUNTREF, &account_guid_handler );
466 }
467 /* ========================== END OF FILE ===================== */
Account * gnc_account_get_parent(const Account *acc)
Definition: Account.c:2623
gboolean qof_object_register_backend(QofIdTypeConst type_name, const char *backend_name, gpointer be_data)
void gnc_account_append_child(Account *new_parent, Account *child)
Definition: Account.c:2525
const GncGUID * qof_instance_get_guid(gconstpointer)
gint gnc_sql_get_table_version(const GncSqlBackend *be, const gchar *table_name)
QofBook * qof_instance_get_book(gconstpointer)
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
load and save data to SQL
void gnc_sql_add_colname_to_list(const GncSqlColumnTableEntry *table_row, GList **pList)
GNCAccountType xaccAccountGetType(const Account *acc)
Definition: Account.c:3009
load and save accounts data to SQL
GncSqlStatement * gnc_sql_create_select_statement(GncSqlBackend *be, const gchar *table_name)
#define COL_NNUL
gboolean qof_instance_get_destroying(gconstpointer ptr)
gboolean string_to_guid(const gchar *string, GncGUID *guid)
gboolean gnc_sql_create_table(GncSqlBackend *be, const gchar *table_name, gint table_version, const GncSqlColumnTableEntry *col_table)
load and save data to SQL
void gnc_sql_register_col_type_handler(const gchar *colType, const GncSqlColumnTypeHandler *handler)
void gnc_sql_add_objectref_guid_col_info_to_list(const GncSqlBackend *be, const GncSqlColumnTableEntry *table_row, GList **pList)
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
Definition: qofclass.h:177
#define ENTER(format, args...)
Definition: qoflog.h:261
QofBook * book
Definition: guid.h:65
#define COL_PKEY
#define PWARN(format, args...)
Definition: qoflog.h:243
const GncGUID * gnc_sql_load_guid(const GncSqlBackend *be, GncSqlRow *row)
void gnc_sql_slots_load_for_sql_subquery(GncSqlBackend *be, const gchar *subquery, BookLookupFn lookup_fn)
Account handling public routines.
load and save accounts data to SQL
void gnc_sql_add_gvalue_objectref_guid_to_slist(const GncSqlBackend *be, QofIdTypeConst obj_name, const gpointer pObject, const GncSqlColumnTableEntry *table_row, GSList **pList)
load and save data to SQL
GSList * gnc_sql_get_account_balances_slist(GncSqlBackend *be)
GncSqlResult * gnc_sql_execute_select_statement(GncSqlBackend *be, GncSqlStatement *stmt)
void gnc_sql_load_object(const GncSqlBackend *be, GncSqlRow *row, QofIdTypeConst obj_name, gpointer pObject, const GncSqlColumnTableEntry *table)
const gchar * gobj_param_name
gboolean gnc_sql_slots_delete(GncSqlBackend *be, const GncGUID *guid)
void xaccAccountBeginEdit(Account *acc)
Definition: Account.c:1280
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Definition: Account.c:3148
#define LEAVE(format, args...)
Definition: qoflog.h:271
gboolean is_pristine_db
void(* QofSetterFunc)(gpointer, gpointer)
Definition: qofclass.h:184
Account * xaccMallocAccount(QofBook *book)
Definition: Account.c:1083
gboolean gnc_sql_do_db_operation(GncSqlBackend *be, E_DB_OPERATION op, const gchar *table_name, QofIdTypeConst obj_name, gpointer pObject, const GncSqlColumnTableEntry *table)
void xaccAccountCommitEdit(Account *acc)
Definition: Account.c:1321
Commodity handling public routines.
gboolean gnc_sql_slots_save(GncSqlBackend *be, const GncGUID *guid, gboolean is_infant, KvpFrame *pFrame)
const gchar * QofLogModule
Definition: qofid.h:89
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
Definition: Account.c:1827