GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-slots-sql.c
Go to the documentation of this file.
1 /********************************************************************
2  * gnc-slots-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 
36 #include "gnc-backend-sql.h"
37 
38 #include "gnc-slots-sql.h"
39 
40 #ifdef S_SPLINT_S
41 #include "splint-defs.h"
42 #endif
43 
44 /*@ unused @*/ static QofLogModule log_module = G_LOG_DOMAIN;
45 
46 #define TABLE_NAME "slots"
47 #define TABLE_VERSION 3
48 
49 typedef enum
50 {
51  NONE,
52  FRAME,
53  LIST
54 } context_t;
55 
56 typedef struct
57 {
58  /*@ dependent @*/ GncSqlBackend* be;
59  /*@ dependent @*/
60  const GncGUID* guid;
61  gboolean is_ok;
62  /*@ dependent @*/
63  KvpFrame* pKvpFrame;
64  KvpValueType value_type;
65  GList *pList;
66  context_t context;
67  /*@ dependent @*/
68  KvpValue* pKvpValue;
69  GString* path;
70 } slot_info_t;
71 
72 
73 static /*@ null @*/ gpointer get_obj_guid( gpointer pObject );
74 static void set_obj_guid( void );
75 static /*@ null @*/ gpointer get_path( gpointer pObject );
76 static void set_path( gpointer pObject, /*@ null @*/ gpointer pValue );
77 static KvpValueType get_slot_type( gpointer pObject );
78 static void set_slot_type( gpointer pObject, /*@ null @*/ gpointer pValue );
79 static gint64 get_int64_val( gpointer pObject );
80 static void set_int64_val( gpointer pObject, gint64 pValue );
81 static /*@ null @*/ gpointer get_string_val( gpointer pObject );
82 static void set_string_val( gpointer pObject, /*@ null @*/ gpointer pValue );
83 static /*@ dependent @*//*@ null @*/ gpointer get_double_val( gpointer pObject );
84 static void set_double_val( gpointer pObject, /*@ null @*/ gpointer pValue );
85 static Timespec get_timespec_val( gpointer pObject );
86 static void set_timespec_val( gpointer pObject, Timespec ts );
87 static /*@ null @*/ gpointer get_guid_val( gpointer pObject );
88 static void set_guid_val( gpointer pObject, /*@ null @*/ gpointer pValue );
89 static gnc_numeric get_numeric_val( gpointer pObject );
90 static void set_numeric_val( gpointer pObject, gnc_numeric value );
91 static GDate* get_gdate_val( gpointer pObject );
92 static void set_gdate_val( gpointer pObject, GDate* value );
93 static slot_info_t *slot_info_copy( slot_info_t *pInfo, GncGUID *guid );
94 static void slots_load_info( slot_info_t *pInfo );
95 
96 #define SLOT_MAX_PATHNAME_LEN 4096
97 #define SLOT_MAX_STRINGVAL_LEN 4096
98 enum
99 {
100  id_col = 0,
101  obj_guid_col,
102  name_col,
103  slot_type_col,
104  int64_val_col,
105  string_val_col,
106  double_val_col,
107  timespec_val_col,
108  guid_val_col,
109  numeric_val_col,
110  gdate_val_col
111 };
112 
113 static const GncSqlColumnTableEntry col_table[] =
114 {
115  /* col_name, col_type, size, flags, g0bj_param_name, qof_param_name, getter, setter */
116  /*@ -full_init_block @*/
117  { "id", CT_INT, 0, COL_PKEY | COL_NNUL | COL_AUTOINC },
118  {
119  "obj_guid", CT_GUID, 0, COL_NNUL, NULL, NULL,
120  (QofAccessFunc)get_obj_guid, (QofSetterFunc)set_obj_guid
121  },
122  {
123  "name", CT_STRING, SLOT_MAX_PATHNAME_LEN, COL_NNUL, NULL, NULL,
124  (QofAccessFunc)get_path, set_path
125  },
126  {
127  "slot_type", CT_INT, 0, COL_NNUL, NULL, NULL,
128  (QofAccessFunc)get_slot_type, set_slot_type,
129  },
130  {
131  "int64_val", CT_INT64, 0, 0, NULL, NULL,
132  (QofAccessFunc)get_int64_val, (QofSetterFunc)set_int64_val
133  },
134  {
135  "string_val", CT_STRING, SLOT_MAX_PATHNAME_LEN, 0, NULL, NULL,
136  (QofAccessFunc)get_string_val, set_string_val
137  },
138  {
139  "double_val", CT_DOUBLE, 0, 0, NULL, NULL,
140  (QofAccessFunc)get_double_val, set_double_val
141  },
142  {
143  "timespec_val", CT_TIMESPEC, 0, 0, NULL, NULL,
144  (QofAccessFunc)get_timespec_val, (QofSetterFunc)set_timespec_val
145  },
146  {
147  "guid_val", CT_GUID, 0, 0, NULL, NULL,
148  (QofAccessFunc)get_guid_val, set_guid_val
149  },
150  {
151  "numeric_val", CT_NUMERIC, 0, 0, NULL, NULL,
152  (QofAccessFunc)get_numeric_val, (QofSetterFunc)set_numeric_val
153  },
154  {
155  "gdate_val", CT_GDATE, 0, 0, NULL, NULL,
156  (QofAccessFunc)get_gdate_val, (QofSetterFunc)set_gdate_val
157  },
158  { NULL }
159  /*@ +full_init_block @*/
160 };
161 
162 /* Special column table because we need to be able to access the table by
163 a column other than the primary key */
164 static const GncSqlColumnTableEntry obj_guid_col_table[] =
165 {
166  /*@ -full_init_block @*/
167  { "obj_guid", CT_GUID, 0, 0, NULL, NULL, (QofAccessFunc)get_obj_guid, _retrieve_guid_ },
168  { NULL }
169  /*@ +full_init_block @*/
170 };
171 
172 static const GncSqlColumnTableEntry gdate_col_table[] =
173 {
174  /*@ -full_init_block @*/
175  { "gdate_val", CT_GDATE, 0, 0, },
176  { NULL }
177  /*@ +full_init_block @*/
178 };
179 
180 /* ================================================================= */
181 
182 static gchar *
183 get_key_from_path( GString *path )
184 {
185  gchar *str = NULL, *key = NULL, *ret = NULL;
186 
187  g_return_val_if_fail( path != NULL, strdup("") );
188 
189  if ( path->str == NULL ) return strdup("");
190  str = g_strdup( path->str );
191  key = strrchr( str, '/');
192 
193  /* Remove trailing /es */
194  if ( key == NULL ) return str;
195  while ( str + strlen(str) - key == 1 )
196  {
197  *key = '\0';
198  key = strrchr( str, '/' );
199  }
200  if ( key == NULL ) return str;
201  /* Now advance key past the last intermediate / to get the post-delimiter string */
202  while ( *key == '/') ++key;
203 
204  ret = strdup( key );
205  g_free( str );
206  return ret;
207 }
208 
209 static gchar *
210 get_path_from_path( GString *path )
211 {
212  gchar *str = NULL, *key = NULL;
213 
214  g_return_val_if_fail( path != NULL, NULL );
215 
216  if ( path->str == NULL ) return NULL;
217  str = g_strdup( path->str );
218  key = strrchr( str, '/');
219  /* No /es means no path, just a key */
220  if ( key == NULL )
221  {
222  g_free( str );
223  return NULL;
224  }
225  /* Remove trailing /es */
226  while ( str + strlen(str) - key == 1 )
227  {
228  *key = '\0';
229  key = strrchr( str, '/' );
230  }
231  if ( key == NULL )
232  {
233  g_free(str);
234  return NULL;
235  }
236  /* reterminate the string at the slash */
237  *key = '\0';
238 
239  return str;
240 }
241 
242 static void
243 set_slot_from_value( slot_info_t *pInfo, KvpValue *pValue)
244 {
245  g_return_if_fail( pInfo != NULL );
246  g_return_if_fail( pValue != NULL );
247 
248  switch ( pInfo->context)
249  {
250  case FRAME:
251  {
252  gchar *key = get_key_from_path( pInfo->path );
253  kvp_frame_set_value_nc( pInfo->pKvpFrame, key, pValue );
254  g_free( key );
255  break;
256  }
257  case LIST:
258  {
259  pInfo->pList = g_list_append(pInfo->pList, pValue);
260  break;
261  }
262  case NONE:
263  default:
264  {
265  gchar *key = get_key_from_path( pInfo->path );
266  gchar *path = get_path_from_path( pInfo->path );
267  KvpFrame* frame = pInfo->pKvpFrame;
268  if ( path )
269  {
270  frame = kvp_frame_get_frame_slash( frame, path );
271  g_free( path );
272  }
273  kvp_frame_set_value_nc( frame, key, pValue );
274  g_free( key );
275  break;
276  }
277  }
278 }
279 
280 static /*@ null @*/ gpointer
281 get_obj_guid( gpointer pObject )
282 {
283  slot_info_t* pInfo = (slot_info_t*)pObject;
284 
285  g_return_val_if_fail( pObject != NULL, NULL );
286 
287  return (gpointer)pInfo->guid;
288 }
289 
290 static void
291 set_obj_guid( void )
292 {
293  // Nowhere to put the GncGUID
294 }
295 
296 static /*@ null @*/ gpointer
297 get_path( gpointer pObject )
298 {
299  slot_info_t* pInfo = (slot_info_t*)pObject;
300 
301  g_return_val_if_fail( pObject != NULL, NULL );
302 
303  return (gpointer)pInfo->path->str;
304 }
305 
306 static void
307 set_path( gpointer pObject, /*@ null @*/ gpointer pValue )
308 {
309  slot_info_t* pInfo = (slot_info_t*)pObject;
310 
311  g_return_if_fail( pObject != NULL );
312  g_return_if_fail( pValue != NULL );
313 
314  if ( pInfo->path != NULL )
315  {
316  (void)g_string_free( pInfo->path, TRUE );
317  }
318  pInfo->path = g_string_new( (gchar*)pValue );
319 }
320 
321 static KvpValueType
322 get_slot_type( gpointer pObject )
323 {
324  slot_info_t* pInfo = (slot_info_t*)pObject;
325 
326  g_return_val_if_fail( pObject != NULL, 0 );
327 
328 // return (gpointer)kvp_value_get_type( pInfo->pKvpValue );
329  return pInfo->value_type;
330 }
331 
332 static void
333 set_slot_type( gpointer pObject, /*@ null @*/ gpointer pValue )
334 {
335  slot_info_t* pInfo = (slot_info_t*)pObject;
336 
337  g_return_if_fail( pObject != NULL );
338  g_return_if_fail( pValue != NULL );
339 
340  pInfo->value_type = (KvpValueType)pValue;
341 }
342 
343 static gint64
344 get_int64_val( gpointer pObject )
345 {
346  slot_info_t* pInfo = (slot_info_t*)pObject;
347 
348  g_return_val_if_fail( pObject != NULL, 0 );
349 
350  if ( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_GINT64 )
351  {
352  return kvp_value_get_gint64( pInfo->pKvpValue );
353  }
354  else
355  {
356  return 0;
357  }
358 }
359 
360 static void
361 set_int64_val( gpointer pObject, gint64 value )
362 {
363  slot_info_t* pInfo = (slot_info_t*)pObject;
364  KvpValue *pValue = NULL;
365 
366  g_return_if_fail( pObject != NULL );
367 
368  if ( pInfo->value_type != KVP_TYPE_GINT64 ) return;
369  pValue = kvp_value_new_gint64( value );
370  set_slot_from_value( pInfo, pValue );
371 }
372 
373 static /*@ null @*/ gpointer
374 get_string_val( gpointer pObject )
375 {
376  slot_info_t* pInfo = (slot_info_t*)pObject;
377 
378  g_return_val_if_fail( pObject != NULL, NULL );
379 
380  if ( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_STRING )
381  {
382  return (gpointer)kvp_value_get_string( pInfo->pKvpValue );
383  }
384  else
385  {
386  return NULL;
387  }
388 }
389 
390 static void
391 set_string_val( gpointer pObject, /*@ null @*/ gpointer pValue )
392 {
393  slot_info_t* pInfo = (slot_info_t*)pObject;
394  KvpValue *value = NULL;
395 
396  g_return_if_fail( pObject != NULL );
397 
398  if ( pInfo->value_type != KVP_TYPE_STRING || pValue == NULL ) return;
399  value = kvp_value_new_string( (gchar*)pValue );
400  set_slot_from_value( pInfo, value );
401 }
402 
403 static /*@ dependent @*//*@ null @*/ gpointer
404 get_double_val( gpointer pObject )
405 {
406  slot_info_t* pInfo = (slot_info_t*)pObject;
407  static double d_val;
408 
409  g_return_val_if_fail( pObject != NULL, NULL );
410 
411  if ( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_DOUBLE )
412  {
413  d_val = kvp_value_get_double( pInfo->pKvpValue );
414  return (gpointer)&d_val;
415  }
416  else
417  {
418  return NULL;
419  }
420 }
421 
422 static void
423 set_double_val( gpointer pObject, /*@ null @*/ gpointer pValue )
424 {
425  slot_info_t* pInfo = (slot_info_t*)pObject;
426  KvpValue *value = NULL;
427 
428  g_return_if_fail( pObject != NULL );
429 
430  if ( pInfo->value_type != KVP_TYPE_DOUBLE || pValue == NULL ) return;
431  value = kvp_value_new_double( *((double*)pValue) );
432  set_slot_from_value( pInfo, value );
433 }
434 
435 static Timespec
436 get_timespec_val( gpointer pObject )
437 {
438  slot_info_t* pInfo = (slot_info_t*)pObject;
439 
440  g_return_val_if_fail( pObject != NULL, gnc_dmy2timespec( 1, 1, 1970 ) );
441 
442 //if( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_TIMESPEC ) {
443  return kvp_value_get_timespec( pInfo->pKvpValue );
444 }
445 
446 static void
447 set_timespec_val( gpointer pObject, Timespec ts )
448 {
449  slot_info_t* pInfo = (slot_info_t*)pObject;
450  KvpValue *value = NULL;
451 
452  g_return_if_fail( pObject != NULL );
453 
454  if ( pInfo->value_type != KVP_TYPE_TIMESPEC ) return;
455  value = kvp_value_new_timespec( ts );
456  set_slot_from_value( pInfo, value );
457 }
458 
459 static /*@ null @*/ gpointer
460 get_guid_val( gpointer pObject )
461 {
462  slot_info_t* pInfo = (slot_info_t*)pObject;
463 
464  g_return_val_if_fail( pObject != NULL, NULL );
465 
466  if ( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_GUID )
467  {
468  return (gpointer)kvp_value_get_guid( pInfo->pKvpValue );
469  }
470  else
471  {
472  return NULL;
473  }
474 }
475 
476 static void
477 set_guid_val( gpointer pObject, /*@ null @*/ gpointer pValue )
478 {
479  slot_info_t* pInfo = (slot_info_t*)pObject;
480 
481  g_return_if_fail( pObject != NULL );
482  if ( pValue == NULL ) return;
483 
484  switch ( pInfo->value_type)
485  {
486  case KVP_TYPE_GUID:
487  {
488  KvpValue *value = kvp_value_new_guid( (GncGUID*)pValue );
489  set_slot_from_value( pInfo, value );
490  break;
491  }
492  case KVP_TYPE_GLIST:
493  {
494  slot_info_t *newInfo = slot_info_copy( pInfo, (GncGUID*)pValue );
495  KvpValue *pValue = NULL;
496  gchar *key = get_key_from_path( pInfo->path );
497 
498  newInfo->context = LIST;
499 
500  slots_load_info( newInfo );
501  pValue = kvp_value_new_glist_nc( newInfo->pList );
502  kvp_frame_set_slot_nc(pInfo->pKvpFrame, key, pValue);
503  g_string_free( newInfo->path, TRUE );
504  g_slice_free( slot_info_t, newInfo );
505  g_free( key );
506  break;
507  }
508  case KVP_TYPE_FRAME:
509  {
510  slot_info_t *newInfo = slot_info_copy( pInfo, (GncGUID*)pValue ) ;
511  KvpFrame *newFrame = kvp_frame_new();
512  newInfo->pKvpFrame = newFrame;
513 
514  switch ( pInfo->context )
515  {
516  case LIST:
517  {
518  KvpValue *value = kvp_value_new_frame_nc( newFrame );
519  gchar *key = get_key_from_path( pInfo->path );
520  newInfo->path = g_string_assign( newInfo->path, key );
521  pInfo->pList = g_list_append( pInfo->pList, value );
522  g_free( key );
523  break;
524  }
525  case FRAME:
526  default:
527  {
528  gchar *key = get_key_from_path( pInfo->path );
529  kvp_frame_set_frame_nc( pInfo->pKvpFrame, key, newFrame );
530  g_free( key );
531  break;
532  }
533  }
534 
535  newInfo->context = FRAME;
536  slots_load_info ( newInfo );
537  g_string_free( newInfo->path, TRUE );
538  g_slice_free( slot_info_t, newInfo );
539  break;
540  }
541  default:
542  break;
543  }
544 }
545 
546 static gnc_numeric
547 get_numeric_val( gpointer pObject )
548 {
549  slot_info_t* pInfo = (slot_info_t*)pObject;
550 
551  g_return_val_if_fail( pObject != NULL, gnc_numeric_zero() );
552 
553  if ( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_NUMERIC )
554  {
555  return kvp_value_get_numeric( pInfo->pKvpValue );
556  }
557  else
558  {
559  return gnc_numeric_zero();
560  }
561 }
562 
563 static void
564 set_numeric_val( gpointer pObject, gnc_numeric value )
565 {
566  slot_info_t* pInfo = (slot_info_t*)pObject;
567  KvpValue *pValue = NULL;
568 
569  g_return_if_fail( pObject != NULL );
570 
571  if ( pInfo->value_type != KVP_TYPE_NUMERIC ) return;
572  pValue = kvp_value_new_numeric( value );
573  set_slot_from_value( pInfo, pValue );
574 }
575 
576 static GDate*
577 get_gdate_val( gpointer pObject )
578 {
579  slot_info_t* pInfo = (slot_info_t*)pObject;
580  static GDate date;
581 
582  g_return_val_if_fail( pObject != NULL, NULL );
583 
584  if ( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_GDATE )
585  {
586  date = kvp_value_get_gdate( pInfo->pKvpValue );
587  return &date;
588  }
589  else
590  {
591  return NULL;
592  }
593 }
594 
595 static void
596 set_gdate_val( gpointer pObject, GDate* value )
597 {
598  slot_info_t* pInfo = (slot_info_t*)pObject;
599  KvpValue *pValue = NULL;
600 
601  g_return_if_fail( pObject != NULL );
602 
603  if ( pInfo->value_type != KVP_TYPE_GDATE ) return;
604  pValue = kvp_value_new_gdate( *value );
605  set_slot_from_value( pInfo, pValue );
606 }
607 
608 static slot_info_t *
609 slot_info_copy( slot_info_t *pInfo, GncGUID *guid )
610 {
611  slot_info_t *newSlot;
612  g_return_val_if_fail( pInfo != NULL, NULL );
613  newSlot = g_slice_new0(slot_info_t);
614 
615  newSlot->be = pInfo->be;
616  newSlot->guid = guid == NULL ? pInfo->guid : guid;
617  newSlot->is_ok = pInfo->is_ok;
618  newSlot->pKvpFrame = pInfo->pKvpFrame;
619  newSlot->value_type = pInfo->value_type;
620  newSlot->pList = pInfo->pList;
621  newSlot->context = pInfo->context;
622  newSlot->pKvpValue = pInfo->pKvpValue;
623  newSlot->path = g_string_new(pInfo->path->str);
624  return newSlot;
625 }
626 
627 static void
628 save_slot( const gchar* key, KvpValue* value, gpointer data )
629 {
630  slot_info_t* pSlot_info = (slot_info_t*)data;
631  gsize curlen;
632 
633  g_return_if_fail( key != NULL );
634  g_return_if_fail( value != NULL );
635  g_return_if_fail( data != NULL );
636 
637  // Ignore if we've already run into a failure
638  if ( !pSlot_info->is_ok )
639  {
640  return;
641  }
642 
643  curlen = pSlot_info->path->len;
644  pSlot_info->pKvpValue = value;
645  if ( curlen != 0 )
646  {
647  (void)g_string_append( pSlot_info->path, "/" );
648  }
649  (void)g_string_append( pSlot_info->path, key );
650  pSlot_info->value_type = kvp_value_get_type( value );
651 
652  switch ( pSlot_info->value_type )
653  {
654  case KVP_TYPE_FRAME:
655  {
656  KvpFrame* pKvpFrame = kvp_value_get_frame( value );
657  GncGUID guid = guid_new_return();
658  slot_info_t *pNewInfo = slot_info_copy( pSlot_info, &guid );
659  KvpValue *oldValue = pSlot_info->pKvpValue;
660  pSlot_info->pKvpValue = kvp_value_new_guid( &guid );
661  pSlot_info->is_ok = gnc_sql_do_db_operation( pSlot_info->be,
662  OP_DB_INSERT, TABLE_NAME,
663  TABLE_NAME, pSlot_info,
664  col_table );
665  g_return_if_fail( pSlot_info->is_ok );
666  kvp_frame_for_each_slot( pKvpFrame, save_slot, pNewInfo );
667  kvp_value_delete( pSlot_info->pKvpValue );
668  pSlot_info->pKvpValue = oldValue;
669  g_string_free( pNewInfo->path, TRUE );
670  g_slice_free( slot_info_t, pNewInfo );
671  }
672  break;
673  case KVP_TYPE_GLIST:
674  {
675  GList *cursor;
676  GncGUID guid = guid_new_return();
677  slot_info_t *pNewInfo = slot_info_copy( pSlot_info, &guid );
678  KvpValue *oldValue = pSlot_info->pKvpValue;
679  pSlot_info->pKvpValue = kvp_value_new_guid( &guid );
680  pSlot_info->is_ok = gnc_sql_do_db_operation( pSlot_info->be,
681  OP_DB_INSERT, TABLE_NAME,
682  TABLE_NAME, pSlot_info,
683  col_table );
684  g_return_if_fail( pSlot_info->is_ok );
685  for (cursor = kvp_value_get_glist(value); cursor; cursor = cursor->next)
686  {
687  KvpValue *val = (KvpValue*)cursor->data;
688  save_slot("", val, pNewInfo);
689  }
690  kvp_value_delete( pSlot_info->pKvpValue );
691  pSlot_info->pKvpValue = oldValue;
692  g_string_free( pNewInfo->path, TRUE );
693  g_slice_free( slot_info_t, pNewInfo );
694  }
695  break;
696  default:
697  {
698  pSlot_info->is_ok = gnc_sql_do_db_operation( pSlot_info->be,
699  OP_DB_INSERT, TABLE_NAME,
700  TABLE_NAME, pSlot_info,
701  col_table );
702  }
703  break;
704  }
705 
706  (void)g_string_truncate( pSlot_info->path, curlen );
707 }
708 
709 gboolean
710 gnc_sql_slots_save( GncSqlBackend* be, const GncGUID* guid, gboolean is_infant, KvpFrame* pFrame )
711 {
712  slot_info_t slot_info = { NULL, NULL, TRUE, NULL, 0, NULL, FRAME, NULL, g_string_new(NULL) };
713 
714  g_return_val_if_fail( be != NULL, FALSE );
715  g_return_val_if_fail( guid != NULL, FALSE );
716  g_return_val_if_fail( pFrame != NULL, FALSE );
717 
718  // If this is not saving into a new db, clear out the old saved slots first
719  if ( !be->is_pristine_db && !is_infant )
720  {
721  (void)gnc_sql_slots_delete( be, guid );
722  }
723 
724  slot_info.be = be;
725  slot_info.guid = guid;
726  kvp_frame_for_each_slot( pFrame, save_slot, &slot_info );
727  (void)g_string_free( slot_info.path, TRUE );
728 
729  return slot_info.is_ok;
730 }
731 
732 gboolean
734 {
735  gchar* buf;
736  GncSqlResult* result;
737  gchar guid_buf[GUID_ENCODING_LENGTH + 1];
738  GncSqlStatement* stmt;
739  slot_info_t slot_info = { NULL, NULL, TRUE, NULL, 0, NULL, FRAME, NULL, g_string_new(NULL) };
740 
741  g_return_val_if_fail( be != NULL, FALSE );
742  g_return_val_if_fail( guid != NULL, FALSE );
743 
744  (void)guid_to_string_buff( guid, guid_buf );
745 
746  buf = g_strdup_printf( "SELECT * FROM %s WHERE obj_guid='%s' and slot_type in ('%d', '%d') and not guid_val is null",
747  TABLE_NAME, guid_buf, KVP_TYPE_FRAME, KVP_TYPE_GLIST );
748  stmt = gnc_sql_create_statement_from_sql( be, buf );
749  g_free( buf );
750  if ( stmt != NULL )
751  {
752  result = gnc_sql_execute_select_statement( be, stmt );
753  gnc_sql_statement_dispose( stmt );
754  if ( result != NULL )
755  {
756  GncSqlRow* row = gnc_sql_result_get_first_row( result );
757 
758  while ( row != NULL )
759  {
760  GncSqlColumnTableEntry table_row = col_table[guid_val_col];
761  GncGUID child_guid;
762  const GValue* val =
763  gnc_sql_row_get_value_at_col_name( row, table_row.col_name);
764  if ( val == NULL )
765  continue;
766 
767  (void)string_to_guid( g_value_get_string( val ), &child_guid );
768  gnc_sql_slots_delete( be, &child_guid );
769  row = gnc_sql_result_get_next_row( result );
770  }
771  gnc_sql_result_dispose( result );
772  }
773  }
774 
775  slot_info.be = be;
776  slot_info.guid = guid;
777  slot_info.is_ok = TRUE;
778  slot_info.is_ok = gnc_sql_do_db_operation( be, OP_DB_DELETE, TABLE_NAME,
779  TABLE_NAME, &slot_info, obj_guid_col_table );
780 
781  return slot_info.is_ok;
782 }
783 
784 static void
785 load_slot( slot_info_t *pInfo, GncSqlRow* row )
786 {
787  slot_info_t *slot_info;
788 
789  g_return_if_fail( pInfo != NULL );
790  g_return_if_fail( pInfo->be != NULL );
791  g_return_if_fail( row != NULL );
792  g_return_if_fail( pInfo->pKvpFrame != NULL );
793 
794  slot_info = slot_info_copy( pInfo, NULL );
795  g_string_free( slot_info->path, TRUE );
796  slot_info->path = NULL;
797 
798  gnc_sql_load_object( pInfo->be, row, TABLE_NAME, slot_info, col_table );
799 
800  if ( slot_info->path != NULL )
801  {
802  (void)g_string_free( slot_info->path, TRUE );
803  }
804  if ( slot_info->pList != pInfo->pList )
805  {
806  if (pInfo->pList != NULL)
807  {
808  PWARN("Load slot returned a different list than the original");
809  }
810  else
811  {
812  pInfo->pList = slot_info->pList;
813  }
814  }
815  g_slice_free( slot_info_t, slot_info );
816 }
817 
818 void
820 {
821  slot_info_t info = { NULL, NULL, TRUE, NULL, 0, NULL, FRAME, NULL, g_string_new(NULL) };
822  g_return_if_fail( be != NULL );
823  g_return_if_fail( inst != NULL );
824 
825  info.be = be;
826  info.guid = qof_instance_get_guid( inst );
827  info.pKvpFrame = qof_instance_get_slots( inst );
828  info.context = NONE;
829 
830  slots_load_info( &info );
831 }
832 
833 static void
834 slots_load_info ( slot_info_t *pInfo )
835 {
836  gchar* buf;
837  GncSqlResult* result;
838  gchar guid_buf[GUID_ENCODING_LENGTH + 1];
839  GncSqlStatement* stmt;
840 
841  g_return_if_fail( pInfo != NULL );
842  g_return_if_fail( pInfo->be != NULL );
843  g_return_if_fail( pInfo->guid != NULL );
844  g_return_if_fail( pInfo->pKvpFrame != NULL );
845 
846  (void)guid_to_string_buff( pInfo->guid, guid_buf );
847 
848  buf = g_strdup_printf( "SELECT * FROM %s WHERE obj_guid='%s'",
849  TABLE_NAME, guid_buf );
850  stmt = gnc_sql_create_statement_from_sql( pInfo->be, buf );
851  g_free( buf );
852  if ( stmt != NULL )
853  {
854  result = gnc_sql_execute_select_statement( pInfo->be, stmt );
855  gnc_sql_statement_dispose( stmt );
856  if ( result != NULL )
857  {
858  GncSqlRow* row = gnc_sql_result_get_first_row( result );
859 
860  while ( row != NULL )
861  {
862  load_slot( pInfo, row );
863  row = gnc_sql_result_get_next_row( result );
864  }
865  gnc_sql_result_dispose( result );
866  }
867  }
868 }
869 
870 static /*@ dependent @*//*@ null @*/ const GncGUID*
871 load_obj_guid( const GncSqlBackend* be, GncSqlRow* row )
872 {
873  static GncGUID guid;
874 
875  g_return_val_if_fail( be != NULL, NULL );
876  g_return_val_if_fail( row != NULL, NULL );
877 
878  gnc_sql_load_object( be, row, NULL, &guid, obj_guid_col_table );
879 
880  return &guid;
881 }
882 
883 static void
884 load_slot_for_list_item( GncSqlBackend* be, GncSqlRow* row, QofCollection* coll )
885 {
886  slot_info_t slot_info = { NULL, NULL, TRUE, NULL, 0, NULL, FRAME, NULL, NULL };
887  const GncGUID* guid;
888  QofInstance* inst;
889 
890  g_return_if_fail( be != NULL );
891  g_return_if_fail( row != NULL );
892  g_return_if_fail( coll != NULL );
893 
894  guid = load_obj_guid( be, row );
895  g_assert( guid != NULL );
896  inst = qof_collection_lookup_entity( coll, guid );
897 
898  slot_info.be = be;
899  slot_info.pKvpFrame = qof_instance_get_slots( inst );
900  slot_info.context = NONE;
901 
902  gnc_sql_load_object( be, row, TABLE_NAME, &slot_info, col_table );
903 
904  if ( slot_info.path != NULL )
905  {
906  (void)g_string_free( slot_info.path, TRUE );
907  }
908 }
909 
910 void
912 {
913  QofCollection* coll;
914  GncSqlStatement* stmt;
915  GString* sql;
916  GncSqlResult* result;
917  gboolean single_item;
918 
919  g_return_if_fail( be != NULL );
920 
921  // Ignore empty list
922  if ( list == NULL ) return;
923 
924  coll = qof_instance_get_collection( QOF_INSTANCE(list->data) );
925 
926  // Create the query for all slots for all items on the list
927  sql = g_string_sized_new( 40 + (GUID_ENCODING_LENGTH + 3) * g_list_length( list ) );
928  g_string_append_printf( sql, "SELECT * FROM %s WHERE %s ", TABLE_NAME, obj_guid_col_table[0].col_name );
929  if ( g_list_length( list ) != 1 )
930  {
931  (void)g_string_append( sql, "IN (" );
932  single_item = FALSE;
933  }
934  else
935  {
936  (void)g_string_append( sql, "= " );
937  single_item = TRUE;
938  }
939  (void)gnc_sql_append_guid_list_to_sql( sql, list, G_MAXUINT );
940  if ( !single_item )
941  {
942  (void)g_string_append( sql, ")" );
943  }
944 
945  // Execute the query and load the slots
946  stmt = gnc_sql_create_statement_from_sql( be, sql->str );
947  if ( stmt == NULL )
948  {
949  PERR( "stmt == NULL, SQL = '%s'\n", sql->str );
950  (void)g_string_free( sql, TRUE );
951  return;
952  }
953  (void)g_string_free( sql, TRUE );
954  result = gnc_sql_execute_select_statement( be, stmt );
955  gnc_sql_statement_dispose( stmt );
956  if ( result != NULL )
957  {
958  GncSqlRow* row = gnc_sql_result_get_first_row( result );
959 
960  while ( row != NULL )
961  {
962  load_slot_for_list_item( be, row, coll );
963  row = gnc_sql_result_get_next_row( result );
964  }
965  gnc_sql_result_dispose( result );
966  }
967 }
968 
969 static void
970 load_slot_for_book_object( GncSqlBackend* be, GncSqlRow* row, BookLookupFn lookup_fn )
971 {
972  slot_info_t slot_info = { NULL, NULL, TRUE, NULL, 0, NULL, FRAME, NULL, NULL };
973  const GncGUID* guid;
974  QofInstance* inst;
975 
976  g_return_if_fail( be != NULL );
977  g_return_if_fail( row != NULL );
978  g_return_if_fail( lookup_fn != NULL );
979 
980  guid = load_obj_guid( be, row );
981  g_return_if_fail( guid != NULL );
982  inst = lookup_fn( guid, be->book );
983  g_return_if_fail( inst != NULL );
984 
985  slot_info.be = be;
986  slot_info.pKvpFrame = qof_instance_get_slots( inst );
987  slot_info.path = NULL;
988 
989  gnc_sql_load_object( be, row, TABLE_NAME, &slot_info, col_table );
990 
991  if ( slot_info.path != NULL )
992  {
993  (void)g_string_free( slot_info.path, TRUE );
994  }
995 }
996 
1006 void gnc_sql_slots_load_for_sql_subquery( GncSqlBackend* be, const gchar* subquery,
1007  BookLookupFn lookup_fn )
1008 {
1009  gchar* sql;
1010  GncSqlStatement* stmt;
1011  GncSqlResult* result;
1012 
1013  g_return_if_fail( be != NULL );
1014 
1015  // Ignore empty subquery
1016  if ( subquery == NULL ) return;
1017 
1018  sql = g_strdup_printf( "SELECT * FROM %s WHERE %s IN (%s)",
1019  TABLE_NAME, obj_guid_col_table[0].col_name,
1020  subquery );
1021 
1022  // Execute the query and load the slots
1023  stmt = gnc_sql_create_statement_from_sql( be, sql );
1024  if ( stmt == NULL )
1025  {
1026  PERR( "stmt == NULL, SQL = '%s'\n", sql );
1027  g_free( sql );
1028  return;
1029  }
1030  g_free( sql );
1031  result = gnc_sql_execute_select_statement( be, stmt );
1032  gnc_sql_statement_dispose( stmt );
1033  if ( result != NULL )
1034  {
1035  GncSqlRow* row = gnc_sql_result_get_first_row( result );
1036 
1037  while ( row != NULL )
1038  {
1039  load_slot_for_book_object( be, row, lookup_fn );
1040  row = gnc_sql_result_get_next_row( result );
1041  }
1042  gnc_sql_result_dispose( result );
1043  }
1044 }
1045 
1046 /* ================================================================= */
1047 static void
1048 create_slots_tables( GncSqlBackend* be )
1049 {
1050  gint version;
1051  gboolean ok;
1052 
1053  g_return_if_fail( be != NULL );
1054 
1055  version = gnc_sql_get_table_version( be, TABLE_NAME );
1056  if ( version == 0 )
1057  {
1058  (void)gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table );
1059 
1060  ok = gnc_sql_create_index( be, "slots_guid_index", TABLE_NAME, obj_guid_col_table );
1061  if ( !ok )
1062  {
1063  PERR( "Unable to create index\n" );
1064  }
1065  }
1066  else if ( version < TABLE_VERSION )
1067  {
1068  /* Upgrade:
1069  1->2: 64-bit int values to proper definition, add index
1070  2->3: Add gdate field
1071  */
1072  if ( version == 1 )
1073  {
1074  gnc_sql_upgrade_table( be, TABLE_NAME, col_table );
1075  ok = gnc_sql_create_index( be, "slots_guid_index", TABLE_NAME, obj_guid_col_table );
1076  if ( !ok )
1077  {
1078  PERR( "Unable to create index\n" );
1079  }
1080  }
1081  else if ( version == 2 )
1082  {
1083  ok = gnc_sql_add_columns_to_table( be, TABLE_NAME, gdate_col_table );
1084  if ( !ok )
1085  {
1086  PERR( "Unable to add gdate column\n" );
1087  }
1088  }
1089  (void)gnc_sql_set_table_version( be, TABLE_NAME, TABLE_VERSION );
1090  PINFO("Slots table upgraded from version %d to version %d\n", version, TABLE_VERSION);
1091  }
1092 }
1093 
1094 /* ================================================================= */
1095 void
1096 gnc_sql_init_slots_handler( void )
1097 {
1098  static GncSqlObjectBackend be_data =
1099  {
1100  GNC_SQL_BACKEND_VERSION,
1101  GNC_ID_ACCOUNT,
1102  NULL, /* commit - cannot occur */
1103  NULL, /* initial_load - cannot occur */
1104  create_slots_tables, /* create_tables */
1105  NULL, /* compile_query */
1106  NULL, /* run_query */
1107  NULL, /* free_query */
1108  NULL /* write */
1109  };
1110 
1111  (void)qof_object_register_backend( TABLE_NAME, GNC_SQL_BACKEND, &be_data );
1112 }
1113 /* ========================== 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
void gnc_sql_upgrade_table(GncSqlBackend *be, const gchar *table_name, const GncSqlColumnTableEntry *col_table)
QofInstance * qof_collection_lookup_entity(const QofCollection *, const GncGUID *)
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
#define PINFO(format, args...)
Definition: qoflog.h:249
load and save accounts data to SQL
#define COL_NNUL
KvpValueType
possible types in the union KvpValue
Definition: kvp_frame.h:93
GncGUID guid_new_return(void)
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
Use a 64-bit unsigned int timespec.
Definition: gnc-date.h:299
gboolean gnc_sql_add_columns_to_table(GncSqlBackend *be, const gchar *table_name, const GncSqlColumnTableEntry *new_col_table)
QofCollection * qof_instance_get_collection(gconstpointer inst)
gchar * guid_to_string_buff(const GncGUID *guid, gchar *buff)
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
Definition: qofclass.h:177
Timespec gnc_dmy2timespec(gint day, gint month, gint year)
#define PERR(format, args...)
Definition: qoflog.h:237
KvpValue * kvp_value_new_frame_nc(KvpFrame *value)
QofBook * book
Definition: guid.h:65
gint64 kvp_value_get_gint64(const KvpValue *value)
#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
void gnc_sql_slots_load_for_sql_subquery(GncSqlBackend *be, const gchar *subquery, BookLookupFn lookup_fn)
guint gnc_sql_append_guid_list_to_sql(GString *sql, GList *list, guint maxCount)
char * kvp_value_get_string(const KvpValue *value)
gboolean gnc_sql_set_table_version(GncSqlBackend *be, const gchar *table_name, gint version)
#define GUID_ENCODING_LENGTH
Definition: guid.h:74
void gnc_sql_slots_load_for_list(GncSqlBackend *be, GList *list)
void kvp_frame_set_slot_nc(KvpFrame *frame, const gchar *key, KvpValue *value)
GList * kvp_value_get_glist(const KvpValue *value)
void gnc_sql_slots_load(GncSqlBackend *be, QofInstance *inst)
GncGUID * kvp_value_get_guid(const KvpValue *value)
GncSqlResult * gnc_sql_execute_select_statement(GncSqlBackend *be, GncSqlStatement *stmt)
KvpFrame * kvp_frame_set_value_nc(KvpFrame *frame, const gchar *path, KvpValue *value)
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 kvp_frame_for_each_slot(KvpFrame *f, void(*proc)(const gchar *key, KvpValue *value, gpointer data), gpointer data)
gboolean gnc_sql_slots_delete(GncSqlBackend *be, const GncGUID *guid)
KvpValue * kvp_value_new_glist_nc(GList *lst)
struct KvpFrameImpl KvpFrame
Definition: kvp_frame.h:76
GDate kvp_value_get_gdate(const KvpValue *value)
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)
KvpFrame * kvp_frame_new(void)
KvpFrame * kvp_value_get_frame(const KvpValue *value)
GncSqlStatement * gnc_sql_create_statement_from_sql(GncSqlBackend *be, const gchar *sql)
void kvp_value_delete(KvpValue *value)
struct KvpValueImpl KvpValue
Definition: kvp_frame.h:80
gboolean gnc_sql_slots_save(GncSqlBackend *be, const GncGUID *guid, gboolean is_infant, KvpFrame *pFrame)
const gchar * QofLogModule
Definition: qofid.h:89