GnuCash 2.4.99
gnc-account-sql.c
Go to the documentation of this file.
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 ===================== */
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines