Backend
[Object: Dynamic Object Class Framework]


Data Structures

struct  QofBackendOption_s

Modules

 QOF Serialisation Format

Files

file  qofbackend.h
 API for data storage Backend.
file  qofsession.h
 Encapsulates a connection to a backend (persistent store).

Defines

#define QOF_MOD_BACKEND   "qof.backend"
#define QOF_MOD_SESSION   "qof.session"
#define QOF_STDOUT   ">"
 Allow session data to be printed to stdout.

Typedefs

typedef struct QofBackendProvider_s QofBackendProvider
typedef struct QofBackend_s QofBackend
 Pseudo-object providing an interface between the engine and a persistant data store (e.g. a server, a database, or a file).
typedef void(* QofBePercentageFunc )(const char *message, double percent)
 DOCUMENT ME!
typedef struct _QofSession QofSession
typedef void(* QofPercentageFunc )(const char *message, double percent)

Enumerations

enum  QofBackendError {
  ERR_BACKEND_NO_ERR = 0, ERR_BACKEND_NO_HANDLER, ERR_BACKEND_NO_BACKEND, ERR_BACKEND_BAD_URL,
  ERR_BACKEND_NO_SUCH_DB, ERR_BACKEND_CANT_CONNECT, ERR_BACKEND_CONN_LOST, ERR_BACKEND_LOCKED,
  ERR_BACKEND_READONLY, ERR_BACKEND_TOO_NEW, ERR_BACKEND_DATA_CORRUPT, ERR_BACKEND_SERVER_ERR,
  ERR_BACKEND_ALLOC, ERR_BACKEND_PERM, ERR_BACKEND_MODIFIED, ERR_BACKEND_MOD_DESTROY,
  ERR_BACKEND_MISC, ERR_QSF_INVALID_OBJ, ERR_QSF_INVALID_MAP, ERR_QSF_BAD_OBJ_GUID,
  ERR_QSF_BAD_QOF_VERSION, ERR_QSF_BAD_MAP, ERR_QSF_NO_MAP, ERR_QSF_WRONG_MAP,
  ERR_QSF_MAP_NOT_OBJ, ERR_QSF_OVERFLOW, ERR_QSF_OPEN_NOT_MERGE, ERR_FILEIO_FILE_BAD_READ = 1000,
  ERR_FILEIO_FILE_EMPTY, ERR_FILEIO_FILE_LOCKERR, ERR_FILEIO_FILE_NOT_FOUND, ERR_FILEIO_FILE_TOO_OLD,
  ERR_FILEIO_UNKNOWN_FILE_TYPE, ERR_FILEIO_PARSE_ERROR, ERR_FILEIO_BACKUP_ERROR, ERR_FILEIO_WRITE_ERROR,
  ERR_FILEIO_READ_ERROR, ERR_FILEIO_NO_ENCODING, ERR_FILEIO_FILE_EACCES, ERR_NETIO_SHORT_READ = 2000,
  ERR_NETIO_WRONG_CONTENT_TYPE, ERR_NETIO_NOT_GNCXML, ERR_SQL_MISSING_DATA = 3000, ERR_SQL_DB_TOO_OLD,
  ERR_SQL_DB_BUSY, ERR_RPC_HOST_UNK = 4000, ERR_RPC_CANT_BIND, ERR_RPC_CANT_ACCEPT,
  ERR_RPC_NO_CONNECTION, ERR_RPC_BAD_VERSION, ERR_RPC_FAILED, ERR_RPC_NOT_ADDED
}
 The errors that can be reported to the GUI & other front-end users. More...

Functions

void qof_backend_set_error (QofBackend *be, QofBackendError err)
QofBackendError qof_backend_get_error (QofBackend *be)
gboolean qof_load_backend_library (const gchar *directory, const gchar *module_name)
 Load a QOF-compatible backend shared library.
void qof_finalize_backend_libraries (void)
 Finalize all loaded backend sharable libraries.
QofBackendqof_book_get_backend (const QofBook *book)
 Retrieve the backend used by this book.
void qof_book_set_backend (QofBook *book, QofBackend *)
QofSession * qof_session_new (void)
void qof_session_destroy (QofSession *session)
void qof_session_swap_data (QofSession *session_1, QofSession *session_2)
void qof_session_begin (QofSession *session, const char *book_id, gboolean ignore_lock, gboolean create_if_nonexistent)
void qof_session_load (QofSession *session, QofPercentageFunc percentage_func)
void qof_session_add_book (QofSession *session, QofBook *book)
QofBookqof_session_get_book (const QofSession *session)
const char * qof_session_get_file_path (const QofSession *session)
const char * qof_session_get_url (const QofSession *session)
gboolean qof_session_save_in_progress (const QofSession *session)
gboolean qof_session_save_may_clobber_data (const QofSession *session)
void qof_session_save (QofSession *session, QofPercentageFunc percentage_func)
void qof_session_end (QofSession *session)
void qof_session_add_close_hook (GFunc fn, gpointer data)
void qof_session_call_close_hooks (QofSession *session)
gboolean qof_session_export (QofSession *tmp_session, QofSession *real_session, QofPercentageFunc percentage_func)
GList * qof_backend_get_registered_access_method_list (void)
void qof_session_ensure_all_data_loaded (QofSession *session)

Detailed Description

The QOF Backend is a pseudo-object providing an interface between the engine and a persistant data store (e.g. a server, a database, or a file). Backends are not meant to be used directly by an application; instead the Session should be used to make a connection with some particular backend. There are no backend functions that are 'public' to users of the engine. The backend can, however, report errors to the GUI & other front-end users. This file defines these errors.

Backends are used to save and restore Entities in a Book.

The QOF Session encapsulates a connection to a storage backend. That is, it manages the connection to a persistant data store; whereas the backend is the thing that performs the actual datastore access.

This class provides several important services:

1) It resolves and loads the appropriate backend, based on the URL.

2) It reports backend errors (e.g. network errors, storage corruption errors) through a single, backend-independent API.

3) It reports non-error events received from the backend.

4) It helps manage global dataset locks. For example, for the file backend, the lock prevents multiple users from editing the same file at the same time, thus avoiding lost data due to race conditions. Thus, an open session implies that the associated file is locked.

5) Misc utilities, such as a search path for the file to be edited, and/or other URL resolution utilities. This should simplify install & maintenance problems for naive users who may not have a good grasp on what a file system is, or where they want to keep their data files.

