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. | |
| QofBackend * | qof_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) |
| QofBook * | qof_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) |
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 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 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.
| typedef struct QofBackendOption_s QofBackendOption |
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.
| typedef struct QofBackendProvider_s QofBackendProvider |
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.
| enum QofBackendError |
The errors that can be reported to the GUI & other front-end users.
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;
| 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.
| be | The QofBackend to be configured. |
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.
| 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
| new_session | - the target session | |
| entity_coll | - a QofCollection of any QofIdType. |
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.
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.
| coll | A QofCollection of entities that may or may not have references. | |
| new_session | The QofSession to receive the copied entities. |
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.
| new_session | - the target session | |
| entity_list | - a GList of QofInstance pointers of any type(s). |
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.
| ent | A single entity that may or may not have references. | |
| new_session | The QofSession to receive the copied entities. |
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.
| new_session | - the target session | |
| original | - the QofInstance* to copy |
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.
| 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. |
| 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.
| 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.
| 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.
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.
| 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 }
1.5.7.1