mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
3395 lines
96 KiB
C
3395 lines
96 KiB
C
/********************************************************************
|
|
* gnc-backend-sql.c: load and save data to SQL *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU General Public License as *
|
|
* published by the Free Software Foundation; either version 2 of *
|
|
* the License, or (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License*
|
|
* along with this program; if not, contact: *
|
|
* *
|
|
* Free Software Foundation Voice: +1-617-542-5942 *
|
|
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
|
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
|
\********************************************************************/
|
|
/** @file gnc-backend-sql.c
|
|
* @brief load and save data to SQL
|
|
* @author Copyright (c) 2006-2008 Phil Longstaff <plongstaff@rogers.com>
|
|
*
|
|
* This file implements the top-level QofBackend API for saving/
|
|
* restoring data to/from an SQL db
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include "config.h"
|
|
|
|
#include <errno.h>
|
|
#include <glib.h>
|
|
#include <glib/gi18n.h>
|
|
#include <glib/gstdio.h>
|
|
|
|
#include <qof.h>
|
|
#include <qofquery-p.h>
|
|
#include <qofquerycore-p.h>
|
|
#include <Account.h>
|
|
#include <TransLog.h>
|
|
#include <gnc-engine.h>
|
|
#include <SX-book.h>
|
|
#include <Recurrence.h>
|
|
#include <gncBillTerm.h>
|
|
#include <gncTaxTable.h>
|
|
#include <gncInvoice.h>
|
|
|
|
#include "gnc-backend-sql.h"
|
|
|
|
#include "gnc-account-sql.h"
|
|
#include "gnc-book-sql.h"
|
|
#include "gnc-budget-sql.h"
|
|
#include "gnc-commodity-sql.h"
|
|
#include "gnc-lots-sql.h"
|
|
#include "gnc-price-sql.h"
|
|
#include "gnc-pricedb.h"
|
|
#include "gnc-recurrence-sql.h"
|
|
#include "gnc-schedxaction-sql.h"
|
|
#include "gnc-slots-sql.h"
|
|
#include "gnc-transaction-sql.h"
|
|
|
|
#include "gnc-address-sql.h"
|
|
#include "gnc-bill-term-sql.h"
|
|
#include "gnc-customer-sql.h"
|
|
#include "gnc-employee-sql.h"
|
|
#include "gnc-entry-sql.h"
|
|
#include "gnc-invoice-sql.h"
|
|
#include "gnc-job-sql.h"
|
|
#include "gnc-order-sql.h"
|
|
#include "gnc-owner-sql.h"
|
|
#include "gnc-tax-table-sql.h"
|
|
#include "gnc-vendor-sql.h"
|
|
|
|
#include "gnc-prefs.h"
|
|
|
|
#if defined( S_SPLINT_S )
|
|
#include "splint-defs.h"
|
|
#endif
|
|
|
|
static void gnc_sql_init_object_handlers( void );
|
|
static void update_progress( GncSqlBackend* be );
|
|
static void finish_progress( GncSqlBackend* be );
|
|
static void register_standard_col_type_handlers( void );
|
|
static gboolean reset_version_info( GncSqlBackend* be );
|
|
/*@ null @*/
|
|
static GncSqlStatement* build_insert_statement( GncSqlBackend* be,
|
|
const gchar* table_name,
|
|
QofIdTypeConst obj_name, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table );
|
|
/*@ null @*/
|
|
static GncSqlStatement* build_update_statement( GncSqlBackend* be,
|
|
const gchar* table_name,
|
|
QofIdTypeConst obj_name, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table );
|
|
/*@ null @*/
|
|
static GncSqlStatement* build_delete_statement( GncSqlBackend* be,
|
|
const gchar* table_name,
|
|
QofIdTypeConst obj_name, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table );
|
|
|
|
static GList *post_load_commodities = NULL;
|
|
|
|
#define TRANSACTION_NAME "trans"
|
|
|
|
typedef struct
|
|
{
|
|
/*@ dependent @*/ QofIdType searchObj;
|
|
/*@ dependent @*/
|
|
gpointer pCompiledQuery;
|
|
} gnc_sql_query_info;
|
|
|
|
/* callback structure */
|
|
typedef struct
|
|
{
|
|
gboolean is_known;
|
|
gboolean is_ok;
|
|
/*@ dependent @*/
|
|
GncSqlBackend* be;
|
|
/*@ dependent @*/
|
|
QofInstance* inst;
|
|
/*@ dependent @*/
|
|
QofQuery* pQuery;
|
|
/*@ dependent @*/
|
|
gpointer pCompiledQuery;
|
|
/*@ owned @*/
|
|
gnc_sql_query_info* pQueryInfo;
|
|
} sql_backend;
|
|
|
|
static QofLogModule log_module = G_LOG_DOMAIN;
|
|
|
|
#define SQLITE_PROVIDER_NAME "SQLite"
|
|
|
|
/* ================================================================= */
|
|
|
|
void
|
|
gnc_sql_init( /*@ unused @*/ GncSqlBackend* be )
|
|
{
|
|
static gboolean initialized = FALSE;
|
|
|
|
if ( !initialized )
|
|
{
|
|
register_standard_col_type_handlers();
|
|
gnc_sql_init_object_handlers();
|
|
initialized = TRUE;
|
|
}
|
|
}
|
|
|
|
/* ================================================================= */
|
|
|
|
static void
|
|
create_tables_cb( const gchar* type, gpointer data_p, gpointer be_p )
|
|
{
|
|
GncSqlObjectBackend* pData = data_p;
|
|
GncSqlBackend* be = be_p;
|
|
|
|
g_return_if_fail( type != NULL && data_p != NULL && be_p != NULL );
|
|
g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION );
|
|
|
|
if ( pData->create_tables != NULL )
|
|
{
|
|
update_progress( be );
|
|
(pData->create_tables)( be );
|
|
}
|
|
}
|
|
|
|
/* ================================================================= */
|
|
|
|
/* Main object load order */
|
|
static const gchar* fixed_load_order[] =
|
|
{ GNC_ID_BOOK, GNC_ID_COMMODITY, GNC_ID_ACCOUNT, GNC_ID_LOT, NULL };
|
|
|
|
/* Load order for objects from other modules */
|
|
static const gchar** other_load_order = NULL;
|
|
|
|
void
|
|
gnc_sql_set_load_order( const gchar** load_order )
|
|
{
|
|
other_load_order = load_order;
|
|
}
|
|
|
|
static void
|
|
initial_load_cb( const gchar* type, gpointer data_p, gpointer be_p )
|
|
{
|
|
GncSqlObjectBackend* pData = data_p;
|
|
GncSqlBackend* be = be_p;
|
|
gint i;
|
|
|
|
g_return_if_fail( type != NULL && data_p != NULL && be_p != NULL );
|
|
g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION );
|
|
|
|
// Don't need to load anything if it has already been loaded with the fixed order
|
|
for ( i = 0; fixed_load_order[i] != NULL; i++ )
|
|
{
|
|
update_progress( be );
|
|
if ( g_ascii_strcasecmp( type, fixed_load_order[i] ) == 0 ) return;
|
|
}
|
|
if ( other_load_order != NULL )
|
|
{
|
|
for ( i = 0; other_load_order[i] != NULL; i++ )
|
|
{
|
|
update_progress( be );
|
|
if ( g_ascii_strcasecmp( type, other_load_order[i] ) == 0 ) return;
|
|
}
|
|
}
|
|
|
|
if ( pData->initial_load != NULL )
|
|
{
|
|
(pData->initial_load)( be );
|
|
}
|
|
}
|
|
|
|
void
|
|
gnc_sql_push_commodity_for_postload_processing (GncSqlBackend *be,
|
|
gpointer *comm)
|
|
{
|
|
post_load_commodities = g_list_prepend(post_load_commodities, comm);
|
|
}
|
|
|
|
static void
|
|
commit_commodity (gpointer data)
|
|
{
|
|
gnc_commodity *comm = GNC_COMMODITY (data);
|
|
gnc_sql_commit_commodity (comm);
|
|
}
|
|
|
|
void
|
|
gnc_sql_load( GncSqlBackend* be, /*@ dependent @*/ QofBook *book, QofBackendLoadType loadType )
|
|
{
|
|
GncSqlObjectBackend* pData;
|
|
gint i;
|
|
Account* root;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( book != NULL );
|
|
|
|
ENTER( "be=%p, book=%p", be, book );
|
|
|
|
be->loading = TRUE;
|
|
|
|
if ( loadType == LOAD_TYPE_INITIAL_LOAD )
|
|
{
|
|
g_assert( be->book == NULL );
|
|
be->book = book;
|
|
|
|
/* Load any initial stuff. Some of this needs to happen in a certain order */
|
|
for ( i = 0; fixed_load_order[i] != NULL; i++ )
|
|
{
|
|
pData = qof_object_lookup_backend( fixed_load_order[i], GNC_SQL_BACKEND );
|
|
if ( pData->initial_load != NULL )
|
|
{
|
|
update_progress( be );
|
|
(pData->initial_load)( be );
|
|
}
|
|
}
|
|
if ( other_load_order != NULL )
|
|
{
|
|
for ( i = 0; other_load_order[i] != NULL; i++ )
|
|
{
|
|
pData = qof_object_lookup_backend( other_load_order[i], GNC_SQL_BACKEND );
|
|
if ( pData->initial_load != NULL )
|
|
{
|
|
update_progress( be );
|
|
(pData->initial_load)( be );
|
|
}
|
|
}
|
|
}
|
|
|
|
root = gnc_book_get_root_account( book );
|
|
gnc_account_foreach_descendant( root, (AccountCb)xaccAccountBeginEdit, NULL );
|
|
|
|
qof_object_foreach_backend( GNC_SQL_BACKEND, initial_load_cb, be );
|
|
|
|
gnc_account_foreach_descendant( root, (AccountCb)xaccAccountCommitEdit, NULL );
|
|
}
|
|
else if ( loadType == LOAD_TYPE_LOAD_ALL )
|
|
{
|
|
// Load all transactions
|
|
gnc_sql_transaction_load_all_tx( be );
|
|
}
|
|
|
|
be->loading = FALSE;
|
|
g_list_free_full (post_load_commodities, commit_commodity);
|
|
post_load_commodities = NULL;
|
|
|
|
/* Mark the sessoion as clean -- though it should never be marked
|
|
* dirty with this backend
|
|
*/
|
|
qof_book_mark_session_saved( book );
|
|
finish_progress( be );
|
|
|
|
LEAVE( "" );
|
|
}
|
|
|
|
/* ================================================================= */
|
|
|
|
static gboolean
|
|
write_account_tree( GncSqlBackend* be, Account* root )
|
|
{
|
|
GList* descendants;
|
|
/*@ dependent @*/
|
|
GList* node;
|
|
gboolean is_ok = TRUE;
|
|
|
|
g_return_val_if_fail( be != NULL, FALSE );
|
|
g_return_val_if_fail( root != NULL, FALSE );
|
|
|
|
is_ok = gnc_sql_save_account( be, QOF_INSTANCE(root) );
|
|
if ( is_ok )
|
|
{
|
|
descendants = gnc_account_get_descendants( root );
|
|
for ( node = descendants; node != NULL && is_ok; node = g_list_next(node) )
|
|
{
|
|
is_ok = gnc_sql_save_account( be, QOF_INSTANCE(GNC_ACCOUNT(node->data)) );
|
|
if ( !is_ok ) break;
|
|
}
|
|
g_list_free( descendants );
|
|
}
|
|
update_progress( be );
|
|
|
|
return is_ok;
|
|
}
|
|
|
|
static gboolean
|
|
write_accounts( GncSqlBackend* be )
|
|
{
|
|
gboolean is_ok;
|
|
|
|
g_return_val_if_fail( be != NULL, FALSE );
|
|
|
|
update_progress( be );
|
|
is_ok = write_account_tree( be, gnc_book_get_root_account( be->book ) );
|
|
if ( is_ok )
|
|
{
|
|
update_progress( be );
|
|
is_ok = write_account_tree( be, gnc_book_get_template_root( be->book ) );
|
|
}
|
|
|
|
return is_ok;
|
|
}
|
|
|
|
static int
|
|
write_tx( Transaction* tx, gpointer data )
|
|
{
|
|
write_objects_t* s = (write_objects_t*)data;
|
|
|
|
g_return_val_if_fail( tx != NULL, 0 );
|
|
g_return_val_if_fail( data != NULL, 0 );
|
|
|
|
s->is_ok = gnc_sql_save_transaction( s->be, QOF_INSTANCE(tx) );
|
|
update_progress( s->be );
|
|
|
|
if ( s->is_ok )
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
write_transactions( GncSqlBackend* be )
|
|
{
|
|
write_objects_t data;
|
|
|
|
g_return_val_if_fail( be != NULL, FALSE );
|
|
|
|
data.be = be;
|
|
data.is_ok = TRUE;
|
|
(void)xaccAccountTreeForEachTransaction(
|
|
gnc_book_get_root_account( be->book ), write_tx, &data );
|
|
update_progress( be );
|
|
return data.is_ok;
|
|
}
|
|
|
|
static gboolean
|
|
write_template_transactions( GncSqlBackend* be )
|
|
{
|
|
Account* ra;
|
|
write_objects_t data;
|
|
|
|
g_return_val_if_fail( be != NULL, FALSE );
|
|
|
|
data.is_ok = TRUE;
|
|
data.be = be;
|
|
ra = gnc_book_get_template_root( be->book );
|
|
if ( gnc_account_n_descendants( ra ) > 0 )
|
|
{
|
|
(void)xaccAccountTreeForEachTransaction( ra, write_tx, &data );
|
|
update_progress( be );
|
|
}
|
|
|
|
return data.is_ok;
|
|
}
|
|
|
|
static gboolean
|
|
write_schedXactions( GncSqlBackend* be )
|
|
{
|
|
GList* schedXactions;
|
|
SchedXaction* tmpSX;
|
|
gboolean is_ok = TRUE;
|
|
|
|
g_return_val_if_fail( be != NULL, FALSE );
|
|
|
|
schedXactions = gnc_book_get_schedxactions( be->book )->sx_list;
|
|
|
|
for ( ; schedXactions != NULL && is_ok; schedXactions = schedXactions->next )
|
|
{
|
|
tmpSX = schedXactions->data;
|
|
is_ok = gnc_sql_save_schedxaction( be, QOF_INSTANCE( tmpSX ) );
|
|
}
|
|
update_progress( be );
|
|
|
|
return is_ok;
|
|
}
|
|
|
|
static void
|
|
write_cb( const gchar* type, gpointer data_p, gpointer be_p )
|
|
{
|
|
GncSqlObjectBackend* pData = data_p;
|
|
GncSqlBackend* be = (GncSqlBackend*)be_p;
|
|
|
|
g_return_if_fail( type != NULL && data_p != NULL && be_p != NULL );
|
|
g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION );
|
|
|
|
if ( pData->write != NULL )
|
|
{
|
|
(void)(pData->write)( be );
|
|
update_progress( be );
|
|
}
|
|
}
|
|
|
|
static void
|
|
update_progress( GncSqlBackend* be )
|
|
{
|
|
if ( be->be.percentage != NULL )
|
|
(be->be.percentage)( NULL, 101.0 );
|
|
}
|
|
|
|
static void
|
|
finish_progress( GncSqlBackend* be )
|
|
{
|
|
if ( be->be.percentage != NULL )
|
|
(be->be.percentage)( NULL, -1.0 );
|
|
}
|
|
|
|
void
|
|
gnc_sql_sync_all( GncSqlBackend* be, /*@ dependent @*/ QofBook *book )
|
|
{
|
|
gboolean is_ok;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( book != NULL );
|
|
|
|
ENTER( "book=%p, be->book=%p", book, be->book );
|
|
update_progress( be );
|
|
(void)reset_version_info( be );
|
|
|
|
/* Create new tables */
|
|
be->is_pristine_db = TRUE;
|
|
qof_object_foreach_backend( GNC_SQL_BACKEND, create_tables_cb, be );
|
|
|
|
/* Save all contents */
|
|
be->book = book;
|
|
be->obj_total = 0;
|
|
be->obj_total += 1 + gnc_account_n_descendants( gnc_book_get_root_account( book ) );
|
|
be->obj_total += gnc_book_count_transactions( book );
|
|
be->operations_done = 0;
|
|
|
|
is_ok = gnc_sql_connection_begin_transaction( be->conn );
|
|
|
|
// FIXME: should write the set of commodities that are used
|
|
//write_commodities( be, book );
|
|
if ( is_ok )
|
|
{
|
|
is_ok = gnc_sql_save_book( be, QOF_INSTANCE(book) );
|
|
}
|
|
if ( is_ok )
|
|
{
|
|
is_ok = write_accounts( be );
|
|
}
|
|
if ( is_ok )
|
|
{
|
|
is_ok = write_transactions( be );
|
|
}
|
|
if ( is_ok )
|
|
{
|
|
is_ok = write_template_transactions( be );
|
|
}
|
|
if ( is_ok )
|
|
{
|
|
is_ok = write_schedXactions( be );
|
|
}
|
|
if ( is_ok )
|
|
{
|
|
qof_object_foreach_backend( GNC_SQL_BACKEND, write_cb, be );
|
|
}
|
|
if ( is_ok )
|
|
{
|
|
is_ok = gnc_sql_connection_commit_transaction( be->conn );
|
|
}
|
|
if ( is_ok )
|
|
{
|
|
be->is_pristine_db = FALSE;
|
|
|
|
/* Mark the session as clean -- though it shouldn't ever get
|
|
* marked dirty with this backend
|
|
*/
|
|
qof_book_mark_session_saved( book );
|
|
}
|
|
else
|
|
{
|
|
qof_backend_set_error( (QofBackend*)be, ERR_BACKEND_SERVER_ERR );
|
|
is_ok = gnc_sql_connection_rollback_transaction( be->conn );
|
|
}
|
|
finish_progress( be );
|
|
LEAVE( "book=%p", book );
|
|
}
|
|
|
|
/* ================================================================= */
|
|
/* Routines to deal with the creation of multiple books. */
|
|
|
|
void
|
|
gnc_sql_begin_edit( GncSqlBackend *be, QofInstance *inst )
|
|
{
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( inst != NULL );
|
|
|
|
ENTER( " " );
|
|
LEAVE( "" );
|
|
}
|
|
|
|
void
|
|
gnc_sql_rollback_edit( GncSqlBackend *be, QofInstance *inst )
|
|
{
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( inst != NULL );
|
|
|
|
ENTER( " " );
|
|
LEAVE( "" );
|
|
}
|
|
|
|
static void
|
|
commit_cb( const gchar* type, gpointer data_p, gpointer be_data_p )
|
|
{
|
|
GncSqlObjectBackend* pData = data_p;
|
|
sql_backend* be_data = be_data_p;
|
|
|
|
g_return_if_fail( type != NULL && pData != NULL && be_data != NULL );
|
|
g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION );
|
|
|
|
/* If this has already been handled, or is not the correct handler, return */
|
|
if ( strcmp( pData->type_name, be_data->inst->e_type ) != 0 ) return;
|
|
if ( be_data->is_known ) return;
|
|
|
|
if ( pData->commit != NULL )
|
|
{
|
|
be_data->is_ok = (pData->commit)( be_data->be, be_data->inst );
|
|
be_data->is_known = TRUE;
|
|
}
|
|
}
|
|
|
|
/* Commit_edit handler - find the correct backend handler for this object
|
|
* type and call its commit handler
|
|
*/
|
|
void
|
|
gnc_sql_commit_edit( GncSqlBackend *be, QofInstance *inst )
|
|
{
|
|
sql_backend be_data;
|
|
gboolean is_dirty;
|
|
gboolean is_destroying;
|
|
gboolean is_infant;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( inst != NULL );
|
|
|
|
if ( qof_book_is_readonly( be->book ) )
|
|
{
|
|
qof_backend_set_error( (QofBackend*)be, ERR_BACKEND_READONLY );
|
|
(void)gnc_sql_connection_rollback_transaction( be->conn );
|
|
return;
|
|
}
|
|
/* During initial load where objects are being created, don't commit
|
|
anything, but do mark the object as clean. */
|
|
if ( be->loading )
|
|
{
|
|
qof_instance_mark_clean( inst );
|
|
return;
|
|
}
|
|
|
|
// The engine has a PriceDB object but it isn't in the database
|
|
if ( strcmp( inst->e_type, "PriceDB" ) == 0 )
|
|
{
|
|
qof_instance_mark_clean( inst );
|
|
qof_book_mark_session_saved( be->book );
|
|
return;
|
|
}
|
|
|
|
ENTER( " " );
|
|
|
|
is_dirty = qof_instance_get_dirty_flag( inst );
|
|
is_destroying = qof_instance_get_destroying( inst );
|
|
is_infant = qof_instance_get_infant( inst );
|
|
|
|
DEBUG( "%s dirty = %d, do_free = %d, infant = %d\n",
|
|
(inst->e_type ? inst->e_type : "(null)"),
|
|
is_dirty, is_destroying, is_infant );
|
|
|
|
if ( !is_dirty && !is_destroying )
|
|
{
|
|
LEAVE( "!dirty OR !destroying" );
|
|
return;
|
|
}
|
|
|
|
if ( !gnc_sql_connection_begin_transaction( be->conn ) )
|
|
{
|
|
PERR( "gnc_sql_commit_edit(): begin_transaction failed\n" );
|
|
LEAVE( "Rolled back - database transaction begin error" );
|
|
return;
|
|
}
|
|
|
|
be_data.is_known = FALSE;
|
|
be_data.be = be;
|
|
be_data.inst = inst;
|
|
be_data.is_ok = TRUE;
|
|
|
|
qof_object_foreach_backend( GNC_SQL_BACKEND, commit_cb, &be_data );
|
|
|
|
if ( !be_data.is_known )
|
|
{
|
|
PERR( "gnc_sql_commit_edit(): Unknown object type '%s'\n", inst->e_type );
|
|
(void)gnc_sql_connection_rollback_transaction( be->conn );
|
|
|
|
// Don't let unknown items still mark the book as being dirty
|
|
qof_book_mark_session_saved( be->book );
|
|
qof_instance_mark_clean(inst);
|
|
LEAVE( "Rolled back - unknown object type" );
|
|
return;
|
|
}
|
|
if ( !be_data.is_ok )
|
|
{
|
|
// Error - roll it back
|
|
(void)gnc_sql_connection_rollback_transaction( be->conn );
|
|
|
|
// This *should* leave things marked dirty
|
|
LEAVE( "Rolled back - database error" );
|
|
return;
|
|
}
|
|
|
|
(void)gnc_sql_connection_commit_transaction( be->conn );
|
|
|
|
qof_book_mark_session_saved( be->book );
|
|
qof_instance_mark_clean(inst);
|
|
|
|
LEAVE( "" );
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/* Query processing */
|
|
static void
|
|
handle_and_term( QofQueryTerm* pTerm, GString* sql )
|
|
{
|
|
GSList* pParamPath;
|
|
QofQueryPredData* pPredData;
|
|
gboolean isInverted;
|
|
GSList* name;
|
|
gchar val[G_ASCII_DTOSTR_BUF_SIZE];
|
|
|
|
g_return_if_fail( pTerm != NULL );
|
|
g_return_if_fail( sql != NULL );
|
|
|
|
pParamPath = qof_query_term_get_param_path( pTerm );
|
|
pPredData = qof_query_term_get_pred_data( pTerm );
|
|
isInverted = qof_query_term_is_inverted( pTerm );
|
|
|
|
if ( strcmp( pPredData->type_name, QOF_TYPE_GUID ) == 0 )
|
|
{
|
|
query_guid_t guid_data = (query_guid_t)pPredData;
|
|
GList* guid_entry;
|
|
|
|
for ( name = pParamPath; name != NULL; name = name->next )
|
|
{
|
|
if ( name != pParamPath ) g_string_append( sql, "." );
|
|
g_string_append( sql, name->data );
|
|
}
|
|
|
|
if ( guid_data->options == QOF_GUID_MATCH_ANY )
|
|
{
|
|
if ( isInverted ) g_string_append( sql, " NOT " );
|
|
g_string_append( sql, " IN (" );
|
|
}
|
|
for ( guid_entry = guid_data->guids; guid_entry != NULL; guid_entry = guid_entry->next )
|
|
{
|
|
if ( guid_entry != guid_data->guids ) g_string_append( sql, "." );
|
|
(void)guid_to_string_buff( guid_entry->data, val );
|
|
g_string_append( sql, "'" );
|
|
g_string_append( sql, val );
|
|
g_string_append( sql, "'" );
|
|
}
|
|
if ( guid_data->options == QOF_GUID_MATCH_ANY )
|
|
{
|
|
g_string_append( sql, ")" );
|
|
}
|
|
}
|
|
|
|
g_string_append( sql, "(" );
|
|
if ( isInverted )
|
|
{
|
|
g_string_append( sql, "!" );
|
|
}
|
|
|
|
for ( name = pParamPath; name != NULL; name = name->next )
|
|
{
|
|
if ( name != pParamPath ) g_string_append( sql, "." );
|
|
g_string_append( sql, name->data );
|
|
}
|
|
|
|
if ( pPredData->how == QOF_COMPARE_LT )
|
|
{
|
|
g_string_append( sql, "<" );
|
|
}
|
|
else if ( pPredData->how == QOF_COMPARE_LTE )
|
|
{
|
|
g_string_append( sql, "<=" );
|
|
}
|
|
else if ( pPredData->how == QOF_COMPARE_EQUAL )
|
|
{
|
|
g_string_append( sql, "=" );
|
|
}
|
|
else if ( pPredData->how == QOF_COMPARE_GT )
|
|
{
|
|
g_string_append( sql, ">" );
|
|
}
|
|
else if ( pPredData->how == QOF_COMPARE_GTE )
|
|
{
|
|
g_string_append( sql, ">=" );
|
|
}
|
|
else if ( pPredData->how == QOF_COMPARE_NEQ )
|
|
{
|
|
g_string_append( sql, "~=" );
|
|
}
|
|
else
|
|
{
|
|
g_string_append( sql, "??" );
|
|
}
|
|
|
|
if ( strcmp( pPredData->type_name, "string" ) == 0 )
|
|
{
|
|
query_string_t pData = (query_string_t)pPredData;
|
|
g_string_append( sql, "'" );
|
|
g_string_append( sql, pData->matchstring );
|
|
g_string_append( sql, "'" );
|
|
}
|
|
else if ( strcmp( pPredData->type_name, "date" ) == 0 )
|
|
{
|
|
query_date_t pData = (query_date_t)pPredData;
|
|
|
|
(void)gnc_timespec_to_iso8601_buff( pData->date, val );
|
|
g_string_append( sql, "'" );
|
|
//g_string_append( sql, val, 4+1+2+1+2 );
|
|
g_string_append( sql, "'" );
|
|
}
|
|
else if ( strcmp( pPredData->type_name, "numeric" ) == 0 )
|
|
{
|
|
/* query_numeric_t pData = (query_numeric_t)pPredData; */
|
|
|
|
g_string_append( sql, "numeric" );
|
|
}
|
|
else if ( strcmp( pPredData->type_name, QOF_TYPE_GUID ) == 0 )
|
|
{
|
|
}
|
|
else if ( strcmp( pPredData->type_name, "gint32" ) == 0 )
|
|
{
|
|
query_int32_t pData = (query_int32_t)pPredData;
|
|
|
|
sprintf( val, "%d", pData->val );
|
|
g_string_append( sql, val );
|
|
}
|
|
else if ( strcmp( pPredData->type_name, "gint64" ) == 0 )
|
|
{
|
|
query_int64_t pData = (query_int64_t)pPredData;
|
|
|
|
sprintf( val, "%" G_GINT64_FORMAT, pData->val );
|
|
g_string_append( sql, val );
|
|
}
|
|
else if ( strcmp( pPredData->type_name, "double" ) == 0 )
|
|
{
|
|
query_double_t pData = (query_double_t)pPredData;
|
|
|
|
g_ascii_dtostr( val, sizeof(val), pData->val );
|
|
g_string_append( sql, val );
|
|
}
|
|
else if ( strcmp( pPredData->type_name, "boolean" ) == 0 )
|
|
{
|
|
query_boolean_t pData = (query_boolean_t)pPredData;
|
|
|
|
sprintf( val, "%d", pData->val );
|
|
g_string_append( sql, val );
|
|
}
|
|
else
|
|
{
|
|
g_assert( FALSE );
|
|
}
|
|
|
|
g_string_append( sql, ")" );
|
|
}
|
|
|
|
static void
|
|
compile_query_cb( const gchar* type, gpointer data_p, gpointer be_data_p )
|
|
{
|
|
GncSqlObjectBackend* pData = data_p;
|
|
sql_backend* be_data = be_data_p;
|
|
|
|
g_return_if_fail( type != NULL && pData != NULL && be_data != NULL );
|
|
g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION );
|
|
|
|
// Is this the right item?
|
|
if ( strcmp( type, be_data->pQueryInfo->searchObj ) != 0 ) return;
|
|
if ( be_data->is_ok ) return;
|
|
|
|
if ( pData->compile_query != NULL )
|
|
{
|
|
be_data->pQueryInfo->pCompiledQuery = (pData->compile_query)(
|
|
be_data->be,
|
|
be_data->pQuery );
|
|
be_data->is_ok = TRUE;
|
|
}
|
|
}
|
|
|
|
gchar* gnc_sql_compile_query_to_sql( GncSqlBackend* be, QofQuery* query );
|
|
|
|
/*@ null @*/
|
|
gpointer
|
|
gnc_sql_compile_query( QofBackend* pBEnd, QofQuery* pQuery )
|
|
{
|
|
GncSqlBackend *be = (GncSqlBackend*)pBEnd;
|
|
QofIdType searchObj;
|
|
sql_backend be_data;
|
|
gnc_sql_query_info* pQueryInfo;
|
|
|
|
g_return_val_if_fail( pBEnd != NULL, NULL );
|
|
g_return_val_if_fail( pQuery != NULL, NULL );
|
|
|
|
ENTER( " " );
|
|
|
|
//gnc_sql_compile_query_to_sql( be, pQuery );
|
|
searchObj = qof_query_get_search_for( pQuery );
|
|
|
|
pQueryInfo = g_malloc( (gsize)sizeof( gnc_sql_query_info ) );
|
|
g_assert( pQueryInfo != NULL );
|
|
pQueryInfo->pCompiledQuery = NULL;
|
|
pQueryInfo->searchObj = searchObj;
|
|
|
|
// Try various objects first
|
|
be_data.is_ok = FALSE;
|
|
be_data.be = be;
|
|
be_data.pQuery = pQuery;
|
|
be_data.pQueryInfo = pQueryInfo;
|
|
|
|
qof_object_foreach_backend( GNC_SQL_BACKEND, compile_query_cb, &be_data );
|
|
if ( be_data.is_ok )
|
|
{
|
|
LEAVE( "" );
|
|
return be_data.pQueryInfo;
|
|
}
|
|
|
|
LEAVE( "" );
|
|
|
|
return pQueryInfo;
|
|
}
|
|
|
|
static const gchar*
|
|
convert_search_obj( QofIdType objType )
|
|
{
|
|
return (gchar*)objType;
|
|
}
|
|
|
|
gchar*
|
|
gnc_sql_compile_query_to_sql( GncSqlBackend* be, QofQuery* query )
|
|
{
|
|
QofIdType searchObj;
|
|
GString* sql;
|
|
|
|
g_return_val_if_fail( be != NULL, NULL );
|
|
g_return_val_if_fail( query != NULL, NULL );
|
|
|
|
searchObj = qof_query_get_search_for( query );
|
|
|
|
/* Convert search object type to table name */
|
|
sql = g_string_new( "" );
|
|
g_string_append( sql, "SELECT * FROM " );
|
|
g_string_append( sql, convert_search_obj( searchObj ) );
|
|
if ( !qof_query_has_terms( query ) )
|
|
{
|
|
g_string_append( sql, ";" );
|
|
}
|
|
else
|
|
{
|
|
GList* orterms = qof_query_get_terms( query );
|
|
GList* orTerm;
|
|
|
|
g_string_append( sql, " WHERE " );
|
|
|
|
for ( orTerm = orterms; orTerm != NULL; orTerm = orTerm->next )
|
|
{
|
|
GList* andterms = (GList*)orTerm->data;
|
|
GList* andTerm;
|
|
|
|
if ( orTerm != orterms ) g_string_append( sql, " OR " );
|
|
g_string_append( sql, "(" );
|
|
for ( andTerm = andterms; andTerm != NULL; andTerm = andTerm->next )
|
|
{
|
|
if ( andTerm != andterms ) g_string_append( sql, " AND " );
|
|
handle_and_term( (QofQueryTerm*)andTerm->data, sql );
|
|
}
|
|
g_string_append( sql, ")" );
|
|
}
|
|
}
|
|
|
|
DEBUG( "Compiled: %s\n", sql->str );
|
|
return g_string_free( sql, FALSE );
|
|
}
|
|
|
|
static void
|
|
free_query_cb( const gchar* type, gpointer data_p, gpointer be_data_p )
|
|
{
|
|
GncSqlObjectBackend* pData = data_p;
|
|
sql_backend* be_data = be_data_p;
|
|
|
|
g_return_if_fail( type != NULL && pData != NULL && be_data != NULL );
|
|
g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION );
|
|
if ( be_data->is_ok ) return;
|
|
if ( strcmp( type, be_data->pQueryInfo->searchObj ) != 0 ) return;
|
|
|
|
if ( pData->free_query != NULL )
|
|
{
|
|
(pData->free_query)( be_data->be, be_data->pCompiledQuery );
|
|
be_data->is_ok = TRUE;
|
|
}
|
|
}
|
|
|
|
void
|
|
gnc_sql_free_query( QofBackend* pBEnd, gpointer pQuery )
|
|
{
|
|
GncSqlBackend *be = (GncSqlBackend*)pBEnd;
|
|
gnc_sql_query_info* pQueryInfo = (gnc_sql_query_info*)pQuery;
|
|
sql_backend be_data;
|
|
|
|
g_return_if_fail( pBEnd != NULL );
|
|
g_return_if_fail( pQuery != NULL );
|
|
|
|
ENTER( " " );
|
|
|
|
// Try various objects first
|
|
be_data.is_ok = FALSE;
|
|
be_data.be = be;
|
|
be_data.pCompiledQuery = pQuery;
|
|
be_data.pQueryInfo = pQueryInfo;
|
|
|
|
qof_object_foreach_backend( GNC_SQL_BACKEND, free_query_cb, &be_data );
|
|
if ( be_data.is_ok )
|
|
{
|
|
LEAVE( "" );
|
|
return;
|
|
}
|
|
|
|
if ( pQueryInfo->pCompiledQuery != NULL )
|
|
{
|
|
DEBUG( "%s\n", (gchar*)pQueryInfo->pCompiledQuery );
|
|
g_free( pQueryInfo->pCompiledQuery );
|
|
}
|
|
g_free( pQueryInfo );
|
|
|
|
LEAVE( "" );
|
|
}
|
|
|
|
static void
|
|
run_query_cb( const gchar* type, gpointer data_p, gpointer be_data_p )
|
|
{
|
|
GncSqlObjectBackend* pData = data_p;
|
|
sql_backend* be_data = be_data_p;
|
|
|
|
g_return_if_fail( type != NULL && pData != NULL && be_data != NULL );
|
|
g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION );
|
|
if ( be_data->is_ok ) return;
|
|
|
|
// Is this the right item?
|
|
if ( strcmp( type, be_data->pQueryInfo->searchObj ) != 0 ) return;
|
|
|
|
if ( pData->run_query != NULL )
|
|
{
|
|
(pData->run_query)( be_data->be, be_data->pCompiledQuery );
|
|
be_data->is_ok = TRUE;
|
|
}
|
|
}
|
|
|
|
void
|
|
gnc_sql_run_query( QofBackend* pBEnd, gpointer pQuery )
|
|
{
|
|
GncSqlBackend *be = (GncSqlBackend*)pBEnd;
|
|
gnc_sql_query_info* pQueryInfo = (gnc_sql_query_info*)pQuery;
|
|
sql_backend be_data;
|
|
|
|
g_return_if_fail( pBEnd != NULL );
|
|
g_return_if_fail( pQuery != NULL );
|
|
g_return_if_fail( !be->in_query );
|
|
|
|
ENTER( " " );
|
|
|
|
be->loading = TRUE;
|
|
be->in_query = TRUE;
|
|
|
|
qof_event_suspend();
|
|
|
|
// Try various objects first
|
|
be_data.is_ok = FALSE;
|
|
be_data.be = be;
|
|
be_data.pCompiledQuery = pQueryInfo->pCompiledQuery;
|
|
be_data.pQueryInfo = pQueryInfo;
|
|
|
|
qof_object_foreach_backend( GNC_SQL_BACKEND, run_query_cb, &be_data );
|
|
be->loading = FALSE;
|
|
be->in_query = FALSE;
|
|
qof_event_resume();
|
|
// if( be_data.is_ok ) {
|
|
// LEAVE( "" );
|
|
// return;
|
|
// }
|
|
|
|
// Mark the book as clean
|
|
qof_instance_mark_clean( QOF_INSTANCE(be->book) );
|
|
|
|
// DEBUG( "%s\n", (gchar*)pQueryInfo->pCompiledQuery );
|
|
|
|
LEAVE( "" );
|
|
}
|
|
|
|
/* ================================================================= */
|
|
/* Order in which business objects need to be loaded */
|
|
static const gchar* business_fixed_load_order[] =
|
|
{ GNC_ID_BILLTERM, GNC_ID_TAXTABLE, GNC_ID_INVOICE, NULL };
|
|
|
|
static void
|
|
business_core_sql_init(void)
|
|
{
|
|
/* Initialize our pointers into the backend subsystem */
|
|
gnc_address_sql_initialize();
|
|
gnc_billterm_sql_initialize();
|
|
gnc_customer_sql_initialize();
|
|
gnc_employee_sql_initialize();
|
|
gnc_entry_sql_initialize();
|
|
gnc_invoice_sql_initialize();
|
|
gnc_job_sql_initialize();
|
|
gnc_order_sql_initialize();
|
|
gnc_owner_sql_initialize();
|
|
gnc_taxtable_sql_initialize();
|
|
gnc_vendor_sql_initialize();
|
|
|
|
gnc_sql_set_load_order( business_fixed_load_order );
|
|
}
|
|
|
|
static void
|
|
gnc_sql_init_object_handlers( void )
|
|
{
|
|
gnc_sql_init_book_handler();
|
|
gnc_sql_init_commodity_handler();
|
|
gnc_sql_init_account_handler();
|
|
gnc_sql_init_budget_handler();
|
|
gnc_sql_init_price_handler();
|
|
gnc_sql_init_transaction_handler();
|
|
gnc_sql_init_slots_handler();
|
|
gnc_sql_init_recurrence_handler();
|
|
gnc_sql_init_schedxaction_handler();
|
|
gnc_sql_init_lot_handler();
|
|
|
|
/* And the business objects */
|
|
business_core_sql_init();
|
|
}
|
|
|
|
/* ================================================================= */
|
|
|
|
gint64
|
|
gnc_sql_get_integer_value( const GValue* value )
|
|
{
|
|
g_return_val_if_fail( value != NULL, 0 );
|
|
|
|
if ( G_VALUE_HOLDS_INT(value) )
|
|
{
|
|
return (gint64)g_value_get_int( value );
|
|
}
|
|
else if ( G_VALUE_HOLDS_UINT(value) )
|
|
{
|
|
return (gint64)g_value_get_uint( value );
|
|
}
|
|
else if ( G_VALUE_HOLDS_LONG(value) )
|
|
{
|
|
return (gint64)g_value_get_long( value );
|
|
}
|
|
else if ( G_VALUE_HOLDS_ULONG(value) )
|
|
{
|
|
return (gint64)g_value_get_ulong( value );
|
|
}
|
|
else if ( G_VALUE_HOLDS_INT64(value) )
|
|
{
|
|
return g_value_get_int64( value );
|
|
}
|
|
else if ( G_VALUE_HOLDS_UINT64(value) )
|
|
{
|
|
return (gint64)g_value_get_uint64( value );
|
|
}
|
|
else if ( G_VALUE_HOLDS_STRING( value ) )
|
|
{
|
|
return g_ascii_strtoll( g_value_get_string( value ), NULL, 10 );
|
|
}
|
|
else
|
|
{
|
|
PWARN( "Unknown type: %s", G_VALUE_TYPE_NAME( value ) );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------- */
|
|
/*@ null @*/ static gpointer
|
|
get_autoinc_id()
|
|
{
|
|
// Just need a 0 to force a new autoinc value
|
|
return (gpointer)0;
|
|
}
|
|
|
|
static void
|
|
set_autoinc_id()
|
|
{
|
|
// Nowhere to put the ID
|
|
}
|
|
|
|
/*@ null @*/ QofAccessFunc
|
|
gnc_sql_get_getter( QofIdTypeConst obj_name, const GncSqlColumnTableEntry* table_row )
|
|
{
|
|
QofAccessFunc getter;
|
|
|
|
g_return_val_if_fail( obj_name != NULL, NULL );
|
|
g_return_val_if_fail( table_row != NULL, NULL );
|
|
|
|
if ( (table_row->flags & COL_AUTOINC) != 0 )
|
|
{
|
|
getter = get_autoinc_id;
|
|
}
|
|
else if ( table_row->qof_param_name != NULL )
|
|
{
|
|
getter = qof_class_get_parameter_getter( obj_name,
|
|
table_row->qof_param_name );
|
|
}
|
|
else
|
|
{
|
|
getter = table_row->getter;
|
|
}
|
|
|
|
return getter;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------- */
|
|
void
|
|
gnc_sql_add_colname_to_list( const GncSqlColumnTableEntry* table_row, GList** pList )
|
|
{
|
|
(*pList) = g_list_append( (*pList), g_strdup( table_row->col_name ) );
|
|
}
|
|
|
|
/* ----------------------------------------------------------------- */
|
|
void
|
|
gnc_sql_add_subtable_colnames_to_list( const GncSqlColumnTableEntry* table_row, const GncSqlColumnTableEntry* subtable,
|
|
GList** pList )
|
|
{
|
|
const GncSqlColumnTableEntry* subtable_row;
|
|
gchar* buf;
|
|
|
|
for ( subtable_row = subtable; subtable_row->col_name != NULL; subtable_row++ )
|
|
{
|
|
buf = g_strdup_printf( "%s_%s", table_row->col_name, subtable_row->col_name );
|
|
(*pList) = g_list_append( (*pList), buf );
|
|
}
|
|
}
|
|
|
|
static GncSqlColumnInfo*
|
|
create_column_info( const GncSqlColumnTableEntry* table_row, GncSqlBasicColumnType type,
|
|
gint size, gboolean is_unicode )
|
|
{
|
|
GncSqlColumnInfo* info;
|
|
|
|
info = g_new0( GncSqlColumnInfo, 1 );
|
|
g_assert( info != NULL );
|
|
info->name = g_strdup( table_row->col_name );
|
|
info->type = type;
|
|
info->size = size;
|
|
info->is_primary_key = ((table_row->flags & COL_PKEY) != 0) ? TRUE : FALSE;
|
|
info->null_allowed = ((table_row->flags & COL_NNUL) != 0) ? FALSE : TRUE;
|
|
info->is_unicode = is_unicode;
|
|
info->is_autoinc = ((table_row->flags & COL_AUTOINC) != 0) ? TRUE : FALSE;
|
|
|
|
return info;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------- */
|
|
static void
|
|
load_string( const GncSqlBackend* be, GncSqlRow* row,
|
|
/*@ null @*/ QofSetterFunc setter, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table_row )
|
|
{
|
|
const GValue* val;
|
|
const gchar* s;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( row != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
|
|
|
|
val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
|
|
g_return_if_fail( val != NULL );
|
|
s = g_value_get_string( val );
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_increase_editlevel (QOF_INSTANCE (pObject));
|
|
g_object_set( pObject, table_row->gobj_param_name, s, NULL );
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_decrease_editlevel (QOF_INSTANCE (pObject));
|
|
}
|
|
else
|
|
{
|
|
g_return_if_fail( setter != NULL );
|
|
(*setter)( pObject, (const gpointer)s );
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_string_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
|
|
GList** pList )
|
|
{
|
|
GncSqlColumnInfo* info;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( pList != NULL );
|
|
|
|
info = create_column_info( table_row, BCT_STRING, table_row->size, TRUE );
|
|
|
|
*pList = g_list_append( *pList, info );
|
|
}
|
|
|
|
static void
|
|
add_gvalue_string_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
|
|
const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
|
|
{
|
|
QofAccessFunc getter;
|
|
gchar* s = NULL;
|
|
GValue* value;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( obj_name != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( pList != NULL );
|
|
|
|
value = g_new0( GValue, 1 );
|
|
g_assert( value != NULL );
|
|
memset( value, 0, sizeof( GValue ) );
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
g_object_get( pObject, table_row->gobj_param_name, &s, NULL );
|
|
}
|
|
else
|
|
{
|
|
getter = gnc_sql_get_getter( obj_name, table_row );
|
|
if ( getter != NULL )
|
|
{
|
|
s = (gchar*)(*getter)( pObject, NULL );
|
|
if ( s != NULL )
|
|
{
|
|
s = g_strdup( s );
|
|
}
|
|
}
|
|
}
|
|
(void)g_value_init( value, G_TYPE_STRING );
|
|
if ( s )
|
|
{
|
|
g_value_take_string( value, s );
|
|
}
|
|
|
|
(*pList) = g_slist_append( (*pList), value );
|
|
}
|
|
|
|
static GncSqlColumnTypeHandler string_handler
|
|
=
|
|
{
|
|
load_string,
|
|
add_string_col_info_to_list,
|
|
gnc_sql_add_colname_to_list,
|
|
add_gvalue_string_to_slist
|
|
};
|
|
/* ----------------------------------------------------------------- */
|
|
typedef gint (*IntAccessFunc)( const gpointer );
|
|
typedef void (*IntSetterFunc)( const gpointer, gint );
|
|
|
|
static void
|
|
load_int( const GncSqlBackend* be, GncSqlRow* row,
|
|
/*@ null @*/ QofSetterFunc setter, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table_row )
|
|
{
|
|
const GValue* val;
|
|
gint int_value;
|
|
IntSetterFunc i_setter;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( row != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
|
|
|
|
val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
|
|
if ( val == NULL )
|
|
{
|
|
int_value = 0;
|
|
}
|
|
else
|
|
{
|
|
int_value = (gint)gnc_sql_get_integer_value( val );
|
|
}
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_increase_editlevel (QOF_INSTANCE (pObject));
|
|
g_object_set( pObject, table_row->gobj_param_name, int_value, NULL );
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_decrease_editlevel (QOF_INSTANCE (pObject));
|
|
}
|
|
else
|
|
{
|
|
g_return_if_fail( setter != NULL );
|
|
i_setter = (IntSetterFunc)setter;
|
|
(*i_setter)( pObject, int_value );
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_int_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
|
|
GList** pList )
|
|
{
|
|
GncSqlColumnInfo* info;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( pList != NULL );
|
|
|
|
info = create_column_info( table_row, BCT_INT, 0, FALSE );
|
|
|
|
*pList = g_list_append( *pList, info );
|
|
}
|
|
|
|
static void
|
|
add_gvalue_int_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
|
|
const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
|
|
{
|
|
gint int_value = 0;
|
|
IntAccessFunc i_getter;
|
|
GValue* value;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( obj_name != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( pList != NULL );
|
|
|
|
value = g_new0( GValue, 1 );
|
|
g_assert( value != NULL );
|
|
(void)g_value_init( value, G_TYPE_INT );
|
|
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
g_object_get_property( pObject, table_row->gobj_param_name, value );
|
|
}
|
|
else
|
|
{
|
|
i_getter = (IntAccessFunc)gnc_sql_get_getter( obj_name, table_row );
|
|
if ( i_getter != NULL )
|
|
{
|
|
int_value = (*i_getter)( pObject );
|
|
}
|
|
g_value_set_int( value, int_value );
|
|
}
|
|
|
|
(*pList) = g_slist_append( (*pList), value );
|
|
}
|
|
|
|
static GncSqlColumnTypeHandler int_handler
|
|
=
|
|
{
|
|
load_int,
|
|
add_int_col_info_to_list,
|
|
gnc_sql_add_colname_to_list,
|
|
add_gvalue_int_to_slist
|
|
};
|
|
/* ----------------------------------------------------------------- */
|
|
typedef gboolean (*BooleanAccessFunc)( const gpointer );
|
|
typedef void (*BooleanSetterFunc)( const gpointer, gboolean );
|
|
|
|
static void
|
|
load_boolean( const GncSqlBackend* be, GncSqlRow* row,
|
|
/*@ null @*/ QofSetterFunc setter, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table_row )
|
|
{
|
|
const GValue* val;
|
|
gint int_value;
|
|
BooleanSetterFunc b_setter;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( row != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
|
|
|
|
val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
|
|
if ( val == NULL )
|
|
{
|
|
int_value = 0;
|
|
}
|
|
else
|
|
{
|
|
int_value = (gint)gnc_sql_get_integer_value( val );
|
|
}
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_increase_editlevel (QOF_INSTANCE (pObject));
|
|
g_object_set( pObject, table_row->gobj_param_name, int_value, NULL );
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_decrease_editlevel (QOF_INSTANCE (pObject));
|
|
}
|
|
else
|
|
{
|
|
g_return_if_fail( setter != NULL );
|
|
b_setter = (BooleanSetterFunc)setter;
|
|
(*b_setter)( pObject, (int_value != 0) ? TRUE : FALSE );
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_boolean_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
|
|
GList** pList )
|
|
{
|
|
GncSqlColumnInfo* info;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( pList != NULL );
|
|
|
|
info = create_column_info( table_row, BCT_INT, 0, FALSE );
|
|
|
|
*pList = g_list_append( *pList, info );
|
|
}
|
|
|
|
static void
|
|
add_gvalue_boolean_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
|
|
const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
|
|
{
|
|
gint int_value = 0;
|
|
BooleanAccessFunc b_getter;
|
|
GValue* value;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( obj_name != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( pList != NULL );
|
|
|
|
value = g_new0( GValue, 1 );
|
|
g_assert( value != NULL );
|
|
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
g_object_get( pObject, table_row->gobj_param_name, &int_value, NULL );
|
|
}
|
|
else
|
|
{
|
|
b_getter = (BooleanAccessFunc)gnc_sql_get_getter( obj_name, table_row );
|
|
if ( b_getter != NULL )
|
|
{
|
|
int_value = ((*b_getter)( pObject )) ? 1 : 0;
|
|
}
|
|
}
|
|
(void)g_value_init( value, G_TYPE_INT );
|
|
g_value_set_int( value, int_value );
|
|
|
|
(*pList) = g_slist_append( (*pList), value );
|
|
}
|
|
|
|
static GncSqlColumnTypeHandler boolean_handler
|
|
=
|
|
{
|
|
load_boolean,
|
|
add_boolean_col_info_to_list,
|
|
gnc_sql_add_colname_to_list,
|
|
add_gvalue_boolean_to_slist
|
|
};
|
|
/* ----------------------------------------------------------------- */
|
|
typedef gint64 (*Int64AccessFunc)( const gpointer );
|
|
typedef void (*Int64SetterFunc)( const gpointer, gint64 );
|
|
|
|
static void
|
|
load_int64( const GncSqlBackend* be, GncSqlRow* row,
|
|
/*@ null @*/ QofSetterFunc setter, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table_row )
|
|
{
|
|
const GValue* val;
|
|
gint64 i64_value = 0;
|
|
Int64SetterFunc i64_setter = (Int64SetterFunc)setter;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( row != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
|
|
|
|
val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
|
|
if ( val != NULL )
|
|
{
|
|
i64_value = gnc_sql_get_integer_value( val );
|
|
}
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_increase_editlevel (QOF_INSTANCE (pObject));
|
|
g_object_set( pObject, table_row->gobj_param_name, i64_value, NULL );
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_decrease_editlevel (QOF_INSTANCE (pObject));
|
|
}
|
|
else
|
|
{
|
|
(*i64_setter)( pObject, i64_value );
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_int64_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
|
|
GList** pList )
|
|
{
|
|
GncSqlColumnInfo* info;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( pList != NULL );
|
|
|
|
info = create_column_info( table_row, BCT_INT64, 0, FALSE );
|
|
|
|
*pList = g_list_append( *pList, info );
|
|
}
|
|
|
|
static void
|
|
add_gvalue_int64_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
|
|
const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
|
|
{
|
|
gint64 i64_value = 0;
|
|
Int64AccessFunc getter;
|
|
GValue* value;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( obj_name != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( pList != NULL );
|
|
|
|
value = g_new0( GValue, 1 );
|
|
g_assert( value != NULL );
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
g_object_get( pObject, table_row->gobj_param_name, &i64_value, NULL );
|
|
}
|
|
else
|
|
{
|
|
getter = (Int64AccessFunc)gnc_sql_get_getter( obj_name, table_row );
|
|
if ( getter != NULL )
|
|
{
|
|
i64_value = (*getter)( pObject );
|
|
}
|
|
}
|
|
(void)g_value_init( value, G_TYPE_INT64 );
|
|
g_value_set_int64( value, i64_value );
|
|
|
|
(*pList) = g_slist_append( (*pList), value );
|
|
}
|
|
|
|
static GncSqlColumnTypeHandler int64_handler
|
|
=
|
|
{
|
|
load_int64,
|
|
add_int64_col_info_to_list,
|
|
gnc_sql_add_colname_to_list,
|
|
add_gvalue_int64_to_slist
|
|
};
|
|
/* ----------------------------------------------------------------- */
|
|
|
|
static void
|
|
load_double( const GncSqlBackend* be, GncSqlRow* row,
|
|
/*@ null @*/ QofSetterFunc setter, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table_row )
|
|
{
|
|
const GValue* val;
|
|
gdouble d_value;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( row != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
|
|
|
|
val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
|
|
if ( val == NULL )
|
|
{
|
|
(*setter)( pObject, (gpointer)NULL );
|
|
}
|
|
else
|
|
{
|
|
if ( G_VALUE_HOLDS(val, G_TYPE_INT) )
|
|
{
|
|
d_value = (gdouble)g_value_get_int( val );
|
|
}
|
|
else if ( G_VALUE_HOLDS(val, G_TYPE_FLOAT) )
|
|
{
|
|
d_value = g_value_get_float( val );
|
|
}
|
|
else if (G_VALUE_HOLDS(val, G_TYPE_DOUBLE) )
|
|
{
|
|
d_value = g_value_get_double( val );
|
|
}
|
|
else
|
|
{
|
|
PWARN( "Unknown float value type: %s\n", g_type_name( G_VALUE_TYPE(val) ) );
|
|
d_value = 0;
|
|
}
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_increase_editlevel (QOF_INSTANCE (pObject));
|
|
g_object_set( pObject, table_row->gobj_param_name, d_value, NULL );
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_decrease_editlevel (QOF_INSTANCE (pObject));
|
|
}
|
|
else
|
|
{
|
|
(*setter)( pObject, (gpointer)&d_value );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_double_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
|
|
GList** pList )
|
|
{
|
|
GncSqlColumnInfo* info;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( pList != NULL );
|
|
|
|
info = create_column_info( table_row, BCT_DOUBLE, 0, FALSE );
|
|
|
|
*pList = g_list_append( *pList, info );
|
|
}
|
|
|
|
static void
|
|
add_gvalue_double_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
|
|
const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
|
|
{
|
|
QofAccessFunc getter;
|
|
gdouble* pDouble = NULL;
|
|
gdouble d_value;
|
|
GValue* value;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( obj_name != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
|
|
value = g_new0( GValue, 1 );
|
|
g_assert( value != NULL );
|
|
getter = gnc_sql_get_getter( obj_name, table_row );
|
|
if ( getter != NULL )
|
|
{
|
|
pDouble = (*getter)( pObject, NULL );
|
|
}
|
|
if ( pDouble != NULL )
|
|
{
|
|
d_value = *pDouble;
|
|
(void)g_value_init( value, G_TYPE_DOUBLE );
|
|
g_value_set_double( value, d_value );
|
|
}
|
|
else
|
|
{
|
|
(void)g_value_init( value, G_TYPE_DOUBLE );
|
|
g_value_set_double( value, 0.0 );
|
|
}
|
|
|
|
(*pList) = g_slist_append( (*pList), value );
|
|
}
|
|
|
|
static GncSqlColumnTypeHandler double_handler
|
|
=
|
|
{
|
|
load_double,
|
|
add_double_col_info_to_list,
|
|
gnc_sql_add_colname_to_list,
|
|
add_gvalue_double_to_slist
|
|
};
|
|
/* ----------------------------------------------------------------- */
|
|
|
|
static void
|
|
load_guid( const GncSqlBackend* be, GncSqlRow* row,
|
|
/*@ null @*/ QofSetterFunc setter, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table_row )
|
|
{
|
|
const GValue* val;
|
|
GncGUID guid;
|
|
const GncGUID* pGuid;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( row != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
|
|
|
|
val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
|
|
if ( val == NULL || g_value_get_string( val ) == NULL )
|
|
{
|
|
pGuid = NULL;
|
|
}
|
|
else
|
|
{
|
|
(void)string_to_guid( g_value_get_string( val ), &guid );
|
|
pGuid = &guid;
|
|
}
|
|
if ( pGuid != NULL )
|
|
{
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_increase_editlevel (QOF_INSTANCE (pObject));
|
|
g_object_set( pObject, table_row->gobj_param_name, pGuid, NULL );
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_decrease_editlevel (QOF_INSTANCE (pObject));
|
|
}
|
|
else
|
|
{
|
|
g_return_if_fail( setter != NULL );
|
|
(*setter)( pObject, (const gpointer)pGuid );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_guid_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
|
|
GList** pList )
|
|
{
|
|
GncSqlColumnInfo* info;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( pList != NULL );
|
|
|
|
info = create_column_info( table_row, BCT_STRING, GUID_ENCODING_LENGTH, FALSE );
|
|
|
|
*pList = g_list_append( *pList, info );
|
|
}
|
|
|
|
static void
|
|
add_gvalue_guid_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
|
|
const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
|
|
{
|
|
QofAccessFunc getter;
|
|
GncGUID* guid = NULL;
|
|
gchar guid_buf[GUID_ENCODING_LENGTH+1];
|
|
GValue* value;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( obj_name != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
|
|
value = g_new0( GValue, 1 );
|
|
g_assert( value != NULL );
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
g_object_get( pObject, table_row->gobj_param_name, &guid, NULL );
|
|
}
|
|
else
|
|
{
|
|
getter = gnc_sql_get_getter( obj_name, table_row );
|
|
if ( getter != NULL )
|
|
{
|
|
guid = (*getter)( pObject, NULL );
|
|
}
|
|
}
|
|
(void)g_value_init( value, G_TYPE_STRING );
|
|
if ( guid != NULL )
|
|
{
|
|
(void)guid_to_string_buff( guid, guid_buf );
|
|
g_value_set_string( value, guid_buf );
|
|
}
|
|
|
|
(*pList) = g_slist_append( (*pList), value );
|
|
|
|
}
|
|
|
|
static GncSqlColumnTypeHandler guid_handler
|
|
=
|
|
{
|
|
load_guid,
|
|
add_guid_col_info_to_list,
|
|
gnc_sql_add_colname_to_list,
|
|
add_gvalue_guid_to_slist
|
|
};
|
|
/* ----------------------------------------------------------------- */
|
|
|
|
void
|
|
gnc_sql_add_gvalue_objectref_guid_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
|
|
const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
|
|
{
|
|
QofAccessFunc getter;
|
|
const GncGUID* guid = NULL;
|
|
gchar guid_buf[GUID_ENCODING_LENGTH+1];
|
|
QofInstance* inst = NULL;
|
|
GValue* value;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( obj_name != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
|
|
value = g_new0( GValue, 1 );
|
|
g_assert( value != NULL );
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
g_object_get( pObject, table_row->gobj_param_name, &inst, NULL );
|
|
}
|
|
else
|
|
{
|
|
getter = gnc_sql_get_getter( obj_name, table_row );
|
|
if ( getter != NULL )
|
|
{
|
|
inst = (*getter)( pObject, NULL );
|
|
}
|
|
}
|
|
if ( inst != NULL )
|
|
{
|
|
guid = qof_instance_get_guid( inst );
|
|
}
|
|
(void)g_value_init( value, G_TYPE_STRING );
|
|
if ( guid != NULL )
|
|
{
|
|
(void)guid_to_string_buff( guid, guid_buf );
|
|
g_value_set_string( value, guid_buf );
|
|
}
|
|
|
|
(*pList) = g_slist_append( (*pList), value );
|
|
}
|
|
|
|
void
|
|
gnc_sql_add_objectref_guid_col_info_to_list( const GncSqlBackend* be,
|
|
const GncSqlColumnTableEntry* table_row,
|
|
GList** pList )
|
|
{
|
|
add_guid_col_info_to_list( be, table_row, pList );
|
|
}
|
|
|
|
/* ----------------------------------------------------------------- */
|
|
typedef Timespec (*TimespecAccessFunc)( const gpointer );
|
|
typedef void (*TimespecSetterFunc)( const gpointer, Timespec );
|
|
|
|
#define TIMESPEC_STR_FORMAT "%04d%02d%02d%02d%02d%02d"
|
|
#define TIMESPEC_COL_SIZE (4+2+2+2+2+2)
|
|
|
|
/* This is required because we're passing be->timespace_format to
|
|
* g_strdup_printf.
|
|
*/
|
|
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
|
gchar*
|
|
gnc_sql_convert_timespec_to_string( const GncSqlBackend* be, Timespec ts )
|
|
{
|
|
time64 time;
|
|
struct tm* tm;
|
|
gint year;
|
|
gchar* datebuf;
|
|
|
|
time = timespecToTime64( ts );
|
|
tm = gnc_gmtime( &time );
|
|
|
|
year = tm->tm_year + 1900;
|
|
|
|
datebuf = g_strdup_printf( be->timespec_format,
|
|
year, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec );
|
|
gnc_tm_free (tm);
|
|
return datebuf;
|
|
}
|
|
#pragma GCC diagnostic warning "-Wformat-nonliteral"
|
|
|
|
static void
|
|
load_timespec( const GncSqlBackend* be, GncSqlRow* row,
|
|
/*@ null @*/ QofSetterFunc setter, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table_row )
|
|
{
|
|
const GValue* val;
|
|
Timespec ts = {0, 0};
|
|
TimespecSetterFunc ts_setter;
|
|
gboolean isOK = FALSE;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( row != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
|
|
|
|
ts_setter = (TimespecSetterFunc)setter;
|
|
val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
|
|
if ( val == NULL )
|
|
{
|
|
isOK = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if ( G_VALUE_HOLDS_INT64( val ) )
|
|
{
|
|
timespecFromTime64 (&ts, (time64)(g_value_get_int64 (val)));
|
|
isOK = TRUE;
|
|
}
|
|
else if (G_VALUE_HOLDS_STRING (val))
|
|
{
|
|
const gchar* s = g_value_get_string( val );
|
|
if ( s != NULL )
|
|
{
|
|
gchar* buf;
|
|
buf = g_strdup_printf( "%c%c%c%c-%c%c-%c%c %c%c:%c%c:%c%c",
|
|
s[0], s[1], s[2], s[3],
|
|
s[4], s[5],
|
|
s[6], s[7],
|
|
s[8], s[9],
|
|
s[10], s[11],
|
|
s[12], s[13] );
|
|
ts = gnc_iso8601_to_timespec_gmt( buf );
|
|
g_free( buf );
|
|
isOK = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PWARN( "Unknown timespec type: %s", G_VALUE_TYPE_NAME( val ) );
|
|
}
|
|
}
|
|
if ( isOK )
|
|
{
|
|
if (table_row->gobj_param_name != NULL)
|
|
{
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_increase_editlevel (QOF_INSTANCE (pObject));
|
|
g_object_set( pObject, table_row->gobj_param_name, &ts, NULL );
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_decrease_editlevel (QOF_INSTANCE (pObject));
|
|
}
|
|
else
|
|
{
|
|
(*ts_setter)( pObject, ts );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_timespec_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
|
|
GList** pList )
|
|
{
|
|
GncSqlColumnInfo* info;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( pList != NULL );
|
|
|
|
info = create_column_info( table_row, BCT_DATETIME, TIMESPEC_COL_SIZE, FALSE );
|
|
|
|
*pList = g_list_append( *pList, info );
|
|
}
|
|
|
|
static void
|
|
add_gvalue_timespec_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
|
|
const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
|
|
{
|
|
TimespecAccessFunc ts_getter;
|
|
Timespec ts;
|
|
gchar* datebuf;
|
|
GValue* value;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( obj_name != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( pList != NULL );
|
|
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
Timespec* pts;
|
|
g_object_get( pObject, table_row->gobj_param_name, &pts, NULL );
|
|
ts = *pts;
|
|
}
|
|
else
|
|
{
|
|
ts_getter = (TimespecAccessFunc)gnc_sql_get_getter( obj_name, table_row );
|
|
g_return_if_fail( ts_getter != NULL );
|
|
ts = (*ts_getter)( pObject );
|
|
}
|
|
|
|
value = g_new0( GValue, 1 );
|
|
g_assert( value != NULL );
|
|
(void)g_value_init( value, G_TYPE_STRING );
|
|
if ( ts.tv_sec != 0 || ts.tv_nsec != 0 )
|
|
{
|
|
datebuf = gnc_sql_convert_timespec_to_string( be, ts );
|
|
g_value_take_string( value, datebuf );
|
|
}
|
|
|
|
(*pList) = g_slist_append( (*pList), value );
|
|
}
|
|
|
|
static GncSqlColumnTypeHandler timespec_handler
|
|
=
|
|
{
|
|
load_timespec,
|
|
add_timespec_col_info_to_list,
|
|
gnc_sql_add_colname_to_list,
|
|
add_gvalue_timespec_to_slist
|
|
};
|
|
/* ----------------------------------------------------------------- */
|
|
#define DATE_COL_SIZE 8
|
|
|
|
static void
|
|
load_date( const GncSqlBackend* be, GncSqlRow* row,
|
|
/*@ null @*/ QofSetterFunc setter, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table_row )
|
|
{
|
|
const GValue* val;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( row != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
|
|
|
|
val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name );
|
|
if ( val != NULL )
|
|
{
|
|
if (G_VALUE_HOLDS_INT64 (val))
|
|
{
|
|
gint64 time = g_value_get_int64 (val);
|
|
Timespec ts = {time, 0};
|
|
struct tm tm;
|
|
gint day, month, year;
|
|
GDate date = timespec_to_gdate(ts);
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_increase_editlevel (QOF_INSTANCE (pObject));
|
|
g_object_set( pObject, table_row->gobj_param_name, date, NULL );
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_increase_editlevel (QOF_INSTANCE (pObject));
|
|
}
|
|
else
|
|
{
|
|
(*setter)( pObject, &date );
|
|
}
|
|
}
|
|
else if ( G_VALUE_HOLDS_STRING( val ) )
|
|
{
|
|
// Format of date is YYYYMMDD
|
|
const gchar* s = g_value_get_string( val );
|
|
GDate *date;
|
|
if ( s != NULL )
|
|
{
|
|
gchar buf[5];
|
|
GDateDay day;
|
|
guint month;
|
|
GDateYear year;
|
|
|
|
strncpy( buf, &s[0], 4 );
|
|
buf[4] = '\0';
|
|
year = (GDateYear)atoi( buf );
|
|
strncpy( buf, &s[4], 2 );
|
|
buf[2] = '\0';
|
|
month = (guint)atoi( buf );
|
|
strncpy( buf, &s[6], 2 );
|
|
day = (GDateDay)atoi( buf );
|
|
|
|
if ( year != 0 || month != 0 || day != (GDateDay)0 )
|
|
{
|
|
date = g_date_new_dmy( day, month, year );
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_increase_editlevel (QOF_INSTANCE (pObject));
|
|
g_object_set (pObject, table_row->gobj_param_name,
|
|
date, NULL);
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_increase_editlevel (QOF_INSTANCE (pObject));
|
|
}
|
|
else
|
|
{
|
|
(*setter)( pObject, date );
|
|
}
|
|
g_date_free( date );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PWARN( "Unknown date type: %s", G_VALUE_TYPE_NAME( val ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_date_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
|
|
GList** pList )
|
|
{
|
|
GncSqlColumnInfo* info;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( pList != NULL );
|
|
|
|
info = create_column_info( table_row, BCT_DATE, DATE_COL_SIZE, FALSE );
|
|
|
|
*pList = g_list_append( *pList, info );
|
|
}
|
|
|
|
static void
|
|
add_gvalue_date_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
|
|
const gpointer pObject,
|
|
const GncSqlColumnTableEntry* table_row, GSList** pList )
|
|
{
|
|
GDate* date = NULL;
|
|
QofAccessFunc getter;
|
|
gchar* buf;
|
|
GValue* value;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( obj_name != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
|
|
value = g_new0( GValue, 1 );
|
|
g_assert( value != NULL );
|
|
(void)g_value_init( value, G_TYPE_STRING );
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
g_object_get( pObject, table_row->gobj_param_name, &date, NULL );
|
|
}
|
|
else
|
|
{
|
|
getter = gnc_sql_get_getter( obj_name, table_row );
|
|
if ( getter != NULL )
|
|
{
|
|
date = (GDate*)(*getter)( pObject, NULL );
|
|
}
|
|
}
|
|
if ( date && g_date_valid( date ) )
|
|
{
|
|
buf = g_strdup_printf( "%04d%02d%02d",
|
|
g_date_get_year( date ), g_date_get_month( date ), g_date_get_day( date ) );
|
|
g_value_take_string( value, buf );
|
|
}
|
|
|
|
(*pList) = g_slist_append( (*pList), value );
|
|
}
|
|
|
|
static GncSqlColumnTypeHandler date_handler
|
|
=
|
|
{
|
|
load_date,
|
|
add_date_col_info_to_list,
|
|
gnc_sql_add_colname_to_list,
|
|
add_gvalue_date_to_slist
|
|
};
|
|
/* ----------------------------------------------------------------- */
|
|
typedef gnc_numeric (*NumericGetterFunc)( const gpointer );
|
|
typedef void (*NumericSetterFunc)( gpointer, gnc_numeric );
|
|
|
|
static const GncSqlColumnTableEntry numeric_col_table[] =
|
|
{
|
|
/*@ -full_init_block @*/
|
|
{ "num", CT_INT64, 0, COL_NNUL, "guid" },
|
|
{ "denom", CT_INT64, 0, COL_NNUL, "guid" },
|
|
{ NULL }
|
|
/*@ +full_init_block @*/
|
|
};
|
|
|
|
static void
|
|
load_numeric( const GncSqlBackend* be, GncSqlRow* row,
|
|
/*@ null @*/ QofSetterFunc setter, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table_row )
|
|
{
|
|
const GValue* val;
|
|
gchar* buf;
|
|
gint64 num, denom;
|
|
gnc_numeric n;
|
|
gboolean isNull = FALSE;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( row != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( table_row->gobj_param_name != NULL || setter != NULL );
|
|
|
|
buf = g_strdup_printf( "%s_num", table_row->col_name );
|
|
val = gnc_sql_row_get_value_at_col_name( row, buf );
|
|
g_free( buf );
|
|
if ( val == NULL )
|
|
{
|
|
isNull = TRUE;
|
|
num = 0;
|
|
}
|
|
else
|
|
{
|
|
num = gnc_sql_get_integer_value( val );
|
|
}
|
|
buf = g_strdup_printf( "%s_denom", table_row->col_name );
|
|
val = gnc_sql_row_get_value_at_col_name( row, buf );
|
|
g_free( buf );
|
|
if ( val == NULL )
|
|
{
|
|
isNull = TRUE;
|
|
denom = 1;
|
|
}
|
|
else
|
|
{
|
|
denom = gnc_sql_get_integer_value( val );
|
|
}
|
|
n = gnc_numeric_create( num, denom );
|
|
if ( !isNull )
|
|
{
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_increase_editlevel (QOF_INSTANCE (pObject));
|
|
g_object_set( pObject, table_row->gobj_param_name, &n, NULL );
|
|
if (QOF_IS_INSTANCE (pObject))
|
|
qof_instance_increase_editlevel (QOF_INSTANCE (pObject));
|
|
}
|
|
else
|
|
{
|
|
NumericSetterFunc n_setter = (NumericSetterFunc)setter;
|
|
(*n_setter)( pObject, n );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_numeric_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row,
|
|
GList** pList )
|
|
{
|
|
GncSqlColumnInfo* info;
|
|
gchar* buf;
|
|
const GncSqlColumnTableEntry* subtable_row;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
g_return_if_fail( pList != NULL );
|
|
|
|
for ( subtable_row = numeric_col_table; subtable_row->col_name != NULL; subtable_row++ )
|
|
{
|
|
buf = g_strdup_printf( "%s_%s", table_row->col_name, subtable_row->col_name );
|
|
info = g_new0( GncSqlColumnInfo, 1 );
|
|
g_assert( info != NULL );
|
|
info->name = buf;
|
|
info->type = BCT_INT64;
|
|
info->is_primary_key = ((table_row->flags & COL_PKEY) != 0) ? TRUE : FALSE;
|
|
info->null_allowed = ((table_row->flags & COL_NNUL) != 0) ? FALSE : TRUE;
|
|
info->is_unicode = FALSE;
|
|
*pList = g_list_append( *pList, info );
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_numeric_colname_to_list( const GncSqlColumnTableEntry* table_row, GList** pList )
|
|
{
|
|
gnc_sql_add_subtable_colnames_to_list( table_row, numeric_col_table, pList );
|
|
}
|
|
|
|
static void
|
|
add_gvalue_numeric_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
|
|
const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList )
|
|
{
|
|
NumericGetterFunc getter;
|
|
gnc_numeric n;
|
|
GValue* num_value;
|
|
GValue* denom_value;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( obj_name != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table_row != NULL );
|
|
|
|
if ( table_row->gobj_param_name != NULL )
|
|
{
|
|
gnc_numeric *s;
|
|
g_object_get( pObject, table_row->gobj_param_name, &s, NULL );
|
|
n = *s;
|
|
}
|
|
else
|
|
{
|
|
getter = (NumericGetterFunc)gnc_sql_get_getter( obj_name, table_row );
|
|
if ( getter != NULL )
|
|
{
|
|
n = (*getter)( pObject );
|
|
}
|
|
else
|
|
{
|
|
n = gnc_numeric_zero();
|
|
}
|
|
}
|
|
|
|
num_value = g_new0( GValue, 1 );
|
|
g_assert( num_value != NULL );
|
|
(void)g_value_init( num_value, G_TYPE_INT64 );
|
|
g_value_set_int64( num_value, gnc_numeric_num( n ) );
|
|
denom_value = g_new0( GValue, 1 );
|
|
g_assert( denom_value != NULL );
|
|
(void)g_value_init( denom_value, G_TYPE_INT64 );
|
|
g_value_set_int64( denom_value, gnc_numeric_denom( n ) );
|
|
|
|
(*pList) = g_slist_append( (*pList), num_value );
|
|
(*pList) = g_slist_append( (*pList), denom_value );
|
|
}
|
|
|
|
static GncSqlColumnTypeHandler numeric_handler
|
|
= { load_numeric,
|
|
add_numeric_col_info_to_list,
|
|
add_numeric_colname_to_list,
|
|
add_gvalue_numeric_to_slist
|
|
};
|
|
/* ================================================================= */
|
|
|
|
static /*@ null @*//*@ only @*/ GHashTable* g_columnTypeHash = NULL;
|
|
|
|
void
|
|
gnc_sql_register_col_type_handler( const gchar* colType, const GncSqlColumnTypeHandler* handler )
|
|
{
|
|
g_return_if_fail( colType != NULL );
|
|
g_return_if_fail( handler != NULL );
|
|
|
|
if ( g_columnTypeHash == NULL )
|
|
{
|
|
g_columnTypeHash = g_hash_table_new( g_str_hash, g_str_equal );
|
|
g_assert( g_columnTypeHash != NULL );
|
|
}
|
|
|
|
DEBUG( "Col type %s registered\n", colType );
|
|
g_hash_table_insert( g_columnTypeHash, (gpointer)colType, (gpointer)handler );
|
|
}
|
|
|
|
/*@ dependent @*//*@ null @*/ static GncSqlColumnTypeHandler*
|
|
get_handler( const GncSqlColumnTableEntry* table_row )
|
|
{
|
|
GncSqlColumnTypeHandler* pHandler;
|
|
|
|
g_return_val_if_fail( table_row != NULL, NULL );
|
|
g_return_val_if_fail( table_row->col_type != NULL, NULL );
|
|
|
|
if ( g_columnTypeHash != NULL )
|
|
{
|
|
pHandler = g_hash_table_lookup( g_columnTypeHash, table_row->col_type );
|
|
g_assert( pHandler != NULL );
|
|
}
|
|
else
|
|
{
|
|
pHandler = NULL;
|
|
}
|
|
|
|
return pHandler;
|
|
}
|
|
|
|
static void
|
|
register_standard_col_type_handlers( void )
|
|
{
|
|
gnc_sql_register_col_type_handler( CT_STRING, &string_handler );
|
|
gnc_sql_register_col_type_handler( CT_BOOLEAN, &boolean_handler );
|
|
gnc_sql_register_col_type_handler( CT_INT, &int_handler );
|
|
gnc_sql_register_col_type_handler( CT_INT64, &int64_handler );
|
|
gnc_sql_register_col_type_handler( CT_DOUBLE, &double_handler );
|
|
gnc_sql_register_col_type_handler( CT_GUID, &guid_handler );
|
|
gnc_sql_register_col_type_handler( CT_TIMESPEC, ×pec_handler );
|
|
gnc_sql_register_col_type_handler( CT_GDATE, &date_handler );
|
|
gnc_sql_register_col_type_handler( CT_NUMERIC, &numeric_handler );
|
|
}
|
|
|
|
void
|
|
_retrieve_guid_( gpointer pObject, /*@ null @*/ gpointer pValue )
|
|
{
|
|
GncGUID* pGuid = (GncGUID*)pObject;
|
|
GncGUID* guid = (GncGUID*)pValue;
|
|
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( pValue != NULL );
|
|
|
|
memcpy( pGuid, guid, sizeof( GncGUID ) );
|
|
}
|
|
|
|
|
|
// Table to retrieve just the guid
|
|
static GncSqlColumnTableEntry guid_table[] =
|
|
{
|
|
/*@ -full_init_block @*/
|
|
{ "guid", CT_GUID, 0, 0, NULL, NULL, NULL, _retrieve_guid_ },
|
|
{ NULL }
|
|
/*@ +full_init_block @*/
|
|
};
|
|
|
|
/*@ null @*/
|
|
const GncGUID*
|
|
gnc_sql_load_guid( const GncSqlBackend* be, GncSqlRow* row )
|
|
{
|
|
static GncGUID guid;
|
|
|
|
g_return_val_if_fail( be != NULL, NULL );
|
|
g_return_val_if_fail( row != NULL, NULL );
|
|
|
|
gnc_sql_load_object( be, row, NULL, &guid, guid_table );
|
|
|
|
return &guid;
|
|
}
|
|
|
|
// Table to retrieve just the guid
|
|
static GncSqlColumnTableEntry tx_guid_table[] =
|
|
{
|
|
/*@ -full_init_block @*/
|
|
{ "tx_guid", CT_GUID, 0, 0, NULL, NULL, NULL, _retrieve_guid_ },
|
|
{ NULL }
|
|
/*@ +full_init_block @*/
|
|
};
|
|
|
|
/*@ null @*//*@ dependent @*/
|
|
const GncGUID*
|
|
gnc_sql_load_tx_guid( const GncSqlBackend* be, GncSqlRow* row )
|
|
{
|
|
static GncGUID guid;
|
|
|
|
g_return_val_if_fail( be != NULL, NULL );
|
|
g_return_val_if_fail( row != NULL, NULL );
|
|
|
|
gnc_sql_load_object( be, row, NULL, &guid, tx_guid_table );
|
|
|
|
return &guid;
|
|
}
|
|
|
|
void
|
|
gnc_sql_load_object( const GncSqlBackend* be, GncSqlRow* row,
|
|
/*@ null @*/ QofIdTypeConst obj_name, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table )
|
|
{
|
|
QofSetterFunc setter;
|
|
GncSqlColumnTypeHandler* pHandler;
|
|
const GncSqlColumnTableEntry* table_row;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( row != NULL );
|
|
g_return_if_fail( pObject != NULL );
|
|
g_return_if_fail( table != NULL );
|
|
|
|
for ( table_row = table; table_row->col_name != NULL; table_row++ )
|
|
{
|
|
if ( (table_row->flags & COL_AUTOINC) != 0 )
|
|
{
|
|
setter = set_autoinc_id;
|
|
}
|
|
else if ( table_row->qof_param_name != NULL )
|
|
{
|
|
g_assert( obj_name != NULL );
|
|
setter = qof_class_get_parameter_setter( obj_name,
|
|
table_row->qof_param_name );
|
|
}
|
|
else
|
|
{
|
|
setter = table_row->setter;
|
|
}
|
|
pHandler = get_handler( table_row );
|
|
g_assert( pHandler != NULL );
|
|
pHandler->load_fn( be, row, setter, pObject, table_row );
|
|
}
|
|
}
|
|
|
|
/* ================================================================= */
|
|
/*@ null @*/ GncSqlStatement*
|
|
gnc_sql_create_select_statement( GncSqlBackend* be, const gchar* table_name )
|
|
{
|
|
gchar* sql;
|
|
GncSqlStatement* stmt;
|
|
|
|
g_return_val_if_fail( be != NULL, NULL );
|
|
g_return_val_if_fail( table_name != NULL, NULL );
|
|
|
|
sql = g_strdup_printf( "SELECT * FROM %s", table_name );
|
|
stmt = gnc_sql_create_statement_from_sql( be, sql );
|
|
g_free( sql );
|
|
return stmt;
|
|
}
|
|
|
|
/*@ null @*/ static GncSqlStatement*
|
|
create_single_col_select_statement( GncSqlBackend* be,
|
|
const gchar* table_name,
|
|
const GncSqlColumnTableEntry* table_row )
|
|
{
|
|
gchar* sql;
|
|
GncSqlStatement* stmt;
|
|
|
|
g_return_val_if_fail( be != NULL, NULL );
|
|
g_return_val_if_fail( table_name != NULL, NULL );
|
|
g_return_val_if_fail( table_row != NULL, NULL );
|
|
|
|
sql = g_strdup_printf( "SELECT %s FROM %s", table_row->col_name, table_name );
|
|
stmt = gnc_sql_create_statement_from_sql( be, sql );
|
|
g_free( sql );
|
|
return stmt;
|
|
}
|
|
|
|
/* ================================================================= */
|
|
|
|
/*@ null @*/ GncSqlResult*
|
|
gnc_sql_execute_select_statement( GncSqlBackend* be, GncSqlStatement* stmt )
|
|
{
|
|
GncSqlResult* result;
|
|
|
|
g_return_val_if_fail( be != NULL, NULL );
|
|
g_return_val_if_fail( stmt != NULL, NULL );
|
|
|
|
result = gnc_sql_connection_execute_select_statement( be->conn, stmt );
|
|
if ( result == NULL )
|
|
{
|
|
PERR( "SQL error: %s\n", gnc_sql_statement_to_sql( stmt ) );
|
|
qof_backend_set_error( &be->be, ERR_BACKEND_SERVER_ERR );
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*@ null @*/ GncSqlStatement*
|
|
gnc_sql_create_statement_from_sql( GncSqlBackend* be, const gchar* sql )
|
|
{
|
|
GncSqlStatement* stmt;
|
|
|
|
g_return_val_if_fail( be != NULL, NULL );
|
|
g_return_val_if_fail( sql != NULL, NULL );
|
|
|
|
stmt = gnc_sql_connection_create_statement_from_sql( be->conn, sql );
|
|
if ( stmt == NULL )
|
|
{
|
|
PERR( "SQL error: %s\n", sql );
|
|
qof_backend_set_error( &be->be, ERR_BACKEND_SERVER_ERR );
|
|
}
|
|
|
|
return stmt;
|
|
}
|
|
|
|
/*@ null @*/ GncSqlResult*
|
|
gnc_sql_execute_select_sql( GncSqlBackend* be, const gchar* sql )
|
|
{
|
|
GncSqlStatement* stmt;
|
|
GncSqlResult* result = NULL;
|
|
|
|
g_return_val_if_fail( be != NULL, NULL );
|
|
g_return_val_if_fail( sql != NULL, NULL );
|
|
|
|
stmt = gnc_sql_create_statement_from_sql( be, sql );
|
|
if ( stmt == NULL )
|
|
{
|
|
return NULL;
|
|
}
|
|
result = gnc_sql_connection_execute_select_statement( be->conn, stmt );
|
|
gnc_sql_statement_dispose( stmt );
|
|
if ( result == NULL )
|
|
{
|
|
PERR( "SQL error: %s\n", sql );
|
|
qof_backend_set_error( &be->be, ERR_BACKEND_SERVER_ERR );
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
gint
|
|
gnc_sql_execute_nonselect_sql( GncSqlBackend* be, const gchar* sql )
|
|
{
|
|
GncSqlStatement* stmt;
|
|
gint result;
|
|
|
|
g_return_val_if_fail( be != NULL, 0 );
|
|
g_return_val_if_fail( sql != NULL, 0 );
|
|
|
|
stmt = gnc_sql_create_statement_from_sql( be, sql );
|
|
if ( stmt == NULL )
|
|
{
|
|
return -1;
|
|
}
|
|
result = gnc_sql_connection_execute_nonselect_statement( be->conn, stmt );
|
|
gnc_sql_statement_dispose( stmt );
|
|
return result;
|
|
}
|
|
|
|
static guint
|
|
execute_statement_get_count( GncSqlBackend* be, GncSqlStatement* stmt )
|
|
{
|
|
GncSqlResult* result;
|
|
guint count = 0;
|
|
|
|
g_return_val_if_fail( be != NULL, 0 );
|
|
g_return_val_if_fail( stmt != NULL, 0 );
|
|
|
|
result = gnc_sql_execute_select_statement( be, stmt );
|
|
if ( result != NULL )
|
|
{
|
|
count = gnc_sql_result_get_num_rows( result );
|
|
gnc_sql_result_dispose( result );
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
guint
|
|
gnc_sql_append_guid_list_to_sql( GString* sql, GList* list, guint maxCount )
|
|
{
|
|
gchar guid_buf[GUID_ENCODING_LENGTH+1];
|
|
gboolean first_guid = TRUE;
|
|
guint count;
|
|
|
|
g_return_val_if_fail( sql != NULL, 0 );
|
|
|
|
if ( list == NULL ) return 0;
|
|
|
|
for ( count = 0; list != NULL && count < maxCount; list = list->next, count++ )
|
|
{
|
|
QofInstance* inst = QOF_INSTANCE(list->data);
|
|
(void)guid_to_string_buff( qof_instance_get_guid( inst ), guid_buf );
|
|
|
|
if ( !first_guid )
|
|
{
|
|
(void)g_string_append( sql, "," );
|
|
}
|
|
(void)g_string_append( sql, "'" );
|
|
(void)g_string_append( sql, guid_buf );
|
|
(void)g_string_append( sql, "'" );
|
|
first_guid = FALSE;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
/* ================================================================= */
|
|
|
|
gboolean
|
|
gnc_sql_object_is_it_in_db( GncSqlBackend* be, const gchar* table_name,
|
|
QofIdTypeConst obj_name, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table )
|
|
{
|
|
GncSqlStatement* sqlStmt;
|
|
guint count;
|
|
GncSqlColumnTypeHandler* pHandler;
|
|
GSList* list = NULL;
|
|
|
|
g_return_val_if_fail( be != NULL, FALSE );
|
|
g_return_val_if_fail( table_name != NULL, FALSE );
|
|
g_return_val_if_fail( obj_name != NULL, FALSE );
|
|
g_return_val_if_fail( pObject != NULL, FALSE );
|
|
g_return_val_if_fail( table != NULL, FALSE );
|
|
|
|
/* SELECT * FROM */
|
|
sqlStmt = create_single_col_select_statement( be, table_name, table );
|
|
g_assert( sqlStmt != NULL );
|
|
|
|
/* WHERE */
|
|
pHandler = get_handler( table );
|
|
g_assert( pHandler != NULL );
|
|
pHandler->add_gvalue_to_slist_fn( be, obj_name, pObject, table, &list );
|
|
g_assert( list != NULL );
|
|
gnc_sql_statement_add_where_cond( sqlStmt, obj_name, pObject, &table[0], (GValue*)(list->data) );
|
|
|
|
count = execute_statement_get_count( be, sqlStmt );
|
|
gnc_sql_statement_dispose( sqlStmt );
|
|
if ( count == 0 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
gnc_sql_do_db_operation( GncSqlBackend* be,
|
|
E_DB_OPERATION op,
|
|
const gchar* table_name,
|
|
QofIdTypeConst obj_name, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table )
|
|
{
|
|
GncSqlStatement* stmt = NULL;
|
|
gboolean ok = FALSE;
|
|
|
|
g_return_val_if_fail( be != NULL, FALSE );
|
|
g_return_val_if_fail( table_name != NULL, FALSE );
|
|
g_return_val_if_fail( obj_name != NULL, FALSE );
|
|
g_return_val_if_fail( pObject != NULL, FALSE );
|
|
g_return_val_if_fail( table != NULL, FALSE );
|
|
|
|
if ( op == OP_DB_INSERT )
|
|
{
|
|
stmt = build_insert_statement( be, table_name, obj_name, pObject, table );
|
|
}
|
|
else if ( op == OP_DB_UPDATE )
|
|
{
|
|
stmt = build_update_statement( be, table_name, obj_name, pObject, table );
|
|
}
|
|
else if ( op == OP_DB_DELETE )
|
|
{
|
|
stmt = build_delete_statement( be, table_name, obj_name, pObject, table );
|
|
}
|
|
else
|
|
{
|
|
g_assert( FALSE );
|
|
}
|
|
if ( stmt != NULL )
|
|
{
|
|
gint result;
|
|
|
|
result = gnc_sql_connection_execute_nonselect_statement( be->conn, stmt );
|
|
if ( result == -1 )
|
|
{
|
|
PERR( "SQL error: %s\n", gnc_sql_statement_to_sql( stmt ) );
|
|
qof_backend_set_error( &be->be, ERR_BACKEND_SERVER_ERR );
|
|
}
|
|
else
|
|
{
|
|
ok = TRUE;
|
|
}
|
|
gnc_sql_statement_dispose( stmt );
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
static GSList*
|
|
create_gslist_from_values( GncSqlBackend* be,
|
|
QofIdTypeConst obj_name, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table )
|
|
{
|
|
GSList* list = NULL;
|
|
GncSqlColumnTypeHandler* pHandler;
|
|
const GncSqlColumnTableEntry* table_row;
|
|
|
|
for ( table_row = table; table_row->col_name != NULL; table_row++ )
|
|
{
|
|
if (( table_row->flags & COL_AUTOINC ) == 0 )
|
|
{
|
|
pHandler = get_handler( table_row );
|
|
g_assert( pHandler != NULL );
|
|
pHandler->add_gvalue_to_slist_fn( be, obj_name, pObject, table_row, &list );
|
|
}
|
|
}
|
|
|
|
g_assert( list != NULL );
|
|
return list;
|
|
}
|
|
|
|
gchar*
|
|
gnc_sql_get_sql_value( const GncSqlConnection* conn, const GValue* value )
|
|
{
|
|
if ( value != NULL && G_IS_VALUE( value ) )
|
|
{
|
|
GType type = G_VALUE_TYPE(value);
|
|
|
|
if ( G_VALUE_HOLDS_STRING(value) )
|
|
{
|
|
if ( g_value_get_string( value ) != NULL )
|
|
{
|
|
gchar* before_str;
|
|
gchar* after_str;
|
|
before_str = g_value_dup_string( value );
|
|
after_str = gnc_sql_connection_quote_string( conn, before_str );
|
|
g_free( before_str );
|
|
return after_str;
|
|
}
|
|
else
|
|
{
|
|
return g_strdup( "NULL" );
|
|
}
|
|
}
|
|
else if ( type == G_TYPE_INT64 )
|
|
{
|
|
return g_strdup_printf( "%" G_GINT64_FORMAT, g_value_get_int64( value ) );
|
|
|
|
}
|
|
else if ( type == G_TYPE_INT )
|
|
{
|
|
return g_strdup_printf( "%d", g_value_get_int( value ) );
|
|
|
|
}
|
|
else if ( type == G_TYPE_DOUBLE )
|
|
{
|
|
gchar doublestr[G_ASCII_DTOSTR_BUF_SIZE];
|
|
g_ascii_dtostr( doublestr, sizeof(doublestr),
|
|
g_value_get_double( value ));
|
|
return g_strdup( doublestr );
|
|
|
|
}
|
|
else if ( g_value_type_transformable( type, G_TYPE_STRING ) )
|
|
{
|
|
GValue* string;
|
|
gchar* str;
|
|
|
|
string = g_new0( GValue, 1 );
|
|
g_assert( string != NULL );
|
|
(void)g_value_init( string, G_TYPE_STRING );
|
|
(void)g_value_transform( value, string );
|
|
str = g_value_dup_string( string );
|
|
g_value_unset( string );
|
|
g_free( string );
|
|
PWARN( "using g_value_transform(), gtype = '%s'\n", g_type_name( type ) );
|
|
return str;
|
|
}
|
|
else
|
|
{
|
|
PWARN( "not transformable, gtype = '%s'\n", g_type_name( type ) );
|
|
return g_strdup( "$$$" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PWARN( "value is NULL or not G_IS_VALUE()\n" );
|
|
return g_strdup( "" );
|
|
}
|
|
}
|
|
|
|
static void
|
|
free_gvalue_list( GSList* list )
|
|
{
|
|
GSList* node;
|
|
GValue* value;
|
|
|
|
for ( node = list; node != NULL; node = node->next )
|
|
{
|
|
value = (GValue*)node->data;
|
|
|
|
g_value_unset( value );
|
|
g_free( value );
|
|
}
|
|
g_slist_free( list );
|
|
}
|
|
|
|
/*@ null @*/ static GncSqlStatement*
|
|
build_insert_statement( GncSqlBackend* be,
|
|
const gchar* table_name,
|
|
QofIdTypeConst obj_name, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table )
|
|
{
|
|
GncSqlStatement* stmt;
|
|
GString* sql;
|
|
GSList* values;
|
|
GSList* node;
|
|
gchar* sqlbuf;
|
|
GList* colnames = NULL;
|
|
GList* colname;
|
|
const GncSqlColumnTableEntry* table_row;
|
|
|
|
g_return_val_if_fail( be != NULL, NULL );
|
|
g_return_val_if_fail( table_name != NULL, NULL );
|
|
g_return_val_if_fail( obj_name != NULL, NULL );
|
|
g_return_val_if_fail( pObject != NULL, NULL );
|
|
g_return_val_if_fail( table != NULL, NULL );
|
|
|
|
sqlbuf = g_strdup_printf( "INSERT INTO %s(", table_name );
|
|
sql = g_string_new( sqlbuf );
|
|
g_free( sqlbuf );
|
|
|
|
// Get all col names and all values
|
|
for ( table_row = table; table_row->col_name != NULL; table_row++ )
|
|
{
|
|
if (( table_row->flags & COL_AUTOINC ) == 0 )
|
|
{
|
|
GncSqlColumnTypeHandler* pHandler;
|
|
|
|
// Add col names to the list
|
|
pHandler = get_handler( table_row );
|
|
g_assert( pHandler != NULL );
|
|
pHandler->add_colname_to_list_fn( table_row, &colnames );
|
|
}
|
|
}
|
|
g_assert( colnames != NULL );
|
|
|
|
for ( colname = colnames; colname != NULL; colname = colname->next )
|
|
{
|
|
if ( colname != colnames )
|
|
{
|
|
g_string_append( sql, "," );
|
|
}
|
|
g_string_append( sql, (gchar*)colname->data );
|
|
g_free( colname->data );
|
|
}
|
|
g_list_free( colnames );
|
|
|
|
g_string_append( sql, ") VALUES(" );
|
|
values = create_gslist_from_values( be, obj_name, pObject, table );
|
|
for ( node = values; node != NULL; node = node->next )
|
|
{
|
|
GValue* value = (GValue*)node->data;
|
|
gchar* value_str;
|
|
if ( node != values )
|
|
{
|
|
(void)g_string_append( sql, "," );
|
|
}
|
|
value_str = gnc_sql_get_sql_value( be->conn, value );
|
|
(void)g_string_append( sql, value_str );
|
|
g_free( value_str );
|
|
(void)g_value_reset( value );
|
|
}
|
|
free_gvalue_list( values );
|
|
(void)g_string_append( sql, ")" );
|
|
|
|
stmt = gnc_sql_connection_create_statement_from_sql( be->conn, sql->str );
|
|
(void)g_string_free( sql, TRUE );
|
|
|
|
return stmt;
|
|
}
|
|
|
|
/*@ null @*/ static GncSqlStatement*
|
|
build_update_statement( GncSqlBackend* be,
|
|
const gchar* table_name,
|
|
QofIdTypeConst obj_name, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table )
|
|
{
|
|
GncSqlStatement* stmt;
|
|
GString* sql;
|
|
GSList* values;
|
|
GList* colnames = NULL;
|
|
GSList* value;
|
|
GList* colname;
|
|
gboolean firstCol;
|
|
const GncSqlColumnTableEntry* table_row;
|
|
gchar* sqlbuf;
|
|
|
|
g_return_val_if_fail( be != NULL, NULL );
|
|
g_return_val_if_fail( table_name != NULL, NULL );
|
|
g_return_val_if_fail( obj_name != NULL, NULL );
|
|
g_return_val_if_fail( pObject != NULL, NULL );
|
|
g_return_val_if_fail( table != NULL, NULL );
|
|
|
|
// Get all col names and all values
|
|
for ( table_row = table; table_row->col_name != NULL; table_row++ )
|
|
{
|
|
if (( table_row->flags & COL_AUTOINC ) == 0 )
|
|
{
|
|
GncSqlColumnTypeHandler* pHandler;
|
|
|
|
// Add col names to the list
|
|
pHandler = get_handler( table_row );
|
|
g_assert( pHandler != NULL );
|
|
pHandler->add_colname_to_list_fn( table_row, &colnames );
|
|
}
|
|
}
|
|
g_assert( colnames != NULL );
|
|
values = create_gslist_from_values( be, obj_name, pObject, table );
|
|
|
|
// Create the SQL statement
|
|
sqlbuf = g_strdup_printf( "UPDATE %s SET ", table_name );
|
|
sql = g_string_new( sqlbuf );
|
|
g_free( sqlbuf );
|
|
|
|
firstCol = TRUE;
|
|
for ( colname = colnames->next, value = values->next;
|
|
colname != NULL && value != NULL;
|
|
colname = colname->next, value = value->next )
|
|
{
|
|
gchar* value_str;
|
|
if ( !firstCol )
|
|
{
|
|
(void)g_string_append( sql, "," );
|
|
}
|
|
(void)g_string_append( sql, (gchar*)colname->data );
|
|
(void)g_string_append( sql, "=" );
|
|
value_str = gnc_sql_get_sql_value( be->conn, (GValue*)(value->data) );
|
|
(void)g_string_append( sql, value_str );
|
|
g_free( value_str );
|
|
firstCol = FALSE;
|
|
}
|
|
for ( colname = colnames; colname != NULL; colname = colname->next )
|
|
{
|
|
g_free( colname->data );
|
|
}
|
|
g_list_free( colnames );
|
|
if ( value != NULL || colname != NULL )
|
|
{
|
|
PERR( "Mismatch in number of column names and values" );
|
|
}
|
|
|
|
stmt = gnc_sql_connection_create_statement_from_sql( be->conn, sql->str );
|
|
gnc_sql_statement_add_where_cond( stmt, obj_name, pObject, &table[0], (GValue*)(values->data) );
|
|
free_gvalue_list( values );
|
|
(void)g_string_free( sql, TRUE );
|
|
|
|
return stmt;
|
|
}
|
|
|
|
/*@ null @*/ static GncSqlStatement*
|
|
build_delete_statement( GncSqlBackend* be,
|
|
const gchar* table_name,
|
|
QofIdTypeConst obj_name, gpointer pObject,
|
|
const GncSqlColumnTableEntry* table )
|
|
{
|
|
GncSqlStatement* stmt;
|
|
GncSqlColumnTypeHandler* pHandler;
|
|
GSList* list = NULL;
|
|
gchar* sqlbuf;
|
|
|
|
g_return_val_if_fail( be != NULL, NULL );
|
|
g_return_val_if_fail( table_name != NULL, NULL );
|
|
g_return_val_if_fail( obj_name != NULL, NULL );
|
|
g_return_val_if_fail( pObject != NULL, NULL );
|
|
g_return_val_if_fail( table != NULL, NULL );
|
|
|
|
sqlbuf = g_strdup_printf( "DELETE FROM %s ", table_name );
|
|
stmt = gnc_sql_connection_create_statement_from_sql( be->conn, sqlbuf );
|
|
g_free( sqlbuf );
|
|
|
|
/* WHERE */
|
|
pHandler = get_handler( table );
|
|
g_assert( pHandler != NULL );
|
|
pHandler->add_gvalue_to_slist_fn( be, obj_name, pObject, table, &list );
|
|
g_assert( list != NULL );
|
|
gnc_sql_statement_add_where_cond( stmt, obj_name, pObject, &table[0], (GValue*)(list->data) );
|
|
free_gvalue_list( list );
|
|
|
|
return stmt;
|
|
}
|
|
|
|
/* ================================================================= */
|
|
gboolean
|
|
gnc_sql_commit_standard_item( GncSqlBackend* be, QofInstance* inst, const gchar* tableName,
|
|
QofIdTypeConst obj_name, const GncSqlColumnTableEntry* col_table )
|
|
{
|
|
const GncGUID* guid;
|
|
gboolean is_infant;
|
|
gint op;
|
|
gboolean is_ok;
|
|
|
|
is_infant = qof_instance_get_infant( inst );
|
|
if ( qof_instance_get_destroying( inst ) )
|
|
{
|
|
op = OP_DB_DELETE;
|
|
}
|
|
else if ( be->is_pristine_db || is_infant )
|
|
{
|
|
op = OP_DB_INSERT;
|
|
}
|
|
else
|
|
{
|
|
op = OP_DB_UPDATE;
|
|
}
|
|
is_ok = gnc_sql_do_db_operation( be, op, tableName, obj_name, inst, col_table );
|
|
|
|
if ( is_ok )
|
|
{
|
|
// Now, commit any slots
|
|
guid = qof_instance_get_guid( inst );
|
|
if ( !qof_instance_get_destroying(inst) )
|
|
{
|
|
is_ok = gnc_sql_slots_save( be, guid, is_infant, inst);
|
|
}
|
|
else
|
|
{
|
|
is_ok = gnc_sql_slots_delete( be, guid );
|
|
}
|
|
}
|
|
|
|
return is_ok;
|
|
}
|
|
|
|
/* ================================================================= */
|
|
|
|
static gboolean
|
|
do_create_table( const GncSqlBackend* be, const gchar* table_name,
|
|
const GncSqlColumnTableEntry* col_table )
|
|
{
|
|
GList* col_info_list = NULL;
|
|
gboolean ok = FALSE;
|
|
|
|
g_return_val_if_fail( be != NULL, FALSE );
|
|
g_return_val_if_fail( table_name != NULL, FALSE );
|
|
g_return_val_if_fail( col_table != NULL, FALSE );
|
|
|
|
for ( ; col_table->col_name != NULL; col_table++ )
|
|
{
|
|
GncSqlColumnTypeHandler* pHandler;
|
|
|
|
pHandler = get_handler( col_table );
|
|
g_assert( pHandler != NULL );
|
|
pHandler->add_col_info_to_list_fn( be, col_table, &col_info_list );
|
|
}
|
|
g_assert( col_info_list != NULL );
|
|
ok = gnc_sql_connection_create_table( be->conn, table_name, col_info_list );
|
|
return ok;
|
|
}
|
|
|
|
gboolean
|
|
gnc_sql_create_table( GncSqlBackend* be, const gchar* table_name,
|
|
gint table_version, const GncSqlColumnTableEntry* col_table )
|
|
{
|
|
gboolean ok;
|
|
|
|
g_return_val_if_fail( be != NULL, FALSE );
|
|
g_return_val_if_fail( table_name != NULL, FALSE );
|
|
g_return_val_if_fail( col_table != NULL, FALSE );
|
|
|
|
DEBUG( "Creating %s table\n", table_name );
|
|
|
|
ok = do_create_table( be, table_name, col_table );
|
|
if ( ok )
|
|
{
|
|
ok = gnc_sql_set_table_version( be, table_name, table_version );
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
gboolean
|
|
gnc_sql_create_temp_table( const GncSqlBackend* be, const gchar* table_name,
|
|
const GncSqlColumnTableEntry* col_table )
|
|
{
|
|
g_return_val_if_fail( be != NULL, FALSE );
|
|
g_return_val_if_fail( table_name != NULL, FALSE );
|
|
g_return_val_if_fail( col_table != NULL, FALSE );
|
|
|
|
return do_create_table( be, table_name, col_table );
|
|
}
|
|
|
|
gboolean
|
|
gnc_sql_create_index( const GncSqlBackend* be, const gchar* index_name,
|
|
const gchar* table_name,
|
|
const GncSqlColumnTableEntry* col_table )
|
|
{
|
|
gboolean ok;
|
|
|
|
g_return_val_if_fail( be != NULL, FALSE );
|
|
g_return_val_if_fail( index_name != NULL, FALSE );
|
|
g_return_val_if_fail( table_name != NULL, FALSE );
|
|
g_return_val_if_fail( col_table != NULL, FALSE );
|
|
|
|
ok = gnc_sql_connection_create_index( be->conn, index_name, table_name,
|
|
col_table );
|
|
return ok;
|
|
}
|
|
|
|
gint
|
|
gnc_sql_get_table_version( const GncSqlBackend* be, const gchar* table_name )
|
|
{
|
|
g_return_val_if_fail( be != NULL, 0 );
|
|
g_return_val_if_fail( table_name != NULL, 0 );
|
|
|
|
/* If the db is pristine because it's being saved, the table does not exist. */
|
|
if ( be->is_pristine_db )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return GPOINTER_TO_INT(g_hash_table_lookup( be->versions, table_name ));
|
|
}
|
|
|
|
/* Create a temporary table, copy the data from the old table, delete the
|
|
old table, then rename the new one. */
|
|
void
|
|
gnc_sql_upgrade_table( GncSqlBackend* be, const gchar* table_name,
|
|
const GncSqlColumnTableEntry* col_table )
|
|
{
|
|
gchar* sql;
|
|
gchar* temp_table_name;
|
|
|
|
g_return_if_fail( be != NULL );
|
|
g_return_if_fail( table_name != NULL );
|
|
g_return_if_fail( col_table != NULL );
|
|
|
|
DEBUG( "Upgrading %s table\n", table_name );
|
|
|
|
temp_table_name = g_strdup_printf( "%s_new", table_name );
|
|
(void)gnc_sql_create_temp_table( be, temp_table_name, col_table );
|
|
sql = g_strdup_printf( "INSERT INTO %s SELECT * FROM %s",
|
|
temp_table_name, table_name );
|
|
(void)gnc_sql_execute_nonselect_sql( be, sql );
|
|
g_free( sql );
|
|
|
|
sql = g_strdup_printf( "DROP TABLE %s", table_name );
|
|
(void)gnc_sql_execute_nonselect_sql( be, sql );
|
|
g_free( sql );
|
|
|
|
sql = g_strdup_printf( "ALTER TABLE %s RENAME TO %s", temp_table_name, table_name );
|
|
(void)gnc_sql_execute_nonselect_sql( be, sql );
|
|
g_free( sql );
|
|
g_free( temp_table_name );
|
|
}
|
|
|
|
/* Adds one or more columns to an existing table. */
|
|
gboolean gnc_sql_add_columns_to_table( GncSqlBackend* be, const gchar* table_name,
|
|
const GncSqlColumnTableEntry* new_col_table )
|
|
{
|
|
GList* col_info_list = NULL;
|
|
gboolean ok = FALSE;
|
|
|
|
g_return_val_if_fail( be != NULL, FALSE );
|
|
g_return_val_if_fail( table_name != NULL, FALSE );
|
|
g_return_val_if_fail( new_col_table != NULL, FALSE );
|
|
|
|
for ( ; new_col_table->col_name != NULL; new_col_table++ )
|
|
{
|
|
GncSqlColumnTypeHandler* pHandler;
|
|
|
|
pHandler = get_handler( new_col_table );
|
|
g_assert( pHandler != NULL );
|
|
pHandler->add_col_info_to_list_fn( be, new_col_table, &col_info_list );
|
|
}
|
|
g_assert( col_info_list != NULL );
|
|
ok = gnc_sql_connection_add_columns_to_table( be->conn, table_name, col_info_list );
|
|
return ok;
|
|
}
|
|
|
|
/* ================================================================= */
|
|
#define VERSION_TABLE_NAME "versions"
|
|
#define MAX_TABLE_NAME_LEN 50
|
|
#define TABLE_COL_NAME "table_name"
|
|
#define VERSION_COL_NAME "table_version"
|
|
|
|
static GncSqlColumnTableEntry version_table[] =
|
|
{
|
|
/*@ -full_init_block @*/
|
|
{ TABLE_COL_NAME, CT_STRING, MAX_TABLE_NAME_LEN, COL_PKEY | COL_NNUL },
|
|
{ VERSION_COL_NAME, CT_INT, 0, COL_NNUL },
|
|
{ NULL }
|
|
/*@ +full_init_block @*/
|
|
};
|
|
|
|
/**
|
|
* Sees if the version table exists, and if it does, loads the info into
|
|
* the version hash table. Otherwise, it creates an empty version table.
|
|
*
|
|
* @param be Backend struct
|
|
*/
|
|
void
|
|
gnc_sql_init_version_info( GncSqlBackend* be )
|
|
{
|
|
g_return_if_fail( be != NULL );
|
|
|
|
if ( be->versions != NULL )
|
|
{
|
|
g_hash_table_destroy( be->versions );
|
|
}
|
|
be->versions = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, NULL );
|
|
|
|
if ( gnc_sql_connection_does_table_exist( be->conn, VERSION_TABLE_NAME ) )
|
|
{
|
|
GncSqlResult* result;
|
|
gchar* sql;
|
|
|
|
sql = g_strdup_printf( "SELECT * FROM %s", VERSION_TABLE_NAME );
|
|
result = gnc_sql_execute_select_sql( be, sql );
|
|
g_free( sql );
|
|
if ( result != NULL )
|
|
{
|
|
const GValue* name;
|
|
const GValue* version;
|
|
GncSqlRow* row;
|
|
|
|
row = gnc_sql_result_get_first_row( result );
|
|
while ( row != NULL )
|
|
{
|
|
name = gnc_sql_row_get_value_at_col_name( row, TABLE_COL_NAME );
|
|
version = gnc_sql_row_get_value_at_col_name( row, VERSION_COL_NAME );
|
|
g_hash_table_insert( be->versions,
|
|
g_strdup( g_value_get_string( name ) ),
|
|
GINT_TO_POINTER((gint)g_value_get_int64( version )) );
|
|
row = gnc_sql_result_get_next_row( result );
|
|
}
|
|
gnc_sql_result_dispose( result );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
do_create_table( be, VERSION_TABLE_NAME, version_table );
|
|
gnc_sql_set_table_version( be, "Gnucash",
|
|
gnc_prefs_get_long_version() );
|
|
gnc_sql_set_table_version( be, "Gnucash-Resave",
|
|
GNUCASH_RESAVE_VERSION );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resets the version table information by removing all version table info.
|
|
* It also recreates the version table in the db.
|
|
*
|
|
* @param be Backend struct
|
|
* @return TRUE if successful, FALSE if error
|
|
*/
|
|
static gboolean
|
|
reset_version_info( GncSqlBackend* be )
|
|
{
|
|
gboolean ok;
|
|
|
|
g_return_val_if_fail( be != NULL, FALSE );
|
|
|
|
ok = do_create_table( be, VERSION_TABLE_NAME, version_table );
|
|
if ( be->versions == NULL )
|
|
{
|
|
be->versions = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, NULL );
|
|
}
|
|
else
|
|
{
|
|
g_hash_table_remove_all( be->versions );
|
|
}
|
|
|
|
gnc_sql_set_table_version( be, "Gnucash", gnc_prefs_get_long_version() );
|
|
gnc_sql_set_table_version( be, "Gnucash-Resave", GNUCASH_RESAVE_VERSION );
|
|
return ok;
|
|
}
|
|
|
|
/**
|
|
* Finalizes the version table info by destroying the hash table.
|
|
*
|
|
* @param be Backend struct
|
|
*/
|
|
void
|
|
gnc_sql_finalize_version_info( GncSqlBackend* be )
|
|
{
|
|
g_return_if_fail( be != NULL );
|
|
|
|
if ( be->versions != NULL )
|
|
{
|
|
g_hash_table_destroy( be->versions );
|
|
be->versions = NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registers the version for a table. Registering involves updating the
|
|
* db version table and also the hash table.
|
|
*
|
|
* @param be Backend struct
|
|
* @param table_name Table name
|
|
* @param version Version number
|
|
* @return TRUE if successful, FALSE if unsuccessful
|
|
*/
|
|
gboolean
|
|
gnc_sql_set_table_version( GncSqlBackend* be, const gchar* table_name, gint version )
|
|
{
|
|
gchar* sql;
|
|
gint cur_version;
|
|
gint status;
|
|
|
|
g_return_val_if_fail( be != NULL, FALSE );
|
|
g_return_val_if_fail( table_name != NULL, FALSE );
|
|
g_return_val_if_fail( version > 0, FALSE );
|
|
|
|
cur_version = gnc_sql_get_table_version( be, table_name );
|
|
if ( cur_version != version )
|
|
{
|
|
if ( cur_version == 0 )
|
|
{
|
|
sql = g_strdup_printf( "INSERT INTO %s VALUES('%s',%d)", VERSION_TABLE_NAME,
|
|
table_name, version );
|
|
}
|
|
else
|
|
{
|
|
sql = g_strdup_printf( "UPDATE %s SET %s=%d WHERE %s='%s'", VERSION_TABLE_NAME,
|
|
VERSION_COL_NAME, version,
|
|
TABLE_COL_NAME, table_name );
|
|
}
|
|
status = gnc_sql_execute_nonselect_sql( be, sql );
|
|
if ( status == -1 )
|
|
{
|
|
PERR( "SQL error: %s\n", sql );
|
|
qof_backend_set_error( &be->be, ERR_BACKEND_SERVER_ERR );
|
|
}
|
|
g_free( sql );
|
|
}
|
|
|
|
g_hash_table_insert( be->versions, g_strdup( table_name ), GINT_TO_POINTER(version) );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* ========================== END OF FILE ===================== */
|