6) In the future, this class is probably a good place to manage a portion of the user authentication process, and hold user credentials/cookies/keys/tokens. This is because at the coarsest level, authorization can happen at the datastore level: i.e. does this user even have the authority to connect to and open this datastore?

A brief note about books & sessions: A book encapsulates the datasets manipulated by QOF. A book holds the actual data. By contrast, the session mediates the connection between a book (the thing that lives in virtual memory in the local process) and the datastore (the place where book data lives permanently, e.g., file, database).

In the current design, a session may hold multiple books. For now, exactly what this means is somewhat vague, and code in various places makes some implicit assumptions: first, only one book is 'current' and open for editing. Next, its assumed that all of the books in a session are related in some way. i.e. that they are all earlier accounting periods of the currently open book. In particular, the backends probably make that assumption, in order to store the different accounting periods in a clump so that one can be found, given another.

If you want multiple books that are unrelated to each other, use multiple sessions.

The session now calls QofBackendProvider->check_data_type to check that the incoming path contains data that the backend provider can open. The backend provider should also check if it can contact it's storage media (disk, network, server, etc.) and abort if it can't. Malformed file URL's would be handled the same way.


Define Documentation

#define QOF_STDOUT   ">"

Allow session data to be printed to stdout.

book_id can't be NULL and we do need to have an access_method, so use one to solve the other.

To print a session to stdout, use qof_session_begin. Example:

qof_session_begin(session,QOF_STDOUT,TRUE,FALSE);

When you call qof_session_save(session, NULL), the output will appear on stdout and can be piped or redirected to other processes.

Currently, only the QSF backend supports writing to stdout, other backends may return a QofBackendError.

Definition at line 401 of file qofsession.h.


Typedef Documentation

typedef struct QofBackend_s QofBackend

Pseudo-object providing an interface between the engine and a persistant data store (e.g. a server, a database, or a file).

There are no backend functions that are 'public' to users of the engine. The backend can, however, report errors to the GUI & other front-end users.

Definition at line 159 of file qofbackend.h.

A single Backend Configuration Option.

typedef void(* QofBackendOptionCB)(QofBackendOption *, gpointer data)

Backend configuration option foreach callback prototype.

Definition at line 225 of file qofbackend.h.

A structure that declares backend services that can be gotten. The Provider specifies a URL access method, and specifies the function to create a backend that can handle that URL access function.

Definition at line 149 of file qofbackend.h.

typedef void(* QofPercentageFunc)(const char *message, double percent)

The qof_session_load() method causes the QofBook to be made ready to to use with this URL/datastore. When the URL points at a file, then this routine would load the data from the file. With remote backends, e.g. network or SQL, this would load only enough data to make the book actually usable; it would not cause *all* of the data to be loaded.

XXX the current design tries to accomodate multiple calls to 'load' for each session, each time wiping out the old books; this seems wrong to me, and should be restricted to allow only one load per session.

Definition at line 164 of file qofsession.h.


Enumeration Type Documentation

The errors that can be reported to the GUI & other front-end users.

Warning:
(GnuCash) If you modify QofBackendError, please update src/engine/gw-engine-spec.scm
Enumerator:
ERR_BACKEND_NO_HANDLER  no backend handler found for this access method (ENOSYS)
ERR_BACKEND_NO_BACKEND  Backend * pointer was unexpectedly null
ERR_BACKEND_BAD_URL  Can't parse url
ERR_BACKEND_NO_SUCH_DB  the named database doesn't exist
ERR_BACKEND_CANT_CONNECT  bad dbname/login/passwd or network failure
ERR_BACKEND_CONN_LOST  Lost connection to server
ERR_BACKEND_LOCKED  in use by another user (ETXTBSY)
ERR_BACKEND_READONLY  cannot write to file/directory
ERR_BACKEND_TOO_NEW  file/db version newer than what we can read
ERR_BACKEND_DATA_CORRUPT  data in db is corrupt
ERR_BACKEND_SERVER_ERR  error in response from server
ERR_BACKEND_ALLOC  internal memory allocation failure
ERR_BACKEND_PERM  user login successful, but no permissions to access the desired object
ERR_BACKEND_MODIFIED  commit of object update failed because another user has modified the object
ERR_BACKEND_MOD_DESTROY  commit of object update failed because another user has deleted the object
ERR_BACKEND_MISC  undetermined error
ERR_QSF_INVALID_OBJ  The QSF object failed to validate against the QSF object schema
ERR_QSF_INVALID_MAP  The QSF map failed to validate against the QSF map schema
ERR_QSF_BAD_OBJ_GUID  The QSF object contains one or more invalid GUIDs.
ERR_QSF_BAD_QOF_VERSION  QSF map or object doesn't match the current QOF_OBJECT_VERSION.
ERR_QSF_BAD_MAP  The selected map validates but is unusable.

This is usually because not all the required parameters for the defined objects have calculations described in the map.

ERR_QSF_NO_MAP  The QSF object file was loaded without a map

The QSF Object file requires a map but it was not provided.

ERR_QSF_WRONG_MAP  The selected map validates but is for different objects.

The list of objects defined in this map does not include all the objects described in the current QSF object file.

ERR_QSF_MAP_NOT_OBJ  Selected file is a QSF map and cannot be opened as a QSF object
ERR_QSF_OVERFLOW  EOVERFLOW - generated by strtol or strtoll.

When converting XML strings into numbers, an overflow has been detected. The XML file contains invalid data in a field that is meant to hold a signed long integer or signed long long integer.

ERR_FILEIO_FILE_BAD_READ  QSF files cannot be opened alone. The data must be merged.

This error is more of a warning that can be ignored by any routine that uses qof_book_merge on the new session. read failed or file prematurely truncated

