GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-budget-sql.c
Go to the documentation of this file.
1 /********************************************************************
2  * gnc-budget-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 
35 #include "gnc-backend-sql.h"
36 
37 #include "Recurrence.h"
38 
39 #include "gnc-budget-sql.h"
40 #include "gnc-slots-sql.h"
41 #include "gnc-recurrence-sql.h"
42 
43 #include "gnc-budget.h"
44 
45 #if defined( S_SPLINT_S )
46 #include "splint-defs.h"
47 #endif
48 
49 #define BUDGET_TABLE "budgets"
50 #define TABLE_VERSION 1
51 #define AMOUNTS_TABLE "budget_amounts"
52 #define AMOUNTS_TABLE_VERSION 1
53 
54 /*@ unused @*/ static QofLogModule log_module = G_LOG_DOMAIN;
55 
56 #define BUDGET_MAX_NAME_LEN 2048
57 #define BUDGET_MAX_DESCRIPTION_LEN 2048
58 
59 static const GncSqlColumnTableEntry col_table[] =
60 {
61  /*@ -full_init_block @*/
62  { "guid", CT_GUID, 0, COL_NNUL | COL_PKEY, "guid" },
63  { "name", CT_STRING, BUDGET_MAX_NAME_LEN, COL_NNUL, "name" },
64  { "description", CT_STRING, BUDGET_MAX_DESCRIPTION_LEN, 0, "description" },
65  { "num_periods", CT_INT, 0, COL_NNUL, "num_periods" },
66  { NULL }
67  /*@ +full_init_block @*/
68 };
69 
70 static /*@ dependent @*//*@ null @*/ QofInstance* get_budget( gpointer pObj );
71 static void set_budget( gpointer pObj, gpointer val );
72 static /*@ dependent @*//*@ null @*/ QofInstance* get_account( gpointer pObj );
73 static void set_account( gpointer pObj, gpointer val );
74 static gint get_period_num( gpointer pObj );
75 static void set_period_num( gpointer pObj, gpointer val );
76 static gnc_numeric get_amount( gpointer pObj );
77 static void set_amount( gpointer pObj, gnc_numeric value );
78 
79 typedef struct
80 {
81  GncBudget* budget;
82  Account* account;
83  guint period_num;
85 
86 static const GncSqlColumnTableEntry budget_amounts_col_table[] =
87 {
88  /*@ -full_init_block @*/
89  { "id", CT_INT, 0, COL_NNUL | COL_PKEY | COL_AUTOINC },
90  {
91  "budget_guid", CT_BUDGETREF, 0, COL_NNUL, NULL, NULL,
92  (QofAccessFunc)get_budget, (QofSetterFunc)set_budget
93  },
94  {
95  "account_guid", CT_ACCOUNTREF, 0, COL_NNUL, NULL, NULL,
96  (QofAccessFunc)get_account, (QofSetterFunc)set_account
97  },
98  {
99  "period_num", CT_INT, 0, COL_NNUL, NULL, NULL,
100  (QofAccessFunc)get_period_num, (QofSetterFunc)set_period_num
101  },
102  {
103  "amount", CT_NUMERIC, 0, COL_NNUL, NULL, NULL,
104  (QofAccessFunc)get_amount, (QofSetterFunc)set_amount
105  },
106  { NULL }
107  /*@ +full_init_block @*/
108 };
109 
110 /* ================================================================= */
111 static /*@ dependent @*//*@ null@ */ QofInstance*
112 get_budget( gpointer pObj )
113 {
115 
116  g_return_val_if_fail( pObj != NULL, NULL );
117 
118  return QOF_INSTANCE(info->budget);
119 }
120 
121 static void
122 set_budget( gpointer pObj, gpointer val )
123 {
124 }
125 
126 static /*@ dependent @*//*@ null @*/ QofInstance*
127 get_account( gpointer pObj )
128 {
130 
131  g_return_val_if_fail( pObj != NULL, NULL );
132 
133  return QOF_INSTANCE(info->account);
134 }
135 
136 static void
137 set_account( gpointer pObj, gpointer val )
138 {
140 
141  g_return_if_fail( pObj != NULL );
142  g_return_if_fail( val != NULL );
143  g_return_if_fail( GNC_IS_ACCOUNT(val) );
144 
145  info->account = GNC_ACCOUNT(val);
146 }
147 
148 static gint
149 get_period_num( gpointer pObj )
150 {
152 
153  g_return_val_if_fail( pObj != NULL, 0 );
154 
155  return info->period_num;
156 }
157 
158 static void
159 set_period_num( gpointer pObj, gpointer val )
160 {
162 
163  g_return_if_fail( pObj != NULL );
164 
165  info->period_num = GPOINTER_TO_UINT(val);
166 }
167 
168 static gnc_numeric
169 get_amount( gpointer pObj )
170 {
172 
173  g_return_val_if_fail( pObj != NULL, gnc_numeric_zero() );
174 
175  return gnc_budget_get_account_period_value( info->budget, info->account, info->period_num );
176 }
177 
178 static void
179 set_amount( gpointer pObj, gnc_numeric value )
180 {
182 
183  g_return_if_fail( pObj != NULL );
184 
185  gnc_budget_set_account_period_value( info->budget, info->account, info->period_num, value );
186 }
187 
188 /*----------------------------------------------------------------*/
195 static void
196 load_budget_amounts( GncSqlBackend* be, GncBudget* budget )
197 {
198  gchar guid_buf[GUID_ENCODING_LENGTH+1];
199  gchar* sql;
200  GncSqlStatement* stmt;
201  GncSqlResult* result;
202 
203  g_return_if_fail( be != NULL );
204  g_return_if_fail( budget != NULL );
205 
206  (void)guid_to_string_buff( qof_instance_get_guid( QOF_INSTANCE(budget) ), guid_buf );
207  sql = g_strdup_printf( "SELECT * FROM %s WHERE budget_guid='%s'", AMOUNTS_TABLE, guid_buf );
208  stmt = gnc_sql_create_statement_from_sql( be, sql );
209  g_free( sql );
210  if ( stmt != NULL )
211  {
212  result = gnc_sql_execute_select_statement( be, stmt );
213  gnc_sql_statement_dispose( stmt );
214  if ( result != NULL )
215  {
216  GncSqlRow* row = gnc_sql_result_get_first_row( result );
217  budget_amount_info_t info = { budget, NULL, 0 };
218 
219  while ( row != NULL )
220  {
221  gnc_sql_load_object( be, row, NULL, &info, budget_amounts_col_table );
222  row = gnc_sql_result_get_next_row( result );
223  }
224  gnc_sql_result_dispose( result );
225  }
226  }
227 }
228 
235 static gboolean
236 delete_budget_amounts( GncSqlBackend* be, GncBudget* budget )
237 {
238  gchar guid_buf[GUID_ENCODING_LENGTH+1];
239  gchar* sql;
240 
241  g_return_val_if_fail( be != NULL, FALSE );
242  g_return_val_if_fail( budget != NULL, FALSE );
243 
244  (void)guid_to_string_buff( qof_instance_get_guid( QOF_INSTANCE(budget) ), guid_buf );
245  sql = g_strdup_printf( "DELETE FROM %s WHERE budget_guid='%s'", AMOUNTS_TABLE, guid_buf );
246  (void)gnc_sql_execute_nonselect_sql( be, sql );
247  g_free( sql );
248 
249  return TRUE;
250 }
251 
258 static gboolean
259 save_budget_amounts( GncSqlBackend* be, GncBudget* budget )
260 {
261  GList* descendants;
262  /*@ dependent @*/
263  GList* node;
265  guint num_periods;
266  gboolean is_ok = TRUE;;
267 
268  g_return_val_if_fail( be != NULL, FALSE );
269  g_return_val_if_fail( budget != NULL, FALSE );
270 
271  // Delete the amounts, then save
272  delete_budget_amounts( be, budget );
273 
274  info.budget = budget;
275  num_periods = gnc_budget_get_num_periods( budget );
276  descendants = gnc_account_get_descendants( gnc_book_get_root_account( be->book ) );
277  for ( node = descendants; node != NULL && is_ok; node = g_list_next(node) )
278  {
279  guint i;
280 
281  info.account = GNC_ACCOUNT(node->data);
282  for ( i = 0; i < num_periods && is_ok; i++ )
283  {
284  if ( gnc_budget_is_account_period_value_set( budget, info.account, i ) )
285  {
286  info.period_num = i;
287  is_ok = gnc_sql_do_db_operation( be, OP_DB_INSERT, AMOUNTS_TABLE, "", &info,
288  budget_amounts_col_table );
289  }
290  }
291  }
292  g_list_free( descendants );
293 
294  return is_ok;
295 }
296 /*----------------------------------------------------------------*/
297 static /*@ dependent @*//*@ null @*/ GncBudget*
298 load_single_budget( GncSqlBackend* be, GncSqlRow* row )
299 {
300  const GncGUID* guid;
301  GncBudget* pBudget = NULL;
302  Recurrence* r;
303 
304  g_return_val_if_fail( be != NULL, NULL );
305  g_return_val_if_fail( row != NULL, NULL );
306 
307  guid = gnc_sql_load_guid( be, row );
308  if ( guid != NULL )
309  {
310  pBudget = gnc_budget_lookup( guid, be->book );
311  }
312  if ( pBudget == NULL )
313  {
314  pBudget = gnc_budget_new( be->book );
315  }
316 
317  gnc_budget_begin_edit( pBudget );
318  gnc_sql_load_object( be, row, GNC_ID_BUDGET, pBudget, col_table );
319  load_budget_amounts( be, pBudget );
320  r = gnc_sql_recurrence_load( be, gnc_budget_get_guid( pBudget ) );
321  if ( r != NULL )
322  {
323  gnc_budget_set_recurrence( pBudget, r );
324  g_free( r );
325  }
326  gnc_budget_commit_edit( pBudget );
327 
328  return pBudget;
329 }
330 
331 static void
332 load_all_budgets( GncSqlBackend* be )
333 {
334  GncSqlStatement* stmt;
335  GncSqlResult* result;
336  GList* list = NULL;
337 
338  g_return_if_fail( be != NULL );
339 
340  stmt = gnc_sql_create_select_statement( be, BUDGET_TABLE );
341  if ( stmt != NULL )
342  {
343  result = gnc_sql_execute_select_statement( be, stmt );
344  gnc_sql_statement_dispose( stmt );
345  if ( result != NULL )
346  {
347  GncSqlRow* row = gnc_sql_result_get_first_row( result );
348  GncBudget* b;
349 
350  while ( row != NULL )
351  {
352  b = load_single_budget( be, row );
353  if ( b != NULL )
354  {
355  list = g_list_prepend( list, b );
356  }
357  row = gnc_sql_result_get_next_row( result );
358  }
359  gnc_sql_result_dispose( result );
360 
361  if ( list != NULL )
362  {
363  gnc_sql_slots_load_for_list( be, list );
364  g_list_free( list );
365  }
366  }
367  }
368 }
369 
370 /* ================================================================= */
371 static void
372 create_budget_tables( GncSqlBackend* be )
373 {
374  gint version;
375 
376  g_return_if_fail( be != NULL );
377 
378  version = gnc_sql_get_table_version( be, BUDGET_TABLE );
379  if ( version == 0 )
380  {
381  (void)gnc_sql_create_table( be, BUDGET_TABLE, TABLE_VERSION, col_table );
382  }
383 
384  version = gnc_sql_get_table_version( be, AMOUNTS_TABLE );
385  if ( version == 0 )
386  {
387  (void)gnc_sql_create_table( be, AMOUNTS_TABLE, AMOUNTS_TABLE_VERSION, budget_amounts_col_table );
388  }
389 }
390 
391 /* ================================================================= */
392 static gboolean
393 save_budget( GncSqlBackend* be, QofInstance* inst )
394 {
395  GncBudget* pBudget = GNC_BUDGET(inst);
396  const GncGUID* guid;
397  gint op;
398  gboolean is_infant;
399  gboolean is_ok;
400 
401  g_return_val_if_fail( be != NULL, FALSE );
402  g_return_val_if_fail( inst != NULL, FALSE );
403  g_return_val_if_fail( GNC_IS_BUDGET(inst), FALSE );
404 
405  is_infant = qof_instance_get_infant( inst );
406  if ( qof_instance_get_destroying( inst ) )
407  {
408  op = OP_DB_DELETE;
409  }
410  else if ( be->is_pristine_db || is_infant )
411  {
412  op = OP_DB_INSERT;
413  }
414  else
415  {
416  op = OP_DB_UPDATE;
417  }
418  is_ok = gnc_sql_do_db_operation( be, op, BUDGET_TABLE, GNC_ID_BUDGET, pBudget, col_table );
419 
420  // Now, commit any slots and recurrence
421  if ( is_ok )
422  {
423  guid = qof_instance_get_guid( inst );
424  if ( !qof_instance_get_destroying(inst) )
425  {
426  is_ok = save_budget_amounts( be, pBudget );
427  if ( is_ok )
428  {
429  is_ok = gnc_sql_recurrence_save( be, guid, gnc_budget_get_recurrence( pBudget ) );
430  }
431  if ( is_ok )
432  {
433  is_ok = gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) );
434  }
435  }
436  else
437  {
438  is_ok = delete_budget_amounts( be, pBudget );
439  if ( is_ok )
440  {
441  is_ok = gnc_sql_recurrence_delete( be, guid );
442  }
443  if ( is_ok )
444  {
445  (void)gnc_sql_slots_delete( be, guid );
446  }
447  }
448  }
449 
450  return is_ok;
451 }
452 
453 static void
454 do_save_budget( QofInstance* inst, gpointer data )
455 {
456  write_objects_t* s = (write_objects_t*)data;
457 
458  if ( s->is_ok )
459  {
460  s->is_ok = save_budget( s->be, inst );
461  }
462 }
463 
464 static gboolean
465 write_budgets( GncSqlBackend* be )
466 {
467  write_objects_t data;
468 
469  g_return_val_if_fail( be != NULL, FALSE );
470 
471  data.be = be;
472  data.is_ok = TRUE;
473  qof_collection_foreach( qof_book_get_collection( be->book, GNC_ID_BUDGET ),
474  (QofInstanceForeachCB)do_save_budget, &data );
475 
476  return data.is_ok;
477 }
478 
479 /* ================================================================= */
480 static void
481 load_budget_guid( const GncSqlBackend* be, GncSqlRow* row,
482  /*@ null @*/ QofSetterFunc setter, gpointer pObject,
483  const GncSqlColumnTableEntry* table_row )
484 {
485  const GValue* val;
486  GncGUID guid;
487  GncBudget* budget = NULL;
488 
489  g_return_if_fail( be != NULL );
490  g_return_if_fail( row != NULL );
491  g_return_if_fail( pObject != NULL );
492  g_return_if_fail( table_row != NULL );
493 
494  val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
495  if ( val != NULL && G_VALUE_HOLDS_STRING( val ) && g_value_get_string( val ) != NULL )
496  {
497  (void)string_to_guid( g_value_get_string( val ), &guid );
498  budget = gnc_budget_lookup( &guid, be->book );
499  if ( budget != NULL )
500  {
501  if ( table_row->gobj_param_name != NULL )
502  {
503  qof_instance_increase_editlevel (pObject);
504  g_object_set( pObject, table_row->gobj_param_name, budget, NULL );
505  qof_instance_decrease_editlevel (pObject);
506  }
507  else
508  {
509  g_return_if_fail( setter != NULL );
510  (*setter)( pObject, (const gpointer)budget );
511  }
512  }
513  else
514  {
515  PWARN( "Budget ref '%s' not found", g_value_get_string( val ) );
516  }
517  }
518 }
519 
520 static GncSqlColumnTypeHandler budget_guid_handler
521 = { load_budget_guid,
525  };
526 /* ================================================================= */
527 void
528 gnc_sql_init_budget_handler( void )
529 {
530  static GncSqlObjectBackend be_data =
531  {
532  GNC_SQL_BACKEND_VERSION,
533  GNC_ID_BUDGET,
534  save_budget, /* commit */
535  load_all_budgets, /* initial_load */
536  create_budget_tables, /* create_tables */
537  NULL, /* compile_query */
538  NULL, /* run_query */
539  NULL, /* free_query */
540  write_budgets /* write */
541  };
542 
543  (void)qof_object_register_backend( GNC_ID_BUDGET, GNC_SQL_BACKEND, &be_data );
544 
545  gnc_sql_register_col_type_handler( CT_BUDGETREF, &budget_guid_handler );
546 }
547 /* ========================== END OF FILE ===================== */
gboolean qof_object_register_backend(QofIdTypeConst type_name, const char *backend_name, gpointer be_data)
const GncGUID * qof_instance_get_guid(gconstpointer)
gint gnc_sql_get_table_version(const GncSqlBackend *be, const gchar *table_name)
#define COL_AUTOINC
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
void gnc_sql_add_colname_to_list(const GncSqlColumnTableEntry *table_row, GList **pList)
load and save accounts data to SQL
GncSqlStatement * gnc_sql_create_select_statement(GncSqlBackend *be, const gchar *table_name)
#define COL_NNUL
GnuCash Budgets.
GncBudget * gnc_budget_new(QofBook *book)
Definition: gnc-budget.c:289
gboolean qof_instance_get_destroying(gconstpointer ptr)
gboolean string_to_guid(const gchar *string, GncGUID *guid)
gint gnc_sql_execute_nonselect_sql(GncSqlBackend *be, const gchar *sql)
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)
gchar * guid_to_string_buff(const GncGUID *guid, gchar *buff)
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
Definition: qofclass.h:177
void qof_collection_foreach(const QofCollection *, QofInstanceForeachCB, gpointer user_data)
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)
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)
#define GUID_ENCODING_LENGTH
Definition: guid.h:74
void gnc_sql_slots_load_for_list(GncSqlBackend *be, GList *list)
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
void(* QofInstanceForeachCB)(QofInstance *, gpointer user_data)
Definition: qofid.h:186
GList * gnc_account_get_descendants(const Account *account)
Definition: Account.c:2755
gboolean gnc_sql_slots_delete(GncSqlBackend *be, const GncGUID *guid)
gboolean is_pristine_db
void(* QofSetterFunc)(gpointer, gpointer)
Definition: qofclass.h:184
gboolean gnc_sql_do_db_operation(GncSqlBackend *be, E_DB_OPERATION op, const gchar *table_name, QofIdTypeConst obj_name, gpointer pObject, const GncSqlColumnTableEntry *table)
QofCollection * qof_book_get_collection(const QofBook *, QofIdType)
GncSqlStatement * gnc_sql_create_statement_from_sql(GncSqlBackend *be, const gchar *sql)
load and save data to SQL
gboolean gnc_sql_slots_save(GncSqlBackend *be, const GncGUID *guid, gboolean is_infant, KvpFrame *pFrame)
const gchar * QofLogModule
Definition: qofid.h:89