GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-recurrence-sql.c
Go to the documentation of this file.
1 /********************************************************************
2  * gnc-recurrence-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 "gnc-engine.h"
35 #include "Recurrence.h"
36 
37 #include "gnc-backend-sql.h"
38 
39 #include "gnc-recurrence-sql.h"
40 
41 #if defined( S_SPLINT_S )
42 #include "splint-defs.h"
43 #endif
44 
45 G_GNUC_UNUSED static QofLogModule log_module = G_LOG_DOMAIN;
46 
47 #define TABLE_NAME "recurrences"
48 #define TABLE_VERSION 2
49 
50 #define BUDGET_MAX_RECURRENCE_PERIOD_TYPE_LEN 2048
51 #define BUDGET_MAX_RECURRENCE_WEEKEND_ADJUST_LEN 2048
52 
53 typedef struct
54 {
55  /*@ dependent @*/ GncSqlBackend* be;
56  /*@ dependent @*/
57  const GncGUID* guid;
58  /*@ dependent @*/
59  Recurrence* pRecurrence;
61 
62 static /*@ null @*/ gpointer get_obj_guid( gpointer pObject );
63 static void set_obj_guid( void );
64 static gint get_recurrence_mult( gpointer pObject );
65 static void set_recurrence_mult( gpointer pObject, gint value );
66 static /*@ null @*/ gpointer get_recurrence_period_type( gpointer pObject );
67 static void set_recurrence_period_type( gpointer pObject, /*@ null @*/ gpointer pValue );
68 static /*@ null @*/ gpointer get_recurrence_weekend_adjust( gpointer pObject );
69 static void set_recurrence_weekend_adjust( gpointer pObject, /*@ null @*/ gpointer pValue );
70 static /*@ dependent @*//*@ null @*/ gpointer get_recurrence_period_start( gpointer pObject );
71 static void set_recurrence_period_start( gpointer pObject, /*@ null @*/ gpointer pValue );
72 
73 static const GncSqlColumnTableEntry col_table[] =
74 {
75  /*@ -full_init_block @*/
76  { "id", CT_INT, 0, COL_PKEY | COL_NNUL | COL_AUTOINC },
77  {
78  "obj_guid", CT_GUID, 0, COL_NNUL, NULL, NULL,
79  (QofAccessFunc)get_obj_guid, (QofSetterFunc)set_obj_guid
80  },
81  {
82  "recurrence_mult", CT_INT, 0, COL_NNUL, NULL, NULL,
83  (QofAccessFunc)get_recurrence_mult, (QofSetterFunc)set_recurrence_mult
84  },
85  {
86  "recurrence_period_type", CT_STRING, BUDGET_MAX_RECURRENCE_PERIOD_TYPE_LEN, COL_NNUL, NULL, NULL,
87  (QofAccessFunc)get_recurrence_period_type, set_recurrence_period_type
88  },
89  {
90  "recurrence_period_start", CT_GDATE, 0, COL_NNUL, NULL, NULL,
91  (QofAccessFunc)get_recurrence_period_start, set_recurrence_period_start
92  },
93  {
94  "recurrence_weekend_adjust", CT_STRING, BUDGET_MAX_RECURRENCE_WEEKEND_ADJUST_LEN, COL_NNUL, NULL, NULL,
95  (QofAccessFunc)get_recurrence_weekend_adjust, set_recurrence_weekend_adjust
96  },
97  { NULL }
98  /*@ +full_init_block @*/
99 };
100 
101 /* Special column table because we need to be able to access the table by
102 a column other than the primary key */
103 static const GncSqlColumnTableEntry guid_col_table[] =
104 {
105  /*@ -full_init_block @*/
106  {
107  "obj_guid", CT_GUID, 0, 0, NULL, NULL,
108  (QofAccessFunc)get_obj_guid, (QofSetterFunc)set_obj_guid
109  },
110  { NULL }
111  /*@ +full_init_block @*/
112 };
113 
114 /* Special column table used to upgrade table from version 1 to 2 */
115 static const GncSqlColumnTableEntry weekend_adjust_col_table[] =
116 {
117  /*@ -full_init_block @*/
118  {
119  "recurrence_weekend_adjust", CT_STRING, BUDGET_MAX_RECURRENCE_WEEKEND_ADJUST_LEN, 0,
120  },
121  { NULL }
122  /*@ +full_init_block @*/
123 };
124 
125 /* ================================================================= */
126 
127 static /*@ null @*/ gpointer
128 get_obj_guid( gpointer pObject )
129 {
130  recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
131 
132  g_return_val_if_fail( pObject != NULL, NULL );
133 
134  return (gpointer)pInfo->guid;
135 }
136 
137 static void
138 set_obj_guid( void )
139 {
140  // Nowhere to put the GncGUID
141 }
142 
143 static gint
144 get_recurrence_mult( gpointer pObject )
145 {
146  recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
147 
148  g_return_val_if_fail( pObject != NULL, 0 );
149  g_return_val_if_fail( pInfo->pRecurrence != NULL, 0 );
150 
151  return (gint)pInfo->pRecurrence->mult;
152 }
153 
154 static void
155 set_recurrence_mult( gpointer pObject, gint value )
156 {
157  recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
158 
159  g_return_if_fail( pObject != NULL );
160  g_return_if_fail( pInfo->pRecurrence != NULL );
161 
162  pInfo->pRecurrence->mult = (guint16)value;
163 }
164 
165 static /*@ null @*/ gpointer
166 get_recurrence_period_type( gpointer pObject )
167 {
168  recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
169 
170  g_return_val_if_fail( pObject != NULL, NULL );
171  g_return_val_if_fail( pInfo->pRecurrence != NULL, NULL );
172 
173  return (gpointer)recurrencePeriodTypeToString(
174  recurrenceGetPeriodType( pInfo->pRecurrence ) );
175 }
176 
177 static void
178 set_recurrence_period_type( gpointer pObject, gpointer pValue )
179 {
180  recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
181 
182  g_return_if_fail( pObject != NULL );
183  g_return_if_fail( pInfo->pRecurrence != NULL );
184  g_return_if_fail( pValue != NULL );
185 
186  pInfo->pRecurrence->ptype = recurrencePeriodTypeFromString( (gchar*)pValue );
187 }
188 
189 static /*@ null @*/ gpointer
190 get_recurrence_weekend_adjust( gpointer pObject )
191 {
192  recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
193 
194  g_return_val_if_fail( pObject != NULL, NULL );
195  g_return_val_if_fail( pInfo->pRecurrence != NULL, NULL );
196 
197  return (gpointer)recurrenceWeekendAdjustToString(
198  recurrenceGetWeekendAdjust( pInfo->pRecurrence ) );
199 }
200 
201 static void
202 set_recurrence_weekend_adjust( gpointer pObject, gpointer pValue )
203 {
204  recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
205 
206  g_return_if_fail( pObject != NULL );
207  g_return_if_fail( pInfo->pRecurrence != NULL );
208  g_return_if_fail( pValue != NULL );
209 
210  pInfo->pRecurrence->wadj = recurrenceWeekendAdjustFromString( (gchar*)pValue );
211 }
212 
213 static /*@ dependent @*//*@ null @*/ gpointer
214 get_recurrence_period_start( gpointer pObject )
215 {
216  recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
217  static GDate date;
218 
219  g_return_val_if_fail( pObject != NULL, NULL );
220  g_return_val_if_fail( pInfo->pRecurrence != NULL, NULL );
221 
222  date = recurrenceGetDate( pInfo->pRecurrence );
223  return (gpointer)&date;
224 }
225 
226 static void
227 set_recurrence_period_start( gpointer pObject, gpointer pValue )
228 {
229  recurrence_info_t* pInfo = (recurrence_info_t*)pObject;
230  GDate* date = (GDate*)pValue;
231 
232  g_return_if_fail( pObject != NULL );
233  g_return_if_fail( pInfo->pRecurrence != NULL );
234  g_return_if_fail( pValue != NULL );
235 
236  pInfo->pRecurrence->start = *date;
237 }
238 
239 /* ================================================================= */
240 
241 gboolean
242 gnc_sql_recurrence_save( GncSqlBackend* be, const GncGUID* guid, const Recurrence* r )
243 {
244  recurrence_info_t recurrence_info;
245 
246  g_return_val_if_fail( be != NULL, FALSE );
247  g_return_val_if_fail( guid != NULL, FALSE );
248  g_return_val_if_fail( r != NULL, FALSE );
249 
250  (void)gnc_sql_recurrence_delete( be, guid );
251 
252  recurrence_info.be = be;
253  recurrence_info.guid = guid;
254  recurrence_info.pRecurrence = (Recurrence*)r;
255  return gnc_sql_do_db_operation( be, OP_DB_INSERT, TABLE_NAME,
256  TABLE_NAME, &recurrence_info, col_table );
257 }
258 
259 void
260 gnc_sql_recurrence_save_list( GncSqlBackend* be, const GncGUID* guid, GList* schedule )
261 {
262  recurrence_info_t recurrence_info;
263  GList* l;
264 
265  g_return_if_fail( be != NULL );
266  g_return_if_fail( guid != NULL );
267 
268  (void)gnc_sql_recurrence_delete( be, guid );
269 
270  recurrence_info.be = be;
271  recurrence_info.guid = guid;
272  for ( l = schedule; l != NULL; l = g_list_next( l ) )
273  {
274  recurrence_info.pRecurrence = (Recurrence*)l->data;
275  (void)gnc_sql_do_db_operation( be, OP_DB_INSERT, TABLE_NAME,
276  TABLE_NAME, &recurrence_info, col_table );
277  }
278 }
279 
280 gboolean
281 gnc_sql_recurrence_delete( GncSqlBackend* be, const GncGUID* guid )
282 {
283  recurrence_info_t recurrence_info;
284 
285  g_return_val_if_fail( be != NULL, FALSE );
286  g_return_val_if_fail( guid != NULL, FALSE );
287 
288  recurrence_info.be = be;
289  recurrence_info.guid = guid;
290  return gnc_sql_do_db_operation( be, OP_DB_DELETE, TABLE_NAME,
291  TABLE_NAME, &recurrence_info, guid_col_table );
292 }
293 
294 static void
295 load_recurrence( GncSqlBackend* be, GncSqlRow* row, /*@ out @*/ Recurrence* r )
296 {
297  recurrence_info_t recurrence_info;
298 
299  g_return_if_fail( be != NULL );
300  g_return_if_fail( row != NULL );
301  g_return_if_fail( r != NULL );
302 
303  recurrence_info.be = be;
304  recurrence_info.pRecurrence = r;
305 
306  gnc_sql_load_object( be, row, TABLE_NAME, &recurrence_info, col_table );
307 }
308 
309 static /*@ null @*/ GncSqlResult*
310 gnc_sql_set_recurrences_from_db( GncSqlBackend* be, const GncGUID* guid )
311 {
312  gchar* buf;
313  gchar guid_buf[GUID_ENCODING_LENGTH+1];
314  GncSqlStatement* stmt;
315  GncSqlResult* result;
316 
317  g_return_val_if_fail( be != NULL, NULL );
318  g_return_val_if_fail( guid != NULL, NULL );
319 
320  (void)guid_to_string_buff( guid, guid_buf );
321  buf = g_strdup_printf( "SELECT * FROM %s WHERE obj_guid='%s'", TABLE_NAME, guid_buf );
322  stmt = gnc_sql_connection_create_statement_from_sql( be->conn, buf );
323  g_free( buf );
324  result = gnc_sql_execute_select_statement( be, stmt );
325  gnc_sql_statement_dispose( stmt );
326  return result;
327 }
328 
329 /*@ null @*/ Recurrence*
330 gnc_sql_recurrence_load( GncSqlBackend* be, const GncGUID* guid )
331 {
332  GncSqlResult* result;
333  Recurrence* r = NULL;
334 
335  g_return_val_if_fail( be != NULL, NULL );
336  g_return_val_if_fail( guid != NULL, NULL );
337 
338  result = gnc_sql_set_recurrences_from_db( be, guid );
339  if ( result != NULL )
340  {
341  guint numRows = gnc_sql_result_get_num_rows( result );
342 
343  if ( numRows > 0 )
344  {
345  if ( numRows > 1 )
346  {
347  g_warning( "More than 1 recurrence found: first one used" );
348  }
349  r = g_new0( Recurrence, 1 );
350  g_assert( r != NULL );
351  load_recurrence( be, gnc_sql_result_get_first_row( result ), r );
352  }
353  else
354  {
355  g_warning( "No recurrences found" );
356  }
357  gnc_sql_result_dispose( result );
358  }
359 
360  return r;
361 }
362 
363 /*@ null @*/ GList*
364 gnc_sql_recurrence_load_list( GncSqlBackend* be, const GncGUID* guid )
365 {
366  GncSqlResult* result;
367  GList* list = NULL;
368 
369  g_return_val_if_fail( be != NULL, NULL );
370  g_return_val_if_fail( guid != NULL, NULL );
371 
372  result = gnc_sql_set_recurrences_from_db( be, guid );
373  if ( result != NULL )
374  {
375  GncSqlRow* row = gnc_sql_result_get_first_row( result );
376 
377  while ( row != NULL )
378  {
379  Recurrence* pRecurrence = g_new0( Recurrence, 1 );
380  g_assert( pRecurrence != NULL );
381  load_recurrence( be, row, pRecurrence );
382  list = g_list_append( list, pRecurrence );
383  row = gnc_sql_result_get_next_row( result );
384  }
385  gnc_sql_result_dispose( result );
386  }
387 
388  return list;
389 }
390 
391 /* ================================================================= */
392 static void
393 upgrade_recurrence_table_1_2 ( GncSqlBackend* be )
394 {
395  /* Step 1: add field, but allow it to be null */
396  gboolean ok = gnc_sql_add_columns_to_table( be, TABLE_NAME, weekend_adjust_col_table );
397  if ( !ok )
398  {
399  PERR( "Unable to add recurrence_weekend_adjust column\n" );
400  return;
401  }
402 
403  /* Step 2: insert a default value in the newly created column */
404  {
405  gchar *weekend_adj_str = recurrenceWeekendAdjustToString(WEEKEND_ADJ_NONE);
406  gchar *update_query = g_strdup_printf ("UPDATE %s SET %s = '%s';",
407  TABLE_NAME,
408  weekend_adjust_col_table[0].col_name,
409  weekend_adj_str);
410  (void)gnc_sql_execute_nonselect_sql (be, update_query);
411  g_free (weekend_adj_str);
412  g_free (update_query);
413  }
414 
415  /* Step 3: rewrite the table, requiring the weekend_adj column to be non-null */
416  gnc_sql_upgrade_table( be, TABLE_NAME, col_table );
417 
418 }
419 
420 static void
421 create_recurrence_tables( GncSqlBackend* be )
422 {
423  gint version;
424  gboolean ok;
425 
426  g_return_if_fail( be != NULL );
427 
428  version = gnc_sql_get_table_version( be, TABLE_NAME );
429  if ( version == 0 )
430  {
431  (void)gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table );
432  }
433  else if ( version < TABLE_VERSION )
434  {
435  /* Upgrade:
436  1->2: Add recurrence_weekend_adjust field (mandatory, non-null field)
437  */
438  if ( version == 1 )
439  {
440  upgrade_recurrence_table_1_2 (be);
441  }
442  (void)gnc_sql_set_table_version( be, TABLE_NAME, TABLE_VERSION );
443  PINFO("Recurrence table upgraded from version %d to version %d\n", version, TABLE_VERSION);
444  }
445 }
446 
447 /* ================================================================= */
448 void
449 gnc_sql_init_recurrence_handler( void )
450 {
451  static GncSqlObjectBackend be_data =
452  {
453  GNC_SQL_BACKEND_VERSION,
454  GNC_ID_ACCOUNT,
455  NULL, /* commit - cannot occur */
456  NULL, /* initial_load - cannot occur */
457  create_recurrence_tables, /* create_tables */
458  NULL, /* compile_query */
459  NULL, /* run_query */
460  NULL, /* free_query */
461  NULL /* write */
462  };
463 
464  (void)qof_object_register_backend( TABLE_NAME, GNC_SQL_BACKEND, &be_data );
465 }
466 /* ========================== END OF FILE ===================== */
gboolean qof_object_register_backend(QofIdTypeConst type_name, const char *backend_name, gpointer be_data)
gint gnc_sql_get_table_version(const GncSqlBackend *be, const gchar *table_name)
#define COL_AUTOINC
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
#define COL_NNUL
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
GncSqlConnection * conn
gboolean gnc_sql_add_columns_to_table(GncSqlBackend *be, const gchar *table_name, const GncSqlColumnTableEntry *new_col_table)
gchar * guid_to_string_buff(const GncGUID *guid, gchar *buff)
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
Definition: qofclass.h:177
#define PERR(format, args...)
Definition: qoflog.h:237
Definition: guid.h:65
#define COL_PKEY
load and save accounts data to SQL
gboolean gnc_sql_set_table_version(GncSqlBackend *be, const gchar *table_name, gint version)
#define GUID_ENCODING_LENGTH
Definition: guid.h:74
GncSqlResult * gnc_sql_execute_select_statement(GncSqlBackend *be, GncSqlStatement *stmt)
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)
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)
const gchar * QofLogModule
Definition: qofid.h:89