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.
Files | |
file | qofbackend.h |
API for data storage Backend. | |
file | qofsession.h |
Encapsulates a connection to a backend (persistent store). | |
Modules | |
QOF Serialisation Format | |
Data Structures | |
struct | QofBackendOption_s |
Backend Configuration using KVP | |
The backend uses qof_backend_get_config to pass back a KvpFrame of QofBackendOption that includes the translated strings that serve as description and tooltip for that option. qof_backend_prepare_frame, qof_backend_prepare_option and qof_backend_complete_frame are intended to be used by the backend itself to create the options. qof_backend_get_config, qof_backend_option_foreach and qof_backend_load_config are intended for either the backend or the frontend to retrieve the option data from the frame or set new data.
Backends are loaded using QofBackendProvider via the function specified in prov->backend_new. Before backend_new returns, you should ensure that your backend is fully configured and ready for use. | |
typedef QofBackendOption_s | QofBackendOption |
typedef void(* | QofBackendOptionCB )(QofBackendOption *, gpointer data) |
void | qof_backend_prepare_frame (QofBackend *be) |
void | qof_backend_prepare_option (QofBackend *be, QofBackendOption *option) |
KvpFrame * | qof_backend_complete_frame (QofBackend *be) |
void | qof_backend_option_foreach (KvpFrame *config, QofBackendOptionCB cb, gpointer data) |
void | qof_backend_load_config (QofBackend *be, KvpFrame *config) |
Load configuration options specific to this backend. | |
KvpFrame * | qof_backend_get_config (QofBackend *be) |
Get the available configuration options. | |
Allow access to the begin routine for this backend. | |
QOF_BEGIN_EDIT and QOF_COMMIT_EDIT_PART1 and part2 rely on calling QofBackend *be->begin and be->commit. This means the QofBackend struct becomes part of the public API. These function replaces those calls to allow the macros to be used when QOF is built as a library. | |
void | qof_backend_run_begin (QofBackend *be, QofInstance *inst) |
gboolean | qof_backend_begin_exists (QofBackend *be) |
void | qof_backend_run_commit (QofBackend *be, QofInstance *inst) |
gboolean | qof_backend_commit_exists (QofBackend *be) |
Session Errors | |
QofBackendError | qof_session_get_error (QofSession *session) |
const gchar * | 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_BACKEND "qof-backend" |
#define | QOF_MOD_SESSION "qof-session" |
#define | QOF_STDOUT "file:" |
Allow session data to be printed to stdout. | |
Typedefs | |
typedef QofBackendProvider_s | QofBackendProvider |
typedef QofBackend_s | QofBackend |
Pseudo-object providing an interface between the framework and a persistant data store (e.g. a server, a database, or a file). | |
typedef void(* | QofBePercentageFunc )(const gchar *message, double percent) |
DOCUMENT ME! | |
typedef _QofSession | QofSession |
typedef void(* | QofPercentageFunc )(const gchar *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_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 | |
gboolean | qof_load_backend_library (const gchar *directory, const gchar *filename, const gchar *init_fcn) |
Load a QOF-compatible backend shared library. | |
QofBackend * | qof_book_get_backend (QofBook *book) |
Retrieve the backend used by this book. | |
void | qof_book_set_backend (QofBook *book, QofBackend *) |
Set the backend used by this book. | |
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 (QofSession *session) |
const gchar * | qof_session_get_file_path (QofSession *session) |
const gchar * | 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 400 of file qofsession.h. |
|
Pseudo-object providing an interface between the framework 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 framework. The backend can, however, report errors to the GUI & other front-end users. Definition at line 180 of file qofbackend.h. |
|
A single Backend Configuration Option. |
|
Backend configuration option foreach callback prototype. Definition at line 245 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 171 of file qofbackend.h. |
|
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 163 of file qofsession.h. |
|
The errors that can be reported to the GUI & other front-end users.
Definition at line 54 of file qofbackend.h. 00055 { 00056 ERR_BACKEND_NO_ERR = 0, 00057 ERR_BACKEND_NO_HANDLER, 00059 ERR_BACKEND_NO_BACKEND, 00061 ERR_BACKEND_BAD_URL, 00062 ERR_BACKEND_NO_SUCH_DB, 00063 ERR_BACKEND_CANT_CONNECT, 00065 ERR_BACKEND_CONN_LOST, 00066 ERR_BACKEND_LOCKED, 00067 ERR_BACKEND_READONLY, 00068 ERR_BACKEND_TOO_NEW, 00070 ERR_BACKEND_DATA_CORRUPT, 00071 ERR_BACKEND_SERVER_ERR, 00072 ERR_BACKEND_ALLOC, 00073 ERR_BACKEND_PERM, 00075 ERR_BACKEND_MODIFIED, 00077 ERR_BACKEND_MOD_DESTROY, 00079 ERR_BACKEND_MISC, 00081 /* QSF add-ons */ 00082 ERR_QSF_INVALID_OBJ, 00084 ERR_QSF_INVALID_MAP, 00086 ERR_QSF_BAD_OBJ_GUID, 00088 ERR_QSF_BAD_QOF_VERSION, 00090 ERR_QSF_BAD_MAP, 00096 ERR_QSF_NO_MAP, 00101 ERR_QSF_WRONG_MAP, 00107 ERR_QSF_MAP_NOT_OBJ, 00109 ERR_QSF_OVERFLOW, 00117 ERR_QSF_OPEN_NOT_MERGE, 00122 /* fileio errors */ 00123 ERR_FILEIO_FILE_BAD_READ = 1000, 00125 ERR_FILEIO_FILE_EMPTY, 00127 ERR_FILEIO_FILE_LOCKERR, 00128 ERR_FILEIO_FILE_NOT_FOUND, 00129 ERR_FILEIO_FILE_TOO_OLD, 00131 ERR_FILEIO_UNKNOWN_FILE_TYPE, 00133 ERR_FILEIO_PARSE_ERROR, 00135 ERR_FILEIO_BACKUP_ERROR, 00137 ERR_FILEIO_WRITE_ERROR, 00138 ERR_FILEIO_READ_ERROR, 00140 ERR_FILEIO_NO_ENCODING, 00142 /* network errors */ 00143 ERR_NETIO_SHORT_READ = 2000, 00144 ERR_NETIO_WRONG_CONTENT_TYPE, 00146 ERR_NETIO_NOT_GNCXML, 00149 /* database errors */ 00150 ERR_SQL_MISSING_DATA = 3000, 00152 ERR_SQL_DB_TOO_OLD, 00154 ERR_SQL_DB_BUSY, 00157 /* RPC errors */ 00158 ERR_RPC_HOST_UNK = 4000, 00159 ERR_RPC_CANT_BIND, 00160 ERR_RPC_CANT_ACCEPT, 00161 ERR_RPC_NO_CONNECTION, 00162 ERR_RPC_BAD_VERSION, 00163 ERR_RPC_FAILED, 00164 ERR_RPC_NOT_ADDED, 00165 } QofBackendError;
|
|
Complete the backend_configuration and return the frame. Definition at line 280 of file qofbackend.c. 00281 { 00282 g_return_val_if_fail (be, NULL); 00283 be->config_count = 0; 00284 return be->backend_configuration; 00285 }
|
|
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.
Definition at line 482 of file qofbackend.c. 00483 { 00484 if (!be) 00485 return NULL; 00486 if (!be->get_config) 00487 return NULL; 00488 return (be->get_config) (be); 00489 }
|
|
Load configuration options specific to this backend.
Definition at line 472 of file qofbackend.c. 00473 { 00474 if (!be || !config) 00475 return; 00476 if (!be->load_config) 00477 return; 00478 (be->load_config) (be, config); 00479 }
|
|
Iterate over the frame and process each option. Definition at line 455 of file qofbackend.c. 00457 { 00458 struct config_iterate helper; 00459 00460 if (!config || !cb) 00461 return; 00462 ENTER (" "); 00463 helper.fcn = cb; 00464 helper.count = 1; 00465 helper.data = data; 00466 helper.recursive = config; 00467 kvp_frame_for_each_slot (config, config_foreach_cb, &helper); 00468 LEAVE (" "); 00469 }
|
|
Initialise the backend_configuration Definition at line 183 of file qofbackend.c. 00184 { 00185 g_return_if_fail (be); 00186 if (!kvp_frame_is_empty (be->backend_configuration)) 00187 { 00188 kvp_frame_delete (be->backend_configuration); 00189 be->backend_configuration = kvp_frame_new (); 00190 } 00191 be->config_count = 0; 00192 }
|
|
Add an option to the backend_configuration. Repeat for more. Definition at line 195 of file qofbackend.c. 00197 { 00198 KvpValue *value; 00199 gchar *temp; 00200 gint count; 00201 00202 g_return_if_fail (be || option); 00203 count = be->config_count; 00204 count++; 00205 value = NULL; 00206 switch (option->type) 00207 { 00208 case KVP_TYPE_GINT64: 00209 { 00210 value = kvp_value_new_gint64 (*(gint64 *) option->value); 00211 break; 00212 } 00213 case KVP_TYPE_DOUBLE: 00214 { 00215 value = kvp_value_new_double (*(double *) option->value); 00216 break; 00217 } 00218 case KVP_TYPE_NUMERIC: 00219 { 00220 value = kvp_value_new_numeric (*(gnc_numeric *) option->value); 00221 break; 00222 } 00223 case KVP_TYPE_STRING: 00224 { 00225 value = kvp_value_new_string ((const char *) option->value); 00226 break; 00227 } 00228 case KVP_TYPE_GUID: 00229 { 00230 break; 00231 } /* unsupported */ 00232 case KVP_TYPE_TIME : 00233 { 00234 value = kvp_value_new_time ((QofTime*) option->value); 00235 break; 00236 } 00237 #ifndef QOF_DISABLE_DEPRECATED 00238 case KVP_TYPE_TIMESPEC: 00239 { 00240 value = kvp_value_new_timespec (*(Timespec *) option->value); 00241 break; 00242 } 00243 #endif 00244 case KVP_TYPE_BINARY: 00245 { 00246 break; 00247 } /* unsupported */ 00248 case KVP_TYPE_GLIST: 00249 { 00250 break; 00251 } /* unsupported */ 00252 case KVP_TYPE_FRAME: 00253 { 00254 break; 00255 } /* unsupported */ 00256 } 00257 if (value) 00258 { 00259 temp = g_strdup_printf ("/%s", option->option_name); 00260 kvp_frame_set_value (be->backend_configuration, temp, value); 00261 g_free (temp); 00262 temp = 00263 g_strdup_printf ("/%s/%s", QOF_CONFIG_DESC, 00264 option->option_name); 00265 kvp_frame_set_string (be->backend_configuration, temp, 00266 option->description); 00267 g_free (temp); 00268 temp = 00269 g_strdup_printf ("/%s/%s", QOF_CONFIG_TIP, 00270 option->option_name); 00271 kvp_frame_set_string (be->backend_configuration, temp, 00272 option->tooltip); 00273 g_free (temp); 00274 /* only increment the counter if successful */ 00275 be->config_count = count; 00276 } 00277 }
|
|
Set the backend used by this book. Should only be used within a backend itself. Definition at line 170 of file qofbook.c. 00171 { 00172 if (!book) 00173 return; 00174 ENTER ("book=%p be=%p", book, be); 00175 book->backend = be; 00176 LEAVE (" "); 00177 }
|
|
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 806 of file qofsession.c. 00806 { 00807 QofSession *session; 00808 gboolean success; 00809 GList *ref_list; 00810 GList *ent_list; 00811 }; 00812 00813 static void 00814 recurse_collection_cb (QofEntity * ent, gpointer user_data) 00815 { 00816 struct recurse_s *store; 00817 00818 if (user_data == NULL) 00819 { 00820 return; 00821 } 00822 store = (struct recurse_s *) user_data; 00823 if (!ent || !store) 00824 { 00825 return; 00826 } 00827 store->success = qof_entity_copy_to_session (store->session, ent); 00828 if (store->success) 00829 { 00830 store->ent_list = g_list_append (store->ent_list, ent);
|
|
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 1007 of file qofsession.c. 01012 { 01013 const gchar *libdir; 01014 const gchar *filename; 01015 const gchar *init_fcn; 01016 }; 01017 01018 /* All available QOF backends need to be described here 01019 and the last entry must be three NULL's. 01020 Remember: Use the libdir from the current build environment 01021 and use JUST the module name without .so - .so is not portable! */ 01022 struct backend_providers backend_list[] = { 01023 {QOF_LIB_DIR, QSF_BACKEND_LIB, QSF_MODULE_INIT}, 01024 #ifdef HAVE_DWI 01025 {QOF_LIB_DIR, "libqof_backend_dwi", "dwiend_provider_init"}, 01026 #endif 01027 {NULL, NULL, NULL} 01028 };
|
|
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 780 of file qofsession.c. 00781 { 00782 QofEntityCopyData qecd; 00783 00784 g_return_val_if_fail (new_session, FALSE); 00785 if (!entity_coll) 00786 { 00787 return FALSE; 00788 } 00789 qof_event_suspend (); 00790 qecd.param_list = NULL; 00791 qecd.new_session = new_session; 00792 qof_book_set_partial (qof_session_get_book (qecd.new_session)); 00793 qof_collection_foreach (entity_coll, qof_entity_coll_foreach, &qecd); 00794 qof_class_param_foreach (qof_collection_get_type (entity_coll), 00795 qof_entity_param_cb, &qecd); 00796 qof_collection_foreach (entity_coll, qof_entity_coll_copy, &qecd); 00797 if (qecd.param_list != NULL) 00798 { 00799 g_slist_free (qecd.param_list); 00800 } 00801 qof_event_resume (); 00802 return TRUE; 00803 }
|
|
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 1031 of file qofsession.c. 01032 { 01033 GSList *p; 01034 GList *node; 01035 QofBackendProvider *prov; 01036 QofBook *book; 01037 char *msg; 01038 gint num; 01039 gboolean prov_type; 01040 gboolean (*type_check) (const char *); 01041 01042 ENTER (" list=%d", g_slist_length (provider_list)); 01043 prov_type = FALSE; 01044 if (NULL == provider_list) 01045 { 01046 for (num = 0; backend_list[num].filename != NULL; num++) 01047 { 01048 if (!qof_load_backend_library (backend_list[num].libdir, 01049 backend_list[num].filename, 01050 backend_list[num].init_fcn)) 01051 { 01052 PWARN (" failed to load %s from %s using %s", 01053 backend_list[num].filename, backend_list[num].libdir, 01054 backend_list[num].init_fcn); 01055 } 01056 } 01057 }
|
|
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 746 of file qofsession.c. 00754 { 00755 QofEntityCopyData *qecd; 00756 00757 if (!new_session || !entity_list) 00758 { 00759 return FALSE; 00760 } 00761 ENTER (" list=%d", g_list_length (entity_list)); 00762 qecd = g_new0 (QofEntityCopyData, 1); 00763 qof_event_suspend (); 00764 qecd->param_list = NULL; 00765 qecd->new_session = new_session; 00766 qof_book_set_partial (qof_session_get_book (new_session)); 00767 g_list_foreach (entity_list, qof_entity_list_foreach, qecd); 00768 qof_event_resume (); 00769 if (qecd->error) 00770 { 00771 PWARN (" some/all entities in the list could not be copied."); 00772 } 00773 g_free (qecd); 00774 LEAVE (" "); 00775 return TRUE; 00776 } 00777
|
|
Load a QOF-compatible backend shared library.
Definition at line 503 of file qofbackend.c. 00505 { 00506 gchar *fullpath; 00507 typedef void (*backend_init) (void); 00508 GModule *backend; 00509 backend_init gmod_init; 00510 gpointer g; 00511 00512 g_return_val_if_fail (g_module_supported (), FALSE); 00513 fullpath = g_module_build_path (directory, filename); 00514 backend = g_module_open (fullpath, G_MODULE_BIND_LAZY); 00515 if (!backend) 00516 { 00517 g_message ("%s: %s\n", PACKAGE, g_module_error ()); 00518 return FALSE; 00519 } 00520 g = &gmod_init; 00521 if (!g_module_symbol (backend, init_fcn, g)) 00522 { 00523 g_message ("%s: %s\n", PACKAGE, g_module_error ()); 00524 return FALSE; 00525 } 00526 g_module_make_resident (backend); 00527 gmod_init (); 00528 g_free (fullpath); 00529 return TRUE; 00530 }
|
|
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 233 of file qofsession.c. 00234 { 00235 GList *node; 00236 if (!session) 00237 return; 00238 00239 ENTER (" sess=%p book=%p", session, addbook); 00240 00241 /* See if this book is already there ... */ 00242 for (node = session->books; node; node = node->next) 00243 { 00244 QofBook *book = node->data; 00245 if (addbook == book) 00246 return; 00247 } 00248 00249 if ('y' == addbook->book_open) 00250 { 00251 /* hack alert -- someone should free all the books in the list, 00252 * but it should probably not be us ... since the books backends 00253 * should be shutdown first, etc */ 00254 /* XXX this should probably be an error XXX */ 00255 g_list_free (session->books); 00256 session->books = g_list_append (NULL, addbook); 00257 } 00258 else 00259 { 00260 /* XXX Need to tell the backend to add a book as well */ 00261 session->books = g_list_append (session->books, addbook); 00262 } 00263 00264 qof_book_set_backend (addbook, session->backend); 00265 LEAVE (" "); 00266 }
|
|
Register a function to be called just before a session is closed.
Definition at line 66 of file qofsession.c. 00067 { 00068 GHook *hook; 00069 00070 if (session_closed_hooks == NULL) 00071 { 00072 session_closed_hooks = malloc (sizeof (GHookList)); /* LEAKED */ 00073 g_hook_list_init (session_closed_hooks, sizeof (GHook)); 00074 } 00075 00076 hook = g_hook_alloc (session_closed_hooks); 00077 if (!hook) 00078 return; 00079 00080 hook->func = (GHookFunc) fn; 00081 hook->data = data; 00082 g_hook_append (session_closed_hooks, hook); 00083 }
|
|
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 1185 of file qofsession.c. 01189 { 01190 qof_session_push_error (session, ERR_BACKEND_BAD_URL, NULL); 01191 LEAVE (" BAD: no backend: sess=%p book-id=%s", 01192 session, book_id ? book_id : "(null)"); 01193 return; 01194 } 01195 01196 /* If there's a begin method, call that. */ 01197 if (session->backend->session_begin) 01198 { 01199 01200 (session->backend->session_begin) (session->backend, session, 01201 session->book_id, ignore_lock, create_if_nonexistent); 01202 PINFO ("Done running session_begin on backend"); 01203 err = qof_backend_get_error (session->backend); 01204 msg = qof_backend_get_message (session->backend); 01205 if (err != ERR_BACKEND_NO_ERR) 01206 { 01207 g_free (session->book_id); 01208 session->book_id = NULL; 01209 qof_session_push_error (session, err, msg); 01210 LEAVE (" backend error %d %s", err, msg); 01211 return; 01212 } 01213 if (msg != NULL) 01214 { 01215 PWARN ("%s", msg); 01216 g_free (msg); 01217 } 01218 } 01219 01220 LEAVE (" sess=%p book-id=%s", session, book_id ? book_id : "(null)"); 01221 } 01222 01223 /* ====================================================================== */ 01224 01225 void 01226 qof_session_load (QofSession * session, QofPercentageFunc percentage_func) 01227 { 01228 QofBook *newbook, *ob; 01229 QofBookList *oldbooks, *node; 01230 QofBackend *be; 01231 QofBackendError err; 01232 01233 if (!session) 01234 return; 01235 if (!session->book_id) 01236 return; 01237 01238 ENTER ("sess=%p book_id=%s", session, session->book_id 01239 ? session->book_id : "(null)"); 01240 01241 /* At this point, we should are supposed to have a valid book 01242 * id and a lock on the file. */ 01243 01244 oldbooks = session->books; 01245 01246 /* XXX why are we creating a book here? I think the books 01247 * need to be handled by the backend ... especially since 01248 * the backend may need to load multiple books ... XXX. FIXME. 01249 */ 01250 newbook = qof_book_new (); 01251 session->books = g_list_append (NULL, newbook); 01252 PINFO ("new book=%p", newbook); 01253 01254 qof_session_clear_error (session); 01255 01256 /* This code should be sufficient to initialize *any* backend, 01257 * whether http, postgres, or anything else that might come along. 01258 * Basically, the idea is that by now, a backend has already been 01259 * created & set up. At this point, we only need to get the 01260 * top-level account group out of the backend, and that is a 01261 * generic, backend-independent operation. 01262 */ 01263 be = session->backend; 01264 qof_book_set_backend (newbook, be); 01265 01266 /* Starting the session should result in a bunch of accounts 01267 * and currencies being downloaded, but probably no transactions; 01268 * The GUI will need to do a query for that. 01269 */ 01270 if (be) 01271 { 01272 be->percentage = percentage_func; 01273 01274 if (be->load) 01275 {
|
|
Call all registered session close hooks, informing them that the specified session is about to be closed.
Definition at line 86 of file qofsession.c. 00087 { 00088 GHook *hook; 00089 GFunc fn; 00090 00091 if (session_closed_hooks == NULL) 00092 return; 00093 00094 hook = g_hook_first_valid (session_closed_hooks, FALSE); 00095 while (hook) 00096 { 00097 fn = (GFunc) hook->func; 00098 fn (session, hook->data); 00099 hook = g_hook_next_valid (session_closed_hooks, hook, FALSE); 00100 } 00101 }
|
|
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 1559 of file qofsession.c. 01565 { 01566 GList *books_1, *books_2, *node; 01567 01568 if (session_1 == session_2) 01569 return; 01570 if (!session_1 || !session_2) 01571 return; 01572 01573 ENTER ("sess1=%p sess2=%p", session_1, session_2); 01574 01575 books_1 = session_1->books; 01576 books_2 = session_2->books; 01577 01578 session_1->books = books_2; 01579 session_2->books = books_1; 01580
|
|
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 1652 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 140 of file qofsession.c. 00141 { 00142 QofBackendError err; 00143 00144 if (!session) 00145 return ERR_BACKEND_NO_BACKEND; 00146 00147 /* if we have a local error, return that. */ 00148 if (ERR_BACKEND_NO_ERR != session->last_err) 00149 { 00150 return session->last_err; 00151 } 00152 00153 /* maybe we should return a no-backend error ??? */ 00154 if (!session->backend) 00155 return ERR_BACKEND_NO_ERR; 00156 00157 err = qof_backend_get_error (session->backend); 00158 session->last_err = err; 00159 return err; 00160 }
|
|
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 277 of file qofsession.c. 00278 { 00279 if (!session) 00280 return NULL; 00281 if (!session->backend) 00282 return NULL; 00283 return session->backend->fullpath; 00284 }
|
|
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 179 of file qofsession.c. 00180 { 00181 QofBackendError err; 00182 00183 if (!session) 00184 return ERR_BACKEND_NO_BACKEND; 00185 00186 err = qof_session_get_error (session); 00187 qof_session_clear_error (session); 00188 00189 return err; 00190 }
|
|
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 1665 of file qofsession.c. |
|
Definition at line 1397 of file qofsession.c. 01402 { 01403 prov = p->data; 01404 if (TRUE == prov->partial_book_supported) 01405 { 01407 /* if((TRUE == prov->partial_book_supported) && 01408 (0 == strcasecmp (access_method, prov->access_method))) 01409 { */ 01410 if (NULL == prov->backend_new) 01411 continue; 01412 /* Use the providers creation callback */ 01413 session->backend = (*(prov->backend_new)) (); 01414 session->backend->provider = prov; 01415 if (session->backend->session_begin) 01416 { 01417 /* Call begin - backend has been changed, 01418 so make sure a file can be written, 01419 use ignore_lock and create_if_nonexistent */ 01420 g_free (session->book_id); 01421 session->book_id = NULL; 01422 (session->backend->session_begin) (session->backend, 01423 session, book_id, TRUE, TRUE); 01424 PINFO 01425 ("Done running session_begin on changed backend"); 01426 err = qof_backend_get_error (session->backend); 01427 msg = qof_backend_get_message (session->backend); 01428 if (err != ERR_BACKEND_NO_ERR) 01429 { 01430 g_free (session->book_id); 01431 session->book_id = NULL; 01432 qof_session_push_error (session, err, msg); 01433 LEAVE ("changed backend error %d", err); 01434 return; 01435 } 01436 if (msg != NULL) 01437 { 01438 PWARN ("%s", msg); 01439 g_free (msg); 01440 } 01441 } 01442 /* Tell the books about the backend that they'll be using. */ 01443 for (node = session->books; node; node = node->next) 01444 { 01445 book = node->data; 01446 qof_book_set_backend (book, session->backend); 01447 } 01448 p = NULL; 01449 } 01450 if (p) 01451 { 01452 p = p->next; 01453 } 01454 } 01455 if (!session->backend) 01456 { 01457 msg = g_strdup_printf ("failed to load backend"); 01458 qof_session_push_error (session, ERR_BACKEND_NO_HANDLER, msg); 01459 return; 01460 } 01461 } 01462 /* If there is a backend, and the backend is reachable 01463 * (i.e. we can communicate with it), then synchronize with 01464 * the backend. If we cannot contact the backend (e.g. 01465 * because we've gone offline, the network has crashed, etc.) 01466 * then give the user the option to save to the local disk. 01467 * 01468 * hack alert -- FIXME -- XXX the code below no longer 01469 * does what the words above say. This needs fixing. 01470 */ 01471 be = session->backend; 01472 if (be) 01473 { 01474 for (node = session->books; node; node = node->next) 01475 { 01476 abook = node->data; 01477 /* if invoked as SaveAs(), then backend not yet set */ 01478 qof_book_set_backend (abook, be); 01479 be->percentage = percentage_func; 01480 if (be->sync) 01481 { 01482 (be->sync) (be, abook); 01483 if (save_error_handler (be, session)) 01484 return; 01485 } 01486 } 01487 /* If we got to here, then the backend saved everything 01488 * just fine, and we are done. So return. */ 01489 /* Return the book_id to previous value. */ 01490 qof_session_clear_error (session); 01491 LEAVE ("Success"); 01492 return; 01493 } 01494 else 01495 { 01496 msg = g_strdup_printf ("failed to load backend"); 01497 qof_session_push_error (session, ERR_BACKEND_NO_HANDLER, msg); 01498 } 01499 LEAVE ("error -- No backend!"); 01500 } 01501 01502 /* ====================================================================== */ 01503 01504 void 01505 qof_session_end (QofSession * session) 01506 { 01507 if (!session) 01508 return; 01509 01510 ENTER ("sess=%p book_id=%s", session, session->book_id 01511 ? session->book_id : "(null)"); 01512 01513 /* close down the backend first */ 01514 if (session->backend && session->backend->session_end) 01515 { 01516 (session->backend->session_end) (session->backend); 01517 } 01518 01519 qof_session_clear_error (session); 01520 01521 g_free (session->book_id); 01522 session->book_id = NULL; 01523 01524 LEAVE ("sess=%p book_id=%s", session, session->book_id 01525 ? session->book_id : "(null)"); 01526 } 01527 01528 void 01529 qof_session_destroy (QofSession * session) 01530 { 01531 GList *node; 01532 if (!session) 01533 return; 01534 01535 ENTER ("sess=%p book_id=%s", session, session->book_id 01536 ? session->book_id : "(null)"); 01537 01538 qof_session_end (session); 01539 01540 /* destroy the backend */ 01541 qof_session_destroy_backend (session); 01542 01543 for (node = session->books; node; node = node->next) 01544 { 01545 QofBook *book = node->data; 01546 qof_book_set_backend (book, NULL); 01547 qof_book_destroy (book); 01548 } 01549 01550 session->books = NULL; 01551 #ifndef QOF_DISABLE_DEPRECATED 01552 if (session == qof_session_get_current_session()) 01553 qof_session_clear_current_session(); 01554 #endif
|
|
Allows the backend to warn the user if a dataset already exists. Definition at line 1370 of file qofsession.c. 01370 { 01371 prov = session->backend->provider; 01372 if (TRUE == prov->partial_book_supported) 01373 { 01374 /* if current backend supports partial, leave alone. */ 01375 change_backend = FALSE; 01376 } 01377 else 01378 { 01379 change_backend = TRUE; 01380 }
|
|
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 1618 of file qofsession.c. |