ERR_FILEIO_FILE_EMPTY  file exists, is readable, but is empty
ERR_FILEIO_FILE_LOCKERR  mangled locks (unspecified error)
ERR_FILEIO_FILE_NOT_FOUND  not found / no such file
ERR_FILEIO_FILE_TOO_OLD  file version so old we can't read it
ERR_FILEIO_UNKNOWN_FILE_TYPE  didn't recognize the file type
ERR_FILEIO_PARSE_ERROR  couldn't parse the data in the file
ERR_FILEIO_BACKUP_ERROR  couldn't make a backup of the file
ERR_FILEIO_WRITE_ERROR  couldn't write to the file
ERR_FILEIO_READ_ERROR  Could not open the file for reading.
ERR_FILEIO_NO_ENCODING  file does not specify encoding
ERR_FILEIO_FILE_EACCES  No read access permission for the given file
ERR_NETIO_SHORT_READ  not enough bytes received
ERR_NETIO_WRONG_CONTENT_TYPE  wrong kind of server, wrong data served
ERR_NETIO_NOT_GNCXML  whatever it is, we can't parse it.
ERR_SQL_MISSING_DATA  database doesn't contain expected data
ERR_SQL_DB_TOO_OLD  database is old and needs upgrading
ERR_SQL_DB_BUSY  database is busy, cannot upgrade version
ERR_RPC_HOST_UNK  Host unknown
ERR_RPC_CANT_BIND  can't bind to address
ERR_RPC_CANT_ACCEPT  can't accept connection
ERR_RPC_NO_CONNECTION  no connection to server
ERR_RPC_BAD_VERSION  RPC Version Mismatch
ERR_RPC_FAILED  Operation failed
ERR_RPC_NOT_ADDED  object not added

Definition at line 55 of file qofbackend.h.

00056 {
00057     ERR_BACKEND_NO_ERR = 0,
00058     ERR_BACKEND_NO_HANDLER,   
00059     ERR_BACKEND_NO_BACKEND,   
00060     ERR_BACKEND_BAD_URL,      
00061     ERR_BACKEND_NO_SUCH_DB,   
00062     ERR_BACKEND_CANT_CONNECT, 
00063     ERR_BACKEND_CONN_LOST,    
00064     ERR_BACKEND_LOCKED,       
00065     ERR_BACKEND_READONLY,           
00066     ERR_BACKEND_TOO_NEW,      
00067     ERR_BACKEND_DATA_CORRUPT, 
00068     ERR_BACKEND_SERVER_ERR,   
00069     ERR_BACKEND_ALLOC,        
00070     ERR_BACKEND_PERM,         
00072     ERR_BACKEND_MODIFIED,     
00074     ERR_BACKEND_MOD_DESTROY,  
00076     ERR_BACKEND_MISC,         
00078     /* QSF add-ons */
00079     ERR_QSF_INVALID_OBJ,                
00080     ERR_QSF_INVALID_MAP,                
00081     ERR_QSF_BAD_OBJ_GUID,               
00082     ERR_QSF_BAD_QOF_VERSION,    
00083     ERR_QSF_BAD_MAP,                    
00088     ERR_QSF_NO_MAP,             
00092     ERR_QSF_WRONG_MAP,          
00097     ERR_QSF_MAP_NOT_OBJ,                
00098     ERR_QSF_OVERFLOW,                   
00104     ERR_QSF_OPEN_NOT_MERGE,     
00109     /* fileio errors */
00110     ERR_FILEIO_FILE_BAD_READ = 1000,  
00111     ERR_FILEIO_FILE_EMPTY,     
00112     ERR_FILEIO_FILE_LOCKERR,   
00113     ERR_FILEIO_FILE_NOT_FOUND, 
00114     ERR_FILEIO_FILE_TOO_OLD,   
00115     ERR_FILEIO_UNKNOWN_FILE_TYPE, 
00116     ERR_FILEIO_PARSE_ERROR,    
00117     ERR_FILEIO_BACKUP_ERROR,   
00118     ERR_FILEIO_WRITE_ERROR,    
00119     ERR_FILEIO_READ_ERROR,     
00120     ERR_FILEIO_NO_ENCODING,    
00121     ERR_FILEIO_FILE_EACCES,    
00123     /* network errors */
00124     ERR_NETIO_SHORT_READ = 2000,  
00125     ERR_NETIO_WRONG_CONTENT_TYPE, 
00126     ERR_NETIO_NOT_GNCXML,         
00128     /* database errors */
00129     ERR_SQL_MISSING_DATA = 3000,  
00130     ERR_SQL_DB_TOO_OLD,           
00131     ERR_SQL_DB_BUSY,              
00133     /* RPC errors */
00134     ERR_RPC_HOST_UNK = 4000,      
00135     ERR_RPC_CANT_BIND,            
00136     ERR_RPC_CANT_ACCEPT,          
00137     ERR_RPC_NO_CONNECTION,        
00138     ERR_RPC_BAD_VERSION,          
00139     ERR_RPC_FAILED,               
00140     ERR_RPC_NOT_ADDED,            
00141 } QofBackendError;


Function Documentation

KvpFrame* qof_backend_complete_frame ( QofBackend be  ) 

Complete the backend_configuration and return the frame.

Definition at line 285 of file qofbackend.c.

00286 {
00287     g_return_val_if_fail(be, NULL);
00288     be->config_count = 0;
00289     return be->backend_configuration;
00290 }

KvpFrame* qof_backend_get_config ( QofBackend be  ) 

Get the available configuration options.

To retrieve the options from the returned KvpFrame, the caller needs to parse the XML file that documents the option names and data types. The XML file itself is part of the backend and is installed in a directory determined by the backend. Therefore, loading a new backend requires two paths: the path to the .la file and the path to the xml. Both paths are available by including a generated header file, e.g. gncla-dir.h defines GNC_LIB_DIR for the location of the .la file and GNC_XML_DIR for the xml.

Parameters:
be The QofBackend to be configured.
Returns:
A new KvpFrame containing the available options or NULL on failure.

Definition at line 489 of file qofbackend.c.

00490 {
00491     if (!be)
00492     {
00493         return NULL;
00494     }
00495     if (!be->get_config)
00496     {
00497         return NULL;
00498     }
00499     return (be->get_config) (be);
00500 }

QofBackendError qof_backend_get_error ( QofBackend be  ) 

The qof_backend_get_error() routine pops an error code off the error stack.

Definition at line 56 of file qofbackend.c.

00057 {
00058     QofBackendError err;
00059     if (!be) return ERR_BACKEND_NO_BACKEND;
00060 
00061     /* use 'stack-pop' semantics */
00062     err = be->last_err;
00063     be->last_err = ERR_BACKEND_NO_ERR;
00064     return err;
00065 }

GList* qof_backend_get_registered_access_method_list ( void   ) 

Return a list of strings for the registered access methods. The owner is responsible for freeing the list but not the strings.

Definition at line 70 of file qofsession.c.

00071 {
00072     GList* list = NULL;
00073     GSList* node;
00074 
00075     for ( node = provider_list; node != NULL; node = node->next )
00076     {
00077         QofBackendProvider *prov = node->data;
00078         list = g_list_append( list, (gchar*)prov->access_method );
00079     }
00080 
00081     return list;
00082 }

void qof_backend_load_config ( QofBackend be,
KvpFrame config 
)

Load configuration options specific to this backend.

Parameters:
be The backend to configure.
config A KvpFrame of QofBackendOptions that this backend will recognise. Each backend needs to document their own config types and acceptable values.

Definition at line 475 of file qofbackend.c.

00476 {
00477     if (!be || !config)
00478     {
00479         return;
00480     }
00481     if (!be->load_config)
00482     {
00483         return;
00484     }
00485     (be->load_config) (be, config);
00486 }

void qof_backend_option_foreach ( KvpFrame config,
QofBackendOptionCB  cb,
gpointer  data 
)

Iterate over the frame and process each option.

Definition at line 457 of file qofbackend.c.

00458 {
00459     struct config_iterate helper;
00460 
00461     if (!config || !cb)
00462     {
00463         return;
00464     }
00465     ENTER (" ");
00466     helper.fcn = cb;
00467     helper.count = 1;
00468     helper.data = data;
00469     helper.recursive = config;
00470     kvp_frame_for_each_slot(config, config_foreach_cb, &helper);
00471     LEAVE (" ");
00472 }

void qof_backend_prepare_frame ( QofBackend be  ) 

Initialise the backend_configuration

Definition at line 200 of file qofbackend.c.

00201 {
00202     g_return_if_fail(be);
00203     if (!kvp_frame_is_empty(be->backend_configuration))
00204     {
00205         kvp_frame_delete(be->backend_configuration);
00206         be->backend_configuration = kvp_frame_new();
00207     }
00208     be->config_count = 0;
00209 }

void qof_backend_prepare_option ( QofBackend be,
const QofBackendOption option 
)

Add an option to the backend_configuration. Repeat for more.

Definition at line 211 of file qofbackend.c.

00212 {
00213     KvpValue *value;
00214     gchar *temp;
00215     gint count;
00216 
00217     g_return_if_fail(be || option);
00218     count = be->config_count;
00219     count++;
00220     value = NULL;
00221     switch (option->type)
00222     {
00223     case KVP_TYPE_GINT64   :
00224     {
00225         value = kvp_value_new_gint64(*(gint64*)option->value);
00226         break;
00227     }
00228     case KVP_TYPE_DOUBLE   :
00229     {
00230         value = kvp_value_new_double(*(double*)option->value);
00231         break;
00232     }
00233     case KVP_TYPE_NUMERIC  :
00234     {
00235         value = kvp_value_new_numeric(*(gnc_numeric*)option->value);
00236         break;
00237     }
00238     case KVP_TYPE_STRING   :
00239     {
00240         value = kvp_value_new_string((const char*)option->value);
00241         break;
00242     }
00243     case KVP_TYPE_GUID     :
00244     {
00245         break;  /* unsupported */
00246     }
00247     case KVP_TYPE_TIMESPEC :
00248     {
00249         value = kvp_value_new_timespec(*(Timespec*)option->value);
00250         break;
00251     }
00252     case KVP_TYPE_BINARY   :
00253     {
00254         break;  /* unsupported */
00255     }
00256     case KVP_TYPE_GLIST    :
00257     {
00258         break;  /* unsupported */
00259     }
00260     case KVP_TYPE_FRAME    :
00261     {
00262         break;  /* unsupported */
00263     }
00264     case KVP_TYPE_GDATE    :
00265     {
00266         break;  /* unsupported */
00267     }
00268     }
00269     if (value)
00270     {
00271         temp = g_strdup_printf("/%s", option->option_name);
00272         kvp_frame_set_value(be->backend_configuration, temp, value);
00273         g_free(temp);
00274         temp = g_strdup_printf("/%s/%s", QOF_CONFIG_DESC, option->option_name);
00275         kvp_frame_set_string(be->backend_configuration, temp, option->description);
00276         g_free(temp);
00277         temp = g_strdup_printf("/%s/%s", QOF_CONFIG_TIP, option->option_name);
00278         kvp_frame_set_string(be->backend_configuration, temp, option->tooltip);
00279         g_free(temp);
00280         /* only increment the counter if successful */
00281         be->config_count = count;
00282     }
00283 }

void qof_backend_set_error ( QofBackend be,
QofBackendError  err 
)

The qof_backend_set_error() routine pushes an error code onto the error stack. (FIXME: the stack is 1 deep in current implementation).

Definition at line 46 of file qofbackend.c.

00047 {
00048     if (!be) return;
00049 
00050     /* use stack-push semantics. Only the earliest error counts */
00051     if (ERR_BACKEND_NO_ERR != be->last_err) return;
00052     be->last_err = err;
00053 }

gboolean qof_instance_copy_coll ( QofSession *  new_session,
QofCollection *  entity_coll 
)

Copy a QofCollection of entities.

The QofBook in the new_session must not contain any entities with the same GUID as any entities in the collection - there is no support for handling collisions - instead, use Merging QofBook structures

Parameters:
new_session - the target session
entity_coll - a QofCollection of any QofIdType.
Returns:
FALSE, without copying, if new_session contains any entities with the same GUID. Otherwise TRUE.

Definition at line 775 of file qofsession.c.

00776 {
00777     QofInstanceCopyData qecd;
00778 
00779     g_return_val_if_fail(new_session, FALSE);
00780     if (!entity_coll)
00781     {
00782         return FALSE;
00783     }
00784     qof_event_suspend();
00785     qecd.param_list = NULL;
00786     qecd.new_session = new_session;
00787     qof_book_set_partial(qof_session_get_book(qecd.new_session));
00788     qof_collection_foreach(entity_coll, qof_instance_coll_foreach, &qecd);
00789     qof_class_param_foreach(qof_collection_get_type(entity_coll),
00790                             qof_instance_param_cb, &qecd);
00791     qof_collection_foreach(entity_coll, qof_instance_coll_copy, &qecd);
00792     if (qecd.param_list != NULL)
00793     {
00794         g_slist_free(qecd.param_list);
00795     }
00796     qof_event_resume();
00797     return TRUE;
00798 }

gboolean qof_instance_copy_coll_r ( QofSession *  new_session,
QofCollection *  coll 
)

Recursively copy a collection of entities to a session.

Note:
This function creates a partial QofBook. See qof_instance_copy_to_session for more information.
The QofBook in the new_session must not contain any entities with the same GUID as any entities to be copied - there is no support for handling collisions - instead, use Merging QofBook structures

Objects can be defined solely in terms of QOF data types or as a mix of data types and other objects, which may in turn include other objects. These references can be copied recursively down to the third level. See QofInstanceReference.

Note:
This is a deep recursive copy - every referenced entity is copied to the new session, including all parameters. The starting point is all entities in the top level collection. It can take some time.
Parameters:
coll A QofCollection of entities that may or may not have references.
new_session The QofSession to receive the copied entities.
Returns:
TRUE on success; if any individual copy fails, returns FALSE. Note : Some entities may have been copied successfully even if one of the references fails to copy.

Definition at line 948 of file qofsession.c.

00949 {
00950     struct recurse_s store;
00951     gboolean success;
00952 
00953     if ((!new_session) || (!coll))
00954     {
00955         return FALSE;
00956     }
00957     store.session = new_session;
00958     success = TRUE;
00959     store.success = success;
00960     store.ent_list = NULL;
00961     store.ref_list = qof_class_get_referenceList(qof_collection_get_type(coll));
00962     success = qof_instance_copy_coll(new_session, coll);
00963     if (success)
00964     {
00965         qof_collection_foreach(coll, recurse_ent_cb, &store);
00966     }
00967     return success;
00968 }

gboolean qof_instance_copy_list ( QofSession *  new_session,
GList *  entity_list 
)

Copy a GList of entities to another session.

The QofBook in the new_session must not contain any entities with the same GUID as any of the source entities - there is no support for handling collisions, instead use Merging QofBook structures

Note that the GList (e.g. from qof_sql_query_run) can contain QofInstance pointers of any QofIdType, in any sequence. As long as all members of the list are QofInstance*, and all GUID's are unique, the list can be copied.

Parameters:
new_session - the target session
entity_list - a GList of QofInstance pointers of any type(s).
Returns:
FALSE, without copying, if new_session contains any entities with the same GUID. Otherwise TRUE.

Definition at line 749 of file qofsession.c.

00750 {
00751     QofInstanceCopyData *qecd;
00752 
00753     if (!new_session || !entity_list)
00754     {
00755         return FALSE;
00756     }
00757     ENTER (" list=%d", g_list_length(entity_list));
00758     qecd = g_new0(QofInstanceCopyData, 1);
00759     qof_event_suspend();
00760     qecd->param_list = NULL;
00761     qecd->new_session = new_session;
00762     qof_book_set_partial(qof_session_get_book(new_session));
00763     g_list_foreach(entity_list, qof_instance_list_foreach, qecd);
00764     qof_event_resume();
00765     if (qecd->error)
00766     {
00767         PWARN (" some/all entities in the list could not be copied.");
00768     }
00769     g_free(qecd);
00770     LEAVE (" ");
00771     return TRUE;
00772 }

gboolean qof_instance_copy_one_r ( QofSession *  new_session,
QofInstance *  ent 
)

Recursively copy a single entity to a new session.

Copy the single entity and all referenced entities to the second level.

Only entities that are directly referenced by the top level entity are copied.

This is a deep copy - all parameters of all referenced entities are copied. If the top level entity has no references, this is identical to qof_instance_copy_to_session.

Parameters:
ent A single entity that may or may not have references.
new_session The QofSession to receive the copied entities.
Returns:
TRUE on success; if any individual copy fails, returns FALSE. Note : Some entities may have been copied successfully even if one of the references fails to copy.

Definition at line 970 of file qofsession.c.

00971 {
00972     struct recurse_s store;
00973     QofCollection *coll;
00974     gboolean success;
00975 
00976     if ((!new_session) || (!ent))
00977     {
00978         return FALSE;
00979     }
00980     store.session = new_session;
00981     success = TRUE;
00982     store.success = success;
00983     store.ref_list = qof_class_get_referenceList(ent->e_type);
00984     success = qof_instance_copy_to_session(new_session, ent);
00985     if (success == TRUE)
00986     {
00987         coll = qof_book_get_collection(qof_session_get_book(new_session), ent->e_type);
00988         if (coll)
00989         {
00990             qof_collection_foreach(coll, recurse_ent_cb, &store);
00991         }
00992     }
00993     return success;
00994 }

gboolean qof_instance_copy_to_session ( QofSession *  new_session,
QofInstance *  original 
)

Copy a single QofInstance to another session.

Checks first that no entity in the session book contains the GUID of the source entity.

Parameters:
new_session - the target session
original - the QofInstance* to copy
Returns:
FALSE without copying if the session contains an entity with the same GUID already, otherwise TRUE.

Definition at line 709 of file qofsession.c.

00710 {
00711     QofInstanceCopyData qecd;
00712     QofInstance *inst;
00713     QofBook *book;
00714 
00715     if (!new_session || !original)
00716     {
00717         return FALSE;
00718     }
00719     if (qof_instance_guid_match(new_session, original))
00720     {
00721         return FALSE;
00722     }
00723     if (!qof_object_compliance(original->e_type, TRUE))
00724     {
00725         return FALSE;
00726     }
00727     qof_event_suspend();
00728     qecd.param_list = NULL;
00729     book = qof_session_get_book(new_session);
00730     qecd.new_session = new_session;
00731     qof_book_set_partial(book);
00732     inst = (QofInstance*)qof_object_new_instance(original->e_type, book);
00733     qecd.to = inst;
00734     qecd.from = original;
00735     qof_instance_set_guid(qecd.to, qof_instance_get_guid(original));
00736     qof_begin_edit(inst);
00737     qof_class_param_foreach(original->e_type, qof_instance_param_cb, &qecd);
00738     qof_commit_edit(inst);
00739     if (g_slist_length(qecd.param_list) == 0)
00740     {
00741         return FALSE;
00742     }
00743     g_slist_foreach(qecd.param_list, qof_instance_foreach_copy, &qecd);
00744     g_slist_free(qecd.param_list);
00745     qof_event_resume();
00746     return TRUE;
00747 }

gboolean qof_load_backend_library ( const gchar *  directory,
const gchar *  module_name 
)

Load a QOF-compatible backend shared library.

Parameters:
directory Can be NULL if filename is a complete path.
module_name Name of the .la file that describes the shared library. This provides platform independence, courtesy of libtool.
Returns:
FALSE in case or error, otherwise TRUE.

void qof_session_add_book ( QofSession *  session,
QofBook book 
)

The qof_session_add_book() allows additional books to be added to a session. XXX Under construction, clarify the following when done: XXX There must already be an open book in the session already!? XXX Only one open book at a time per session is allowed!? XXX each book gets its own unique backend ???

Definition at line 249 of file qofsession.c.

00250 {
00251     GList *node;
00252     if (!session) return;
00253 
00254     ENTER (" sess=%p book=%p", session, addbook);
00255 
00256     /* See if this book is already there ... */
00257     for (node = session->books; node; node = node->next)
00258     {
00259         QofBook *book = node->data;
00260         if (addbook == book) return;
00261     }
00262 
00263     if ('y' == addbook->book_open)
00264     {
00265         /* hack alert -- someone should free all the books in the list,
00266          * but it should probably not be us ... since the books backends
00267          * should be shutdown first, etc */
00268         /* XXX this should probably be an error XXX */
00269         g_list_free (session->books);
00270         session->books = g_list_append (NULL, addbook);
00271     }
00272     else
00273     {
00274         /* XXX Need to tell the backend to add a book as well */
00275         session->books = g_list_append (session->books, addbook);
00276     }
00277 
00278     qof_book_set_backend (addbook, session->backend);
00279     LEAVE (" ");
00280 }

void qof_session_add_close_hook ( GFunc  fn,
gpointer  data 
)

Register a function to be called just before a session is closed.

Parameters:
fn The function to be called. The function definition must be func(gpointer session, gpointer user_data);
data The data to be passed to the function.

Definition at line 89 of file qofsession.c.

00090 {
00091     GHook *hook;
00092 
00093     if (session_closed_hooks == NULL)
00094     {
00095         session_closed_hooks = malloc(sizeof(GHookList)); /* LEAKED */
00096         g_hook_list_init (session_closed_hooks, sizeof(GHook));
00097     }
00098 
00099     hook = g_hook_alloc(session_closed_hooks);
00100     if (!hook)
00101         return;
00102 
00103     hook->func = (GHookFunc)fn;
00104     hook->data = data;
00105     g_hook_append(session_closed_hooks, hook);
00106 }

void qof_session_begin ( QofSession *  session,
const char *  book_id,
gboolean  ignore_lock,
gboolean  create_if_nonexistent 
)

The qof_session_begin () method begins a new session. It takes as an argument the book id. The book id must be a string in the form of a URI/URL. The access method specified depends on the loaded backends. In the absence of a customised backend, only QSF XML would be accepted). Paths may be relative or absolute. If the path is relative; that is, if the argument is "file:somefile.xml" then the current working directory is assumed. Customised backends can choose to search other, application-specific, directories as well.

The 'ignore_lock' argument, if set to TRUE, will cause this routine to ignore any global-datastore locks (e.g. file locks) that it finds. If set to FALSE, then file/database-global locks will be tested and obeyed.

If the datastore exists, can be reached (e.g over the net), connected to, opened and read, and a lock can be obtained then a lock will be obtained. Note that multi-user datastores (e.g. the SQL backend) typically will not need to get a global lock, and thus, the user will not be locked out. That's the whole point of 'multi-user'.

If the file/database doesn't exist, and the create_if_nonexistent flag is set to TRUE, then the database is created.

If an error occurs, it will be pushed onto the session error stack, and that is where it should be examined.

Definition at line 1140 of file qofsession.c.

01142 {
01143     gchar **splituri;
01144 
01145     if (!session) return;
01146 
01147     ENTER (" sess=%p ignore_lock=%d, book-id=%s",
01148            session, ignore_lock,
01149            book_id ? book_id : "(null)");
01150 
01151     /* Clear the error condition of previous errors */
01152     qof_session_clear_error (session);
01153 
01154     /* Check to see if this session is already open */
01155     if (session->book_id)
01156     {
01157         qof_session_push_error (session, ERR_BACKEND_LOCKED, NULL);
01158         LEAVE("push error book is already open ");
01159         return;
01160     }
01161 
01162     /* seriously invalid */
01163     if (!book_id)
01164     {
01165         qof_session_push_error (session, ERR_BACKEND_BAD_URL, NULL);
01166         LEAVE("push error missing book_id");
01167         return;
01168     }
01169 
01170     /* destroy the old backend */
01171     qof_session_destroy_backend(session);
01172 
01173     /* Store the session URL  */
01174     session->book_id = g_strdup (book_id);
01175 
01176     /* Look for something of the form of "file://", "http://" or
01177      * "postgres://". Everything before the colon is the access
01178      * method.  Load the first backend found for that access method.
01179      */
01180     splituri = g_strsplit ( book_id, "://", 2 );
01181     if ( splituri[1] == NULL ) /* no access method in the uri, use generic "file" backend */
01182         qof_session_load_backend(session, "file");
01183     else                       /* access method found, load appropriate backend */
01184         qof_session_load_backend(session, splituri[0]);
01185     g_strfreev ( splituri );
01186 
01187     /* No backend was found. That's bad. */
01188     if (NULL == session->backend)
01189     {
01190         g_free(session->book_id);
01191         session->book_id = NULL;
01192         qof_session_push_error (session, ERR_BACKEND_BAD_URL, NULL);
01193         LEAVE (" BAD: no backend: sess=%p book-id=%s",
01194                session,  book_id ? book_id : "(null)");
01195         return;
01196     }
01197 
01198     /* If there's a begin method, call that. */
01199     if (session->backend->session_begin)
01200     {
01201         char *msg;
01202         int err;
01203 
01204         (session->backend->session_begin)(session->backend, session,
01205                                           session->book_id, ignore_lock,
01206                                           create_if_nonexistent);
01207         PINFO("Done running session_begin on backend");
01208         err = qof_backend_get_error(session->backend);
01209         msg = qof_backend_get_message(session->backend);
01210         if (err != ERR_BACKEND_NO_ERR)
01211         {
01212             g_free(session->book_id);
01213             session->book_id = NULL;
01214             qof_session_push_error (session, err, msg);
01215             LEAVE(" backend error %d %s", err, msg ? msg : "(null)");
01216             return;
01217         }
01218         if (msg != NULL)
01219         {
01220             PWARN("%s", msg);
01221             g_free(msg);
01222         }
01223     }
01224 
01225     LEAVE (" sess=%p book-id=%s",
01226            session,  book_id ? book_id : "(null)");
01227 }

void qof_session_call_close_hooks ( QofSession *  session  ) 

Call all registered session close hooks, informing them that the specified session is about to be closed.

Parameters:
session A pointer to the session being closed.

Definition at line 109 of file qofsession.c.

00110 {
00111     GHook *hook;
00112     GFunc fn;
00113 
00114     if (session_closed_hooks == NULL)
00115         return;
00116 
00117     hook = g_hook_first_valid (session_closed_hooks, FALSE);
00118     while (hook)
00119     {
00120         fn = (GFunc)hook->func;
00121         fn(session, hook->data);
00122         hook = g_hook_next_valid (session_closed_hooks, hook, FALSE);
00123     }
00124 }

void qof_session_end ( QofSession *  session  ) 

The qof_session_end() method will release the session lock. For the file backend, it will *not* save the data to a file. Thus, this method acts as an "abort" or "rollback" primitive. However, for other backends, such as the sql backend, the data would have been written out before this, and so this routines wouldn't roll-back anything; it would just shut the connection.

Definition at line 1516 of file qofsession.c.

01517 {
01518     if (!session) return;
01519 
01520     ENTER ("sess=%p book_id=%s", session, session->book_id
01521            ? session->book_id : "(null)");
01522 
01523     /* close down the backend first */
01524     if (session->backend && session->backend->session_end)
01525     {
01526         (session->backend->session_end)(session->backend);
01527     }
01528 
01529     qof_session_clear_error (session);
01530 
01531     g_free (session->book_id);
01532     session->book_id = NULL;
01533 
01534     LEAVE ("sess=%p book_id=%s", session, session->book_id
01535            ? session->book_id : "(null)");
01536 }

void qof_session_ensure_all_data_loaded ( QofSession *  session  ) 

Ensure all of the data is loaded from the session.

Definition at line 305 of file qofsession.c.

00306 {
00307     QofBackend* backend;
00308 
00309     if (session == NULL) return;
00310     backend = qof_session_get_backend(session);
00311     if (backend == NULL) return;
00312 
00313     if (backend->load == NULL) return;
00314     backend->load(backend, qof_session_get_book(session), LOAD_TYPE_LOAD_ALL);
00315     qof_session_push_error (session, qof_backend_get_error(backend), NULL);
00316 }

gboolean qof_session_events_pending ( const QofSession *  session  ) 

The qof_session_events_pending() method will return TRUE if the backend has pending events which must be processed to bring the engine up to date with the backend.

Definition at line 1602 of file qofsession.c.

01603 {
01604     if (!session) return FALSE;
01605     if (!session->backend) return FALSE;
01606     if (!session->backend->events_pending) return FALSE;
01607 
01608     return session->backend->events_pending (session->backend);
01609 }

QofBackendError qof_session_get_error ( QofSession *  session  ) 

The qof_session_get_error() routine can be used to obtain the reason for any failure. Calling this routine returns the current error.

Definition at line 162 of file qofsession.c.

00163 {
00164     QofBackendError err;
00165 
00166     if (!session) return ERR_BACKEND_NO_BACKEND;
00167 
00168     /* if we have a local error, return that. */
00169     if (ERR_BACKEND_NO_ERR != session->last_err)
00170     {
00171         return session->last_err;
00172     }
00173 
00174     /* maybe we should return a no-backend error ??? */
00175     if (! session->backend) return ERR_BACKEND_NO_ERR;
00176 
00177     err = qof_backend_get_error (session->backend);
00178     session->last_err = err;
00179     return err;
00180 }

const char* qof_session_get_file_path ( const QofSession *  session  ) 

The qof_session_get_file_path() routine returns the fully-qualified file path for the session. That is, if a relative or partial filename was for the session, then it had to have been fully resolved to open the session. This routine returns the result of this resolution. The path is always guaranteed to reside in the local file system, even if the session itself was opened as a URL. (currently, the filepath is derived from the url by substituting commas for slashes).

The qof_session_get_url() routine returns the url that was opened. URL's for local files take the form of file:/some/where/some/file.gml

Definition at line 290 of file qofsession.c.

00291 {
00292     if (!session) return NULL;
00293     if (!session->backend) return NULL;
00294     return session->backend->fullpath;
00295 }

QofBackendError qof_session_pop_error ( QofSession *  session  ) 

The qof_session_pop_error() routine can be used to obtain the reason for any failure. Calling this routine resets the error value.

This routine allows an implementation of multiple error values, e.g. in a stack, where this routine pops the top value. The current implementation has a stack that is one-deep.

See qofbackend.h for a listing of returned errors.

Definition at line 198 of file qofsession.c.

00199 {
00200     QofBackendError err;
00201 
00202     if (!session) return ERR_BACKEND_NO_BACKEND;
00203 
00204     err = qof_session_get_error(session);
00205     qof_session_clear_error(session);
00206 
00207     return err;
00208 }

gboolean qof_session_process_events ( QofSession *  session  ) 

The qof_session_process_events() method will process any events indicated by the qof_session_events_pending() method. It returns TRUE if the engine was modified while engine events were suspended.

Definition at line 1612 of file qofsession.c.

01613 {
01614     if (!session) return FALSE;
01615     if (!session->backend) return FALSE;
01616     if (!session->backend->process_events) return FALSE;
01617 
01618     return session->backend->process_events (session->backend);
01619 }

void qof_session_save ( QofSession *  session,
QofPercentageFunc  percentage_func 
)

The qof_session_save() method will commit all changes that have been made to the session. For the file backend, this is nothing more than a write to the file of the current Accounts & etc. For the SQL backend, this is typically a no-op (since all data has already been written out to the database.

Todo:
check the access_method too, not in scope here, yet.

Definition at line 1345 of file qofsession.c.

01347 {
01348     GList *node;
01349     QofBackend *be;
01350     gboolean partial, change_backend;
01351     QofBackendProvider *prov;
01352     GSList *p;
01353     QofBook *book, *abook;
01354     int err;
01355     gint num;
01356     char *msg = NULL;
01357     char *book_id;
01358 
01359     if (!session) return;
01360     if (!g_atomic_int_dec_and_test(&session->lock))
01361         goto leave;
01362     ENTER ("sess=%p book_id=%s",
01363            session, session->book_id ? session->book_id : "(null)");
01364     /* Partial book handling. */
01365     book = qof_session_get_book(session);
01366     partial = (gboolean)GPOINTER_TO_INT(qof_book_get_data(book, PARTIAL_QOFBOOK));
01367     change_backend = FALSE;
01368     msg = g_strdup_printf(" ");
01369     book_id = g_strdup(session->book_id);
01370     if (partial == TRUE)
01371     {
01372         if (session->backend && session->backend->provider)
01373         {
01374             prov = session->backend->provider;
01375             if (TRUE == prov->partial_book_supported)
01376             {
01377                 /* if current backend supports partial, leave alone. */
01378                 change_backend = FALSE;
01379             }
01380             else
01381             {
01382                 change_backend = TRUE;
01383             }
01384         }
01385         /* If provider is undefined, assume partial not supported. */
01386         else
01387         {
01388             change_backend = TRUE;
01389         }
01390     }
01391     if (change_backend == TRUE)
01392     {
01393         qof_session_destroy_backend(session);
01394         if (!qof_providers_initialized)
01395         {
01396             for (num = 0; backend_list[num].filename != NULL; num++)
01397             {
01398                 qof_load_backend_library(backend_list[num].libdir,
01399                                          backend_list[num].filename);
01400             }
01401             qof_providers_initialized = TRUE;
01402         }
01403         p = provider_list;
01404         while (p != NULL)
01405         {
01406             prov = p->data;
01407             if (TRUE == prov->partial_book_supported)
01408             {
01410                 /*      if((TRUE == prov->partial_book_supported) &&
01411                 (0 == g_ascii_strcasecmp (access_method, prov->access_method)))
01412                 {*/
01413                 if (NULL == prov->backend_new) continue;
01414                 /* Use the providers creation callback */
01415                 session->backend = (*(prov->backend_new))();
01416                 session->backend->provider = prov;
01417                 if (session->backend->session_begin)
01418                 {
01419                     /* Call begin - backend has been changed,
01420                        so make sure a file can be written,
01421                        use ignore_lock and create_if_nonexistent */
01422                     g_free(session->book_id);
01423                     session->book_id = NULL;
01424                     (session->backend->session_begin)(session->backend, session,
01425                                                       book_id, TRUE, TRUE);
01426                     PINFO("Done running session_begin on changed backend");
01427                     err = qof_backend_get_error(session->backend);
01428                     msg = qof_backend_get_message(session->backend);
01429                     if (err != ERR_BACKEND_NO_ERR)
01430                     {
01431                         g_free(session->book_id);
01432                         session->book_id = NULL;
01433                         qof_session_push_error (session, err, msg);
01434                         LEAVE("changed backend error %d", err);
01435                         goto leave;
01436                     }
01437                     if (msg != NULL)
01438                     {
01439                         PWARN("%s", msg);
01440                         g_free(msg);
01441                         msg = NULL;
01442                     }
01443                 }
01444                 /* Tell the books about the backend that they'll be using. */
01445                 for (node = session->books; node; node = node->next)
01446                 {
01447                     book = node->data;
01448                     qof_book_set_backend (book, session->backend);
01449                 }
01450                 p = NULL;
01451             }
01452             if (p)
01453             {
01454                 p = p->next;
01455             }
01456         }
01457         if (!session->backend)
01458         {
01459             msg = g_strdup_printf("failed to load backend");
01460             qof_session_push_error(session, ERR_BACKEND_NO_HANDLER, msg);
01461             goto leave;
01462         }
01463     }
01464     /* If there is a backend, and the backend is reachable
01465     * (i.e. we can communicate with it), then synchronize with
01466     * the backend.  If we cannot contact the backend (e.g.
01467     * because we've gone offline, the network has crashed, etc.)
01468     * then give the user the option to save to the local disk.
01469     *
01470     * hack alert -- FIXME -- XXX the code below no longer
01471     * does what the words above say.  This needs fixing.
01472     */
01473     be = session->backend;
01474     if (be)
01475     {
01476         for (node = session->books; node; node = node->next)
01477         {
01478             abook = node->data;
01479             /* if invoked as SaveAs(), then backend not yet set */
01480             qof_book_set_backend (abook, be);
01481             be->percentage = percentage_func;
01482             if (be->sync)
01483             {
01484                 (be->sync)(be, abook);
01485                 if (save_error_handler(be, session))
01486                     goto leave;
01487             }
01488         }
01489         /* If we got to here, then the backend saved everything
01490         * just fine, and we are done. So return. */
01491         /* Return the book_id to previous value. */
01492         qof_session_clear_error (session);
01493         LEAVE("Success");
01494         goto leave;
01495     }
01496     else
01497     {
01498         msg = g_strdup_printf("failed to load backend");
01499         qof_session_push_error(session, ERR_BACKEND_NO_HANDLER, msg);
01500     }
01501     LEAVE("error -- No backend!");
01502 leave:
01503     if (msg != NULL) g_free(msg);
01504     g_atomic_int_inc(&session->lock);
01505     return;
01506 }

gboolean qof_session_save_in_progress ( const QofSession *  session  ) 

The qof_session_not_saved() subroutine will return TRUE if any data in the session hasn't been saved to long-term storage.

Definition at line 1510 of file qofsession.c.

01511 {
01512     return (session && g_atomic_int_get(&session->lock) != 1);
01513 }

gboolean qof_session_save_may_clobber_data ( const QofSession *  session  ) 

Allows the backend to warn the user if a dataset already exists.

Definition at line 1321 of file qofsession.c.

01322 {
01323     if (!session) return FALSE;
01324     if (!session->backend) return FALSE;
01325     if (!session->backend->save_may_clobber_data) return FALSE;
01326 
01327     return (*(session->backend->save_may_clobber_data)) (session->backend);
01328 }

void qof_session_swap_data ( QofSession *  session_1,
QofSession *  session_2 
)

The qof_session_swap_data () method swaps the book of the two given sessions. It is useful for 'Save As' type functionality.

Definition at line 1570 of file qofsession.c.

01571 {
01572     GList *books_1, *books_2, *node;
01573 
01574     if (session_1 == session_2) return;
01575     if (!session_1 || !session_2) return;
01576 
01577     ENTER ("sess1=%p sess2=%p", session_1, session_2);
01578 
01579     books_1 = session_1->books;
01580     books_2 = session_2->books;
01581 
01582     session_1->books = books_2;
01583     session_2->books = books_1;
01584 
01585     for (node = books_1; node; node = node->next)
01586     {
01587         QofBook *book_1 = node->data;
01588         qof_book_set_backend (book_1, session_2->backend);
01589     }
01590     for (node = books_2; node; node = node->next)
01591     {
01592         QofBook *book_2 = node->data;
01593         qof_book_set_backend (book_2, session_1->backend);
01594     }
01595 
01596     LEAVE (" ");
01597 }


Generated on Fri Mar 19 04:58:58 2010 for GnuCash by  doxygen 1.5.7.1