Copyright (c) 2000 Dave Peticolas
Copyright (c) 2005 Neil Williams <linux@codehelp.co.uk>
Definition in file qofsession.h.
#include "qofbackend.h"
#include "qofbook.h"
#include "qofclass.h"
#include "qofobject.h"
Go to the source code of this file.
Session Errors | |
QofBackendError | qof_session_get_error (QofSession *session) |
const char * | qof_session_get_error_message (QofSession *session) |
QofBackendError | qof_session_pop_error (QofSession *session) |
Copying entities between sessions. | |
Only certain backends can cope with selective copying of entities and only fully defined QOF entities can be copied between sessions - see the QOF Serialisation Format (QSF) documentation (qsf_write_file) for more information. The recommended backend for the new session is QSF or a future SQL backend. Using any of these entity copy functions sets a flag in the backend that this is now a partial QofBook. See Referring to entities outside a partial book.. When you save a session containing a partial QofBook, the session will check that the backend is able to handle the partial book. If not, the backend will be replaced by one that can handle partial books, preferably one using the same access_method. Currently, this means that a book using the GnuCash XML v2 file backend will be switched to QSF. Copied entities are identical to the source entity, all parameters defined with QofAccessFunc and QofSetterFunc in QOF are copied and the GUID of the original QofEntity is set in the new entity. Sessions containing copied entities are intended for use as mechanisms for data export. It is acceptable to add entities to new_session in batches. Note that any of these calls will fail if an entity already exists in new_session with the same GUID as any entity to be copied.
To merge a whole QofBook or where there is any possibility of collisions or requirement for user intervention, see Merging QofBook structures | |
gboolean | qof_entity_copy_to_session (QofSession *new_session, QofEntity *original) |
Copy a single QofEntity to another session. | |
gboolean | qof_entity_copy_list (QofSession *new_session, GList *entity_list) |
Copy a GList of entities to another session. | |
gboolean | qof_entity_copy_coll (QofSession *new_session, QofCollection *entity_coll) |
Copy a QofCollection of entities. | |
gboolean | qof_entity_copy_coll_r (QofSession *new_session, QofCollection *coll) |
Recursively copy a collection of entities to a session. | |
gboolean | qof_entity_copy_one_r (QofSession *new_session, QofEntity *ent) |
Recursively copy a single entity to a new session. | |
Event Handling | |
gboolean | qof_session_events_pending (QofSession *session) |
gboolean | qof_session_process_events (QofSession *session) |
Defines | |
#define | QOF_MOD_SESSION "qof-session" |
#define | QOF_STDOUT "file:" |
Allow session data to be printed to stdout. | |
Typedefs | |
typedef _QofSession | QofSession |
typedef void(* | QofPercentageFunc )(const char *message, double percent) |
Functions | |
QofSession * | qof_session_new (void) |
void | qof_session_destroy (QofSession *session) |
QofSession * | qof_session_get_current_session (void) |
void | qof_session_set_current_session (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) |
QofBook * | qof_session_get_book (QofSession *session) |
const char * | qof_session_get_file_path (QofSession *session) |
const char * | qof_session_get_url (QofSession *session) |
gboolean | qof_session_not_saved (QofSession *session) |
gboolean | qof_session_save_may_clobber_data (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) |
|
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 402 of file qofsession.h. |
|
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
Definition at line 706 of file qofsession.c. 00708 { 00709 struct recurse_s *store; 00710 00711 if(user_data == NULL) { return; } 00712 store = (struct recurse_s*)user_data; 00713 if(!ent || !store) { return; } 00714 store->success = qof_entity_copy_to_session(store->session, ent); 00715 if(store->success) { 00716 store->ent_list = g_list_append(store->ent_list, ent); 00717 } 00718 } 00719 00720 static void 00721 recurse_ent_cb(QofEntity *ent, gpointer user_data) 00722 { 00723 GList *ref_list, *i, *j, *ent_list, *child_list;
|
|
Recursively copy a collection of entities to a session.
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 QofEntityReference.
Definition at line 855 of file qofsession.c. 00856 { QOF_LIB_DIR, "libqof_backend_dwi", "dwiend_provider_init" }, 00857 #endif 00858 { NULL, NULL, NULL } 00859 }; 00860 00861 static void 00862 qof_session_load_backend(QofSession * session, char * access_method) 00863 { 00864 GSList *p; 00865 GList *node; 00866 QofBackendProvider *prov; 00867 QofBook *book; 00868 char *msg; 00869 gint num;
|
|
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 QofEntity pointers of any QofIdType, in any sequence. As long as all members of the list are QofEntity*, and all GUID's are unique, the list can be copied.
Definition at line 683 of file qofsession.c. 00684 { return FALSE; } 00685 qof_event_suspend(); 00686 qecd.param_list = NULL; 00687 qecd.new_session = new_session; 00688 qof_book_set_partial(qof_session_get_book(qecd.new_session)); 00689 qof_collection_foreach(entity_coll, qof_entity_coll_foreach, &qecd); 00690 qof_class_param_foreach(qof_collection_get_type(entity_coll), 00691 qof_entity_param_cb, &qecd); 00692 qof_collection_foreach(entity_coll, qof_entity_coll_copy, &qecd); 00693 if(qecd.param_list != NULL) { g_slist_free(qecd.param_list); } 00694 qof_event_resume(); 00695 return TRUE; 00696 } 00697 00698 struct recurse_s 00699 { 00700 QofSession *session; 00701 gboolean success; 00702 GList *ref_list; 00703 GList *ent_list;
|
|
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_entity_copy_to_session.
Definition at line 871 of file qofsession.c. 00876 { 00877 for (num = 0; backend_list[num].filename != NULL; num++) { 00878 if(!qof_load_backend_library(backend_list[num].libdir, 00879 backend_list[num].filename, backend_list[num].init_fcn)) 00880 { 00881 PWARN (" failed to load %s from %s using %s", 00882 backend_list[num].filename, backend_list[num].libdir, 00883 backend_list[num].init_fcn); 00884 } 00885 } 00886 } 00887 p = g_slist_copy(provider_list); 00888 while(p != NULL)
|
|
Copy a single QofEntity to another session. Checks first that no entity in the session book contains the GUID of the source entity.
Definition at line 655 of file qofsession.c. 00657 { 00658 QofEntityCopyData *qecd; 00659 00660 if(!new_session || !entity_list) { return FALSE; } 00661 ENTER (" list=%d", g_list_length(entity_list)); 00662 qecd = g_new0(QofEntityCopyData, 1); 00663 qof_event_suspend(); 00664 qecd->param_list = NULL; 00665 qecd->new_session = new_session; 00666 qof_book_set_partial(qof_session_get_book(new_session)); 00667 g_list_foreach(entity_list, qof_entity_list_foreach, qecd); 00668 qof_event_resume(); 00669 if(qecd->error) 00670 { 00671 PWARN (" some/all entities in the list could not be copied."); 00672 } 00673 g_free(qecd); 00674 LEAVE (" "); 00675 return TRUE; 00676 } 00677 00678 gboolean 00679 qof_entity_copy_coll(QofSession *new_session, QofCollection *entity_coll) 00680 { 00681 QofEntityCopyData qecd;
|
|
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 248 of file qofsession.c. 00249 { 00250 GList *node; 00251 if (!session) return; 00252 00253 ENTER (" sess=%p book=%p", session, addbook); 00254 00255 /* See if this book is already there ... */ 00256 for (node=session->books; node; node=node->next) 00257 { 00258 QofBook *book = node->data; 00259 if (addbook == book) return; 00260 } 00261 00262 if ('y' == addbook->book_open) 00263 { 00264 /* hack alert -- someone should free all the books in the list, 00265 * but it should probably not be us ... since the books backends 00266 * should be shutdown first, etc */ 00267 /* XXX this should probably be an error XXX */ 00268 g_list_free (session->books); 00269 session->books = g_list_append (NULL, addbook); 00270 } 00271 else 00272 { 00273 /* XXX Need to tell the backend to add a book as well */ 00274 session->books = g_list_append (session->books, addbook); 00275 } 00276 00277 qof_book_set_backend (addbook, session->backend); 00278 LEAVE (" "); 00279 }
|
|
Register a function to be called just before a session is closed.
Definition at line 69 of file qofsession.c. 00070 { 00071 GHook *hook; 00072 00073 if (session_closed_hooks == NULL) { 00074 session_closed_hooks = malloc(sizeof(GHookList)); /* LEAKED */ 00075 g_hook_list_init (session_closed_hooks, sizeof(GHook)); 00076 } 00077 00078 hook = g_hook_alloc(session_closed_hooks); 00079 if (!hook) 00080 return; 00081 00082 hook->func = (GHookFunc)fn; 00083 hook->data = data; 00084 g_hook_append(session_closed_hooks, hook); 00085 }
|
|
Call all registered session close hooks, informing them that the specified session is about to be closed.
Definition at line 88 of file qofsession.c. 00089 { 00090 GHook *hook; 00091 GFunc fn; 00092 00093 if (session_closed_hooks == NULL) 00094 return; 00095 00096 hook = g_hook_first_valid (session_closed_hooks, FALSE); 00097 while (hook) { 00098 fn = (GFunc)hook->func; 00099 fn(session, hook->data); 00100 hook = g_hook_next_valid (session_closed_hooks, hook, FALSE); 00101 } 00102 }
|
|
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 1368 of file qofsession.c. 01371 { 01372 GList *books_1, *books_2, *node; 01373 01374 if (session_1 == session_2) return; 01375 if (!session_1 || !session_2) return; 01376 01377 ENTER ("sess1=%p sess2=%p", session_1, session_2); 01378 01379 books_1 = session_1->books; 01380 books_2 = session_2->books; 01381 01382 session_1->books = books_2; 01383 session_2->books = books_1; 01384 01385 for (node=books_1; node; node=node->next) 01386 { 01387 QofBook *book_1 = node->data; 01388 qof_book_set_backend (book_1, session_2->backend);
|
|
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 1456 of file qofsession.c. |
|
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 139 of file qofsession.c. 00140 { 00141 QofBackendError err; 00142 00143 if (!session) return ERR_BACKEND_NO_BACKEND; 00144 00145 /* if we have a local error, return that. */ 00146 if (ERR_BACKEND_NO_ERR != session->last_err) 00147 { 00148 return session->last_err; 00149 } 00150 00151 /* maybe we should return a no-backend error ??? */ 00152 if (! session->backend) return ERR_BACKEND_NO_ERR; 00153 00154 err = qof_backend_get_error (session->backend); 00155 session->last_err = err; 00156 return err; 00157 }
|
|
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 289 of file qofsession.c. 00290 { 00291 if (!session) return NULL; 00292 if (!session->backend) return NULL; 00293 return session->backend->fullpath; 00294 }
|
|
The qof_session_not_saved() subroutine will return TRUE if any data in the session hasn't been saved to long-term storage. |
|
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 175 of file qofsession.c. 00176 { 00177 QofBackendError err; 00178 00179 if (!session) return ERR_BACKEND_NO_BACKEND; 00180 00181 err = qof_session_get_error(session); 00182 qof_session_clear_error(session); 00183 00184 return err; 00185 }
|
|
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 1466 of file qofsession.c. |
|
Definition at line 1221 of file qofsession.c. 01222 {*/ 01223 if (NULL == prov->backend_new) continue; 01224 /* Use the providers creation callback */ 01225 session->backend = (*(prov->backend_new))(); 01226 session->backend->provider = prov; 01227 if (session->backend->session_begin) 01228 { 01229 /* Call begin - backend has been changed, 01230 so make sure a file can be written, 01231 use ignore_lock and create_if_nonexistent */ 01232 g_free(session->book_id); 01233 session->book_id = NULL; 01234 (session->backend->session_begin)(session->backend, session, 01235 book_id, TRUE, TRUE); 01236 PINFO("Done running session_begin on changed backend"); 01237 err = qof_backend_get_error(session->backend); 01238 msg = qof_backend_get_message(session->backend); 01239 if (err != ERR_BACKEND_NO_ERR) 01240 { 01241 g_free(session->book_id); 01242 session->book_id = NULL; 01243 qof_session_push_error (session, err, msg); 01244 LEAVE("changed backend error %d", err); 01245 return; 01246 } 01247 if (msg != NULL) 01248 { 01249 PWARN("%s", msg); 01250 g_free(msg); 01251 } 01252 } 01253 /* Tell the books about the backend that they'll be using. */ 01254 for (node=session->books; node; node=node->next) 01255 { 01256 book = node->data; 01257 qof_book_set_backend (book, session->backend); 01258 } 01259 p = NULL; 01260 } 01261 if(p) { 01262 p = p->next; 01263 } 01264 } 01265 if(!session->backend) 01266 { 01267 msg = g_strdup_printf("failed to load backend"); 01268 qof_session_push_error(session, ERR_BACKEND_NO_HANDLER, msg); 01269 return; 01270 } 01271 } 01272 /* If there is a backend, and the backend is reachable 01273 * (i.e. we can communicate with it), then synchronize with 01274 * the backend. If we cannot contact the backend (e.g. 01275 * because we've gone offline, the network has crashed, etc.) 01276 * then give the user the option to save to the local disk. 01277 * 01278 * hack alert -- FIXME -- XXX the code below no longer 01279 * does what the words above say. This needs fixing. 01280 */ 01281 be = session->backend; 01282 if (be) 01283 { 01284 for (node = session->books; node; node=node->next) 01285 { 01286 abook = node->data; 01287 /* if invoked as SaveAs(), then backend not yet set */ 01288 qof_book_set_backend (abook, be); 01289 be->percentage = percentage_func; 01290 if (be->sync) 01291 { 01292 (be->sync)(be, abook); 01293 if (save_error_handler(be, session)) return; 01294 } 01295 } 01296 /* If we got to here, then the backend saved everything 01297 * just fine, and we are done. So return. */ 01298 /* Return the book_id to previous value. */ 01299 qof_session_clear_error (session); 01300 LEAVE("Success"); 01301 return; 01302 } 01303 else 01304 { 01305 msg = g_strdup_printf("failed to load backend"); 01306 qof_session_push_error(session, ERR_BACKEND_NO_HANDLER, msg); 01307 } 01308 LEAVE("error -- No backend!"); 01309 } 01310 01311 /* ====================================================================== */ 01312 01313 void 01314 qof_session_end (QofSession *session) 01315 { 01316 if (!session) return; 01317 01318 ENTER ("sess=%p book_id=%s", session, session->book_id 01319 ? session->book_id : "(null)"); 01320 01321 /* close down the backend first */ 01322 if (session->backend && session->backend->session_end) 01323 { 01324 (session->backend->session_end)(session->backend); 01325 } 01326 01327 qof_session_clear_error (session); 01328 01329 g_free (session->book_id); 01330 session->book_id = NULL; 01331 01332 LEAVE ("sess=%p book_id=%s", session, session->book_id 01333 ? session->book_id : "(null)"); 01334 } 01335 01336 void 01337 qof_session_destroy (QofSession *session) 01338 { 01339 GList *node; 01340 if (!session) return; 01341 01342 ENTER ("sess=%p book_id=%s", session, session->book_id 01343 ? session->book_id : "(null)"); 01344 01345 qof_session_end (session); 01346 01347 /* destroy the backend */ 01348 qof_session_destroy_backend(session); 01349 01350 for (node = session->books; node; node = node->next) 01351 { 01352 QofBook *book = node->data; 01353 qof_book_set_backend (book, NULL); 01354 qof_book_destroy (book); 01355 } 01356 01357 session->books = NULL; 01358 if (session == current_session) 01359 current_session = NULL; 01360 01361 g_free (session); 01362 01363 LEAVE ("sess=%p", session);
|
|
Allows the backend to warn the user if a dataset already exists. Definition at line 1197 of file qofsession.c. 01198 { change_backend = TRUE; } 01199 } 01200 /* If provider is undefined, assume partial not supported. */ 01201 else { change_backend = TRUE; } 01202 } 01203 if(change_backend == TRUE) 01204 {
|