|
GnuCash 2.4.99
|
00001 /******************************************************************** 00002 * gnc-account-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 "Account.h" 00035 #include "AccountP.h" 00036 #include "gnc-commodity.h" 00037 00038 #include "gnc-backend-sql.h" 00039 00040 #include "gnc-account-sql.h" 00041 #include "gnc-commodity-sql.h" 00042 #include "gnc-slots-sql.h" 00043 #include "gnc-transaction-sql.h" 00044 00045 #if defined( S_SPLINT_S ) 00046 #include "splint-defs.h" 00047 #endif 00048 00049 static QofLogModule log_module = G_LOG_DOMAIN; 00050 00051 #define TABLE_NAME "accounts" 00052 #define TABLE_VERSION 1 00053 00054 static /*@ null @*//*@ dependent @*/ gpointer get_parent( gpointer pObject ); 00055 static void set_parent( gpointer pObject, /*@ null @*/ gpointer pValue ); 00056 static void set_parent_guid( gpointer pObject, /*@ null @*/ gpointer pValue ); 00057 00058 #define ACCOUNT_MAX_NAME_LEN 2048 00059 #define ACCOUNT_MAX_TYPE_LEN 2048 00060 #define ACCOUNT_MAX_CODE_LEN 2048 00061 #define ACCOUNT_MAX_DESCRIPTION_LEN 2048 00062 00063 static const GncSqlColumnTableEntry col_table[] = 00064 { 00065 /*@ -full_init_block @*/ 00066 { "guid", CT_GUID, 0, COL_NNUL | COL_PKEY, "guid" }, 00067 { "name", CT_STRING, ACCOUNT_MAX_NAME_LEN, COL_NNUL, "name" }, 00068 { "account_type", CT_STRING, ACCOUNT_MAX_TYPE_LEN, COL_NNUL, NULL, ACCOUNT_TYPE_ }, 00069 { "commodity_guid", CT_COMMODITYREF, 0, 0, "commodity" }, 00070 { "commodity_scu", CT_INT, 0, COL_NNUL, "commodity-scu" }, 00071 { "non_std_scu", CT_BOOLEAN, 0, COL_NNUL, "non-std-scu" }, 00072 { 00073 "parent_guid", CT_GUID, 0, 0, NULL, NULL, 00074 (QofAccessFunc)get_parent, set_parent 00075 }, 00076 { "code", CT_STRING, ACCOUNT_MAX_CODE_LEN, 0, "code" }, 00077 { "description", CT_STRING, ACCOUNT_MAX_DESCRIPTION_LEN, 0, "description" }, 00078 { "hidden", CT_BOOLEAN, 0, 0, "hidden" }, 00079 { "placeholder", CT_BOOLEAN, 0, 0, "placeholder" }, 00080 { NULL } 00081 /*@ +full_init_block @*/ 00082 }; 00083 static GncSqlColumnTableEntry parent_col_table[] = 00084 { 00085 /*@ -full_init_block @*/ 00086 { "parent_guid", CT_GUID, 0, 0, NULL, NULL, NULL, set_parent_guid }, 00087 { NULL } 00088 /*@ +full_init_block @*/ 00089 }; 00090 00091 typedef struct 00092 { 00093 /*@ dependent @*/ Account* pAccount; 00094 GncGUID guid; 00095 } account_parent_guid_struct; 00096 00097 /* ================================================================= */ 00098 00099 static /*@ null @*//*@ dependent @*/ gpointer 00100 get_parent( gpointer pObject ) 00101 { 00102 const Account* pAccount; 00103 const Account* pParent; 00104 const GncGUID* parent_guid; 00105 00106 g_return_val_if_fail( pObject != NULL, NULL ); 00107 g_return_val_if_fail( GNC_IS_ACCOUNT(pObject), NULL ); 00108 00109 pAccount = GNC_ACCOUNT(pObject); 00110 pParent = gnc_account_get_parent( pAccount ); 00111 if ( pParent == NULL ) 00112 { 00113 parent_guid = NULL; 00114 } 00115 else 00116 { 00117 parent_guid = qof_instance_get_guid( QOF_INSTANCE(pParent) ); 00118 } 00119 00120 return (gpointer)parent_guid; 00121 } 00122 00123 static void 00124 set_parent( gpointer pObject, /*@ null @*/ gpointer pValue ) 00125 { 00126 Account* pAccount; 00127 QofBook* pBook; 00128 GncGUID* guid = (GncGUID*)pValue; 00129 Account* pParent; 00130 00131 g_return_if_fail( pObject != NULL ); 00132 g_return_if_fail( GNC_IS_ACCOUNT(pObject) ); 00133 00134 pAccount = GNC_ACCOUNT(pObject); 00135 pBook = qof_instance_get_book( QOF_INSTANCE(pAccount) ); 00136 if ( guid != NULL ) 00137 { 00138 pParent = xaccAccountLookup( guid, pBook ); 00139 if ( pParent != NULL ) 00140 { 00141 gnc_account_append_child( pParent, pAccount ); 00142 } 00143 } 00144 } 00145 00146 static void 00147 set_parent_guid( gpointer pObject, /*@ null @*/ gpointer pValue ) 00148 { 00149 account_parent_guid_struct* s = (account_parent_guid_struct*)pObject; 00150 GncGUID* guid = (GncGUID*)pValue; 00151 00152 g_return_if_fail( pObject != NULL ); 00153 g_return_if_fail( pValue != NULL ); 00154 00155 s->guid = *guid; 00156 } 00157 00158 static /*@ dependent @*//*@ null @*/ Account* 00159 load_single_account( GncSqlBackend* be, GncSqlRow* row, 00160 GList** l_accounts_needing_parents ) 00161 { 00162 const GncGUID* guid; 00163 Account* pAccount = NULL; 00164 00165 g_return_val_if_fail( be != NULL, NULL ); 00166 g_return_val_if_fail( row != NULL, NULL ); 00167 g_return_val_if_fail( l_accounts_needing_parents != NULL, NULL ); 00168 00169 guid = gnc_sql_load_guid( be, row ); 00170 if ( guid != NULL ) 00171 { 00172 pAccount = xaccAccountLookup( guid, be->book ); 00173 } 00174 if ( pAccount == NULL ) 00175 { 00176 pAccount = xaccMallocAccount( be->book ); 00177 } 00178 xaccAccountBeginEdit( pAccount ); 00179 gnc_sql_load_object( be, row, GNC_ID_ACCOUNT, pAccount, col_table ); 00180 xaccAccountCommitEdit( pAccount ); 00181 00182 /* If we don't have a parent and this isn't the root account, it might be because the parent 00183 account hasn't been loaded yet. Remember the account and its parent guid for later. */ 00184 if ( gnc_account_get_parent( pAccount ) == NULL 00185 && pAccount != gnc_book_get_root_account( be->book ) ) 00186 { 00187 account_parent_guid_struct* s = g_malloc( (gsize)sizeof(account_parent_guid_struct) ); 00188 g_assert( s != NULL ); 00189 00190 s->pAccount = pAccount; 00191 gnc_sql_load_object( be, row, GNC_ID_ACCOUNT, s, parent_col_table ); 00192 *l_accounts_needing_parents = g_list_prepend( *l_accounts_needing_parents, s ); 00193 } 00194 00195 return pAccount; 00196 } 00197 00198 static void 00199 load_all_accounts( GncSqlBackend* be ) 00200 { 00201 GncSqlStatement* stmt = NULL; 00202 GncSqlResult* result; 00203 QofBook* pBook; 00204 gnc_commodity_table* pTable; 00205 GList* l_accounts_needing_parents = NULL; 00206 GSList* bal_slist; 00207 GSList* bal; 00208 00209 g_return_if_fail( be != NULL ); 00210 00211 ENTER( "" ); 00212 00213 pBook = be->book; 00214 pTable = gnc_commodity_table_get_table( pBook ); 00215 00216 stmt = gnc_sql_create_select_statement( be, TABLE_NAME ); 00217 if ( stmt == NULL ) 00218 { 00219 LEAVE( "stmt == NULL" ); 00220 return; 00221 } 00222 result = gnc_sql_execute_select_statement( be, stmt ); 00223 gnc_sql_statement_dispose( stmt ); 00224 if ( result != NULL ) 00225 { 00226 GncSqlRow* row = gnc_sql_result_get_first_row( result ); 00227 Account* acc; 00228 gchar* sql; 00229 00230 while ( row != NULL ) 00231 { 00232 acc = load_single_account( be, row, &l_accounts_needing_parents ); 00233 row = gnc_sql_result_get_next_row( result ); 00234 } 00235 gnc_sql_result_dispose( result ); 00236 00237 sql = g_strdup_printf( "SELECT DISTINCT guid FROM %s", TABLE_NAME ); 00238 gnc_sql_slots_load_for_sql_subquery( be, sql, (BookLookupFn)xaccAccountLookup ); 00239 g_free( sql ); 00240 00241 /* While there are items on the list of accounts needing parents, 00242 try to see if the parent has now been loaded. Theory says that if 00243 items are removed from the front and added to the back if the 00244 parent is still not available, then eventually, the list will 00245 shrink to size 0. */ 00246 if ( l_accounts_needing_parents != NULL ) 00247 { 00248 gboolean progress_made = TRUE; 00249 Account* root; 00250 Account* pParent; 00251 GList* elem; 00252 00253 while ( progress_made ) 00254 { 00255 progress_made = FALSE; 00256 for ( elem = l_accounts_needing_parents; elem != NULL; ) 00257 { 00258 account_parent_guid_struct* s = (account_parent_guid_struct*)elem->data; 00259 pParent = xaccAccountLookup( &s->guid, be->book ); 00260 if ( pParent != NULL ) 00261 { 00262 GList* next_elem; 00263 00264 gnc_account_append_child( pParent, s->pAccount ); 00265 next_elem = g_list_next( elem ); 00266 l_accounts_needing_parents = g_list_delete_link( l_accounts_needing_parents, elem ); 00267 g_free( s ); 00268 elem = next_elem; 00269 progress_made = TRUE; 00270 } 00271 else 00272 { 00273 /* Can't be up in the for loop because the 'then' clause reads inside a node freed 00274 by g_list_delete_link(). */ 00275 elem = g_list_next( elem ); 00276 } 00277 } 00278 } 00279 00280 /* Any non-ROOT accounts left over must be parented by the root account */ 00281 root = gnc_book_get_root_account( pBook ); 00282 while ( l_accounts_needing_parents != NULL ) 00283 { 00284 account_parent_guid_struct* s = (account_parent_guid_struct*)l_accounts_needing_parents->data; 00285 if ( xaccAccountGetType( s->pAccount ) != ACCT_TYPE_ROOT ) 00286 { 00287 gnc_account_append_child( root, s->pAccount ); 00288 } 00289 g_free( s ); 00290 l_accounts_needing_parents = g_list_delete_link( l_accounts_needing_parents, l_accounts_needing_parents ); 00291 } 00292 } 00293 00294 /* Load starting balances */ 00295 bal_slist = gnc_sql_get_account_balances_slist( be ); 00296 for ( bal = bal_slist; bal != NULL; bal = bal->next ) 00297 { 00298 acct_balances_t* balances = (acct_balances_t*)bal->data; 00299 00300 g_object_set( balances->acct, 00301 "start-balance", &balances->balance, 00302 "start-cleared-balance", &balances->cleared_balance, 00303 "start-reconciled-balance", &balances->reconciled_balance, 00304 NULL); 00305 00306 } 00307 if ( bal_slist != NULL ) 00308 { 00309 g_slist_free( bal_slist ); 00310 } 00311 } 00312 00313 LEAVE( "" ); 00314 } 00315 00316 /* ================================================================= */ 00317 static void 00318 create_account_tables( GncSqlBackend* be ) 00319 { 00320 gint version; 00321 00322 g_return_if_fail( be != NULL ); 00323 00324 version = gnc_sql_get_table_version( be, TABLE_NAME ); 00325 if ( version == 0 ) 00326 { 00327 (void)gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table ); 00328 } 00329 } 00330 00331 /* ================================================================= */ 00332 gboolean 00333 gnc_sql_save_account( GncSqlBackend* be, QofInstance* inst ) 00334 { 00335 Account* pAcc = GNC_ACCOUNT(inst); 00336 const GncGUID* guid; 00337 gboolean is_infant; 00338 gboolean is_ok = FALSE; 00339 gnc_commodity* commodity; 00340 gint op; 00341 00342 g_return_val_if_fail( be != NULL, FALSE ); 00343 g_return_val_if_fail( inst != NULL, FALSE ); 00344 g_return_val_if_fail( GNC_IS_ACCOUNT(inst), FALSE ); 00345 00346 ENTER( "inst=%p", inst ); 00347 00348 is_infant = qof_instance_get_infant( inst ); 00349 00350 // If there is no commodity yet, this might be because a new account name 00351 // has been entered directly into the register and an account window will 00352 // be opened. The account info is not complete yet, but the name has been 00353 // set, triggering this commit 00354 commodity = xaccAccountGetCommodity( pAcc ); 00355 00356 is_ok = TRUE; 00357 if ( qof_instance_get_destroying( inst ) ) 00358 { 00359 op = OP_DB_DELETE; 00360 } 00361 else if ( be->is_pristine_db || is_infant ) 00362 { 00363 op = OP_DB_INSERT; 00364 } 00365 else 00366 { 00367 op = OP_DB_UPDATE; 00368 } 00369 00370 // If not deleting the account, ensure the commodity is in the db 00371 if ( op != OP_DB_DELETE && commodity != NULL ) 00372 { 00373 is_ok = gnc_sql_save_commodity( be, commodity ); 00374 } 00375 00376 if ( is_ok ) 00377 { 00378 is_ok = gnc_sql_do_db_operation( be, op, TABLE_NAME, GNC_ID_ACCOUNT, pAcc, col_table ); 00379 } 00380 00381 if ( is_ok ) 00382 { 00383 // Now, commit or delete any slots 00384 guid = qof_instance_get_guid( inst ); 00385 if ( !qof_instance_get_destroying(inst) ) 00386 { 00387 is_ok = gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) ); 00388 } 00389 else 00390 { 00391 is_ok = gnc_sql_slots_delete( be, guid ); 00392 } 00393 } 00394 00395 LEAVE( "is_ok=%d", is_ok ); 00396 00397 return is_ok; 00398 } 00399 00400 /* ================================================================= */ 00401 static void 00402 load_account_guid( const GncSqlBackend* be, GncSqlRow* row, 00403 /*@ null @*/ QofSetterFunc setter, gpointer pObject, 00404 const GncSqlColumnTableEntry* table_row ) 00405 { 00406 const GValue* val; 00407 GncGUID guid; 00408 Account* account = NULL; 00409 00410 g_return_if_fail( be != NULL ); 00411 g_return_if_fail( row != NULL ); 00412 g_return_if_fail( pObject != NULL ); 00413 g_return_if_fail( table_row != NULL ); 00414 00415 val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); 00416 if ( val != NULL && G_VALUE_HOLDS_STRING( val ) && g_value_get_string( val ) != NULL ) 00417 { 00418 (void)string_to_guid( g_value_get_string( val ), &guid ); 00419 account = xaccAccountLookup( &guid, be->book ); 00420 if ( account != NULL ) 00421 { 00422 if ( table_row->gobj_param_name != NULL ) 00423 { 00424 g_object_set( pObject, table_row->gobj_param_name, account, NULL ); 00425 } 00426 else 00427 { 00428 g_return_if_fail( setter != NULL ); 00429 (*setter)( pObject, (const gpointer)account ); 00430 } 00431 } 00432 else 00433 { 00434 PWARN( "Account ref '%s' not found", g_value_get_string( val ) ); 00435 } 00436 } 00437 } 00438 00439 static GncSqlColumnTypeHandler account_guid_handler 00440 = { load_account_guid, 00441 gnc_sql_add_objectref_guid_col_info_to_list, 00442 gnc_sql_add_colname_to_list, 00443 gnc_sql_add_gvalue_objectref_guid_to_slist 00444 }; 00445 /* ================================================================= */ 00446 void 00447 gnc_sql_init_account_handler( void ) 00448 { 00449 static GncSqlObjectBackend be_data = 00450 { 00451 GNC_SQL_BACKEND_VERSION, 00452 GNC_ID_ACCOUNT, 00453 gnc_sql_save_account, /* commit */ 00454 load_all_accounts, /* initial_load */ 00455 create_account_tables, /* create_tables */ 00456 NULL, /* compile_query */ 00457 NULL, /* run_query */ 00458 NULL, /* free_query */ 00459 NULL /* write */ 00460 }; 00461 00462 (void)qof_object_register_backend( GNC_ID_ACCOUNT, GNC_SQL_BACKEND, &be_data ); 00463 00464 gnc_sql_register_col_type_handler( CT_ACCOUNTREF, &account_guid_handler ); 00465 } 00466 /* ========================== END OF FILE ===================== */
1.7.4