GnuCash 2.4.99
gnc-budget-sql.c
Go to the documentation of this file.
00001 /********************************************************************
00002  * gnc-budget-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 
00035 #include "gnc-backend-sql.h"
00036 
00037 #include "Recurrence.h"
00038 
00039 #include "gnc-budget-sql.h"
00040 #include "gnc-slots-sql.h"
00041 #include "gnc-recurrence-sql.h"
00042 
00043 #include "gnc-budget.h"
00044 
00045 #if defined( S_SPLINT_S )
00046 #include "splint-defs.h"
00047 #endif
00048 
00049 #define BUDGET_TABLE "budgets"
00050 #define TABLE_VERSION 1
00051 #define AMOUNTS_TABLE "budget_amounts"
00052 #define AMOUNTS_TABLE_VERSION 1
00053 
00054 /*@ unused @*/ static QofLogModule log_module = G_LOG_DOMAIN;
00055 
00056 #define BUDGET_MAX_NAME_LEN 2048
00057 #define BUDGET_MAX_DESCRIPTION_LEN 2048
00058 
00059 static const GncSqlColumnTableEntry col_table[] =
00060 {
00061     /*@ -full_init_block @*/
00062     { "guid",        CT_GUID,   0,                          COL_NNUL | COL_PKEY, "guid" },
00063     { "name",        CT_STRING, BUDGET_MAX_NAME_LEN,        COL_NNUL,          "name" },
00064     { "description", CT_STRING, BUDGET_MAX_DESCRIPTION_LEN, 0,                 "description" },
00065     { "num_periods", CT_INT,    0,                          COL_NNUL,          "num_periods" },
00066     { NULL }
00067     /*@ +full_init_block @*/
00068 };
00069 
00070 static /*@ dependent @*//*@ null @*/ QofInstance* get_budget( gpointer pObj );
00071 static void set_budget( gpointer pObj, gpointer val );
00072 static /*@ dependent @*//*@ null @*/ QofInstance* get_account( gpointer pObj );
00073 static void set_account( gpointer pObj, gpointer val );
00074 static gint get_period_num( gpointer pObj );
00075 static void set_period_num( gpointer pObj, gpointer val );
00076 static gnc_numeric get_amount( gpointer pObj );
00077 static void set_amount( gpointer pObj, gnc_numeric value );
00078 
00079 typedef struct
00080 {
00081     GncBudget* budget;
00082     Account* account;
00083     guint period_num;
00084 } budget_amount_info_t;
00085 
00086 static const GncSqlColumnTableEntry budget_amounts_col_table[] =
00087 {
00088     /*@ -full_init_block @*/
00089     { "id",           CT_INT,        0, COL_NNUL | COL_PKEY | COL_AUTOINC },
00090     {
00091         "budget_guid",  CT_BUDGETREF,  0, COL_NNUL,                     NULL, NULL,
00092         (QofAccessFunc)get_budget, (QofSetterFunc)set_budget
00093     },
00094     {
00095         "account_guid", CT_ACCOUNTREF, 0, COL_NNUL,                     NULL, NULL,
00096         (QofAccessFunc)get_account, (QofSetterFunc)set_account
00097     },
00098     {
00099         "period_num",   CT_INT,        0, COL_NNUL,                     NULL, NULL,
00100         (QofAccessFunc)get_period_num, (QofSetterFunc)set_period_num
00101     },
00102     {
00103         "amount",       CT_NUMERIC,    0, COL_NNUL,                     NULL, NULL,
00104         (QofAccessFunc)get_amount, (QofSetterFunc)set_amount
00105     },
00106     { NULL }
00107     /*@ +full_init_block @*/
00108 };
00109 
00110 /* ================================================================= */
00111 static /*@ dependent @*//*@ null@ */ QofInstance*
00112 get_budget( gpointer pObj )
00113 {
00114     budget_amount_info_t* info = (budget_amount_info_t*)pObj;
00115 
00116     g_return_val_if_fail( pObj != NULL, NULL );
00117 
00118     return QOF_INSTANCE(info->budget);
00119 }
00120 
00121 static void
00122 set_budget( gpointer pObj, gpointer val )
00123 {
00124 }
00125 
00126 static /*@ dependent @*//*@ null @*/ QofInstance*
00127 get_account( gpointer pObj )
00128 {
00129     budget_amount_info_t* info = (budget_amount_info_t*)pObj;
00130 
00131     g_return_val_if_fail( pObj != NULL, NULL );
00132 
00133     return QOF_INSTANCE(info->account);
00134 }
00135 
00136 static void
00137 set_account( gpointer pObj, gpointer val )
00138 {
00139     budget_amount_info_t* info = (budget_amount_info_t*)pObj;
00140 
00141     g_return_if_fail( pObj != NULL );
00142     g_return_if_fail( val != NULL );
00143     g_return_if_fail( GNC_IS_ACCOUNT(val) );
00144 
00145     info->account = GNC_ACCOUNT(val);
00146 }
00147 
00148 static gint
00149 get_period_num( gpointer pObj )
00150 {
00151     budget_amount_info_t* info = (budget_amount_info_t*)pObj;
00152 
00153     g_return_val_if_fail( pObj != NULL, 0 );
00154 
00155     return info->period_num;
00156 }
00157 
00158 static void
00159 set_period_num( gpointer pObj, gpointer val )
00160 {
00161     budget_amount_info_t* info = (budget_amount_info_t*)pObj;
00162 
00163     g_return_if_fail( pObj != NULL );
00164 
00165     info->period_num = GPOINTER_TO_UINT(val);
00166 }
00167 
00168 static gnc_numeric
00169 get_amount( gpointer pObj )
00170 {
00171     budget_amount_info_t* info = (budget_amount_info_t*)pObj;
00172 
00173     g_return_val_if_fail( pObj != NULL, gnc_numeric_zero() );
00174 
00175     return gnc_budget_get_account_period_value( info->budget, info->account, info->period_num );
00176 }
00177 
00178 static void
00179 set_amount( gpointer pObj, gnc_numeric value )
00180 {
00181     budget_amount_info_t* info = (budget_amount_info_t*)pObj;
00182 
00183     g_return_if_fail( pObj != NULL );
00184 
00185     gnc_budget_set_account_period_value( info->budget, info->account, info->period_num, value );
00186 }
00187 
00188 /*----------------------------------------------------------------*/
00195 static void
00196 load_budget_amounts( GncSqlBackend* be, GncBudget* budget )
00197 {
00198     gchar guid_buf[GUID_ENCODING_LENGTH+1];
00199     gchar* sql;
00200     GncSqlStatement* stmt;
00201     GncSqlResult* result;
00202 
00203     g_return_if_fail( be != NULL );
00204     g_return_if_fail( budget != NULL );
00205 
00206     (void)guid_to_string_buff( qof_instance_get_guid( QOF_INSTANCE(budget) ), guid_buf );
00207     sql = g_strdup_printf( "SELECT * FROM %s WHERE budget_guid='%s'", AMOUNTS_TABLE, guid_buf );
00208     stmt = gnc_sql_create_statement_from_sql( be, sql );
00209     g_free( sql );
00210     if ( stmt != NULL )
00211     {
00212         result = gnc_sql_execute_select_statement( be, stmt );
00213         gnc_sql_statement_dispose( stmt );
00214         if ( result != NULL )
00215         {
00216             GncSqlRow* row = gnc_sql_result_get_first_row( result );
00217             budget_amount_info_t info = { budget, NULL, 0 };
00218 
00219             while ( row != NULL )
00220             {
00221                 gnc_sql_load_object( be, row, NULL, &info, budget_amounts_col_table );
00222                 row = gnc_sql_result_get_next_row( result );
00223             }
00224             gnc_sql_result_dispose( result );
00225         }
00226     }
00227 }
00228 
00235 static gboolean
00236 delete_budget_amounts( GncSqlBackend* be, GncBudget* budget )
00237 {
00238     gchar guid_buf[GUID_ENCODING_LENGTH+1];
00239     gchar* sql;
00240 
00241     g_return_val_if_fail( be != NULL, FALSE );
00242     g_return_val_if_fail( budget != NULL, FALSE );
00243 
00244     (void)guid_to_string_buff( qof_instance_get_guid( QOF_INSTANCE(budget) ), guid_buf );
00245     sql = g_strdup_printf( "DELETE FROM %s WHERE budget_guid='%s'", AMOUNTS_TABLE, guid_buf );
00246     (void)gnc_sql_execute_nonselect_sql( be, sql );
00247     g_free( sql );
00248 
00249     return TRUE;
00250 }
00251 
00258 static gboolean
00259 save_budget_amounts( GncSqlBackend* be, GncBudget* budget )
00260 {
00261     GList* descendants;
00262     /*@ dependent @*/
00263     GList* node;
00264     budget_amount_info_t info;
00265     guint num_periods;
00266     gboolean is_ok = TRUE;;
00267 
00268     g_return_val_if_fail( be != NULL, FALSE );
00269     g_return_val_if_fail( budget != NULL, FALSE );
00270 
00271     // Delete the amounts, then save
00272     delete_budget_amounts( be, budget );
00273 
00274     info.budget = budget;
00275     num_periods = gnc_budget_get_num_periods( budget );
00276     descendants = gnc_account_get_descendants( gnc_book_get_root_account( be->book ) );
00277     for ( node = descendants; node != NULL && is_ok; node = g_list_next(node) )
00278     {
00279         guint i;
00280 
00281         info.account = GNC_ACCOUNT(node->data);
00282         for ( i = 0; i < num_periods && is_ok; i++ )
00283         {
00284             if ( gnc_budget_is_account_period_value_set( budget, info.account, i ) )
00285             {
00286                 info.period_num = i;
00287                 is_ok = gnc_sql_do_db_operation( be, OP_DB_INSERT, AMOUNTS_TABLE, "", &info,
00288                                                  budget_amounts_col_table );
00289             }
00290         }
00291     }
00292     g_list_free( descendants );
00293 
00294     return is_ok;
00295 }
00296 /*----------------------------------------------------------------*/
00297 static /*@ dependent @*//*@ null @*/ GncBudget*
00298 load_single_budget( GncSqlBackend* be, GncSqlRow* row )
00299 {
00300     const GncGUID* guid;
00301     GncBudget* pBudget = NULL;
00302     Recurrence* r;
00303 
00304     g_return_val_if_fail( be != NULL, NULL );
00305     g_return_val_if_fail( row != NULL, NULL );
00306 
00307     guid = gnc_sql_load_guid( be, row );
00308     if ( guid != NULL )
00309     {
00310         pBudget = gnc_budget_lookup( guid, be->book );
00311     }
00312     if ( pBudget == NULL )
00313     {
00314         pBudget = gnc_budget_new( be->book );
00315     }
00316 
00317     gnc_budget_begin_edit( pBudget );
00318     gnc_sql_load_object( be, row, GNC_ID_BUDGET, pBudget, col_table );
00319     load_budget_amounts( be, pBudget );
00320     r = gnc_sql_recurrence_load( be, gnc_budget_get_guid( pBudget ) );
00321     if ( r != NULL )
00322     {
00323         gnc_budget_set_recurrence( pBudget, r );
00324         g_free( r );
00325     }
00326     gnc_budget_commit_edit( pBudget );
00327 
00328     return pBudget;
00329 }
00330 
00331 static void
00332 load_all_budgets( GncSqlBackend* be )
00333 {
00334     GncSqlStatement* stmt;
00335     GncSqlResult* result;
00336     GList* list = NULL;
00337 
00338     g_return_if_fail( be != NULL );
00339 
00340     stmt = gnc_sql_create_select_statement( be, BUDGET_TABLE );
00341     if ( stmt != NULL )
00342     {
00343         result = gnc_sql_execute_select_statement( be, stmt );
00344         gnc_sql_statement_dispose( stmt );
00345         if ( result != NULL )
00346         {
00347             GncSqlRow* row = gnc_sql_result_get_first_row( result );
00348             GncBudget* b;
00349 
00350             while ( row != NULL )
00351             {
00352                 b = load_single_budget( be, row );
00353                 if ( b != NULL )
00354                 {
00355                     list = g_list_prepend( list, b );
00356                 }
00357                 row = gnc_sql_result_get_next_row( result );
00358             }
00359             gnc_sql_result_dispose( result );
00360 
00361             if ( list != NULL )
00362             {
00363                 gnc_sql_slots_load_for_list( be, list );
00364                 g_list_free( list );
00365             }
00366         }
00367     }
00368 }
00369 
00370 /* ================================================================= */
00371 static void
00372 create_budget_tables( GncSqlBackend* be )
00373 {
00374     gint version;
00375 
00376     g_return_if_fail( be != NULL );
00377 
00378     version = gnc_sql_get_table_version( be, BUDGET_TABLE );
00379     if ( version == 0 )
00380     {
00381         (void)gnc_sql_create_table( be, BUDGET_TABLE, TABLE_VERSION, col_table );
00382     }
00383 
00384     version = gnc_sql_get_table_version( be, AMOUNTS_TABLE );
00385     if ( version == 0 )
00386     {
00387         (void)gnc_sql_create_table( be, AMOUNTS_TABLE, AMOUNTS_TABLE_VERSION, budget_amounts_col_table );
00388     }
00389 }
00390 
00391 /* ================================================================= */
00392 static gboolean
00393 save_budget( GncSqlBackend* be, QofInstance* inst )
00394 {
00395     GncBudget* pBudget = GNC_BUDGET(inst);
00396     const GncGUID* guid;
00397     gint op;
00398     gboolean is_infant;
00399     gboolean is_ok;
00400 
00401     g_return_val_if_fail( be != NULL, FALSE );
00402     g_return_val_if_fail( inst != NULL, FALSE );
00403     g_return_val_if_fail( GNC_IS_BUDGET(inst), FALSE );
00404 
00405     is_infant = qof_instance_get_infant( inst );
00406     if ( qof_instance_get_destroying( inst ) )
00407     {
00408         op = OP_DB_DELETE;
00409     }
00410     else if ( be->is_pristine_db || is_infant )
00411     {
00412         op = OP_DB_INSERT;
00413     }
00414     else
00415     {
00416         op = OP_DB_UPDATE;
00417     }
00418     is_ok = gnc_sql_do_db_operation( be, op, BUDGET_TABLE, GNC_ID_BUDGET, pBudget, col_table );
00419 
00420     // Now, commit any slots and recurrence
00421     if ( is_ok )
00422     {
00423         guid = qof_instance_get_guid( inst );
00424         if ( !qof_instance_get_destroying(inst) )
00425         {
00426             is_ok = save_budget_amounts( be, pBudget );
00427             if ( is_ok )
00428             {
00429                 is_ok = gnc_sql_recurrence_save( be, guid, gnc_budget_get_recurrence( pBudget ) );
00430             }
00431             if ( is_ok )
00432             {
00433                 is_ok = gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) );
00434             }
00435         }
00436         else
00437         {
00438             is_ok = delete_budget_amounts( be, pBudget );
00439             if ( is_ok )
00440             {
00441                 is_ok = gnc_sql_recurrence_delete( be, guid );
00442             }
00443             if ( is_ok )
00444             {
00445                 (void)gnc_sql_slots_delete( be, guid );
00446             }
00447         }
00448     }
00449 
00450     return is_ok;
00451 }
00452 
00453 static void
00454 do_save_budget( QofInstance* inst, gpointer data )
00455 {
00456     write_objects_t* s = (write_objects_t*)data;
00457 
00458     if ( s->is_ok )
00459     {
00460         s->is_ok = save_budget( s->be, inst );
00461     }
00462 }
00463 
00464 static gboolean
00465 write_budgets( GncSqlBackend* be )
00466 {
00467     write_objects_t data;
00468 
00469     g_return_val_if_fail( be != NULL, FALSE );
00470 
00471     data.be = be;
00472     data.is_ok = TRUE;
00473     qof_collection_foreach( qof_book_get_collection( be->book, GNC_ID_BUDGET ),
00474                             (QofInstanceForeachCB)do_save_budget, &data );
00475 
00476     return data.is_ok;
00477 }
00478 
00479 /* ================================================================= */
00480 static void
00481 load_budget_guid( const GncSqlBackend* be, GncSqlRow* row,
00482                   /*@ null @*/ QofSetterFunc setter, gpointer pObject,
00483                   const GncSqlColumnTableEntry* table_row )
00484 {
00485     const GValue* val;
00486     GncGUID guid;
00487     GncBudget* budget = NULL;
00488 
00489     g_return_if_fail( be != NULL );
00490     g_return_if_fail( row != NULL );
00491     g_return_if_fail( pObject != NULL );
00492     g_return_if_fail( table_row != NULL );
00493 
00494     val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
00495     if ( val != NULL && G_VALUE_HOLDS_STRING( val ) && g_value_get_string( val ) != NULL )
00496     {
00497         (void)string_to_guid( g_value_get_string( val ), &guid );
00498         budget = gnc_budget_lookup( &guid, be->book );
00499         if ( budget != NULL )
00500         {
00501             if ( table_row->gobj_param_name != NULL )
00502             {
00503                 g_object_set( pObject, table_row->gobj_param_name, budget, NULL );
00504             }
00505             else
00506             {
00507                 g_return_if_fail( setter != NULL );
00508                 (*setter)( pObject, (const gpointer)budget );
00509             }
00510         }
00511         else
00512         {
00513             PWARN( "Budget ref '%s' not found", g_value_get_string( val ) );
00514         }
00515     }
00516 }
00517 
00518 static GncSqlColumnTypeHandler budget_guid_handler
00519 = { load_budget_guid,
00520     gnc_sql_add_objectref_guid_col_info_to_list,
00521     gnc_sql_add_colname_to_list,
00522     gnc_sql_add_gvalue_objectref_guid_to_slist
00523   };
00524 /* ================================================================= */
00525 void
00526 gnc_sql_init_budget_handler( void )
00527 {
00528     static GncSqlObjectBackend be_data =
00529     {
00530         GNC_SQL_BACKEND_VERSION,
00531         GNC_ID_BUDGET,
00532         save_budget,                            /* commit */
00533         load_all_budgets,               /* initial_load */
00534         create_budget_tables,           /* create_tables */
00535         NULL,                           /* compile_query */
00536         NULL,                           /* run_query */
00537         NULL,                           /* free_query */
00538         write_budgets                                   /* write */
00539     };
00540 
00541     (void)qof_object_register_backend( GNC_ID_BUDGET, GNC_SQL_BACKEND, &be_data );
00542 
00543     gnc_sql_register_col_type_handler( CT_BUDGETREF, &budget_guid_handler );
00544 }
00545 /* ========================== END OF FILE ===================== */
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines