GnuCash 2.4.99
gnc-backend-sql.c
Go to the documentation of this file.
00001 /********************************************************************
00002  * gnc-backend-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 <stdlib.h>
00030 #include "config.h"
00031 
00032 #include <errno.h>
00033 #include <glib.h>
00034 #include <glib/gi18n.h>
00035 #include <glib/gstdio.h>
00036 
00037 #include "qof.h"
00038 #include "qofquery-p.h"
00039 #include "qofquerycore-p.h"
00040 #include "Account.h"
00041 #include "TransLog.h"
00042 #include "gnc-engine.h"
00043 #include "SX-book.h"
00044 #include "Recurrence.h"
00045 #include "gncBillTerm.h"
00046 #include "gncTaxTable.h"
00047 #include "gncInvoice.h"
00048 
00049 #include "gnc-gconf-utils.h"
00050 
00051 #include "gnc-backend-sql.h"
00052 
00053 #include "gnc-account-sql.h"
00054 #include "gnc-book-sql.h"
00055 #include "gnc-budget-sql.h"
00056 #include "gnc-commodity-sql.h"
00057 #include "gnc-lots-sql.h"
00058 #include "gnc-price-sql.h"
00059 #include "gnc-pricedb.h"
00060 #include "gnc-recurrence-sql.h"
00061 #include "gnc-schedxaction-sql.h"
00062 #include "gnc-slots-sql.h"
00063 #include "gnc-transaction-sql.h"
00064 
00065 #include "gnc-address-sql.h"
00066 #include "gnc-bill-term-sql.h"
00067 #include "gnc-customer-sql.h"
00068 #include "gnc-employee-sql.h"
00069 #include "gnc-entry-sql.h"
00070 #include "gnc-invoice-sql.h"
00071 #include "gnc-job-sql.h"
00072 #include "gnc-order-sql.h"
00073 #include "gnc-owner-sql.h"
00074 #include "gnc-tax-table-sql.h"
00075 #include "gnc-vendor-sql.h"
00076 
00077 #include "gnc-main.h"
00078 
00079 #if defined( S_SPLINT_S )
00080 #include "splint-defs.h"
00081 #endif
00082 
00083 #if 0
00084 static const gchar* convert_search_obj( QofIdType objType );
00085 #endif
00086 static void gnc_sql_init_object_handlers( void );
00087 static void update_progress( GncSqlBackend* be );
00088 static void finish_progress( GncSqlBackend* be );
00089 static void register_standard_col_type_handlers( void );
00090 static gboolean reset_version_info( GncSqlBackend* be );
00091 /*@ null @*/
00092 static GncSqlStatement* build_insert_statement( GncSqlBackend* be,
00093         const gchar* table_name,
00094         QofIdTypeConst obj_name, gpointer pObject,
00095         const GncSqlColumnTableEntry* table );
00096 /*@ null @*/
00097 static GncSqlStatement* build_update_statement( GncSqlBackend* be,
00098         const gchar* table_name,
00099         QofIdTypeConst obj_name, gpointer pObject,
00100         const GncSqlColumnTableEntry* table );
00101 /*@ null @*/
00102 static GncSqlStatement* build_delete_statement( GncSqlBackend* be,
00103         const gchar* table_name,
00104         QofIdTypeConst obj_name, gpointer pObject,
00105         const GncSqlColumnTableEntry* table );
00106 
00107 #define TRANSACTION_NAME "trans"
00108 
00109 typedef struct
00110 {
00111     /*@ dependent @*/ QofIdType searchObj;
00112     /*@ dependent @*/
00113     gpointer pCompiledQuery;
00114 } gnc_sql_query_info;
00115 
00116 /* callback structure */
00117 typedef struct
00118 {
00119     gboolean is_known;
00120     gboolean is_ok;
00121     /*@ dependent @*/
00122     GncSqlBackend* be;
00123     /*@ dependent @*/
00124     QofInstance* inst;
00125     /*@ dependent @*/
00126     QofQuery* pQuery;
00127     /*@ dependent @*/
00128     gpointer pCompiledQuery;
00129     /*@ owned @*/
00130     gnc_sql_query_info* pQueryInfo;
00131 } sql_backend;
00132 
00133 static QofLogModule log_module = G_LOG_DOMAIN;
00134 
00135 #define SQLITE_PROVIDER_NAME "SQLite"
00136 
00137 /* ================================================================= */
00138 
00139 void
00140 gnc_sql_init( /*@ unused @*/ GncSqlBackend* be )
00141 {
00142     static gboolean initialized = FALSE;
00143 
00144     if ( !initialized )
00145     {
00146         register_standard_col_type_handlers();
00147         gnc_sql_init_object_handlers();
00148         initialized = TRUE;
00149     }
00150 }
00151 
00152 /* ================================================================= */
00153 
00154 static void
00155 create_tables_cb( const gchar* type, gpointer data_p, gpointer be_p )
00156 {
00157     GncSqlObjectBackend* pData = data_p;
00158     GncSqlBackend* be = be_p;
00159 
00160     g_return_if_fail( type != NULL && data_p != NULL && be_p != NULL );
00161     g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION );
00162 
00163     if ( pData->create_tables != NULL )
00164     {
00165         update_progress( be );
00166         (pData->create_tables)( be );
00167     }
00168 }
00169 
00170 /* ================================================================= */
00171 
00172 /* Main object load order */
00173 static const gchar* fixed_load_order[] =
00174 { GNC_ID_BOOK, GNC_ID_COMMODITY, GNC_ID_ACCOUNT, GNC_ID_LOT, NULL };
00175 
00176 /* Load order for objects from other modules */
00177 static const gchar** other_load_order = NULL;
00178 
00179 void
00180 gnc_sql_set_load_order( const gchar** load_order )
00181 {
00182     other_load_order = load_order;
00183 }
00184 
00185 static void
00186 initial_load_cb( const gchar* type, gpointer data_p, gpointer be_p )
00187 {
00188     GncSqlObjectBackend* pData = data_p;
00189     GncSqlBackend* be = be_p;
00190     gint i;
00191 
00192     g_return_if_fail( type != NULL && data_p != NULL && be_p != NULL );
00193     g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION );
00194 
00195     // Don't need to load anything if it has already been loaded with the fixed order
00196     for ( i = 0; fixed_load_order[i] != NULL; i++ )
00197     {
00198         update_progress( be );
00199         if ( g_ascii_strcasecmp( type, fixed_load_order[i] ) == 0 ) return;
00200     }
00201     if ( other_load_order != NULL )
00202     {
00203         for ( i = 0; other_load_order[i] != NULL; i++ )
00204         {
00205             update_progress( be );
00206             if ( g_ascii_strcasecmp( type, other_load_order[i] ) == 0 ) return;
00207         }
00208     }
00209 
00210     if ( pData->initial_load != NULL )
00211     {
00212         (pData->initial_load)( be );
00213     }
00214 }
00215 
00216 void
00217 gnc_sql_load( GncSqlBackend* be, /*@ dependent @*/ QofBook *book, QofBackendLoadType loadType )
00218 {
00219     GncSqlObjectBackend* pData;
00220     gint i;
00221     Account* root;
00222 
00223     g_return_if_fail( be != NULL );
00224     g_return_if_fail( book != NULL );
00225 
00226     ENTER( "be=%p, book=%p", be, book );
00227 
00228     be->loading = TRUE;
00229 
00230     if ( loadType == LOAD_TYPE_INITIAL_LOAD )
00231     {
00232         g_assert( be->book == NULL );
00233         be->book = book;
00234 
00235         /* Load any initial stuff. Some of this needs to happen in a certain order */
00236         for ( i = 0; fixed_load_order[i] != NULL; i++ )
00237         {
00238             pData = qof_object_lookup_backend( fixed_load_order[i], GNC_SQL_BACKEND );
00239             if ( pData->initial_load != NULL )
00240             {
00241                 update_progress( be );
00242                 (pData->initial_load)( be );
00243             }
00244         }
00245         if ( other_load_order != NULL )
00246         {
00247             for ( i = 0; other_load_order[i] != NULL; i++ )
00248             {
00249                 pData = qof_object_lookup_backend( other_load_order[i], GNC_SQL_BACKEND );
00250                 if ( pData->initial_load != NULL )
00251                 {
00252                     update_progress( be );
00253                     (pData->initial_load)( be );
00254                 }
00255             }
00256         }
00257 
00258         root = gnc_book_get_root_account( book );
00259         gnc_account_foreach_descendant( root, (AccountCb)xaccAccountBeginEdit, NULL );
00260 
00261         qof_object_foreach_backend( GNC_SQL_BACKEND, initial_load_cb, be );
00262 
00263         gnc_account_foreach_descendant( root, (AccountCb)xaccAccountCommitEdit, NULL );
00264     }
00265     else if ( loadType == LOAD_TYPE_LOAD_ALL )
00266     {
00267         // Load all transactions
00268         gnc_sql_transaction_load_all_tx( be );
00269     }
00270 
00271     be->loading = FALSE;
00272 
00273     /* Mark the sessoion as clean -- though it should never be marked
00274      * dirty with this backend
00275      */
00276     qof_book_mark_session_saved( book );
00277     finish_progress( be );
00278 
00279     LEAVE( "" );
00280 }
00281 
00282 /* ================================================================= */
00283 
00284 #if 0
00285 static gint
00286 compare_namespaces(gconstpointer a, gconstpointer b)
00287 {
00288     const gchar *sa = (const gchar *) a;
00289     const gchar *sb = (const gchar *) b;
00290 
00291     return( safe_strcmp( sa, sb ) );
00292 }
00293 
00294 static gint
00295 compare_commodity_ids(gconstpointer a, gconstpointer b)
00296 {
00297     const gnc_commodity *ca = (const gnc_commodity *) a;
00298     const gnc_commodity *cb = (const gnc_commodity *) b;
00299 
00300     return( safe_strcmp( gnc_commodity_get_mnemonic( ca ),
00301     gnc_commodity_get_mnemonic( cb ) ) );
00302 }
00303 
00304 static void
00305 write_commodities( GncSqlBackend* be, QofBook* book )
00306 {
00307     gnc_commodity_table* tbl;
00308     GList* namespaces;
00309     GList* lp;
00310 
00311     g_return_if_fail( be != NULL );
00312     g_return_if_fail( book != NULL );
00313 
00314     tbl = gnc_commodity_table_get_table( book );
00315     namespaces = gnc_commodity_table_get_namespaces( tbl );
00316     if ( namespaces != NULL )
00317     {
00318         namespaces = g_list_sort( namespaces, compare_namespaces );
00319     }
00320     for ( lp = namespaces; lp != NULL; lp = lp->next )
00321     {
00322         GList* comms;
00323         GList* lp2;
00324 
00325         comms = gnc_commodity_table_get_commodities( tbl, lp->data );
00326         comms = g_list_sort( comms, compare_commodity_ids );
00327 
00328         for ( lp2 = comms; lp2 != NULL; lp2 = lp2->next )
00329         {
00330             gnc_sql_save_commodity( be, GNC_COMMODITY(lp2->data) );
00331         }
00332     }
00333     update_progress( be );
00334 }
00335 #endif
00336 
00337 static gboolean
00338 write_account_tree( GncSqlBackend* be, Account* root )
00339 {
00340     GList* descendants;
00341     /*@ dependent @*/
00342     GList* node;
00343     gboolean is_ok = TRUE;
00344 
00345     g_return_val_if_fail( be != NULL, FALSE );
00346     g_return_val_if_fail( root != NULL, FALSE );
00347 
00348     is_ok = gnc_sql_save_account( be, QOF_INSTANCE(root) );
00349     if ( is_ok )
00350     {
00351         descendants = gnc_account_get_descendants( root );
00352         for ( node = descendants; node != NULL && is_ok; node = g_list_next(node) )
00353         {
00354             is_ok = gnc_sql_save_account( be, QOF_INSTANCE(GNC_ACCOUNT(node->data)) );
00355             if ( !is_ok ) break;
00356         }
00357         g_list_free( descendants );
00358     }
00359     update_progress( be );
00360 
00361     return is_ok;
00362 }
00363 
00364 static gboolean
00365 write_accounts( GncSqlBackend* be )
00366 {
00367     gboolean is_ok;
00368 
00369     g_return_val_if_fail( be != NULL, FALSE );
00370 
00371     update_progress( be );
00372     is_ok = write_account_tree( be, gnc_book_get_root_account( be->book ) );
00373     if ( is_ok )
00374     {
00375         update_progress( be );
00376         is_ok = write_account_tree( be, gnc_book_get_template_root( be->book ) );
00377     }
00378 
00379     return is_ok;
00380 }
00381 
00382 static int
00383 write_tx( Transaction* tx, gpointer data )
00384 {
00385     write_objects_t* s = (write_objects_t*)data;
00386 
00387     g_return_val_if_fail( tx != NULL, 0 );
00388     g_return_val_if_fail( data != NULL, 0 );
00389 
00390     s->is_ok = gnc_sql_save_transaction( s->be, QOF_INSTANCE(tx) );
00391     update_progress( s->be );
00392 
00393     if ( s->is_ok )
00394     {
00395         return 0;
00396     }
00397     else
00398     {
00399         return 1;
00400     }
00401 }
00402 
00403 static gboolean
00404 write_transactions( GncSqlBackend* be )
00405 {
00406     write_objects_t data;
00407 
00408     g_return_val_if_fail( be != NULL, FALSE );
00409 
00410     data.be = be;
00411     data.is_ok = TRUE;
00412     (void)xaccAccountTreeForEachTransaction(
00413         gnc_book_get_root_account( be->book ), write_tx, &data );
00414     update_progress( be );
00415     return data.is_ok;
00416 }
00417 
00418 static gboolean
00419 write_template_transactions( GncSqlBackend* be )
00420 {
00421     Account* ra;
00422     write_objects_t data;
00423 
00424     g_return_val_if_fail( be != NULL, FALSE );
00425 
00426     data.is_ok = TRUE;
00427     data.be = be;
00428     ra = gnc_book_get_template_root( be->book );
00429     if ( gnc_account_n_descendants( ra ) > 0 )
00430     {
00431         (void)xaccAccountTreeForEachTransaction( ra, write_tx, &data );
00432         update_progress( be );
00433     }
00434 
00435     return data.is_ok;
00436 }
00437 
00438 static gboolean
00439 write_schedXactions( GncSqlBackend* be )
00440 {
00441     GList* schedXactions;
00442     SchedXaction* tmpSX;
00443     gboolean is_ok = TRUE;
00444 
00445     g_return_val_if_fail( be != NULL, FALSE );
00446 
00447     schedXactions = gnc_book_get_schedxactions( be->book )->sx_list;
00448 
00449     for ( ; schedXactions != NULL && is_ok; schedXactions = schedXactions->next )
00450     {
00451         tmpSX = schedXactions->data;
00452         is_ok = gnc_sql_save_schedxaction( be, QOF_INSTANCE( tmpSX ) );
00453     }
00454     update_progress( be );
00455 
00456     return is_ok;
00457 }
00458 
00459 static void
00460 write_cb( const gchar* type, gpointer data_p, gpointer be_p )
00461 {
00462     GncSqlObjectBackend* pData = data_p;
00463     GncSqlBackend* be = (GncSqlBackend*)be_p;
00464 
00465     g_return_if_fail( type != NULL && data_p != NULL && be_p != NULL );
00466     g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION );
00467 
00468     if ( pData->write != NULL )
00469     {
00470         (void)(pData->write)( be );
00471         update_progress( be );
00472     }
00473 }
00474 
00475 static void
00476 update_progress( GncSqlBackend* be )
00477 {
00478     if ( be->be.percentage != NULL )
00479         (be->be.percentage)( NULL, 101.0 );
00480 }
00481 
00482 static void
00483 finish_progress( GncSqlBackend* be )
00484 {
00485     if ( be->be.percentage != NULL )
00486         (be->be.percentage)( NULL, -1.0 );
00487 }
00488 
00489 void
00490 gnc_sql_sync_all( GncSqlBackend* be, /*@ dependent @*/ QofBook *book )
00491 {
00492     gboolean is_ok;
00493 
00494     g_return_if_fail( be != NULL );
00495     g_return_if_fail( book != NULL );
00496 
00497     ENTER( "book=%p, be->book=%p", book, be->book );
00498     update_progress( be );
00499     (void)reset_version_info( be );
00500     gnc_sql_set_table_version( be, "Gnucash", gnc_get_long_version() );
00501     gnc_sql_set_table_version( be, "Gnucash-Resave", GNUCASH_RESAVE_VERSION );
00502 
00503     /* Create new tables */
00504     be->is_pristine_db = TRUE;
00505     qof_object_foreach_backend( GNC_SQL_BACKEND, create_tables_cb, be );
00506 
00507     /* Save all contents */
00508     be->book = book;
00509     be->obj_total = 0;
00510     be->obj_total += 1 + gnc_account_n_descendants( gnc_book_get_root_account( book ) );
00511     be->obj_total += gnc_book_count_transactions( book );
00512     be->operations_done = 0;
00513 
00514     is_ok = gnc_sql_connection_begin_transaction( be->conn );
00515 
00516     // FIXME: should write the set of commodities that are used
00517     //write_commodities( be, book );
00518     if ( is_ok )
00519     {
00520         is_ok = gnc_sql_save_book( be, QOF_INSTANCE(book) );
00521     }
00522     if ( is_ok )
00523     {
00524         is_ok = write_accounts( be );
00525     }
00526     if ( is_ok )
00527     {
00528         is_ok = write_transactions( be );
00529     }
00530     if ( is_ok )
00531     {
00532         is_ok = write_template_transactions( be );
00533     }
00534     if ( is_ok )
00535     {
00536         is_ok = write_schedXactions( be );
00537     }
00538     if ( is_ok )
00539     {
00540         qof_object_foreach_backend( GNC_SQL_BACKEND, write_cb, be );
00541     }
00542     if ( is_ok )
00543     {
00544         is_ok = gnc_sql_connection_commit_transaction( be->conn );
00545     }
00546     if ( is_ok )
00547     {
00548         be->is_pristine_db = FALSE;
00549 
00550         /* Mark the session as clean -- though it shouldn't ever get
00551          * marked dirty with this backend
00552          */
00553         qof_book_mark_session_saved( book );
00554     }
00555     else
00556     {
00557         qof_backend_set_error( (QofBackend*)be, ERR_BACKEND_SERVER_ERR );
00558         is_ok = gnc_sql_connection_rollback_transaction( be->conn );
00559     }
00560     finish_progress( be );
00561     LEAVE( "book=%p", book );
00562 }
00563 
00564 /* ================================================================= */
00565 /* Routines to deal with the creation of multiple books. */
00566 
00567 void
00568 gnc_sql_begin_edit( GncSqlBackend *be, QofInstance *inst )
00569 {
00570     g_return_if_fail( be != NULL );
00571     g_return_if_fail( inst != NULL );
00572 
00573     ENTER( " " );
00574     LEAVE( "" );
00575 }
00576 
00577 void
00578 gnc_sql_rollback_edit( GncSqlBackend *be, QofInstance *inst )
00579 {
00580     g_return_if_fail( be != NULL );
00581     g_return_if_fail( inst != NULL );
00582 
00583     ENTER( " " );
00584     LEAVE( "" );
00585 }
00586 
00587 static void
00588 commit_cb( const gchar* type, gpointer data_p, gpointer be_data_p )
00589 {
00590     GncSqlObjectBackend* pData = data_p;
00591     sql_backend* be_data = be_data_p;
00592 
00593     g_return_if_fail( type != NULL && pData != NULL && be_data != NULL );
00594     g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION );
00595 
00596     /* If this has already been handled, or is not the correct handler, return */
00597     if ( strcmp( pData->type_name, be_data->inst->e_type ) != 0 ) return;
00598     if ( be_data->is_known ) return;
00599 
00600     if ( pData->commit != NULL )
00601     {
00602         be_data->is_ok = (pData->commit)( be_data->be, be_data->inst );
00603         be_data->is_known = TRUE;
00604     }
00605 }
00606 
00607 /* Commit_edit handler - find the correct backend handler for this object
00608  * type and call its commit handler
00609  */
00610 void
00611 gnc_sql_commit_edit( GncSqlBackend *be, QofInstance *inst )
00612 {
00613     sql_backend be_data;
00614     gboolean is_dirty;
00615     gboolean is_destroying;
00616     gboolean is_infant;
00617 
00618     g_return_if_fail( be != NULL );
00619     g_return_if_fail( inst != NULL );
00620 
00621     if ( qof_book_is_readonly( be->book ) )
00622     {
00623         qof_backend_set_error( (QofBackend*)be, ERR_BACKEND_READONLY );
00624         (void)gnc_sql_connection_rollback_transaction( be->conn );
00625         return;
00626     }
00627     /* During initial load where objects are being created, don't commit
00628     anything, but do mark the object as clean. */
00629     if ( be->loading )
00630     {
00631         qof_instance_mark_clean( inst );
00632         return;
00633     }
00634 
00635     // The engine has a PriceDB object but it isn't in the database
00636     if ( strcmp( inst->e_type, "PriceDB" ) == 0 )
00637     {
00638         qof_instance_mark_clean( inst );
00639         qof_book_mark_session_saved( be->book );
00640         return;
00641     }
00642 
00643     ENTER( " " );
00644 
00645     is_dirty = qof_instance_get_dirty_flag( inst );
00646     is_destroying = qof_instance_get_destroying( inst );
00647     is_infant = qof_instance_get_infant( inst );
00648 
00649     DEBUG( "%s dirty = %d, do_free = %d, infant = %d\n",
00650     (inst->e_type ? inst->e_type : "(null)"),
00651     is_dirty, is_destroying, is_infant );
00652 
00653     if ( !is_dirty && !is_destroying )
00654     {
00655         LEAVE( "!dirty OR !destroying" );
00656         return;
00657     }
00658 
00659     if ( !gnc_sql_connection_begin_transaction( be->conn ) )
00660     {
00661         PERR( "gnc_sql_commit_edit(): begin_transaction failed\n" );
00662         LEAVE( "Rolled back - database transaction begin error" );
00663         return;
00664     }
00665 
00666     be_data.is_known = FALSE;
00667     be_data.be = be;
00668     be_data.inst = inst;
00669     be_data.is_ok = TRUE;
00670 
00671     qof_object_foreach_backend( GNC_SQL_BACKEND, commit_cb, &be_data );
00672 
00673     if ( !be_data.is_known )
00674     {
00675         PERR( "gnc_sql_commit_edit(): Unknown object type '%s'\n", inst->e_type );
00676         (void)gnc_sql_connection_rollback_transaction( be->conn );
00677 
00678         // Don't let unknown items still mark the book as being dirty
00679         qof_book_mark_session_saved( be->book );
00680         qof_instance_mark_clean(inst);
00681         LEAVE( "Rolled back - unknown object type" );
00682         return;
00683     }
00684     if ( !be_data.is_ok )
00685     {
00686         // Error - roll it back
00687         (void)gnc_sql_connection_rollback_transaction( be->conn );
00688 
00689         // This *should* leave things marked dirty
00690         LEAVE( "Rolled back - database error" );
00691         return;
00692     }
00693 
00694     (void)gnc_sql_connection_commit_transaction( be->conn );
00695 
00696     qof_book_mark_session_saved( be->book );
00697     qof_instance_mark_clean(inst);
00698 
00699     LEAVE( "" );
00700 }
00701 /* ---------------------------------------------------------------------- */
00702 
00703 /* Query processing */
00704 static void
00705 handle_and_term( QofQueryTerm* pTerm, GString* sql )
00706 {
00707     GSList* pParamPath;
00708     QofQueryPredData* pPredData;
00709     gboolean isInverted;
00710     GSList* name;
00711     gchar val[G_ASCII_DTOSTR_BUF_SIZE];
00712 
00713     g_return_if_fail( pTerm != NULL );
00714     g_return_if_fail( sql != NULL );
00715 
00716     pParamPath = qof_query_term_get_param_path( pTerm );
00717     pPredData = qof_query_term_get_pred_data( pTerm );
00718     isInverted = qof_query_term_is_inverted( pTerm );
00719 
00720     if ( strcmp( pPredData->type_name, QOF_TYPE_GUID ) == 0 )
00721     {
00722         query_guid_t guid_data = (query_guid_t)pPredData;
00723         GList* guid_entry;
00724 
00725         for ( name = pParamPath; name != NULL; name = name->next )
00726         {
00727             if ( name != pParamPath ) g_string_append( sql, "." );
00728             g_string_append( sql, name->data );
00729         }
00730 
00731         if ( guid_data->options == QOF_GUID_MATCH_ANY )
00732         {
00733             if ( isInverted ) g_string_append( sql, " NOT " );
00734             g_string_append( sql, " IN (" );
00735         }
00736         for ( guid_entry = guid_data->guids; guid_entry != NULL; guid_entry = guid_entry->next )
00737         {
00738             if ( guid_entry != guid_data->guids ) g_string_append( sql, "." );
00739             (void)guid_to_string_buff( guid_entry->data, val );
00740             g_string_append( sql, "'" );
00741             g_string_append( sql, val );
00742             g_string_append( sql, "'" );
00743         }
00744         if ( guid_data->options == QOF_GUID_MATCH_ANY )
00745         {
00746             g_string_append( sql, ")" );
00747         }
00748     }
00749 
00750     g_string_append( sql, "(" );
00751     if ( isInverted )
00752     {
00753         g_string_append( sql, "!" );
00754     }
00755 
00756     for ( name = pParamPath; name != NULL; name = name->next )
00757     {
00758         if ( name != pParamPath ) g_string_append( sql, "." );
00759         g_string_append( sql, name->data );
00760     }
00761 
00762     if ( pPredData->how == QOF_COMPARE_LT )
00763     {
00764         g_string_append( sql, "<" );
00765     }
00766     else if ( pPredData->how == QOF_COMPARE_LTE )
00767     {
00768         g_string_append( sql, "<=" );
00769     }
00770     else if ( pPredData->how == QOF_COMPARE_EQUAL )
00771     {
00772         g_string_append( sql, "=" );
00773     }
00774     else if ( pPredData->how == QOF_COMPARE_GT )
00775     {
00776         g_string_append( sql, ">" );
00777     }
00778     else if ( pPredData->how == QOF_COMPARE_GTE )
00779     {
00780         g_string_append( sql, ">=" );
00781     }
00782     else if ( pPredData->how == QOF_COMPARE_NEQ )
00783     {
00784         g_string_append( sql, "~=" );
00785     }
00786     else
00787     {
00788         g_string_append( sql, "??" );
00789     }
00790 
00791     if ( strcmp( pPredData->type_name, "string" ) == 0 )
00792     {
00793         query_string_t pData = (query_string_t)pPredData;
00794         g_string_append( sql, "'" );
00795         g_string_append( sql, pData->matchstring );
00796         g_string_append( sql, "'" );
00797     }
00798     else if ( strcmp( pPredData->type_name, "date" ) == 0 )
00799     {
00800         query_date_t pData = (query_date_t)pPredData;
00801 
00802         (void)gnc_timespec_to_iso8601_buff( pData->date, val );
00803         g_string_append( sql, "'" );
00804         //g_string_append( sql, val, 4+1+2+1+2 );
00805         g_string_append( sql, "'" );
00806     }
00807     else if ( strcmp( pPredData->type_name, "numeric" ) == 0 )
00808     {
00809         query_numeric_t pData = (query_numeric_t)pPredData;
00810 
00811         g_string_append( sql, "numeric" );
00812     }
00813     else if ( strcmp( pPredData->type_name, QOF_TYPE_GUID ) == 0 )
00814     {
00815     }
00816     else if ( strcmp( pPredData->type_name, "gint32" ) == 0 )
00817     {
00818         query_int32_t pData = (query_int32_t)pPredData;
00819 
00820         sprintf( val, "%d", pData->val );
00821         g_string_append( sql, val );
00822     }
00823     else if ( strcmp( pPredData->type_name, "gint64" ) == 0 )
00824     {
00825         query_int64_t pData = (query_int64_t)pPredData;
00826 
00827         sprintf( val, "%" G_GINT64_FORMAT, pData->val );
00828         g_string_append( sql, val );
00829     }
00830     else if ( strcmp( pPredData->type_name, "double" ) == 0 )
00831     {
00832         query_double_t pData = (query_double_t)pPredData;
00833 
00834         g_ascii_dtostr( val, sizeof(val), pData->val );
00835         g_string_append( sql, val );
00836     }
00837     else if ( strcmp( pPredData->type_name, "boolean" ) == 0 )
00838     {
00839         query_boolean_t pData = (query_boolean_t)pPredData;
00840 
00841         sprintf( val, "%d", pData->val );
00842         g_string_append( sql, val );
00843     }
00844     else
00845     {
00846         g_assert( FALSE );
00847     }
00848 
00849     g_string_append( sql, ")" );
00850 }
00851 
00852 static void
00853 compile_query_cb( const gchar* type, gpointer data_p, gpointer be_data_p )
00854 {
00855     GncSqlObjectBackend* pData = data_p;
00856     sql_backend* be_data = be_data_p;
00857 
00858     g_return_if_fail( type != NULL && pData != NULL && be_data != NULL );
00859     g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION );
00860 
00861     // Is this the right item?
00862     if ( strcmp( type, be_data->pQueryInfo->searchObj ) != 0 ) return;
00863     if ( be_data->is_ok ) return;
00864 
00865     if ( pData->compile_query != NULL )
00866     {
00867         be_data->pQueryInfo->pCompiledQuery = (pData->compile_query)(
00868             be_data->be,
00869             be_data->pQuery );
00870         be_data->is_ok = TRUE;
00871     }
00872 }
00873 
00874 gchar* gnc_sql_compile_query_to_sql( GncSqlBackend* be, QofQuery* query );
00875 
00876 /*@ null @*/
00877 gpointer
00878 gnc_sql_compile_query( QofBackend* pBEnd, QofQuery* pQuery )
00879 {
00880     GncSqlBackend *be = (GncSqlBackend*)pBEnd;
00881     QofIdType searchObj;
00882     sql_backend be_data;
00883     gnc_sql_query_info* pQueryInfo;
00884 
00885     g_return_val_if_fail( pBEnd != NULL, NULL );
00886     g_return_val_if_fail( pQuery != NULL, NULL );
00887 
00888     ENTER( " " );
00889 
00890 //gnc_sql_compile_query_to_sql( be, pQuery );
00891     searchObj = qof_query_get_search_for( pQuery );
00892 
00893     pQueryInfo = g_malloc( (gsize)sizeof( gnc_sql_query_info ) );
00894     g_assert( pQueryInfo != NULL );
00895     pQueryInfo->pCompiledQuery = NULL;
00896     pQueryInfo->searchObj = searchObj;
00897 
00898     // Try various objects first
00899     be_data.is_ok = FALSE;
00900     be_data.be = be;
00901     be_data.pQuery = pQuery;
00902     be_data.pQueryInfo = pQueryInfo;
00903 
00904     qof_object_foreach_backend( GNC_SQL_BACKEND, compile_query_cb, &be_data );
00905     if ( be_data.is_ok )
00906     {
00907         LEAVE( "" );
00908         return be_data.pQueryInfo;
00909     }
00910 
00911     LEAVE( "" );
00912 
00913     return pQueryInfo;
00914 }
00915 
00916 static const gchar*
00917 convert_search_obj( QofIdType objType )
00918 {
00919     return (gchar*)objType;
00920 }
00921 
00922 gchar*
00923 gnc_sql_compile_query_to_sql( GncSqlBackend* be, QofQuery* query )
00924 {
00925     QofIdType searchObj;
00926     GList* bookList;
00927     GString* sql;
00928 
00929     g_return_val_if_fail( be != NULL, NULL );
00930     g_return_val_if_fail( query != NULL, NULL );
00931 
00932     searchObj = qof_query_get_search_for( query );
00933     bookList = qof_query_get_books( query );
00934 
00935     /* Convert search object type to table name */
00936     sql = g_string_new( "" );
00937     g_string_append( sql, "SELECT * FROM " );
00938     g_string_append( sql, convert_search_obj( searchObj ) );
00939     if ( !qof_query_has_terms( query ) )
00940     {
00941         g_string_append( sql, ";" );
00942     }
00943     else
00944     {
00945         GList* orterms = qof_query_get_terms( query );
00946         GList* orTerm;
00947 
00948         g_string_append( sql, " WHERE " );
00949 
00950         for ( orTerm = orterms; orTerm != NULL; orTerm = orTerm->next )
00951         {
00952             GList* andterms = (GList*)orTerm->data;
00953             GList* andTerm;
00954 
00955             if ( orTerm != orterms ) g_string_append( sql, " OR " );
00956             g_string_append( sql, "(" );
00957             for ( andTerm = andterms; andTerm != NULL; andTerm = andTerm->next )
00958             {
00959                 if ( andTerm != andterms ) g_string_append( sql, " AND " );
00960                 handle_and_term( (QofQueryTerm*)andTerm->data, sql );
00961             }
00962             g_string_append( sql, ")" );
00963         }
00964     }
00965 
00966     DEBUG( "Compiled: %s\n", sql->str );
00967     return g_string_free( sql, FALSE );
00968 }
00969 
00970 static void
00971 free_query_cb( const gchar* type, gpointer data_p, gpointer be_data_p )
00972 {
00973     GncSqlObjectBackend* pData = data_p;
00974     sql_backend* be_data = be_data_p;
00975 
00976     g_return_if_fail( type != NULL && pData != NULL && be_data != NULL );
00977     g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION );
00978     if ( be_data->is_ok ) return;
00979     if ( strcmp( type, be_data->pQueryInfo->searchObj ) != 0 ) return;
00980 
00981     if ( pData->free_query != NULL )
00982     {
00983         (pData->free_query)( be_data->be, be_data->pCompiledQuery );
00984         be_data->is_ok = TRUE;
00985     }
00986 }
00987 
00988 void
00989 gnc_sql_free_query( QofBackend* pBEnd, gpointer pQuery )
00990 {
00991     GncSqlBackend *be = (GncSqlBackend*)pBEnd;
00992     gnc_sql_query_info* pQueryInfo = (gnc_sql_query_info*)pQuery;
00993     sql_backend be_data;
00994 
00995     g_return_if_fail( pBEnd != NULL );
00996     g_return_if_fail( pQuery != NULL );
00997 
00998     ENTER( " " );
00999 
01000     // Try various objects first
01001     be_data.is_ok = FALSE;
01002     be_data.be = be;
01003     be_data.pCompiledQuery = pQuery;
01004     be_data.pQueryInfo = pQueryInfo;
01005 
01006     qof_object_foreach_backend( GNC_SQL_BACKEND, free_query_cb, &be_data );
01007     if ( be_data.is_ok )
01008     {
01009         LEAVE( "" );
01010         return;
01011     }
01012 
01013     if ( pQueryInfo->pCompiledQuery != NULL )
01014     {
01015         DEBUG( "%s\n", (gchar*)pQueryInfo->pCompiledQuery );
01016         g_free( pQueryInfo->pCompiledQuery );
01017     }
01018     g_free( pQueryInfo );
01019 
01020     LEAVE( "" );
01021 }
01022 
01023 static void
01024 run_query_cb( const gchar* type, gpointer data_p, gpointer be_data_p )
01025 {
01026     GncSqlObjectBackend* pData = data_p;
01027     sql_backend* be_data = be_data_p;
01028 
01029     g_return_if_fail( type != NULL && pData != NULL && be_data != NULL );
01030     g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION );
01031     if ( be_data->is_ok ) return;
01032 
01033     // Is this the right item?
01034     if ( strcmp( type, be_data->pQueryInfo->searchObj ) != 0 ) return;
01035 
01036     if ( pData->run_query != NULL )
01037     {
01038         (pData->run_query)( be_data->be, be_data->pCompiledQuery );
01039         be_data->is_ok = TRUE;
01040     }
01041 }
01042 
01043 void
01044 gnc_sql_run_query( QofBackend* pBEnd, gpointer pQuery )
01045 {
01046     GncSqlBackend *be = (GncSqlBackend*)pBEnd;
01047     gnc_sql_query_info* pQueryInfo = (gnc_sql_query_info*)pQuery;
01048     sql_backend be_data;
01049 
01050     g_return_if_fail( pBEnd != NULL );
01051     g_return_if_fail( pQuery != NULL );
01052     g_return_if_fail( !be->in_query );
01053 
01054     ENTER( " " );
01055 
01056     be->loading = TRUE;
01057     be->in_query = TRUE;
01058 
01059     qof_event_suspend();
01060 
01061     // Try various objects first
01062     be_data.is_ok = FALSE;
01063     be_data.be = be;
01064     be_data.pCompiledQuery = pQueryInfo->pCompiledQuery;
01065     be_data.pQueryInfo = pQueryInfo;
01066 
01067     qof_object_foreach_backend( GNC_SQL_BACKEND, run_query_cb, &be_data );
01068     be->loading = FALSE;
01069     be->in_query = FALSE;
01070     qof_event_resume();
01071 //    if( be_data.is_ok ) {
01072 //        LEAVE( "" );
01073 //        return;
01074 //    }
01075 
01076     // Mark the book as clean
01077     qof_instance_mark_clean( QOF_INSTANCE(be->book) );
01078 
01079 //    DEBUG( "%s\n", (gchar*)pQueryInfo->pCompiledQuery );
01080 
01081     LEAVE( "" );
01082 }
01083 
01084 /* ================================================================= */
01085 /* Order in which business objects need to be loaded */
01086 static const gchar* business_fixed_load_order[] =
01087 { GNC_ID_BILLTERM, GNC_ID_TAXTABLE, GNC_ID_INVOICE, NULL };
01088 
01089 static void
01090 business_core_sql_init(void)
01091 {
01092     /* Initialize our pointers into the backend subsystem */
01093     gnc_address_sql_initialize();
01094     gnc_billterm_sql_initialize();
01095     gnc_customer_sql_initialize();
01096     gnc_employee_sql_initialize();
01097     gnc_entry_sql_initialize();
01098     gnc_invoice_sql_initialize();
01099     gnc_job_sql_initialize();
01100     gnc_order_sql_initialize();
01101     gnc_owner_sql_initialize();
01102     gnc_taxtable_sql_initialize();
01103     gnc_vendor_sql_initialize();
01104 
01105     gnc_sql_set_load_order( business_fixed_load_order );
01106 }
01107 
01108 static void
01109 gnc_sql_init_object_handlers( void )
01110 {
01111     gnc_sql_init_book_handler();
01112     gnc_sql_init_commodity_handler();
01113     gnc_sql_init_account_handler();
01114     gnc_sql_init_budget_handler();
01115     gnc_sql_init_price_handler();
01116     gnc_sql_init_transaction_handler();
01117     gnc_sql_init_slots_handler();
01118     gnc_sql_init_recurrence_handler();
01119     gnc_sql_init_schedxaction_handler();
01120     gnc_sql_init_lot_handler();
01121 
01122     /* And the business objects */
01123     business_core_sql_init();
01124 }
01125 
01126 /* ================================================================= */
01127 
01128 gint64
01129 gnc_sql_get_integer_value( const GValue* value )
01130 {
01131     g_return_val_if_fail( value != NULL, 0 );
01132 
01133     if ( G_VALUE_HOLDS_INT(value) )
01134     {
01135         return (gint64)g_value_get_int( value );
01136     }
01137     else if ( G_VALUE_HOLDS_UINT(value) )
01138     {
01139         return (gint64)g_value_get_uint( value );
01140     }
01141     else if ( G_VALUE_HOLDS_LONG(value) )
01142     {
01143         return (gint64)g_value_get_long( value );
01144     }
01145     else if ( G_VALUE_HOLDS_ULONG(value) )
01146     {
01147         return (gint64)g_value_get_ulong( value );
01148     }
01149     else if ( G_VALUE_HOLDS_INT64(value) )
01150     {
01151         return g_value_get_int64( value );
01152     }
01153     else if ( G_VALUE_HOLDS_UINT64(value) )
01154     {
01155         return (gint64)g_value_get_uint64( value );
01156     }
01157     else if ( G_VALUE_HOLDS_STRING( value ) )
01158     {
01159         return g_ascii_strtoll( g_value_get_string( value ), NULL, 10 );
01160     }
01161     else
01162     {
01163         PWARN( "Unknown type: %s", G_VALUE_TYPE_NAME( value ) );
01164     }
01165 
01166     return 0;
01167 }
01168 
01169 /* ----------------------------------------------------------------- */
01170 /*@ null @*/ static gpointer
01171 get_autoinc_id()
01172 {
01173     // Just need a 0 to force a new autoinc value
01174     return (gpointer)0;
01175 }
01176 
01177 static void
01178 set_autoinc_id()
01179 {
01180     // Nowhere to put the ID
01181 }
01182 
01183 /*@ null @*/ QofAccessFunc
01184 gnc_sql_get_getter( QofIdTypeConst obj_name, const GncSqlColumnTableEntry* table_row )
01185 {
01186     QofAccessFunc getter;
01187 
01188     g_return_val_if_fail( obj_name != NULL, NULL );
01189     g_return_val_if_fail( table_row != NULL, NULL );
01190 
01191     if ( (table_row->flags & COL_AUTOINC) != 0 )
01192     {
01193         getter = get_autoinc_id;
01194     }
01195     else if ( table_row->qof_param_name != NULL )
01196     {
01197         getter = qof_class_get_parameter_getter( obj_name,
01198         table_row->qof_param_name );
01199     }
01200     else
01201     {
01202         getter = table_row->getter;
01203     }
01204 
01205     return getter;
01206 }
01207 
01208 /* ----------------------------------------------------------------- */
01209 void
01210 gnc_sql_add_colname_to_list( const GncSqlColumnTableEntry* table_row, GList** pList )
01211 {
01212     (*pList) = g_list_append( (*pList), g_strdup( table_row->col_name ) );
01213 }
01214 
01215 /* ----------------------------------------------------------------- */
01216 void
01217 gnc_sql_add_subtable_colnames_to_list( const GncSqlColumnTableEntry* table_row, const GncSqlColumnTableEntry* subtable,
01218 GList** pList )
01219 {
01220     const GncSqlColumnTableEntry* subtable_row;
01221     gchar* buf;
01222 
01223     for ( subtable_row = subtable; subtable_row->col_name != NULL; subtable_row++ )
01224     {
01225         buf = g_strdup_printf( "%s_%s", table_row->col_name, subtable_row->col_name );
01226         (*pList) = g_list_append( (*pList), buf );
01227     }
01228 }
01229 
01230 static GncSqlColumnInfo*
01231 create_column_info( const GncSqlColumnTableEntry* table_row, GncSqlBasicColumnType type,
01232 gint size, gboolean is_unicode )
01233 {
01234     GncSqlColumnInfo* info;
01235 
01236     info = g_new0( GncSqlColumnInfo, 1 );
01237     g_assert( info != NULL );
01238     info->name = g_strdup( table_row->col_name );
01239     info->type = type;
01240     info->size = size;
01241     info->is_primary_key = ((table_row->flags & COL_PKEY) != 0) ? TRUE : FALSE;
01242     info->null_allowed = ((table_row->flags & COL_NNUL) != 0) ? FALSE : TRUE;
01243     info->is_unicode = is_unicode;
01244     info->is_autoinc = ((table_row->flags & COL_AUTOINC) != 0) ? TRUE : FALSE;
01245 
01246     return info;
01247 }
01248 
01249 /* ----------------------------------------------------------------- */
01250 static void
01251 load_string( const GncSqlBackend* be, GncSqlRow* row,
01252 /*@ null @*/ QofSetterFunc setter, gpointer pObject,
01253 const GncSqlColumnTableEntry* table_row )
01254 {
01255     const GValue* val;
01256     const gchar* s;
01257 
01258     g_return_if_fail( be != NULL );
01259     g_return_if_fail( row != NULL );
01260     g_return_if_fail( pObject != NULL );
01261     g_return_if_fail( table_row != NULL );
01262     g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
01263 
01264     val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
01265     g_return_if_fail( val != NULL );
01266     s = g_value_get_string( val );
01267     if ( table_row->gobj_param_name != NULL )
01268     {
01269         g_object_set( pObject, table_row->gobj_param_name, s, NULL );
01270     }
01271     else
01272     {
01273         g_return_if_fail( setter != NULL );
01274         (*setter)( pObject, (const gpointer)s );
01275     }
01276 }
01277 
01278 static void
01279 add_string_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
01280 GList** pList )
01281 {
01282     GncSqlColumnInfo* info;
01283 
01284     g_return_if_fail( be != NULL );
01285     g_return_if_fail( table_row != NULL );
01286     g_return_if_fail( pList != NULL );
01287 
01288     info = create_column_info( table_row, BCT_STRING, table_row->size, TRUE );
01289 
01290     *pList = g_list_append( *pList, info );
01291 }
01292 
01293 static void
01294 add_gvalue_string_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
01295 const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
01296 {
01297     QofAccessFunc getter;
01298     gchar* s = NULL;
01299     GValue* value;
01300 
01301     g_return_if_fail( be != NULL );
01302     g_return_if_fail( obj_name != NULL );
01303     g_return_if_fail( pObject != NULL );
01304     g_return_if_fail( table_row != NULL );
01305     g_return_if_fail( pList != NULL );
01306 
01307     value = g_new0( GValue, 1 );
01308     g_assert( value != NULL );
01309     memset( value, 0, sizeof( GValue ) );
01310     if ( table_row->gobj_param_name != NULL )
01311     {
01312         g_object_get( pObject, table_row->gobj_param_name, &s, NULL );
01313     }
01314     else
01315     {
01316         getter = gnc_sql_get_getter( obj_name, table_row );
01317         if ( getter != NULL )
01318         {
01319             s = (gchar*)(*getter)( pObject, NULL );
01320             if ( s != NULL )
01321             {
01322                 s = g_strdup( s );
01323             }
01324         }
01325     }
01326     (void)g_value_init( value, G_TYPE_STRING );
01327     if ( s )
01328     {
01329         g_value_take_string( value, s );
01330     }
01331 
01332     (*pList) = g_slist_append( (*pList), value );
01333 }
01334 
01335 static GncSqlColumnTypeHandler string_handler
01336 =
01337 {
01338     load_string,
01339     add_string_col_info_to_list,
01340     gnc_sql_add_colname_to_list,
01341     add_gvalue_string_to_slist
01342 };
01343 /* ----------------------------------------------------------------- */
01344 typedef gint (*IntAccessFunc)( const gpointer );
01345 typedef void (*IntSetterFunc)( const gpointer, gint );
01346 
01347 static void
01348 load_int( const GncSqlBackend* be, GncSqlRow* row,
01349           /*@ null @*/ QofSetterFunc setter, gpointer pObject,
01350           const GncSqlColumnTableEntry* table_row )
01351 {
01352     const GValue* val;
01353     gint int_value;
01354     IntSetterFunc i_setter;
01355 
01356     g_return_if_fail( be != NULL );
01357     g_return_if_fail( row != NULL );
01358     g_return_if_fail( pObject != NULL );
01359     g_return_if_fail( table_row != NULL );
01360     g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
01361 
01362     val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
01363     if ( val == NULL )
01364     {
01365         int_value = 0;
01366     }
01367     else
01368     {
01369         int_value = (gint)gnc_sql_get_integer_value( val );
01370     }
01371     if ( table_row->gobj_param_name != NULL )
01372     {
01373         g_object_set( pObject, table_row->gobj_param_name, int_value, NULL );
01374     }
01375     else
01376     {
01377         g_return_if_fail( setter != NULL );
01378         i_setter = (IntSetterFunc)setter;
01379         (*i_setter)( pObject, int_value );
01380     }
01381 }
01382 
01383 static void
01384 add_int_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
01385                           GList** pList )
01386 {
01387     GncSqlColumnInfo* info;
01388 
01389     g_return_if_fail( be != NULL );
01390     g_return_if_fail( table_row != NULL );
01391     g_return_if_fail( pList != NULL );
01392 
01393     info = create_column_info( table_row, BCT_INT, 0, FALSE );
01394 
01395     *pList = g_list_append( *pList, info );
01396 }
01397 
01398 static void
01399 add_gvalue_int_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
01400                          const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
01401 {
01402     gint int_value = 0;
01403     IntAccessFunc i_getter;
01404     GValue* value;
01405 
01406     g_return_if_fail( be != NULL );
01407     g_return_if_fail( obj_name != NULL );
01408     g_return_if_fail( pObject != NULL );
01409     g_return_if_fail( table_row != NULL );
01410     g_return_if_fail( pList != NULL );
01411 
01412     value = g_new0( GValue, 1 );
01413     g_assert( value != NULL );
01414     (void)g_value_init( value, G_TYPE_INT );
01415 
01416     if ( table_row->gobj_param_name != NULL )
01417     {
01418         g_object_get_property( pObject, table_row->gobj_param_name, value );
01419     }
01420     else
01421     {
01422         i_getter = (IntAccessFunc)gnc_sql_get_getter( obj_name, table_row );
01423         if ( i_getter != NULL )
01424         {
01425             int_value = (*i_getter)( pObject );
01426         }
01427         g_value_set_int( value, int_value );
01428     }
01429 
01430     (*pList) = g_slist_append( (*pList), value );
01431 }
01432 
01433 static GncSqlColumnTypeHandler int_handler
01434 =
01435 {
01436     load_int,
01437     add_int_col_info_to_list,
01438     gnc_sql_add_colname_to_list,
01439     add_gvalue_int_to_slist
01440 };
01441 /* ----------------------------------------------------------------- */
01442 typedef gboolean (*BooleanAccessFunc)( const gpointer );
01443 typedef void (*BooleanSetterFunc)( const gpointer, gboolean );
01444 
01445 static void
01446 load_boolean( const GncSqlBackend* be, GncSqlRow* row,
01447               /*@ null @*/ QofSetterFunc setter, gpointer pObject,
01448               const GncSqlColumnTableEntry* table_row )
01449 {
01450     const GValue* val;
01451     gint int_value;
01452     BooleanSetterFunc b_setter;
01453 
01454     g_return_if_fail( be != NULL );
01455     g_return_if_fail( row != NULL );
01456     g_return_if_fail( pObject != NULL );
01457     g_return_if_fail( table_row != NULL );
01458     g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
01459 
01460     val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
01461     if ( val == NULL )
01462     {
01463         int_value = 0;
01464     }
01465     else
01466     {
01467         int_value = (gint)gnc_sql_get_integer_value( val );
01468     }
01469     if ( table_row->gobj_param_name != NULL )
01470     {
01471         g_object_set( pObject, table_row->gobj_param_name, int_value, NULL );
01472     }
01473     else
01474     {
01475         g_return_if_fail( setter != NULL );
01476         b_setter = (BooleanSetterFunc)setter;
01477         (*b_setter)( pObject, (int_value != 0) ? TRUE : FALSE );
01478     }
01479 }
01480 
01481 static void
01482 add_boolean_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
01483                               GList** pList )
01484 {
01485     GncSqlColumnInfo* info;
01486 
01487     g_return_if_fail( be != NULL );
01488     g_return_if_fail( table_row != NULL );
01489     g_return_if_fail( pList != NULL );
01490 
01491     info = create_column_info( table_row, BCT_INT, 0, FALSE );
01492 
01493     *pList = g_list_append( *pList, info );
01494 }
01495 
01496 static void
01497 add_gvalue_boolean_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
01498                              const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
01499 {
01500     gint int_value = 0;
01501     BooleanAccessFunc b_getter;
01502     GValue* value;
01503 
01504     g_return_if_fail( be != NULL );
01505     g_return_if_fail( obj_name != NULL );
01506     g_return_if_fail( pObject != NULL );
01507     g_return_if_fail( table_row != NULL );
01508     g_return_if_fail( pList != NULL );
01509 
01510     value = g_new0( GValue, 1 );
01511     g_assert( value != NULL );
01512 
01513     if ( table_row->gobj_param_name != NULL )
01514     {
01515         g_object_get( pObject, table_row->gobj_param_name, &int_value, NULL );
01516     }
01517     else
01518     {
01519         b_getter = (BooleanAccessFunc)gnc_sql_get_getter( obj_name, table_row );
01520         if ( b_getter != NULL )
01521         {
01522             int_value = ((*b_getter)( pObject )) ? 1 : 0;
01523         }
01524     }
01525     (void)g_value_init( value, G_TYPE_INT );
01526     g_value_set_int( value, int_value );
01527 
01528     (*pList) = g_slist_append( (*pList), value );
01529 }
01530 
01531 static GncSqlColumnTypeHandler boolean_handler
01532 =
01533 {
01534     load_boolean,
01535     add_boolean_col_info_to_list,
01536     gnc_sql_add_colname_to_list,
01537     add_gvalue_boolean_to_slist
01538 };
01539 /* ----------------------------------------------------------------- */
01540 typedef gint64 (*Int64AccessFunc)( const gpointer );
01541 typedef void (*Int64SetterFunc)( const gpointer, gint64 );
01542 
01543 static void
01544 load_int64( const GncSqlBackend* be, GncSqlRow* row,
01545             /*@ null @*/ QofSetterFunc setter, gpointer pObject,
01546             const GncSqlColumnTableEntry* table_row )
01547 {
01548     const GValue* val;
01549     gint64 i64_value = 0;
01550     Int64SetterFunc i64_setter = (Int64SetterFunc)setter;
01551 
01552     g_return_if_fail( be != NULL );
01553     g_return_if_fail( row != NULL );
01554     g_return_if_fail( table_row != NULL );
01555     g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
01556 
01557     val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
01558     if ( val != NULL )
01559     {
01560         i64_value = gnc_sql_get_integer_value( val );
01561     }
01562     if ( table_row->gobj_param_name != NULL )
01563     {
01564         g_object_set( pObject, table_row->gobj_param_name, i64_value, NULL );
01565     }
01566     else
01567     {
01568         (*i64_setter)( pObject, i64_value );
01569     }
01570 }
01571 
01572 static void
01573 add_int64_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
01574                             GList** pList )
01575 {
01576     GncSqlColumnInfo* info;
01577 
01578     g_return_if_fail( be != NULL );
01579     g_return_if_fail( table_row != NULL );
01580     g_return_if_fail( pList != NULL );
01581 
01582     info = create_column_info( table_row, BCT_INT64, 0, FALSE );
01583 
01584     *pList = g_list_append( *pList, info );
01585 }
01586 
01587 static void
01588 add_gvalue_int64_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
01589                            const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
01590 {
01591     gint64 i64_value = 0;
01592     Int64AccessFunc getter;
01593     GValue* value;
01594 
01595     g_return_if_fail( be != NULL );
01596     g_return_if_fail( obj_name != NULL );
01597     g_return_if_fail( pObject != NULL );
01598     g_return_if_fail( table_row != NULL );
01599     g_return_if_fail( pList != NULL );
01600 
01601     value = g_new0( GValue, 1 );
01602     g_assert( value != NULL );
01603     if ( table_row->gobj_param_name != NULL )
01604     {
01605         g_object_get( pObject, table_row->gobj_param_name, &i64_value, NULL );
01606     }
01607     else
01608     {
01609         getter = (Int64AccessFunc)gnc_sql_get_getter( obj_name, table_row );
01610         if ( getter != NULL )
01611         {
01612             i64_value = (*getter)( pObject );
01613         }
01614     }
01615     (void)g_value_init( value, G_TYPE_INT64 );
01616     g_value_set_int64( value, i64_value );
01617 
01618     (*pList) = g_slist_append( (*pList), value );
01619 }
01620 
01621 static GncSqlColumnTypeHandler int64_handler
01622 =
01623 {
01624     load_int64,
01625     add_int64_col_info_to_list,
01626     gnc_sql_add_colname_to_list,
01627     add_gvalue_int64_to_slist
01628 };
01629 /* ----------------------------------------------------------------- */
01630 
01631 static void
01632 load_double( const GncSqlBackend* be, GncSqlRow* row,
01633              /*@ null @*/ QofSetterFunc setter, gpointer pObject,
01634              const GncSqlColumnTableEntry* table_row )
01635 {
01636     const GValue* val;
01637     gdouble d_value;
01638 
01639     g_return_if_fail( be != NULL );
01640     g_return_if_fail( row != NULL );
01641     g_return_if_fail( pObject != NULL );
01642     g_return_if_fail( table_row != NULL );
01643     g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
01644 
01645     val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
01646     if ( val == NULL )
01647     {
01648         (*setter)( pObject, (gpointer)NULL );
01649     }
01650     else
01651     {
01652         if ( G_VALUE_HOLDS(val, G_TYPE_INT) )
01653         {
01654             d_value = (gdouble)g_value_get_int( val );
01655         }
01656         else if ( G_VALUE_HOLDS(val, G_TYPE_FLOAT) )
01657         {
01658             d_value = g_value_get_float( val );
01659         }
01660         else if (G_VALUE_HOLDS(val, G_TYPE_DOUBLE) )
01661         {
01662             d_value = g_value_get_double( val );
01663         }
01664         else
01665         {
01666             PWARN( "Unknown float value type: %s\n", g_type_name( G_VALUE_TYPE(val) ) );
01667             d_value = 0;
01668         }
01669         if ( table_row->gobj_param_name != NULL )
01670         {
01671             g_object_set( pObject, table_row->gobj_param_name, d_value, NULL );
01672         }
01673         else
01674         {
01675             (*setter)( pObject, (gpointer)&d_value );
01676         }
01677     }
01678 }
01679 
01680 static void
01681 add_double_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
01682                              GList** pList )
01683 {
01684     GncSqlColumnInfo* info;
01685 
01686     g_return_if_fail( be != NULL );
01687     g_return_if_fail( table_row != NULL );
01688     g_return_if_fail( pList != NULL );
01689 
01690     info = create_column_info( table_row, BCT_DOUBLE, 0, FALSE );
01691 
01692     *pList = g_list_append( *pList, info );
01693 }
01694 
01695 static void
01696 add_gvalue_double_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
01697                             const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
01698 {
01699     QofAccessFunc getter;
01700     gdouble* pDouble = NULL;
01701     gdouble d_value;
01702     GValue* value;
01703 
01704     g_return_if_fail( be != NULL );
01705     g_return_if_fail( obj_name != NULL );
01706     g_return_if_fail( pObject != NULL );
01707     g_return_if_fail( table_row != NULL );
01708 
01709     value = g_new0( GValue, 1 );
01710     g_assert( value != NULL );
01711     getter = gnc_sql_get_getter( obj_name, table_row );
01712     if ( getter != NULL )
01713     {
01714         pDouble = (*getter)( pObject, NULL );
01715     }
01716     if ( pDouble != NULL )
01717     {
01718         d_value = *pDouble;
01719         (void)g_value_init( value, G_TYPE_DOUBLE );
01720         g_value_set_double( value, d_value );
01721     }
01722     else
01723     {
01724         (void)g_value_init( value, G_TYPE_DOUBLE );
01725         g_value_set_double( value, 0.0 );
01726     }
01727 
01728     (*pList) = g_slist_append( (*pList), value );
01729 }
01730 
01731 static GncSqlColumnTypeHandler double_handler
01732 =
01733 {
01734     load_double,
01735     add_double_col_info_to_list,
01736     gnc_sql_add_colname_to_list,
01737     add_gvalue_double_to_slist
01738 };
01739 /* ----------------------------------------------------------------- */
01740 
01741 static void
01742 load_guid( const GncSqlBackend* be, GncSqlRow* row,
01743            /*@ null @*/ QofSetterFunc setter, gpointer pObject,
01744            const GncSqlColumnTableEntry* table_row )
01745 {
01746     const GValue* val;
01747     GncGUID guid;
01748     const GncGUID* pGuid;
01749 
01750     g_return_if_fail( be != NULL );
01751     g_return_if_fail( row != NULL );
01752     g_return_if_fail( pObject != NULL );
01753     g_return_if_fail( table_row != NULL );
01754     g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
01755 
01756     val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
01757     if ( val == NULL || g_value_get_string( val ) == NULL )
01758     {
01759         pGuid = NULL;
01760     }
01761     else
01762     {
01763         (void)string_to_guid( g_value_get_string( val ), &guid );
01764         pGuid = &guid;
01765     }
01766     if ( pGuid != NULL )
01767     {
01768         if ( table_row->gobj_param_name != NULL )
01769         {
01770             g_object_set( pObject, table_row->gobj_param_name, pGuid, NULL );
01771         }
01772         else
01773         {
01774             g_return_if_fail( setter != NULL );
01775             (*setter)( pObject, (const gpointer)pGuid );
01776         }
01777     }
01778 }
01779 
01780 static void
01781 add_guid_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
01782                            GList** pList )
01783 {
01784     GncSqlColumnInfo* info;
01785 
01786     g_return_if_fail( be != NULL );
01787     g_return_if_fail( table_row != NULL );
01788     g_return_if_fail( pList != NULL );
01789 
01790     info = create_column_info( table_row, BCT_STRING, GUID_ENCODING_LENGTH, FALSE );
01791 
01792     *pList = g_list_append( *pList, info );
01793 }
01794 
01795 static void
01796 add_gvalue_guid_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
01797                           const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
01798 {
01799     QofAccessFunc getter;
01800     GncGUID* guid = NULL;
01801     gchar guid_buf[GUID_ENCODING_LENGTH+1];
01802     GValue* value;
01803     gboolean free_guid = FALSE;
01804 
01805     g_return_if_fail( be != NULL );
01806     g_return_if_fail( obj_name != NULL );
01807     g_return_if_fail( pObject != NULL );
01808     g_return_if_fail( table_row != NULL );
01809 
01810     value = g_new0( GValue, 1 );
01811     g_assert( value != NULL );
01812     if ( table_row->gobj_param_name != NULL )
01813     {
01814         g_object_get( pObject, table_row->gobj_param_name, &guid, NULL );
01815         free_guid = TRUE;
01816     }
01817     else
01818     {
01819         getter = gnc_sql_get_getter( obj_name, table_row );
01820         if ( getter != NULL )
01821         {
01822             guid = (*getter)( pObject, NULL );
01823         }
01824     }
01825     (void)g_value_init( value, G_TYPE_STRING );
01826     if ( guid != NULL )
01827     {
01828         (void)guid_to_string_buff( guid, guid_buf );
01829         g_value_set_string( value, guid_buf );
01830     }
01831 
01832     (*pList) = g_slist_append( (*pList), value );
01833 
01834 #if 0
01835     if ( free_guid )
01836     {
01837         g_free( guid );
01838     }
01839 #endif
01840 }
01841 
01842 static GncSqlColumnTypeHandler guid_handler
01843 =
01844 {
01845     load_guid,
01846     add_guid_col_info_to_list,
01847     gnc_sql_add_colname_to_list,
01848     add_gvalue_guid_to_slist
01849 };
01850 /* ----------------------------------------------------------------- */
01851 
01852 void
01853 gnc_sql_add_gvalue_objectref_guid_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
01854         const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
01855 {
01856     QofAccessFunc getter;
01857     const GncGUID* guid = NULL;
01858     gchar guid_buf[GUID_ENCODING_LENGTH+1];
01859     QofInstance* inst = NULL;
01860     GValue* value;
01861 
01862     g_return_if_fail( be != NULL );
01863     g_return_if_fail( obj_name != NULL );
01864     g_return_if_fail( pObject != NULL );
01865     g_return_if_fail( table_row != NULL );
01866 
01867     value = g_new0( GValue, 1 );
01868     g_assert( value != NULL );
01869     if ( table_row->gobj_param_name != NULL )
01870     {
01871         g_object_get( pObject, table_row->gobj_param_name, &inst, NULL );
01872     }
01873     else
01874     {
01875         getter = gnc_sql_get_getter( obj_name, table_row );
01876         if ( getter != NULL )
01877         {
01878             inst = (*getter)( pObject, NULL );
01879         }
01880     }
01881     if ( inst != NULL )
01882     {
01883         guid = qof_instance_get_guid( inst );
01884     }
01885     (void)g_value_init( value, G_TYPE_STRING );
01886     if ( guid != NULL )
01887     {
01888         (void)guid_to_string_buff( guid, guid_buf );
01889         g_value_set_string( value, guid_buf );
01890     }
01891 
01892     (*pList) = g_slist_append( (*pList), value );
01893 }
01894 
01895 void
01896 gnc_sql_add_objectref_guid_col_info_to_list( const GncSqlBackend* be,
01897         const GncSqlColumnTableEntry* table_row,
01898         GList** pList )
01899 {
01900     add_guid_col_info_to_list( be, table_row, pList );
01901 }
01902 
01903 /* ----------------------------------------------------------------- */
01904 typedef Timespec (*TimespecAccessFunc)( const gpointer );
01905 typedef void (*TimespecSetterFunc)( const gpointer, Timespec );
01906 
01907 #define TIMESPEC_STR_FORMAT "%04d%02d%02d%02d%02d%02d"
01908 #define TIMESPEC_COL_SIZE (4+2+2+2+2+2)
01909 
01910 gchar*
01911 gnc_sql_convert_timespec_to_string( const GncSqlBackend* be, Timespec ts )
01912 {
01913     time_t time;
01914     struct tm* tm;
01915     gint year;
01916     gchar* datebuf;
01917 
01918     time = timespecToTime_t( ts );
01919     tm = gmtime( &time );
01920 
01921     if ( tm->tm_year < 60 ) year = tm->tm_year + 2000;
01922     else year = tm->tm_year + 1900;
01923 
01924     datebuf = g_strdup_printf( be->timespec_format,
01925                                year, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec );
01926     return datebuf;
01927 }
01928 
01929 static void
01930 load_timespec( const GncSqlBackend* be, GncSqlRow* row,
01931                /*@ null @*/ QofSetterFunc setter, gpointer pObject,
01932                const GncSqlColumnTableEntry* table_row )
01933 {
01934     const GValue* val;
01935     Timespec ts = {0, 0};
01936     TimespecSetterFunc ts_setter;
01937     gboolean isOK = FALSE;
01938 
01939     g_return_if_fail( be != NULL );
01940     g_return_if_fail( row != NULL );
01941     g_return_if_fail( pObject != NULL );
01942     g_return_if_fail( table_row != NULL );
01943     g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
01944 
01945     ts_setter = (TimespecSetterFunc)setter;
01946     val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
01947     if ( val == NULL )
01948     {
01949         isOK = TRUE;
01950     }
01951     else
01952     {
01953         if ( G_VALUE_HOLDS_STRING( val ) )
01954         {
01955             const gchar* s = g_value_get_string( val );
01956             if ( s != NULL )
01957             {
01958                 gchar* buf;
01959                 buf = g_strdup_printf( "%c%c%c%c-%c%c-%c%c %c%c:%c%c:%c%c",
01960                                        s[0], s[1], s[2], s[3],
01961                                        s[4], s[5],
01962                                        s[6], s[7],
01963                                        s[8], s[9],
01964                                        s[10], s[11],
01965                                        s[12], s[13] );
01966                 ts = gnc_iso8601_to_timespec_gmt( buf );
01967                 g_free( buf );
01968                 isOK = TRUE;
01969             }
01970 
01971         }
01972         else
01973         {
01974             PWARN( "Unknown timespec type: %s", G_VALUE_TYPE_NAME( val ) );
01975         }
01976     }
01977     if ( isOK )
01978     {
01979         if (table_row->gobj_param_name != NULL)
01980         {
01981             g_object_set( pObject, table_row->gobj_param_name, &ts, NULL );
01982         }
01983         else
01984         {
01985             (*ts_setter)( pObject, ts );
01986         }
01987     }
01988 }
01989 
01990 static void
01991 add_timespec_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
01992                                GList** pList )
01993 {
01994     GncSqlColumnInfo* info;
01995 
01996     g_return_if_fail( be != NULL );
01997     g_return_if_fail( table_row != NULL );
01998     g_return_if_fail( pList != NULL );
01999 
02000     info = create_column_info( table_row, BCT_DATETIME, TIMESPEC_COL_SIZE, FALSE );
02001 
02002     *pList = g_list_append( *pList, info );
02003 }
02004 
02005 static void
02006 add_gvalue_timespec_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
02007                               const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
02008 {
02009     TimespecAccessFunc ts_getter;
02010     Timespec ts;
02011     gchar* datebuf;
02012     GValue* value;
02013 
02014     g_return_if_fail( be != NULL );
02015     g_return_if_fail( obj_name != NULL );
02016     g_return_if_fail( pObject != NULL );
02017     g_return_if_fail( table_row != NULL );
02018     g_return_if_fail( pList != NULL );
02019 
02020     if ( table_row->gobj_param_name != NULL )
02021     {
02022         Timespec* pts;
02023         g_object_get( pObject, table_row->gobj_param_name, &pts, NULL );
02024         ts = *pts;
02025     }
02026     else
02027     {
02028         ts_getter = (TimespecAccessFunc)gnc_sql_get_getter( obj_name, table_row );
02029         g_return_if_fail( ts_getter != NULL );
02030         ts = (*ts_getter)( pObject );
02031     }
02032 
02033     value = g_new0( GValue, 1 );
02034     g_assert( value != NULL );
02035     (void)g_value_init( value, G_TYPE_STRING );
02036     if ( ts.tv_sec != 0 || ts.tv_nsec != 0 )
02037     {
02038         datebuf = gnc_sql_convert_timespec_to_string( be, ts );
02039         g_value_take_string( value, datebuf );
02040     }
02041 
02042     (*pList) = g_slist_append( (*pList), value );
02043 }
02044 
02045 static GncSqlColumnTypeHandler timespec_handler
02046 =
02047 {
02048     load_timespec,
02049     add_timespec_col_info_to_list,
02050     gnc_sql_add_colname_to_list,
02051     add_gvalue_timespec_to_slist
02052 };
02053 /* ----------------------------------------------------------------- */
02054 #define DATE_COL_SIZE 8
02055 
02056 static void
02057 load_date( const GncSqlBackend* be, GncSqlRow* row,
02058            /*@ null @*/ QofSetterFunc setter, gpointer pObject,
02059            const GncSqlColumnTableEntry* table_row )
02060 {
02061     const GValue* val;
02062     GDate* date;
02063 
02064     g_return_if_fail( be != NULL );
02065     g_return_if_fail( row != NULL );
02066     g_return_if_fail( pObject != NULL );
02067     g_return_if_fail( table_row != NULL );
02068     g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
02069 
02070     val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
02071     if ( val != NULL )
02072     {
02073         if ( G_VALUE_HOLDS_STRING( val ) )
02074         {
02075             // Format of date is YYYYMMDD
02076             const gchar* s = g_value_get_string( val );
02077 
02078             if ( s != NULL )
02079             {
02080                 gchar buf[5];
02081                 GDateDay day;
02082                 guint month;
02083                 GDateYear year;
02084 
02085                 strncpy( buf, &s[0], 4 );
02086                 buf[4] = '\0';
02087                 year = (GDateYear)atoi( buf );
02088                 strncpy( buf, &s[4], 2 );
02089                 buf[2] = '\0';
02090                 month = (guint)atoi( buf );
02091                 strncpy( buf, &s[6], 2 );
02092                 day = (GDateDay)atoi( buf );
02093 
02094                 if ( year != 0 || month != 0 || day != (GDateDay)0 )
02095                 {
02096                     date = g_date_new_dmy( day, month, year );
02097                     if ( table_row->gobj_param_name != NULL )
02098                     {
02099                         g_object_set( pObject, table_row->gobj_param_name, date, NULL );
02100                     }
02101                     else
02102                     {
02103                         (*setter)( pObject, date );
02104                     }
02105                     g_date_free( date );
02106                 }
02107             }
02108         }
02109         else
02110         {
02111             PWARN( "Unknown date type: %s", G_VALUE_TYPE_NAME( val ) );
02112         }
02113     }
02114 }
02115 
02116 static void
02117 add_date_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
02118                            GList** pList )
02119 {
02120     GncSqlColumnInfo* info;
02121 
02122     g_return_if_fail( be != NULL );
02123     g_return_if_fail( table_row != NULL );
02124     g_return_if_fail( pList != NULL );
02125 
02126     info = create_column_info( table_row, BCT_DATE, DATE_COL_SIZE, FALSE );
02127 
02128     *pList = g_list_append( *pList, info );
02129 }
02130 
02131 static void
02132 add_gvalue_date_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
02133                           const gpointer pObject,
02134                           const GncSqlColumnTableEntry* table_row, GSList** pList )
02135 {
02136     GDate* date = NULL;
02137     QofAccessFunc getter;
02138     gchar* buf;
02139     GValue* value;
02140 
02141     g_return_if_fail( be != NULL );
02142     g_return_if_fail( obj_name != NULL );
02143     g_return_if_fail( pObject != NULL );
02144     g_return_if_fail( table_row != NULL );
02145 
02146     value = g_new0( GValue, 1 );
02147     g_assert( value != NULL );
02148     (void)g_value_init( value, G_TYPE_STRING );
02149     if ( table_row->gobj_param_name != NULL )
02150     {
02151         g_object_get( pObject, table_row->gobj_param_name, &date, NULL );
02152     }
02153     else
02154     {
02155         getter = gnc_sql_get_getter( obj_name, table_row );
02156         if ( getter != NULL )
02157         {
02158             date = (GDate*)(*getter)( pObject, NULL );
02159         }
02160     }
02161     if ( date && g_date_valid( date ) )
02162     {
02163         buf = g_strdup_printf( "%04d%02d%02d",
02164                                g_date_get_year( date ), g_date_get_month( date ), g_date_get_day( date ) );
02165         g_value_take_string( value, buf );
02166     }
02167 
02168     (*pList) = g_slist_append( (*pList), value );
02169 }
02170 
02171 static GncSqlColumnTypeHandler date_handler
02172 =
02173 {
02174     load_date,
02175     add_date_col_info_to_list,
02176     gnc_sql_add_colname_to_list,
02177     add_gvalue_date_to_slist
02178 };
02179 /* ----------------------------------------------------------------- */
02180 typedef gnc_numeric (*NumericGetterFunc)( const gpointer );
02181 typedef void (*NumericSetterFunc)( gpointer, gnc_numeric );
02182 
02183 static const GncSqlColumnTableEntry numeric_col_table[] =
02184 {
02185     /*@ -full_init_block @*/
02186     { "num",    CT_INT64, 0, COL_NNUL, "guid" },
02187     { "denom",  CT_INT64, 0, COL_NNUL, "guid" },
02188     { NULL }
02189     /*@ +full_init_block @*/
02190 };
02191 
02192 static void
02193 load_numeric( const GncSqlBackend* be, GncSqlRow* row,
02194               /*@ null @*/ QofSetterFunc setter, gpointer pObject,
02195               const GncSqlColumnTableEntry* table_row )
02196 {
02197     const GValue* val;
02198     gchar* buf;
02199     gint64 num, denom;
02200     gnc_numeric n;
02201     gboolean isNull = FALSE;
02202 
02203     g_return_if_fail( be != NULL );
02204     g_return_if_fail( row != NULL );
02205     g_return_if_fail( pObject != NULL );
02206     g_return_if_fail( table_row != NULL );
02207     g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
02208 
02209     buf = g_strdup_printf( "%s_num", table_row->col_name );
02210     val = gnc_sql_row_get_value_at_col_name( row, buf );
02211     g_free( buf );
02212     if ( val == NULL )
02213     {
02214         isNull = TRUE;
02215         num = 0;
02216     }
02217     else
02218     {
02219         num = gnc_sql_get_integer_value( val );
02220     }
02221     buf = g_strdup_printf( "%s_denom", table_row->col_name );
02222     val = gnc_sql_row_get_value_at_col_name( row, buf );
02223     g_free( buf );
02224     if ( val == NULL )
02225     {
02226         isNull = TRUE;
02227         denom = 1;
02228     }
02229     else
02230     {
02231         denom = gnc_sql_get_integer_value( val );
02232     }
02233     n = gnc_numeric_create( num, denom );
02234     if ( !isNull )
02235     {
02236         if ( table_row->gobj_param_name != NULL )
02237         {
02238             g_object_set( pObject, table_row->gobj_param_name, &n, NULL );
02239         }
02240         else
02241         {
02242             NumericSetterFunc n_setter = (NumericSetterFunc)setter;
02243             (*n_setter)( pObject, n );
02244         }
02245     }
02246 }
02247 
02248 static void
02249 add_numeric_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
02250                               GList** pList )
02251 {
02252     GncSqlColumnInfo* info;
02253     gchar* buf;
02254     const GncSqlColumnTableEntry* subtable_row;
02255 
02256     g_return_if_fail( be != NULL );
02257     g_return_if_fail( table_row != NULL );
02258     g_return_if_fail( pList != NULL );
02259 
02260     for ( subtable_row = numeric_col_table; subtable_row->col_name != NULL; subtable_row++ )
02261     {
02262         buf = g_strdup_printf( "%s_%s", table_row->col_name, subtable_row->col_name );
02263         info = g_new0( GncSqlColumnInfo, 1 );
02264         g_assert( info != NULL );
02265         info->name = buf;
02266         info->type = BCT_INT64;
02267         info->is_primary_key = ((table_row->flags & COL_PKEY) != 0) ? TRUE : FALSE;
02268         info->null_allowed = ((table_row->flags & COL_NNUL) != 0) ? FALSE : TRUE;
02269         info->is_unicode = FALSE;
02270         *pList = g_list_append( *pList, info );
02271     }
02272 }
02273 
02274 static void
02275 add_numeric_colname_to_list( const GncSqlColumnTableEntry* table_row, GList** pList )
02276 {
02277     gnc_sql_add_subtable_colnames_to_list( table_row, numeric_col_table, pList );
02278 }
02279 
02280 static void
02281 add_gvalue_numeric_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
02282                              const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
02283 {
02284     NumericGetterFunc getter;
02285     gnc_numeric n;
02286     GValue* num_value;
02287     GValue* denom_value;
02288 
02289     g_return_if_fail( be != NULL );
02290     g_return_if_fail( obj_name != NULL );
02291     g_return_if_fail( pObject != NULL );
02292     g_return_if_fail( table_row != NULL );
02293 
02294     if ( table_row->gobj_param_name != NULL )
02295     {
02296         gnc_numeric *s;
02297         g_object_get( pObject, table_row->gobj_param_name, &s, NULL );
02298         n = *s;
02299     }
02300     else
02301     {
02302         getter = (NumericGetterFunc)gnc_sql_get_getter( obj_name, table_row );
02303         if ( getter != NULL )
02304         {
02305             n = (*getter)( pObject );
02306         }
02307         else
02308         {
02309             n = gnc_numeric_zero();
02310         }
02311     }
02312 
02313     num_value = g_new0( GValue, 1 );
02314     g_assert( num_value != NULL );
02315     (void)g_value_init( num_value, G_TYPE_INT64 );
02316     g_value_set_int64( num_value, gnc_numeric_num( n ) );
02317     denom_value = g_new0( GValue, 1 );
02318     g_assert( denom_value != NULL );
02319     (void)g_value_init( denom_value, G_TYPE_INT64 );
02320     g_value_set_int64( denom_value, gnc_numeric_denom( n ) );
02321 
02322     (*pList) = g_slist_append( (*pList), num_value );
02323     (*pList) = g_slist_append( (*pList), denom_value );
02324 }
02325 
02326 static GncSqlColumnTypeHandler numeric_handler
02327 = { load_numeric,
02328     add_numeric_col_info_to_list,
02329     add_numeric_colname_to_list,
02330     add_gvalue_numeric_to_slist
02331   };
02332 /* ================================================================= */
02333 
02334 static /*@ null @*//*@ only @*/ GHashTable* g_columnTypeHash = NULL;
02335 
02336 void
02337 gnc_sql_register_col_type_handler( const gchar* colType, const GncSqlColumnTypeHandler* handler )
02338 {
02339     g_return_if_fail( colType != NULL );
02340     g_return_if_fail( handler != NULL );
02341 
02342     if ( g_columnTypeHash == NULL )
02343     {
02344         g_columnTypeHash = g_hash_table_new( g_str_hash, g_str_equal );
02345         g_assert( g_columnTypeHash != NULL );
02346     }
02347 
02348     DEBUG( "Col type %s registered\n", colType );
02349     g_hash_table_insert( g_columnTypeHash, (gpointer)colType, (gpointer)handler );
02350 }
02351 
02352 /*@ dependent @*//*@ null @*/ static GncSqlColumnTypeHandler*
02353 get_handler( const GncSqlColumnTableEntry* table_row )
02354 {
02355     GncSqlColumnTypeHandler* pHandler;
02356 
02357     g_return_val_if_fail( table_row != NULL, NULL );
02358     g_return_val_if_fail( table_row->col_type != NULL, NULL );
02359 
02360     if ( g_columnTypeHash != NULL )
02361     {
02362         pHandler = g_hash_table_lookup( g_columnTypeHash, table_row->col_type );
02363         g_assert( pHandler != NULL );
02364     }
02365     else
02366     {
02367         pHandler = NULL;
02368     }
02369 
02370     return pHandler;
02371 }
02372 
02373 static void
02374 register_standard_col_type_handlers( void )
02375 {
02376     gnc_sql_register_col_type_handler( CT_STRING, &string_handler );
02377     gnc_sql_register_col_type_handler( CT_BOOLEAN, &boolean_handler );
02378     gnc_sql_register_col_type_handler( CT_INT, &int_handler );
02379     gnc_sql_register_col_type_handler( CT_INT64, &int64_handler );
02380     gnc_sql_register_col_type_handler( CT_DOUBLE, &double_handler );
02381     gnc_sql_register_col_type_handler( CT_GUID, &guid_handler );
02382     gnc_sql_register_col_type_handler( CT_TIMESPEC, &timespec_handler );
02383     gnc_sql_register_col_type_handler( CT_GDATE, &date_handler );
02384     gnc_sql_register_col_type_handler( CT_NUMERIC, &numeric_handler );
02385 }
02386 
02387 void
02388 _retrieve_guid_( gpointer pObject, /*@ null @*/ gpointer pValue )
02389 {
02390     GncGUID* pGuid = (GncGUID*)pObject;
02391     GncGUID* guid = (GncGUID*)pValue;
02392 
02393     g_return_if_fail( pObject != NULL );
02394     g_return_if_fail( pValue != NULL );
02395 
02396     memcpy( pGuid, guid, sizeof( GncGUID ) );
02397 }
02398 
02399 
02400 // Table to retrieve just the guid
02401 static GncSqlColumnTableEntry guid_table[] =
02402 {
02403     /*@ -full_init_block @*/
02404     { "guid", CT_GUID, 0, 0, NULL, NULL, NULL, _retrieve_guid_ },
02405     { NULL }
02406     /*@ +full_init_block @*/
02407 };
02408 
02409 /*@ null @*/
02410 const GncGUID*
02411 gnc_sql_load_guid( const GncSqlBackend* be, GncSqlRow* row )
02412 {
02413     static GncGUID guid;
02414 
02415     g_return_val_if_fail( be != NULL, NULL );
02416     g_return_val_if_fail( row != NULL, NULL );
02417 
02418     gnc_sql_load_object( be, row, NULL, &guid, guid_table );
02419 
02420     return &guid;
02421 }
02422 
02423 // Table to retrieve just the guid
02424 static GncSqlColumnTableEntry tx_guid_table[] =
02425 {
02426     /*@ -full_init_block @*/
02427     { "tx_guid", CT_GUID, 0, 0, NULL, NULL, NULL, _retrieve_guid_ },
02428     { NULL }
02429     /*@ +full_init_block @*/
02430 };
02431 
02432 /*@ null @*//*@ dependent @*/
02433 const GncGUID*
02434 gnc_sql_load_tx_guid( const GncSqlBackend* be, GncSqlRow* row )
02435 {
02436     static GncGUID guid;
02437 
02438     g_return_val_if_fail( be != NULL, NULL );
02439     g_return_val_if_fail( row != NULL, NULL );
02440 
02441     gnc_sql_load_object( be, row, NULL, &guid, tx_guid_table );
02442 
02443     return &guid;
02444 }
02445 
02446 void
02447 gnc_sql_load_object( const GncSqlBackend* be, GncSqlRow* row,
02448                      /*@ null @*/ QofIdTypeConst obj_name, gpointer pObject,
02449                      const GncSqlColumnTableEntry* table )
02450 {
02451     QofSetterFunc setter;
02452     GncSqlColumnTypeHandler* pHandler;
02453     const GncSqlColumnTableEntry* table_row;
02454 
02455     g_return_if_fail( be != NULL );
02456     g_return_if_fail( row != NULL );
02457     g_return_if_fail( pObject != NULL );
02458     g_return_if_fail( table != NULL );
02459 
02460     for ( table_row = table; table_row->col_name != NULL; table_row++ )
02461     {
02462         if ( (table_row->flags & COL_AUTOINC) != 0 )
02463         {
02464             setter = set_autoinc_id;
02465         }
02466         else if ( table_row->qof_param_name != NULL )
02467         {
02468             g_assert( obj_name != NULL );
02469             setter = qof_class_get_parameter_setter( obj_name,
02470                      table_row->qof_param_name );
02471         }
02472         else
02473         {
02474             setter = table_row->setter;
02475         }
02476         pHandler = get_handler( table_row );
02477         g_assert( pHandler != NULL );
02478         pHandler->load_fn( be, row, setter, pObject, table_row );
02479     }
02480 }
02481 
02482 /* ================================================================= */
02483 /*@ null @*/ GncSqlStatement*
02484 gnc_sql_create_select_statement( GncSqlBackend* be, const gchar* table_name )
02485 {
02486     gchar* sql;
02487     GncSqlStatement* stmt;
02488 
02489     g_return_val_if_fail( be != NULL, NULL );
02490     g_return_val_if_fail( table_name != NULL, NULL );
02491 
02492     sql = g_strdup_printf( "SELECT * FROM %s", table_name );
02493     stmt = gnc_sql_create_statement_from_sql( be, sql );
02494     g_free( sql );
02495     return stmt;
02496 }
02497 
02498 /*@ null @*/ static GncSqlStatement*
02499 create_single_col_select_statement( GncSqlBackend* be,
02500                                     const gchar* table_name,
02501                                     const GncSqlColumnTableEntry* table_row )
02502 {
02503     gchar* sql;
02504     GncSqlStatement* stmt;
02505 
02506     g_return_val_if_fail( be != NULL, NULL );
02507     g_return_val_if_fail( table_name != NULL, NULL );
02508     g_return_val_if_fail( table_row != NULL, NULL );
02509 
02510     sql = g_strdup_printf( "SELECT %s FROM %s", table_row->col_name, table_name );
02511     stmt = gnc_sql_create_statement_from_sql( be, sql );
02512     g_free( sql );
02513     return stmt;
02514 }
02515 
02516 /* ================================================================= */
02517 
02518 /*@ null @*/ GncSqlResult*
02519 gnc_sql_execute_select_statement( GncSqlBackend* be, GncSqlStatement* stmt )
02520 {
02521     GncSqlResult* result;
02522 
02523     g_return_val_if_fail( be != NULL, NULL );
02524     g_return_val_if_fail( stmt != NULL, NULL );
02525 
02526     result = gnc_sql_connection_execute_select_statement( be->conn, stmt );
02527     if ( result == NULL )
02528     {
02529         PERR( "SQL error: %s\n", gnc_sql_statement_to_sql( stmt ) );
02530         qof_backend_set_error( &be->be, ERR_BACKEND_SERVER_ERR );
02531     }
02532 
02533     return result;
02534 }
02535 
02536 /*@ null @*/ GncSqlStatement*
02537 gnc_sql_create_statement_from_sql( GncSqlBackend* be, const gchar* sql )
02538 {
02539     GncSqlStatement* stmt;
02540 
02541     g_return_val_if_fail( be != NULL, NULL );
02542     g_return_val_if_fail( sql != NULL, NULL );
02543 
02544     stmt = gnc_sql_connection_create_statement_from_sql( be->conn, sql );
02545     if ( stmt == NULL )
02546     {
02547         PERR( "SQL error: %s\n", sql );
02548         qof_backend_set_error( &be->be, ERR_BACKEND_SERVER_ERR );
02549     }
02550 
02551     return stmt;
02552 }
02553 
02554 /*@ null @*/ GncSqlResult*
02555 gnc_sql_execute_select_sql( GncSqlBackend* be, const gchar* sql )
02556 {
02557     GncSqlStatement* stmt;
02558     GncSqlResult* result = NULL;
02559 
02560     g_return_val_if_fail( be != NULL, NULL );
02561     g_return_val_if_fail( sql != NULL, NULL );
02562 
02563     stmt = gnc_sql_create_statement_from_sql( be, sql );
02564     if ( stmt == NULL )
02565     {
02566         return NULL;
02567     }
02568     result = gnc_sql_connection_execute_select_statement( be->conn, stmt );
02569     gnc_sql_statement_dispose( stmt );
02570     if ( result == NULL )
02571     {
02572         PERR( "SQL error: %s\n", sql );
02573         qof_backend_set_error( &be->be, ERR_BACKEND_SERVER_ERR );
02574     }
02575 
02576     return result;
02577 }
02578 
02579 gint
02580 gnc_sql_execute_nonselect_sql( GncSqlBackend* be, const gchar* sql )
02581 {
02582     GncSqlStatement* stmt;
02583     gint result;
02584 
02585     g_return_val_if_fail( be != NULL, 0 );
02586     g_return_val_if_fail( sql != NULL, 0 );
02587 
02588     stmt = gnc_sql_create_statement_from_sql( be, sql );
02589     if ( stmt == NULL )
02590     {
02591         return -1;
02592     }
02593     result = gnc_sql_connection_execute_nonselect_statement( be->conn, stmt );
02594     gnc_sql_statement_dispose( stmt );
02595     return result;
02596 }
02597 
02598 static guint
02599 execute_statement_get_count( GncSqlBackend* be, GncSqlStatement* stmt )
02600 {
02601     GncSqlResult* result;
02602     guint count = 0;
02603 
02604     g_return_val_if_fail( be != NULL, 0 );
02605     g_return_val_if_fail( stmt != NULL, 0 );
02606 
02607     result = gnc_sql_execute_select_statement( be, stmt );
02608     if ( result != NULL )
02609     {
02610         count = gnc_sql_result_get_num_rows( result );
02611         gnc_sql_result_dispose( result );
02612     }
02613 
02614     return count;
02615 }
02616 
02617 guint
02618 gnc_sql_append_guid_list_to_sql( GString* sql, GList* list, guint maxCount )
02619 {
02620     gchar guid_buf[GUID_ENCODING_LENGTH+1];
02621     gboolean first_guid = TRUE;
02622     guint count;
02623 
02624     g_return_val_if_fail( sql != NULL, 0 );
02625 
02626     if ( list == NULL ) return 0;
02627 
02628     for ( count = 0; list != NULL && count < maxCount; list = list->next, count++ )
02629     {
02630         QofInstance* inst = QOF_INSTANCE(list->data);
02631         (void)guid_to_string_buff( qof_instance_get_guid( inst ), guid_buf );
02632 
02633         if ( !first_guid )
02634         {
02635             (void)g_string_append( sql, "," );
02636         }
02637         (void)g_string_append( sql, "'" );
02638         (void)g_string_append( sql, guid_buf );
02639         (void)g_string_append( sql, "'" );
02640         first_guid = FALSE;
02641     }
02642 
02643     return count;
02644 }
02645 /* ================================================================= */
02646 
02647 gboolean
02648 gnc_sql_object_is_it_in_db( GncSqlBackend* be, const gchar* table_name,
02649                             QofIdTypeConst obj_name, gpointer pObject,
02650                             const GncSqlColumnTableEntry* table )
02651 {
02652     GncSqlStatement* sqlStmt;
02653     guint count;
02654     GncSqlColumnTypeHandler* pHandler;
02655     GSList* list = NULL;
02656 
02657     g_return_val_if_fail( be != NULL, FALSE );
02658     g_return_val_if_fail( table_name != NULL, FALSE );
02659     g_return_val_if_fail( obj_name != NULL, FALSE );
02660     g_return_val_if_fail( pObject != NULL, FALSE );
02661     g_return_val_if_fail( table != NULL, FALSE );
02662 
02663     /* SELECT * FROM */
02664     sqlStmt = create_single_col_select_statement( be, table_name, table );
02665     g_assert( sqlStmt != NULL );
02666 
02667     /* WHERE */
02668     pHandler = get_handler( table );
02669     g_assert( pHandler != NULL );
02670     pHandler->add_gvalue_to_slist_fn( be, obj_name, pObject, table, &list );
02671     g_assert( list != NULL );
02672     gnc_sql_statement_add_where_cond( sqlStmt, obj_name, pObject, &table[0], (GValue*)(list->data) );
02673 
02674     count = execute_statement_get_count( be, sqlStmt );
02675     gnc_sql_statement_dispose( sqlStmt );
02676     if ( count == 0 )
02677     {
02678         return FALSE;
02679     }
02680     else
02681     {
02682         return TRUE;
02683     }
02684 }
02685 
02686 gboolean
02687 gnc_sql_do_db_operation( GncSqlBackend* be,
02688                          E_DB_OPERATION op,
02689                          const gchar* table_name,
02690                          QofIdTypeConst obj_name, gpointer pObject,
02691                          const GncSqlColumnTableEntry* table )
02692 {
02693     GncSqlStatement* stmt = NULL;
02694     gboolean ok = FALSE;
02695 
02696     g_return_val_if_fail( be != NULL, FALSE );
02697     g_return_val_if_fail( table_name != NULL, FALSE );
02698     g_return_val_if_fail( obj_name != NULL, FALSE );
02699     g_return_val_if_fail( pObject != NULL, FALSE );
02700     g_return_val_if_fail( table != NULL, FALSE );
02701 
02702     if ( op == OP_DB_INSERT )
02703     {
02704         stmt = build_insert_statement( be, table_name, obj_name, pObject, table );
02705     }
02706     else if ( op == OP_DB_UPDATE )
02707     {
02708         stmt = build_update_statement( be, table_name, obj_name, pObject, table );
02709     }
02710     else if ( op == OP_DB_DELETE )
02711     {
02712         stmt = build_delete_statement( be, table_name, obj_name, pObject, table );
02713     }
02714     else
02715     {
02716         g_assert( FALSE );
02717     }
02718     if ( stmt != NULL )
02719     {
02720         gint result;
02721 
02722         result = gnc_sql_connection_execute_nonselect_statement( be->conn, stmt );
02723         if ( result == -1 )
02724         {
02725             PERR( "SQL error: %s\n", gnc_sql_statement_to_sql( stmt ) );
02726             qof_backend_set_error( &be->be, ERR_BACKEND_SERVER_ERR );
02727         }
02728         else
02729         {
02730             ok = TRUE;
02731         }
02732         gnc_sql_statement_dispose( stmt );
02733     }
02734 
02735     return ok;
02736 }
02737 
02738 static GSList*
02739 create_gslist_from_values( GncSqlBackend* be,
02740                            QofIdTypeConst obj_name, gpointer pObject,
02741                            const GncSqlColumnTableEntry* table )
02742 {
02743     GSList* list = NULL;
02744     GncSqlColumnTypeHandler* pHandler;
02745     const GncSqlColumnTableEntry* table_row;
02746 
02747     for ( table_row = table; table_row->col_name != NULL; table_row++ )
02748     {
02749         if (( table_row->flags & COL_AUTOINC ) == 0 )
02750         {
02751             pHandler = get_handler( table_row );
02752             g_assert( pHandler != NULL );
02753             pHandler->add_gvalue_to_slist_fn( be, obj_name, pObject, table_row, &list );
02754         }
02755     }
02756 
02757     g_assert( list != NULL );
02758     return list;
02759 }
02760 
02761 gchar*
02762 gnc_sql_get_sql_value( const GncSqlConnection* conn, const GValue* value )
02763 {
02764     if ( value != NULL && G_IS_VALUE( value ) )
02765     {
02766         GType type = G_VALUE_TYPE(value);
02767 
02768         if ( G_VALUE_HOLDS_STRING(value) )
02769         {
02770             if ( g_value_get_string( value ) != NULL )
02771             {
02772                 gchar* before_str;
02773                 gchar* after_str;
02774                 before_str = g_value_dup_string( value );
02775                 after_str = gnc_sql_connection_quote_string( conn, before_str );
02776                 g_free( before_str );
02777                 return after_str;
02778             }
02779             else
02780             {
02781                 return g_strdup( "NULL" );
02782             }
02783         }
02784         else if ( type == G_TYPE_INT64 )
02785         {
02786             return g_strdup_printf( "%" G_GINT64_FORMAT, g_value_get_int64( value ) );
02787 
02788         }
02789         else if ( type == G_TYPE_INT )
02790         {
02791             return g_strdup_printf( "%d", g_value_get_int( value ) );
02792 
02793         }
02794         else if ( type == G_TYPE_DOUBLE )
02795         {
02796             gchar doublestr[G_ASCII_DTOSTR_BUF_SIZE];
02797             g_ascii_dtostr( doublestr, sizeof(doublestr),
02798                             g_value_get_double( value ));
02799             return g_strdup( doublestr );
02800 
02801         }
02802         else if ( g_value_type_transformable( type, G_TYPE_STRING ) )
02803         {
02804             GValue* string;
02805             gchar* str;
02806 
02807             string = g_new0( GValue, 1 );
02808             g_assert( string != NULL );
02809             (void)g_value_init( string, G_TYPE_STRING );
02810             (void)g_value_transform( value, string );
02811             str = g_value_dup_string( string );
02812             g_value_unset( string );
02813             g_free( string );
02814             PWARN( "using g_value_transform(), gtype = '%s'\n", g_type_name( type ) );
02815             return str;
02816         }
02817         else
02818         {
02819             PWARN( "not transformable, gtype = '%s'\n", g_type_name( type ) );
02820             return g_strdup( "$$$" );
02821         }
02822     }
02823     else
02824     {
02825         PWARN( "value is NULL or not G_IS_VALUE()\n" );
02826         return g_strdup( "" );
02827     }
02828 }
02829 
02830 static void
02831 free_gvalue_list( GSList* list )
02832 {
02833     GSList* node;
02834     GValue* value;
02835 
02836     for ( node = list; node != NULL; node = node->next )
02837     {
02838         value = (GValue*)node->data;
02839 
02840         g_value_unset( value );
02841         g_free( value );
02842     }
02843     g_slist_free( list );
02844 }
02845 
02846 /*@ null @*/ static GncSqlStatement*
02847 build_insert_statement( GncSqlBackend* be,
02848                         const gchar* table_name,
02849                         QofIdTypeConst obj_name, gpointer pObject,
02850                         const GncSqlColumnTableEntry* table )
02851 {
02852     GncSqlStatement* stmt;
02853     GString* sql;
02854     GSList* values;
02855     GSList* node;
02856     gchar* sqlbuf;
02857     GList* colnames = NULL;
02858     GList* colname;
02859     const GncSqlColumnTableEntry* table_row;
02860 
02861     g_return_val_if_fail( be != NULL, NULL );
02862     g_return_val_if_fail( table_name != NULL, NULL );
02863     g_return_val_if_fail( obj_name != NULL, NULL );
02864     g_return_val_if_fail( pObject != NULL, NULL );
02865     g_return_val_if_fail( table != NULL, NULL );
02866 
02867     sqlbuf = g_strdup_printf( "INSERT INTO %s(", table_name );
02868     sql = g_string_new( sqlbuf );
02869     g_free( sqlbuf );
02870 
02871     // Get all col names and all values
02872     for ( table_row = table; table_row->col_name != NULL; table_row++ )
02873     {
02874         if (( table_row->flags & COL_AUTOINC ) == 0 )
02875         {
02876             GncSqlColumnTypeHandler* pHandler;
02877 
02878             // Add col names to the list
02879             pHandler = get_handler( table_row );
02880             g_assert( pHandler != NULL );
02881             pHandler->add_colname_to_list_fn( table_row, &colnames );
02882         }
02883     }
02884     g_assert( colnames != NULL );
02885 
02886     for ( colname = colnames; colname != NULL; colname = colname->next )
02887     {
02888         if ( colname != colnames )
02889         {
02890             g_string_append( sql, "," );
02891         }
02892         g_string_append( sql, (gchar*)colname->data );
02893         g_free( colname->data );
02894     }
02895     g_list_free( colnames );
02896 
02897     g_string_append( sql, ") VALUES(" );
02898     values = create_gslist_from_values( be, obj_name, pObject, table );
02899     for ( node = values; node != NULL; node = node->next )
02900     {
02901         GValue* value = (GValue*)node->data;
02902         gchar* value_str;
02903         if ( node != values )
02904         {
02905             (void)g_string_append( sql, "," );
02906         }
02907         value_str = gnc_sql_get_sql_value( be->conn, value );
02908         (void)g_string_append( sql, value_str );
02909         g_free( value_str );
02910         (void)g_value_reset( value );
02911     }
02912     free_gvalue_list( values );
02913     (void)g_string_append( sql, ")" );
02914 
02915     stmt = gnc_sql_connection_create_statement_from_sql( be->conn, sql->str );
02916     (void)g_string_free( sql, TRUE );
02917 
02918     return stmt;
02919 }
02920 
02921 /*@ null @*/ static GncSqlStatement*
02922 build_update_statement( GncSqlBackend* be,
02923                         const gchar* table_name,
02924                         QofIdTypeConst obj_name, gpointer pObject,
02925                         const GncSqlColumnTableEntry* table )
02926 {
02927     GncSqlStatement* stmt;
02928     GString* sql;
02929     GSList* values;
02930     GList* colnames = NULL;
02931     GSList* value;
02932     GList* colname;
02933     gboolean firstCol;
02934     const GncSqlColumnTableEntry* table_row;
02935     gchar* sqlbuf;
02936 
02937     g_return_val_if_fail( be != NULL, NULL );
02938     g_return_val_if_fail( table_name != NULL, NULL );
02939     g_return_val_if_fail( obj_name != NULL, NULL );
02940     g_return_val_if_fail( pObject != NULL, NULL );
02941     g_return_val_if_fail( table != NULL, NULL );
02942 
02943     // Get all col names and all values
02944     for ( table_row = table; table_row->col_name != NULL; table_row++ )
02945     {
02946         if (( table_row->flags & COL_AUTOINC ) == 0 )
02947         {
02948             GncSqlColumnTypeHandler* pHandler;
02949 
02950             // Add col names to the list
02951             pHandler = get_handler( table_row );
02952             g_assert( pHandler != NULL );
02953             pHandler->add_colname_to_list_fn( table_row, &colnames );
02954         }
02955     }
02956     g_assert( colnames != NULL );
02957     values = create_gslist_from_values( be, obj_name, pObject, table );
02958 
02959     // Create the SQL statement
02960     sqlbuf = g_strdup_printf( "UPDATE %s SET ", table_name );
02961     sql = g_string_new( sqlbuf );
02962     g_free( sqlbuf );
02963 
02964     firstCol = TRUE;
02965     for ( colname = colnames->next, value = values->next;
02966             colname != NULL && value != NULL;
02967             colname = colname->next, value = value->next )
02968     {
02969         gchar* value_str;
02970         if ( !firstCol )
02971         {
02972             (void)g_string_append( sql, "," );
02973         }
02974         (void)g_string_append( sql, (gchar*)colname->data );
02975         (void)g_string_append( sql, "=" );
02976         value_str = gnc_sql_get_sql_value( be->conn, (GValue*)(value->data) );
02977         (void)g_string_append( sql, value_str );
02978         g_free( value_str );
02979         firstCol = FALSE;
02980     }
02981     for ( colname = colnames; colname != NULL; colname = colname->next )
02982     {
02983         g_free( colname->data );
02984     }
02985     g_list_free( colnames );
02986     if ( value != NULL || colname != NULL )
02987     {
02988         PERR( "Mismatch in number of column names and values" );
02989     }
02990 
02991     stmt = gnc_sql_connection_create_statement_from_sql( be->conn, sql->str );
02992     gnc_sql_statement_add_where_cond( stmt, obj_name, pObject, &table[0], (GValue*)(values->data) );
02993     free_gvalue_list( values );
02994     (void)g_string_free( sql, TRUE );
02995 
02996     return stmt;
02997 }
02998 
02999 /*@ null @*/ static GncSqlStatement*
03000 build_delete_statement( GncSqlBackend* be,
03001                         const gchar* table_name,
03002                         QofIdTypeConst obj_name, gpointer pObject,
03003                         const GncSqlColumnTableEntry* table )
03004 {
03005     GncSqlStatement* stmt;
03006     GncSqlColumnTypeHandler* pHandler;
03007     GSList* list = NULL;
03008     gchar* sqlbuf;
03009 
03010     g_return_val_if_fail( be != NULL, NULL );
03011     g_return_val_if_fail( table_name != NULL, NULL );
03012     g_return_val_if_fail( obj_name != NULL, NULL );
03013     g_return_val_if_fail( pObject != NULL, NULL );
03014     g_return_val_if_fail( table != NULL, NULL );
03015 
03016     sqlbuf = g_strdup_printf( "DELETE FROM %s ", table_name );
03017     stmt = gnc_sql_connection_create_statement_from_sql( be->conn, sqlbuf );
03018     g_free( sqlbuf );
03019 
03020     /* WHERE */
03021     pHandler = get_handler( table );
03022     g_assert( pHandler != NULL );
03023     pHandler->add_gvalue_to_slist_fn( be, obj_name, pObject, table, &list );
03024     g_assert( list != NULL );
03025     gnc_sql_statement_add_where_cond( stmt, obj_name, pObject, &table[0], (GValue*)(list->data) );
03026     free_gvalue_list( list );
03027 
03028     return stmt;
03029 }
03030 
03031 /* ================================================================= */
03032 gboolean
03033 gnc_sql_commit_standard_item( GncSqlBackend* be, QofInstance* inst, const gchar* tableName,
03034                               QofIdTypeConst obj_name, const GncSqlColumnTableEntry* col_table )
03035 {
03036     const GncGUID* guid;
03037     gboolean is_infant;
03038     gint op;
03039     gboolean is_ok;
03040 
03041     is_infant = qof_instance_get_infant( inst );
03042     if ( qof_instance_get_destroying( inst ) )
03043     {
03044         op = OP_DB_DELETE;
03045     }
03046     else if ( be->is_pristine_db || is_infant )
03047     {
03048         op = OP_DB_INSERT;
03049     }
03050     else
03051     {
03052         op = OP_DB_UPDATE;
03053     }
03054     is_ok = gnc_sql_do_db_operation( be, op, tableName, obj_name, inst, col_table );
03055 
03056     if ( is_ok )
03057     {
03058         // Now, commit any slots
03059         guid = qof_instance_get_guid( inst );
03060         if ( !qof_instance_get_destroying(inst) )
03061         {
03062             is_ok = gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) );
03063         }
03064         else
03065         {
03066             is_ok = gnc_sql_slots_delete( be, guid );
03067         }
03068     }
03069 
03070     return is_ok;
03071 }
03072 
03073 /* ================================================================= */
03074 
03075 static gboolean
03076 do_create_table( const GncSqlBackend* be, const gchar* table_name,
03077                  const GncSqlColumnTableEntry* col_table )
03078 {
03079     GList* col_info_list = NULL;
03080     gboolean ok = FALSE;
03081 
03082     g_return_val_if_fail( be != NULL, FALSE );
03083     g_return_val_if_fail( table_name != NULL, FALSE );
03084     g_return_val_if_fail( col_table != NULL, FALSE );
03085 
03086     for ( ; col_table->col_name != NULL; col_table++ )
03087     {
03088         GncSqlColumnTypeHandler* pHandler;
03089 
03090         pHandler = get_handler( col_table );
03091         g_assert( pHandler != NULL );
03092         pHandler->add_col_info_to_list_fn( be, col_table, &col_info_list );
03093     }
03094     g_assert( col_info_list != NULL );
03095     ok = gnc_sql_connection_create_table( be->conn, table_name, col_info_list );
03096     return ok;
03097 }
03098 
03099 gboolean
03100 gnc_sql_create_table( GncSqlBackend* be, const gchar* table_name,
03101                       gint table_version, const GncSqlColumnTableEntry* col_table )
03102 {
03103     gboolean ok;
03104 
03105     g_return_val_if_fail( be != NULL, FALSE );
03106     g_return_val_if_fail( table_name != NULL, FALSE );
03107     g_return_val_if_fail( col_table != NULL, FALSE );
03108 
03109     DEBUG( "Creating %s table\n", table_name );
03110 
03111     ok = do_create_table( be, table_name, col_table );
03112     if ( ok )
03113     {
03114         ok = gnc_sql_set_table_version( be, table_name, table_version );
03115     }
03116     return ok;
03117 }
03118 
03119 gboolean
03120 gnc_sql_create_temp_table( const GncSqlBackend* be, const gchar* table_name,
03121                            const GncSqlColumnTableEntry* col_table )
03122 {
03123     g_return_val_if_fail( be != NULL, FALSE );
03124     g_return_val_if_fail( table_name != NULL, FALSE );
03125     g_return_val_if_fail( col_table != NULL, FALSE );
03126 
03127     return do_create_table( be, table_name, col_table );
03128 }
03129 
03130 gboolean
03131 gnc_sql_create_index( const GncSqlBackend* be, const gchar* index_name,
03132                       const gchar* table_name,
03133                       const GncSqlColumnTableEntry* col_table )
03134 {
03135     gboolean ok;
03136 
03137     g_return_val_if_fail( be != NULL, FALSE );
03138     g_return_val_if_fail( index_name != NULL, FALSE );
03139     g_return_val_if_fail( table_name != NULL, FALSE );
03140     g_return_val_if_fail( col_table != NULL, FALSE );
03141 
03142     ok = gnc_sql_connection_create_index( be->conn, index_name, table_name,
03143                                           col_table );
03144     return ok;
03145 }
03146 
03147 gint
03148 gnc_sql_get_table_version( const GncSqlBackend* be, const gchar* table_name )
03149 {
03150     g_return_val_if_fail( be != NULL, 0 );
03151     g_return_val_if_fail( table_name != NULL, 0 );
03152 
03153     /* If the db is pristine because it's being saved, the table does not exist. */
03154     if ( be->is_pristine_db )
03155     {
03156         return 0;
03157     }
03158 
03159     return GPOINTER_TO_INT(g_hash_table_lookup( be->versions, table_name ));
03160 }
03161 
03162 /* Create a temporary table, copy the data from the old table, delete the
03163    old table, then rename the new one. */
03164 void
03165 gnc_sql_upgrade_table( GncSqlBackend* be, const gchar* table_name,
03166                        const GncSqlColumnTableEntry* col_table )
03167 {
03168     gchar* sql;
03169     gchar* temp_table_name;
03170 
03171     g_return_if_fail( be != NULL );
03172     g_return_if_fail( table_name != NULL );
03173     g_return_if_fail( col_table != NULL );
03174 
03175     DEBUG( "Upgrading %s table\n", table_name );
03176 
03177     temp_table_name = g_strdup_printf( "%s_new", table_name );
03178     (void)gnc_sql_create_temp_table( be, temp_table_name, col_table );
03179     sql = g_strdup_printf( "INSERT INTO %s SELECT * FROM %s",
03180                            temp_table_name, table_name );
03181     (void)gnc_sql_execute_nonselect_sql( be, sql );
03182     g_free( sql );
03183 
03184     sql = g_strdup_printf( "DROP TABLE %s", table_name );
03185     (void)gnc_sql_execute_nonselect_sql( be, sql );
03186     g_free( sql );
03187 
03188     sql = g_strdup_printf( "ALTER TABLE %s RENAME TO %s", temp_table_name, table_name );
03189     (void)gnc_sql_execute_nonselect_sql( be, sql );
03190     g_free( sql );
03191     g_free( temp_table_name );
03192 }
03193 
03194 /* Adds one or more columns to an existing table. */
03195 gboolean gnc_sql_add_columns_to_table( GncSqlBackend* be, const gchar* table_name,
03196                                        const GncSqlColumnTableEntry* new_col_table )
03197 {
03198     GList* col_info_list = NULL;
03199     gboolean ok = FALSE;
03200 
03201     g_return_val_if_fail( be != NULL, FALSE );
03202     g_return_val_if_fail( table_name != NULL, FALSE );
03203     g_return_val_if_fail( new_col_table != NULL, FALSE );
03204 
03205     for ( ; new_col_table->col_name != NULL; new_col_table++ )
03206     {
03207         GncSqlColumnTypeHandler* pHandler;
03208 
03209         pHandler = get_handler( new_col_table );
03210         g_assert( pHandler != NULL );
03211         pHandler->add_col_info_to_list_fn( be, new_col_table, &col_info_list );
03212     }
03213     g_assert( col_info_list != NULL );
03214     ok = gnc_sql_connection_add_columns_to_table( be->conn, table_name, col_info_list );
03215     return ok;
03216 }
03217 
03218 /* ================================================================= */
03219 #define VERSION_TABLE_NAME "versions"
03220 #define MAX_TABLE_NAME_LEN 50
03221 #define TABLE_COL_NAME "table_name"
03222 #define VERSION_COL_NAME "table_version"
03223 
03224 static GncSqlColumnTableEntry version_table[] =
03225 {
03226     /*@ -full_init_block @*/
03227     { TABLE_COL_NAME,   CT_STRING, MAX_TABLE_NAME_LEN, COL_PKEY | COL_NNUL },
03228     { VERSION_COL_NAME, CT_INT,    0,                  COL_NNUL },
03229     { NULL }
03230     /*@ +full_init_block @*/
03231 };
03232 
03239 void
03240 gnc_sql_init_version_info( GncSqlBackend* be )
03241 {
03242     g_return_if_fail( be != NULL );
03243 
03244     if ( be->versions != NULL )
03245     {
03246         g_hash_table_destroy( be->versions );
03247     }
03248     be->versions = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, NULL );
03249 
03250     if ( gnc_sql_connection_does_table_exist( be->conn, VERSION_TABLE_NAME ) )
03251     {
03252         GncSqlResult* result;
03253         gchar* sql;
03254 
03255         sql = g_strdup_printf( "SELECT * FROM %s", VERSION_TABLE_NAME );
03256         result = gnc_sql_execute_select_sql( be, sql );
03257         g_free( sql );
03258         if ( result != NULL )
03259         {
03260             const GValue* name;
03261             const GValue* version;
03262             GncSqlRow* row;
03263 
03264             row = gnc_sql_result_get_first_row( result );
03265             while ( row != NULL )
03266             {
03267                 name = gnc_sql_row_get_value_at_col_name( row, TABLE_COL_NAME );
03268                 version = gnc_sql_row_get_value_at_col_name( row, VERSION_COL_NAME );
03269                 g_hash_table_insert( be->versions,
03270                                      g_strdup( g_value_get_string( name ) ),
03271                                      GINT_TO_POINTER((gint)g_value_get_int64( version )) );
03272                 row = gnc_sql_result_get_next_row( result );
03273             }
03274             gnc_sql_result_dispose( result );
03275         }
03276     }
03277     else
03278     {
03279         gboolean ok;
03280 
03281         ok = do_create_table( be, VERSION_TABLE_NAME, version_table );
03282     }
03283 }
03284 
03292 static gboolean
03293 reset_version_info( GncSqlBackend* be )
03294 {
03295     gboolean ok;
03296 
03297     g_return_val_if_fail( be != NULL, FALSE );
03298 
03299     ok = do_create_table( be, VERSION_TABLE_NAME, version_table );
03300     if ( be->versions == NULL )
03301     {
03302         be->versions = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, NULL );
03303     }
03304     else
03305     {
03306         g_hash_table_remove_all( be->versions );
03307     }
03308 
03309     return ok;
03310 }
03311 
03317 void
03318 gnc_sql_finalize_version_info( GncSqlBackend* be )
03319 {
03320     g_return_if_fail( be != NULL );
03321 
03322     if ( be->versions != NULL )
03323     {
03324         g_hash_table_destroy( be->versions );
03325         be->versions = NULL;
03326     }
03327 }
03328 
03338 gboolean
03339 gnc_sql_set_table_version( GncSqlBackend* be, const gchar* table_name, gint version )
03340 {
03341     gchar* sql;
03342     gint cur_version;
03343     gint status;
03344 
03345     g_return_val_if_fail( be != NULL, FALSE );
03346     g_return_val_if_fail( table_name != NULL, FALSE );
03347     g_return_val_if_fail( version > 0, FALSE );
03348 
03349     cur_version = gnc_sql_get_table_version( be, table_name );
03350     if ( cur_version != version )
03351     {
03352         if ( cur_version == 0 )
03353         {
03354             sql = g_strdup_printf( "INSERT INTO %s VALUES('%s',%d)", VERSION_TABLE_NAME,
03355                                    table_name, version );
03356         }
03357         else
03358         {
03359             sql = g_strdup_printf( "UPDATE %s SET %s=%d WHERE %s='%s'", VERSION_TABLE_NAME,
03360                                    VERSION_COL_NAME, version,
03361                                    TABLE_COL_NAME, table_name );
03362         }
03363         status = gnc_sql_execute_nonselect_sql( be, sql );
03364         if ( status == -1 )
03365         {
03366             PERR( "SQL error: %s\n", sql );
03367             qof_backend_set_error( &be->be, ERR_BACKEND_SERVER_ERR );
03368         }
03369         g_free( sql );
03370     }
03371 
03372     g_hash_table_insert( be->versions, g_strdup( table_name ), GINT_TO_POINTER(version) );
03373 
03374     return TRUE;
03375 }
03376 
03377 /* ========================== END OF FILE ===================== */
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines