|
GnuCash 2.4.99
|
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 ===================== */
1.7.4