GnuCash 2.4.99
gnc-slots-sql.c
Go to the documentation of this file.
00001 /********************************************************************
00002  * gnc-slots-sql.c: load and save data to SQL                       *
00003  *                                                                  *
00004  * This program is free software; you can redistribute it and/or    *
00005  * modify it under the terms of the GNU General Public License as   *
00006  * published by the Free Software Foundation; either version 2 of   *
00007  * the License, or (at your option) any later version.              *
00008  *                                                                  *
00009  * This program is distributed in the hope that it will be useful,  *
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00012  * GNU General Public License for more details.                     *
00013  *                                                                  *
00014  * You should have received a copy of the GNU General Public License*
00015  * along with this program; if not, contact:                        *
00016  *                                                                  *
00017  * Free Software Foundation           Voice:  +1-617-542-5942       *
00018  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00019  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00020 \********************************************************************/
00029 #include "config.h"
00030 
00031 #include <glib.h>
00032 
00033 #include "qof.h"
00034 #include "gnc-engine.h"
00035 
00036 #include "gnc-backend-sql.h"
00037 
00038 #include "gnc-slots-sql.h"
00039 
00040 #ifdef S_SPLINT_S
00041 #include "splint-defs.h"
00042 #endif
00043 
00044 /*@ unused @*/ static QofLogModule log_module = G_LOG_DOMAIN;
00045 
00046 #define TABLE_NAME "slots"
00047 #define TABLE_VERSION 3
00048 
00049 typedef enum
00050 {
00051     NONE,
00052     FRAME,
00053     LIST
00054 } context_t;
00055 
00056 typedef struct
00057 {
00058     /*@ dependent @*/   GncSqlBackend* be;
00059     /*@ dependent @*/
00060     const GncGUID* guid;
00061     gboolean is_ok;
00062     /*@ dependent @*/
00063     KvpFrame* pKvpFrame;
00064     KvpValueType value_type;
00065     GList *pList;
00066     context_t context;
00067     /*@ dependent @*/
00068     KvpValue* pKvpValue;
00069     GString* path;
00070 } slot_info_t;
00071 
00072 
00073 static /*@ null @*/ gpointer get_obj_guid( gpointer pObject );
00074 static void set_obj_guid( void );
00075 static /*@ null @*/ gpointer get_path( gpointer pObject );
00076 static void set_path( gpointer pObject, /*@ null @*/ gpointer pValue );
00077 static KvpValueType get_slot_type( gpointer pObject );
00078 static void set_slot_type( gpointer pObject, /*@ null @*/ gpointer pValue );
00079 static gint64 get_int64_val( gpointer pObject );
00080 static void set_int64_val( gpointer pObject, gint64 pValue );
00081 static /*@ null @*/ gpointer get_string_val( gpointer pObject );
00082 static void set_string_val( gpointer pObject, /*@ null @*/ gpointer pValue );
00083 static /*@ dependent @*//*@ null @*/ gpointer get_double_val( gpointer pObject );
00084 static void set_double_val( gpointer pObject, /*@ null @*/ gpointer pValue );
00085 static Timespec get_timespec_val( gpointer pObject );
00086 static void set_timespec_val( gpointer pObject, Timespec ts );
00087 static /*@ null @*/ gpointer get_guid_val( gpointer pObject );
00088 static void set_guid_val( gpointer pObject, /*@ null @*/ gpointer pValue );
00089 static gnc_numeric get_numeric_val( gpointer pObject );
00090 static void set_numeric_val( gpointer pObject, gnc_numeric value );
00091 static GDate* get_gdate_val( gpointer pObject );
00092 static void set_gdate_val( gpointer pObject, GDate* value );
00093 static slot_info_t *slot_info_copy( slot_info_t *pInfo, GncGUID *guid );
00094 static void slots_load_info( slot_info_t *pInfo );
00095 
00096 #define SLOT_MAX_PATHNAME_LEN 4096
00097 #define SLOT_MAX_STRINGVAL_LEN 4096
00098 enum
00099 {
00100     id_col = 0,
00101     obj_guid_col,
00102     name_col,
00103     slot_type_col,
00104     int64_val_col,
00105     string_val_col,
00106     double_val_col,
00107     timespec_val_col,
00108     guid_val_col,
00109     numeric_val_col,
00110     gdate_val_col
00111 };
00112 
00113 static const GncSqlColumnTableEntry col_table[] =
00114 {
00115     /* col_name, col_type, size, flags, g0bj_param_name, qof_param_name, getter, setter */
00116     /*@ -full_init_block @*/
00117     { "id",             CT_INT,      0, COL_PKEY | COL_NNUL | COL_AUTOINC },
00118     {
00119         "obj_guid",     CT_GUID,     0,                     COL_NNUL, NULL, NULL,
00120         (QofAccessFunc)get_obj_guid,     (QofSetterFunc)set_obj_guid
00121     },
00122     {
00123         "name",         CT_STRING,   SLOT_MAX_PATHNAME_LEN, COL_NNUL, NULL, NULL,
00124         (QofAccessFunc)get_path,         set_path
00125     },
00126     {
00127         "slot_type",    CT_INT,      0,                     COL_NNUL, NULL, NULL,
00128         (QofAccessFunc)get_slot_type,    set_slot_type,
00129     },
00130     {
00131         "int64_val",    CT_INT64,    0,                     0,        NULL, NULL,
00132         (QofAccessFunc)get_int64_val,    (QofSetterFunc)set_int64_val
00133     },
00134     {
00135         "string_val",   CT_STRING,   SLOT_MAX_PATHNAME_LEN, 0,        NULL, NULL,
00136         (QofAccessFunc)get_string_val,   set_string_val
00137     },
00138     {
00139         "double_val",   CT_DOUBLE,   0,                     0,        NULL, NULL,
00140         (QofAccessFunc)get_double_val,   set_double_val
00141     },
00142     {
00143         "timespec_val", CT_TIMESPEC, 0,                     0,        NULL, NULL,
00144         (QofAccessFunc)get_timespec_val, (QofSetterFunc)set_timespec_val
00145     },
00146     {
00147         "guid_val",     CT_GUID,     0,                     0,        NULL, NULL,
00148         (QofAccessFunc)get_guid_val,     set_guid_val
00149     },
00150     {
00151         "numeric_val",  CT_NUMERIC,  0,                     0,        NULL, NULL,
00152         (QofAccessFunc)get_numeric_val, (QofSetterFunc)set_numeric_val
00153     },
00154     {
00155         "gdate_val",    CT_GDATE,    0,                     0,        NULL, NULL,
00156         (QofAccessFunc)get_gdate_val, (QofSetterFunc)set_gdate_val
00157     },
00158     { NULL }
00159     /*@ +full_init_block @*/
00160 };
00161 
00162 /* Special column table because we need to be able to access the table by
00163 a column other than the primary key */
00164 static const GncSqlColumnTableEntry obj_guid_col_table[] =
00165 {
00166     /*@ -full_init_block @*/
00167     { "obj_guid", CT_GUID, 0, 0, NULL, NULL, (QofAccessFunc)get_obj_guid, _retrieve_guid_ },
00168     { NULL }
00169     /*@ +full_init_block @*/
00170 };
00171 
00172 static const GncSqlColumnTableEntry gdate_col_table[] =
00173 {
00174     /*@ -full_init_block @*/
00175     { "gdate_val", CT_GDATE, 0, 0, },
00176     { NULL }
00177     /*@ +full_init_block @*/
00178 };
00179 
00180 /* ================================================================= */
00181 
00182 static gchar *
00183 get_key_from_path( GString *path )
00184 {
00185     gchar *str = NULL, *key = NULL, *ret = NULL;
00186 
00187     g_return_val_if_fail( path != NULL, strdup("") );
00188 
00189     if ( path->str == NULL ) return strdup("");
00190     str = g_strdup( path->str );
00191     key = strrchr( str, '/');
00192 
00193     /* Remove trailing /es */
00194     if ( key == NULL ) return str;
00195     while ( str + strlen(str) - key == 1 )
00196     {
00197         *key = '\0';
00198         key = strrchr( str, '/' );
00199     }
00200     if ( key == NULL ) return str;
00201     /* Now advance key past the last intermediate / to get the post-delimiter string */
00202     while ( *key == '/') ++key;
00203 
00204     ret = strdup( key );
00205     g_free( str );
00206     return ret;
00207 }
00208 
00209 static gchar *
00210 get_path_from_path( GString *path )
00211 {
00212     gchar *str = NULL, *key = NULL, *ret = NULL;
00213 
00214     g_return_val_if_fail( path != NULL, NULL );
00215 
00216     if ( path->str == NULL ) return NULL;
00217     str = g_strdup( path->str );
00218     key = strrchr( str, '/');
00219     /* No /es means no path, just a key */
00220     if ( key == NULL )
00221     {
00222         g_free( str );
00223         return NULL;
00224     }
00225     /* Remove trailing /es */
00226     while ( str + strlen(str) - key == 1 )
00227     {
00228         *key = '\0';
00229         key = strrchr( str, '/' );
00230     }
00231     if ( key == NULL )
00232     {
00233         g_free(str);
00234         return NULL;
00235     }
00236     /* reterminate the string at the slash */
00237     *key = '\0';
00238 
00239     return str;
00240 }
00241 
00242 static void
00243 set_slot_from_value( slot_info_t *pInfo, KvpValue *pValue)
00244 {
00245     g_return_if_fail( pInfo != NULL );
00246     g_return_if_fail( pValue != NULL );
00247 
00248     switch ( pInfo->context)
00249     {
00250     case FRAME:
00251     {
00252         gchar *key = get_key_from_path( pInfo->path );
00253         kvp_frame_set_value_nc( pInfo->pKvpFrame, key, pValue );
00254         g_free( key );
00255         break;
00256     }
00257     case LIST:
00258     {
00259         pInfo->pList = g_list_append(pInfo->pList, pValue);
00260         break;
00261     }
00262     case NONE:
00263     default:
00264     {
00265         gchar *key = get_key_from_path( pInfo->path );
00266         gchar *path = get_path_from_path( pInfo->path );
00267         KvpFrame* frame = pInfo->pKvpFrame;
00268         if ( path )
00269         {
00270             frame = kvp_frame_get_frame_slash( frame, path );
00271             g_free( path );
00272         }
00273         kvp_frame_set_value_nc( frame, key, pValue );
00274         g_free( key );
00275         break;
00276     }
00277     }
00278 }
00279 
00280 static /*@ null @*/ gpointer
00281 get_obj_guid( gpointer pObject )
00282 {
00283     slot_info_t* pInfo = (slot_info_t*)pObject;
00284 
00285     g_return_val_if_fail( pObject != NULL, NULL );
00286 
00287     return (gpointer)pInfo->guid;
00288 }
00289 
00290 static void
00291 set_obj_guid( void )
00292 {
00293     // Nowhere to put the GncGUID
00294 }
00295 
00296 static /*@ null @*/ gpointer
00297 get_path( gpointer pObject )
00298 {
00299     slot_info_t* pInfo = (slot_info_t*)pObject;
00300 
00301     g_return_val_if_fail( pObject != NULL, NULL );
00302 
00303     return (gpointer)pInfo->path->str;
00304 }
00305 
00306 static void
00307 set_path( gpointer pObject, /*@ null @*/ gpointer pValue )
00308 {
00309     slot_info_t* pInfo = (slot_info_t*)pObject;
00310 
00311     g_return_if_fail( pObject != NULL );
00312     g_return_if_fail( pValue != NULL );
00313 
00314     if ( pInfo->path != NULL )
00315     {
00316         (void)g_string_free( pInfo->path, TRUE );
00317     }
00318     pInfo->path = g_string_new( (gchar*)pValue );
00319 }
00320 
00321 static KvpValueType
00322 get_slot_type( gpointer pObject )
00323 {
00324     slot_info_t* pInfo = (slot_info_t*)pObject;
00325 
00326     g_return_val_if_fail( pObject != NULL, 0 );
00327 
00328 //    return (gpointer)kvp_value_get_type( pInfo->pKvpValue );
00329     return pInfo->value_type;
00330 }
00331 
00332 static void
00333 set_slot_type( gpointer pObject, /*@ null @*/ gpointer pValue )
00334 {
00335     slot_info_t* pInfo = (slot_info_t*)pObject;
00336 
00337     g_return_if_fail( pObject != NULL );
00338     g_return_if_fail( pValue != NULL );
00339 
00340     pInfo->value_type = (KvpValueType)pValue;
00341 }
00342 
00343 static gint64
00344 get_int64_val( gpointer pObject )
00345 {
00346     slot_info_t* pInfo = (slot_info_t*)pObject;
00347 
00348     g_return_val_if_fail( pObject != NULL, 0 );
00349 
00350     if ( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_GINT64 )
00351     {
00352         return kvp_value_get_gint64( pInfo->pKvpValue );
00353     }
00354     else
00355     {
00356         return 0;
00357     }
00358 }
00359 
00360 static void
00361 set_int64_val( gpointer pObject, gint64 value )
00362 {
00363     slot_info_t* pInfo = (slot_info_t*)pObject;
00364     KvpValue *pValue = NULL;
00365 
00366     g_return_if_fail( pObject != NULL );
00367 
00368     if ( pInfo->value_type != KVP_TYPE_GINT64 ) return;
00369     pValue = kvp_value_new_gint64( value );
00370     set_slot_from_value( pInfo, pValue );
00371 }
00372 
00373 static /*@ null @*/ gpointer
00374 get_string_val( gpointer pObject )
00375 {
00376     slot_info_t* pInfo = (slot_info_t*)pObject;
00377 
00378     g_return_val_if_fail( pObject != NULL, NULL );
00379 
00380     if ( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_STRING )
00381     {
00382         return (gpointer)kvp_value_get_string( pInfo->pKvpValue );
00383     }
00384     else
00385     {
00386         return NULL;
00387     }
00388 }
00389 
00390 static void
00391 set_string_val( gpointer pObject, /*@ null @*/ gpointer pValue )
00392 {
00393     slot_info_t* pInfo = (slot_info_t*)pObject;
00394     KvpValue *value = NULL;
00395 
00396     g_return_if_fail( pObject != NULL );
00397 
00398     if ( pInfo->value_type != KVP_TYPE_STRING || pValue == NULL ) return;
00399     value = kvp_value_new_string( (gchar*)pValue );
00400     set_slot_from_value( pInfo, value );
00401 }
00402 
00403 static /*@ dependent @*//*@ null @*/ gpointer
00404 get_double_val( gpointer pObject )
00405 {
00406     slot_info_t* pInfo = (slot_info_t*)pObject;
00407     static double d_val;
00408 
00409     g_return_val_if_fail( pObject != NULL, NULL );
00410 
00411     if ( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_DOUBLE )
00412     {
00413         d_val = kvp_value_get_double( pInfo->pKvpValue );
00414         return (gpointer)&d_val;
00415     }
00416     else
00417     {
00418         return NULL;
00419     }
00420 }
00421 
00422 static void
00423 set_double_val( gpointer pObject, /*@ null @*/ gpointer pValue )
00424 {
00425     slot_info_t* pInfo = (slot_info_t*)pObject;
00426     KvpValue *value = NULL;
00427 
00428     g_return_if_fail( pObject != NULL );
00429 
00430     if ( pInfo->value_type != KVP_TYPE_DOUBLE || pValue == NULL ) return;
00431     value = kvp_value_new_double( *((double*)pValue) );
00432     set_slot_from_value( pInfo, value );
00433 }
00434 
00435 static Timespec
00436 get_timespec_val( gpointer pObject )
00437 {
00438     slot_info_t* pInfo = (slot_info_t*)pObject;
00439 
00440     g_return_val_if_fail( pObject != NULL, gnc_dmy2timespec( 1, 1, 1970 ) );
00441 
00442 //if( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_TIMESPEC ) {
00443     return kvp_value_get_timespec( pInfo->pKvpValue );
00444 }
00445 
00446 static void
00447 set_timespec_val( gpointer pObject, Timespec ts )
00448 {
00449     slot_info_t* pInfo = (slot_info_t*)pObject;
00450     KvpValue *value = NULL;
00451 
00452     g_return_if_fail( pObject != NULL );
00453 
00454     if ( pInfo->value_type != KVP_TYPE_TIMESPEC ) return;
00455     value = kvp_value_new_timespec( ts );
00456     set_slot_from_value( pInfo, value );
00457 }
00458 
00459 static /*@ null @*/ gpointer
00460 get_guid_val( gpointer pObject )
00461 {
00462     slot_info_t* pInfo = (slot_info_t*)pObject;
00463 
00464     g_return_val_if_fail( pObject != NULL, NULL );
00465 
00466     if ( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_GUID )
00467     {
00468         return (gpointer)kvp_value_get_guid( pInfo->pKvpValue );
00469     }
00470     else
00471     {
00472         return NULL;
00473     }
00474 }
00475 
00476 static void
00477 set_guid_val( gpointer pObject, /*@ null @*/ gpointer pValue )
00478 {
00479     slot_info_t* pInfo = (slot_info_t*)pObject;
00480 
00481     g_return_if_fail( pObject != NULL );
00482     if ( pValue == NULL ) return;
00483 
00484     switch ( pInfo->value_type)
00485     {
00486     case KVP_TYPE_GUID:
00487     {
00488         KvpValue *value = kvp_value_new_guid( (GncGUID*)pValue );
00489         set_slot_from_value( pInfo, value );
00490         break;
00491     }
00492     case KVP_TYPE_GLIST:
00493     {
00494         slot_info_t *newInfo = slot_info_copy( pInfo, (GncGUID*)pValue );
00495         kvp_value *pValue = NULL;
00496         gchar *key = get_key_from_path( pInfo->path );
00497 
00498         newInfo->context = LIST;
00499 
00500         slots_load_info( newInfo );
00501         pValue = kvp_value_new_glist_nc( newInfo->pList );
00502         kvp_frame_set_slot_nc(pInfo->pKvpFrame, key, pValue);
00503         g_string_free( newInfo->path, TRUE );
00504         g_slice_free( slot_info_t, newInfo );
00505         g_free( key );
00506         break;
00507     }
00508     case KVP_TYPE_FRAME:
00509     {
00510         slot_info_t *newInfo = slot_info_copy( pInfo, (GncGUID*)pValue ) ;
00511         KvpFrame *newFrame = kvp_frame_new();
00512         newInfo->pKvpFrame = newFrame;
00513 
00514         switch ( pInfo->context )
00515         {
00516         case LIST:
00517         {
00518             KvpValue *value = kvp_value_new_frame_nc( newFrame );
00519             gchar *key = get_key_from_path( pInfo->path );
00520             newInfo->path = g_string_assign( newInfo->path, key );
00521             pInfo->pList = g_list_append( pInfo->pList, value );
00522             g_free( key );
00523             break;
00524         }
00525         case FRAME:
00526         default:
00527         {
00528             gchar *key = get_key_from_path( pInfo->path );
00529             kvp_frame_set_frame_nc( pInfo->pKvpFrame, key, newFrame );
00530             g_free( key );
00531             break;
00532         }
00533         }
00534 
00535         newInfo->context = FRAME;
00536         slots_load_info ( newInfo );
00537         g_string_free( newInfo->path, TRUE );
00538         g_slice_free( slot_info_t, newInfo );
00539         break;
00540     }
00541     default:
00542         break;
00543     }
00544 }
00545 
00546 static gnc_numeric
00547 get_numeric_val( gpointer pObject )
00548 {
00549     slot_info_t* pInfo = (slot_info_t*)pObject;
00550 
00551     g_return_val_if_fail( pObject != NULL, gnc_numeric_zero() );
00552 
00553     if ( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_NUMERIC )
00554     {
00555         return kvp_value_get_numeric( pInfo->pKvpValue );
00556     }
00557     else
00558     {
00559         return gnc_numeric_zero();
00560     }
00561 }
00562 
00563 static void
00564 set_numeric_val( gpointer pObject, gnc_numeric value )
00565 {
00566     slot_info_t* pInfo = (slot_info_t*)pObject;
00567     KvpValue *pValue = NULL;
00568 
00569     g_return_if_fail( pObject != NULL );
00570 
00571     if ( pInfo->value_type != KVP_TYPE_NUMERIC ) return;
00572     pValue = kvp_value_new_numeric( value );
00573     set_slot_from_value( pInfo, pValue );
00574 }
00575 
00576 static GDate*
00577 get_gdate_val( gpointer pObject )
00578 {
00579     slot_info_t* pInfo = (slot_info_t*)pObject;
00580     static GDate date;
00581 
00582     g_return_val_if_fail( pObject != NULL, NULL );
00583 
00584     if ( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_GDATE )
00585     {
00586         date = kvp_value_get_gdate( pInfo->pKvpValue );
00587         return &date;
00588     }
00589     else
00590     {
00591         return NULL;
00592     }
00593 }
00594 
00595 static void
00596 set_gdate_val( gpointer pObject, GDate* value )
00597 {
00598     slot_info_t* pInfo = (slot_info_t*)pObject;
00599     KvpValue *pValue = NULL;
00600 
00601     g_return_if_fail( pObject != NULL );
00602 
00603     if ( pInfo->value_type != KVP_TYPE_GDATE ) return;
00604     pValue = kvp_value_new_gdate( *value );
00605     set_slot_from_value( pInfo, pValue );
00606 }
00607 
00608 static slot_info_t *
00609 slot_info_copy( slot_info_t *pInfo, GncGUID *guid )
00610 {
00611     slot_info_t *newSlot;
00612     g_return_val_if_fail( pInfo != NULL, NULL );
00613     newSlot = g_slice_new0(slot_info_t);
00614 
00615     newSlot->be = pInfo->be;
00616     newSlot->guid = guid == NULL ? pInfo->guid : guid;
00617     newSlot->is_ok = pInfo->is_ok;
00618     newSlot->pKvpFrame = pInfo->pKvpFrame;
00619     newSlot->value_type = pInfo->value_type;
00620     newSlot->pList = pInfo->pList;
00621     newSlot->context = pInfo->context;
00622     newSlot->pKvpValue = pInfo->pKvpValue;
00623     newSlot->path = g_string_new(pInfo->path->str);
00624     return newSlot;
00625 }
00626 
00627 static void
00628 save_slot( const gchar* key, KvpValue* value, gpointer data )
00629 {
00630     slot_info_t* pSlot_info = (slot_info_t*)data;
00631     gsize curlen;
00632 
00633     g_return_if_fail( key != NULL );
00634     g_return_if_fail( value != NULL );
00635     g_return_if_fail( data != NULL );
00636 
00637     // Ignore if we've already run into a failure
00638     if ( !pSlot_info->is_ok )
00639     {
00640         return;
00641     }
00642 
00643     curlen = pSlot_info->path->len;
00644     pSlot_info->pKvpValue = value;
00645     if ( curlen != 0 )
00646     {
00647         (void)g_string_append( pSlot_info->path, "/" );
00648     }
00649     (void)g_string_append( pSlot_info->path, key );
00650     pSlot_info->value_type = kvp_value_get_type( value );
00651 
00652     switch ( pSlot_info->value_type )
00653     {
00654     case KVP_TYPE_FRAME:
00655     {
00656         KvpFrame* pKvpFrame = kvp_value_get_frame( value );
00657         GncGUID guid = guid_new_return();
00658         slot_info_t *pNewInfo = slot_info_copy( pSlot_info, &guid );
00659         KvpValue *oldValue = pSlot_info->pKvpValue;
00660         pSlot_info->pKvpValue = kvp_value_new_guid( &guid );
00661         pSlot_info->is_ok = gnc_sql_do_db_operation( pSlot_info->be,
00662                             OP_DB_INSERT, TABLE_NAME,
00663                             TABLE_NAME, pSlot_info,
00664                             col_table );
00665         g_return_if_fail( pSlot_info->is_ok );
00666         kvp_frame_for_each_slot( pKvpFrame, save_slot, pNewInfo );
00667         kvp_value_delete( pSlot_info->pKvpValue );
00668         pSlot_info->pKvpValue = oldValue;
00669         g_string_free( pNewInfo->path, TRUE );
00670         g_slice_free( slot_info_t, pNewInfo );
00671     }
00672     break;
00673     case KVP_TYPE_GLIST:
00674     {
00675         GList *cursor;
00676         GncGUID guid = guid_new_return();
00677         slot_info_t *pNewInfo = slot_info_copy( pSlot_info, &guid );
00678         KvpValue *oldValue = pSlot_info->pKvpValue;
00679         pSlot_info->pKvpValue = kvp_value_new_guid( &guid );
00680         pSlot_info->is_ok = gnc_sql_do_db_operation( pSlot_info->be,
00681                             OP_DB_INSERT, TABLE_NAME,
00682                             TABLE_NAME, pSlot_info,
00683                             col_table );
00684         g_return_if_fail( pSlot_info->is_ok );
00685         for (cursor = kvp_value_get_glist(value); cursor; cursor = cursor->next)
00686         {
00687             kvp_value *val = (kvp_value*)cursor->data;
00688             save_slot("", val, pNewInfo);
00689         }
00690         kvp_value_delete( pSlot_info->pKvpValue );
00691         pSlot_info->pKvpValue = oldValue;
00692         g_string_free( pNewInfo->path, TRUE );
00693         g_slice_free( slot_info_t, pNewInfo );
00694     }
00695     break;
00696     default:
00697     {
00698         pSlot_info->is_ok = gnc_sql_do_db_operation( pSlot_info->be,
00699                             OP_DB_INSERT, TABLE_NAME,
00700                             TABLE_NAME, pSlot_info,
00701                             col_table );
00702     }
00703     break;
00704     }
00705 
00706     (void)g_string_truncate( pSlot_info->path, curlen );
00707 }
00708 
00709 gboolean
00710 gnc_sql_slots_save( GncSqlBackend* be, const GncGUID* guid, gboolean is_infant, KvpFrame* pFrame )
00711 {
00712     slot_info_t slot_info = { NULL, NULL, TRUE, NULL, 0, NULL, FRAME, NULL, g_string_new('\0') };
00713 
00714     g_return_val_if_fail( be != NULL, FALSE );
00715     g_return_val_if_fail( guid != NULL, FALSE );
00716     g_return_val_if_fail( pFrame != NULL, FALSE );
00717 
00718     // If this is not saving into a new db, clear out the old saved slots first
00719     if ( !be->is_pristine_db && !is_infant )
00720     {
00721         (void)gnc_sql_slots_delete( be, guid );
00722     }
00723 
00724     slot_info.be = be;
00725     slot_info.guid = guid;
00726     kvp_frame_for_each_slot( pFrame, save_slot, &slot_info );
00727     (void)g_string_free( slot_info.path, TRUE );
00728 
00729     return slot_info.is_ok;
00730 }
00731 
00732 gboolean
00733 gnc_sql_slots_delete( GncSqlBackend* be, const GncGUID* guid )
00734 {
00735     gchar* buf;
00736     GncSqlResult* result;
00737     gchar guid_buf[GUID_ENCODING_LENGTH + 1];
00738     GncSqlStatement* stmt;
00739     slot_info_t slot_info = { NULL, NULL, TRUE, NULL, 0, NULL, FRAME, NULL, g_string_new('\0') };
00740 
00741     g_return_val_if_fail( be != NULL, FALSE );
00742     g_return_val_if_fail( guid != NULL, FALSE );
00743 
00744     (void)guid_to_string_buff( guid, guid_buf );
00745 
00746     buf = g_strdup_printf( "SELECT * FROM %s WHERE obj_guid='%s' and slot_type in ('%d', '%d') and not guid_val is null",
00747                            TABLE_NAME, guid_buf, KVP_TYPE_FRAME, KVP_TYPE_GLIST );
00748     stmt = gnc_sql_create_statement_from_sql( be, buf );
00749     g_free( buf );
00750     if ( stmt != NULL )
00751     {
00752         result = gnc_sql_execute_select_statement( be, stmt );
00753         gnc_sql_statement_dispose( stmt );
00754         if ( result != NULL )
00755         {
00756             GncSqlRow* row = gnc_sql_result_get_first_row( result );
00757 
00758             while ( row != NULL )
00759             {
00760                 GncSqlColumnTableEntry table_row = col_table[guid_val_col];
00761                 GncGUID child_guid;
00762                 const GValue* val =
00763                     gnc_sql_row_get_value_at_col_name( row, table_row.col_name);
00764                 if ( val == NULL )
00765                     continue;
00766 
00767                 (void)string_to_guid( g_value_get_string( val ), &child_guid );
00768                 gnc_sql_slots_delete( be, &child_guid );
00769                 row = gnc_sql_result_get_next_row( result );
00770             }
00771             gnc_sql_result_dispose( result );
00772         }
00773     }
00774 
00775     slot_info.be = be;
00776     slot_info.guid = guid;
00777     slot_info.is_ok = TRUE;
00778     slot_info.is_ok = gnc_sql_do_db_operation( be, OP_DB_DELETE, TABLE_NAME,
00779                       TABLE_NAME, &slot_info, obj_guid_col_table );
00780 
00781     return slot_info.is_ok;
00782 }
00783 
00784 static void
00785 load_slot( slot_info_t *pInfo, GncSqlRow* row )
00786 {
00787     slot_info_t *slot_info;
00788 
00789     g_return_if_fail( pInfo != NULL );
00790     g_return_if_fail( pInfo->be != NULL );
00791     g_return_if_fail( row != NULL );
00792     g_return_if_fail( pInfo->pKvpFrame != NULL );
00793 
00794     slot_info = slot_info_copy( pInfo, NULL );
00795     g_string_free( slot_info->path, TRUE );
00796     slot_info->path = NULL;
00797 
00798     gnc_sql_load_object( pInfo->be, row, TABLE_NAME, slot_info, col_table );
00799 
00800     if ( slot_info->path != NULL )
00801     {
00802         (void)g_string_free( slot_info->path, TRUE );
00803     }
00804     if ( slot_info->pList != pInfo->pList )
00805     {
00806         if (pInfo->pList != NULL)
00807         {
00808             PWARN("Load slot returned a different list than the original");
00809         }
00810         else
00811         {
00812             pInfo->pList = slot_info->pList;
00813         }
00814     }
00815     g_slice_free( slot_info_t, slot_info );
00816 }
00817 
00818 void
00819 gnc_sql_slots_load( GncSqlBackend* be, QofInstance* inst )
00820 {
00821     slot_info_t info = { NULL, NULL, TRUE, NULL, 0, NULL, FRAME, NULL, g_string_new('\0') };
00822     g_return_if_fail( be != NULL );
00823     g_return_if_fail( inst != NULL );
00824 
00825     info.be = be;
00826     info.guid = qof_instance_get_guid( inst );
00827     info.pKvpFrame = qof_instance_get_slots( inst );
00828     info.context = NONE;
00829 
00830     slots_load_info( &info );
00831 }
00832 
00833 static void
00834 slots_load_info ( slot_info_t *pInfo )
00835 {
00836     gchar* buf;
00837     GncSqlResult* result;
00838     gchar guid_buf[GUID_ENCODING_LENGTH + 1];
00839     GncSqlStatement* stmt;
00840 
00841     g_return_if_fail( pInfo != NULL );
00842     g_return_if_fail( pInfo->be != NULL );
00843     g_return_if_fail( pInfo->guid != NULL );
00844     g_return_if_fail( pInfo->pKvpFrame != NULL );
00845 
00846     (void)guid_to_string_buff( pInfo->guid, guid_buf );
00847 
00848     buf = g_strdup_printf( "SELECT * FROM %s WHERE obj_guid='%s'",
00849                            TABLE_NAME, guid_buf );
00850     stmt = gnc_sql_create_statement_from_sql( pInfo->be, buf );
00851     g_free( buf );
00852     if ( stmt != NULL )
00853     {
00854         result = gnc_sql_execute_select_statement( pInfo->be, stmt );
00855         gnc_sql_statement_dispose( stmt );
00856         if ( result != NULL )
00857         {
00858             GncSqlRow* row = gnc_sql_result_get_first_row( result );
00859 
00860             while ( row != NULL )
00861             {
00862                 load_slot( pInfo, row );
00863                 row = gnc_sql_result_get_next_row( result );
00864             }
00865             gnc_sql_result_dispose( result );
00866         }
00867     }
00868 }
00869 
00870 static /*@ dependent @*//*@ null @*/ const GncGUID*
00871 load_obj_guid( const GncSqlBackend* be, GncSqlRow* row )
00872 {
00873     static GncGUID guid;
00874 
00875     g_return_val_if_fail( be != NULL, NULL );
00876     g_return_val_if_fail( row != NULL, NULL );
00877 
00878     gnc_sql_load_object( be, row, NULL, &guid, obj_guid_col_table );
00879 
00880     return &guid;
00881 }
00882 
00883 static void
00884 load_slot_for_list_item( GncSqlBackend* be, GncSqlRow* row, QofCollection* coll )
00885 {
00886     slot_info_t slot_info = { NULL, NULL, TRUE, NULL, 0, NULL, FRAME, NULL, NULL };
00887     const GncGUID* guid;
00888     QofInstance* inst;
00889 
00890     g_return_if_fail( be != NULL );
00891     g_return_if_fail( row != NULL );
00892     g_return_if_fail( coll != NULL );
00893 
00894     guid = load_obj_guid( be, row );
00895     g_assert( guid != NULL );
00896     inst = qof_collection_lookup_entity( coll, guid );
00897 
00898     slot_info.be = be;
00899     slot_info.pKvpFrame = qof_instance_get_slots( inst );
00900     slot_info.context = NONE;
00901 
00902     gnc_sql_load_object( be, row, TABLE_NAME, &slot_info, col_table );
00903 
00904     if ( slot_info.path != NULL )
00905     {
00906         (void)g_string_free( slot_info.path, TRUE );
00907     }
00908 }
00909 
00910 void
00911 gnc_sql_slots_load_for_list( GncSqlBackend* be, GList* list )
00912 {
00913     QofCollection* coll;
00914     GncSqlStatement* stmt;
00915     GString* sql;
00916     GncSqlResult* result;
00917     gboolean single_item;
00918 
00919     g_return_if_fail( be != NULL );
00920 
00921     // Ignore empty list
00922     if ( list == NULL ) return;
00923 
00924     coll = qof_instance_get_collection( QOF_INSTANCE(list->data) );
00925 
00926     // Create the query for all slots for all items on the list
00927     sql = g_string_sized_new( 40 + (GUID_ENCODING_LENGTH + 3) * g_list_length( list ) );
00928     g_string_append_printf( sql, "SELECT * FROM %s WHERE %s ", TABLE_NAME, obj_guid_col_table[0].col_name );
00929     if ( g_list_length( list ) != 1 )
00930     {
00931         (void)g_string_append( sql, "IN (" );
00932         single_item = FALSE;
00933     }
00934     else
00935     {
00936         (void)g_string_append( sql, "= " );
00937         single_item = TRUE;
00938     }
00939     (void)gnc_sql_append_guid_list_to_sql( sql, list, G_MAXUINT );
00940     if ( !single_item )
00941     {
00942         (void)g_string_append( sql, ")" );
00943     }
00944 
00945     // Execute the query and load the slots
00946     stmt = gnc_sql_create_statement_from_sql( be, sql->str );
00947     if ( stmt == NULL )
00948     {
00949         PERR( "stmt == NULL, SQL = '%s'\n", sql->str );
00950         (void)g_string_free( sql, TRUE );
00951         return;
00952     }
00953     (void)g_string_free( sql, TRUE );
00954     result = gnc_sql_execute_select_statement( be, stmt );
00955     gnc_sql_statement_dispose( stmt );
00956     if ( result != NULL )
00957     {
00958         GncSqlRow* row = gnc_sql_result_get_first_row( result );
00959 
00960         while ( row != NULL )
00961         {
00962             load_slot_for_list_item( be, row, coll );
00963             row = gnc_sql_result_get_next_row( result );
00964         }
00965         gnc_sql_result_dispose( result );
00966     }
00967 }
00968 
00969 static void
00970 load_slot_for_book_object( GncSqlBackend* be, GncSqlRow* row, BookLookupFn lookup_fn )
00971 {
00972     slot_info_t slot_info = { NULL, NULL, TRUE, NULL, 0, NULL, FRAME, NULL, NULL };
00973     const GncGUID* guid;
00974     QofInstance* inst;
00975 
00976     g_return_if_fail( be != NULL );
00977     g_return_if_fail( row != NULL );
00978     g_return_if_fail( lookup_fn != NULL );
00979 
00980     guid = load_obj_guid( be, row );
00981     g_return_if_fail( guid != NULL );
00982     inst = lookup_fn( guid, be->book );
00983     g_return_if_fail( inst != NULL );
00984 
00985     slot_info.be = be;
00986     slot_info.pKvpFrame = qof_instance_get_slots( inst );
00987     slot_info.path = NULL;
00988 
00989     gnc_sql_load_object( be, row, TABLE_NAME, &slot_info, col_table );
00990 
00991     if ( slot_info.path != NULL )
00992     {
00993         (void)g_string_free( slot_info.path, TRUE );
00994     }
00995 }
00996 
01006 void gnc_sql_slots_load_for_sql_subquery( GncSqlBackend* be, const gchar* subquery,
01007         BookLookupFn lookup_fn )
01008 {
01009     gchar* sql;
01010     GncSqlStatement* stmt;
01011     GncSqlResult* result;
01012 
01013     g_return_if_fail( be != NULL );
01014 
01015     // Ignore empty subquery
01016     if ( subquery == NULL ) return;
01017 
01018     sql = g_strdup_printf( "SELECT * FROM %s WHERE %s IN (%s)",
01019                            TABLE_NAME, obj_guid_col_table[0].col_name,
01020                            subquery );
01021 
01022     // Execute the query and load the slots
01023     stmt = gnc_sql_create_statement_from_sql( be, sql );
01024     if ( stmt == NULL )
01025     {
01026         PERR( "stmt == NULL, SQL = '%s'\n", sql );
01027         g_free( sql );
01028         return;
01029     }
01030     g_free( sql );
01031     result = gnc_sql_execute_select_statement( be, stmt );
01032     gnc_sql_statement_dispose( stmt );
01033     if ( result != NULL )
01034     {
01035         GncSqlRow* row = gnc_sql_result_get_first_row( result );
01036 
01037         while ( row != NULL )
01038         {
01039             load_slot_for_book_object( be, row, lookup_fn );
01040             row = gnc_sql_result_get_next_row( result );
01041         }
01042         gnc_sql_result_dispose( result );
01043     }
01044 }
01045 
01046 /* ================================================================= */
01047 static void
01048 create_slots_tables( GncSqlBackend* be )
01049 {
01050     gint version;
01051     gboolean ok;
01052 
01053     g_return_if_fail( be != NULL );
01054 
01055     version = gnc_sql_get_table_version( be, TABLE_NAME );
01056     if ( version == 0 )
01057     {
01058         (void)gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table );
01059 
01060         ok = gnc_sql_create_index( be, "slots_guid_index", TABLE_NAME, obj_guid_col_table );
01061         if ( !ok )
01062         {
01063             PERR( "Unable to create index\n" );
01064         }
01065     }
01066     else if ( version < TABLE_VERSION )
01067     {
01068         /* Upgrade:
01069             1->2: 64-bit int values to proper definition, add index
01070             2->3: Add gdate field
01071         */
01072         if ( version == 1 )
01073         {
01074             gnc_sql_upgrade_table( be, TABLE_NAME, col_table );
01075             ok = gnc_sql_create_index( be, "slots_guid_index", TABLE_NAME, obj_guid_col_table );
01076             if ( !ok )
01077             {
01078                 PERR( "Unable to create index\n" );
01079             }
01080         }
01081         else if ( version == 2 )
01082         {
01083             ok = gnc_sql_add_columns_to_table( be, TABLE_NAME, gdate_col_table );
01084             if ( !ok )
01085             {
01086                 PERR( "Unable to add gdate column\n" );
01087             }
01088         }
01089         (void)gnc_sql_set_table_version( be, TABLE_NAME, TABLE_VERSION );
01090         PINFO("Slots table upgraded from version %d to version %d\n", version, TABLE_VERSION);
01091     }
01092 }
01093 
01094 /* ================================================================= */
01095 void
01096 gnc_sql_init_slots_handler( void )
01097 {
01098     static GncSqlObjectBackend be_data =
01099     {
01100         GNC_SQL_BACKEND_VERSION,
01101         GNC_ID_ACCOUNT,
01102         NULL,                    /* commit - cannot occur */
01103         NULL,                    /* initial_load - cannot occur */
01104         create_slots_tables,     /* create_tables */
01105         NULL,                    /* compile_query */
01106         NULL,                    /* run_query */
01107         NULL,                    /* free_query */
01108         NULL                     /* write */
01109     };
01110 
01111     (void)qof_object_register_backend( TABLE_NAME, GNC_SQL_BACKEND, &be_data );
01112 }
01113 /* ========================== END OF FILE ===================== */
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines