Backend
[Object: Dynamic Object Class Framework]


Detailed Description

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

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

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

This class provides several important services:

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

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

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

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

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

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

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

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

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

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


Files

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

Modules

 QOF Serialisation Format

Supported backend configurations

#define QSF_COMPRESS   "compression_level"
 compression level
#define QSF_MAP_FILES   "selected_map_files"
 selected QSF maps
#define QSF_ENCODING   "encoding_string"
 Encoding string.

Defines

#define QOF_MOD_BACKEND   "qof-backend"
#define QOF_MOD_SESSION   "qof-session"

Typedefs

typedef QofBackendProvider_s QofBackendProvider
typedef 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 _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_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

QofSessionqof_session_new (void)
void qof_session_destroy (QofSession *session)
QofSessionqof_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)


Define Documentation

#define QSF_COMPRESS   "compression_level"
 

compression level

Type: gint (KVP_TYPE_GINT64)

Use GINT_TO_POINTER() to set a integer value between 0 and 9.

Definition at line 177 of file qof-backend-qsf.h.

#define QSF_ENCODING   "encoding_string"
 

Encoding string.

Defaults of UTF-8.

Definition at line 199 of file qof-backend-qsf.h.

#define QSF_MAP_FILES   "selected_map_files"
 

selected QSF maps

Type: GList* (KVP_TYPE_GLIST) of const char* (QOF_TYPE_STRING) values.

Defaults to the pre-installed QSF map(s) but may be overridden by the application to pass full path(s) of user selected QSF map file(s).

If you override the list, it is advisable to only specify the single map file for this QSF object to reduce the amount of error checking required within the backend. Simply reset the QofBackendOption before another file is to be opened. It is up to the application to decide how to offer multiple map selections to the user.

Definition at line 194 of file qof-backend-qsf.h.


Typedef Documentation

typedef struct QofBackend_s QofBackend
 

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

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

Definition at line 157 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 147 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 166 of file qofsession.h.


Enumeration Type Documentation

enum QofBackendError
 

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

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

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

ERR_QSF_NO_MAP  The QSF object file was loaded without a map

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

ERR_QSF_WRONG_MAP  The selected map validates but is for different objects.

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

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

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

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

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

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

Definition at line 55 of file qofbackend.h.

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


Function Documentation

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 1011 of file qofsession.c.

01015   {
01016     qof_session_push_error (session, ERR_BACKEND_BAD_URL, NULL);
01017     LEAVE (" BAD: no backend: sess=%p book-id=%s", 
01018          session,  book_id ? book_id : "(null)");
01019     return;
01020   }
01021 
01022   /* If there's a begin method, call that. */
01023   if (session->backend->session_begin)
01024   {
01025       
01026       (session->backend->session_begin)(session->backend, session,
01027                                   session->book_id, ignore_lock,
01028                                   create_if_nonexistent);
01029       PINFO("Done running session_begin on backend");
01030       err = qof_backend_get_error(session->backend);
01031       msg = qof_backend_get_message(session->backend);
01032       if (err != ERR_BACKEND_NO_ERR)
01033       {
01034           g_free(session->book_id);
01035           session->book_id = NULL;
01036           qof_session_push_error (session, err, msg);
01037           LEAVE(" backend error %d %s", err, msg);
01038           return;
01039       }
01040       if (msg != NULL) 
01041       {
01042           PWARN("%s", msg);
01043           g_free(msg);
01044       }
01045   }
01046 
01047   LEAVE (" sess=%p book-id=%s", 
01048          session,  book_id ? book_id : "(null)");
01049 }
01050 
01051 /* ====================================================================== */
01052 
01053 void
01054 qof_session_load (QofSession *session,
01055                   QofPercentageFunc percentage_func)
01056 {
01057         QofBook *newbook, *ob;
01058         QofBookList *oldbooks, *node;
01059         QofBackend *be;
01060         QofBackendError err;
01061         
01062         if (!session) return;
01063         if (!session->book_id) return;
01064         
01065         ENTER ("sess=%p book_id=%s", session, session->book_id
01066                  ? session->book_id : "(null)");
01067         
01068         /* At this point, we should are supposed to have a valid book 
01069         * id and a lock on the file. */
01070         
01071         oldbooks = session->books;
01072         
01073         /* XXX why are we creating a book here? I think the books
01074         * need to be handled by the backend ... especially since 
01075         * the backend may need to load multiple books ... XXX. FIXME.
01076         */
01077         newbook = qof_book_new();
01078         session->books = g_list_append (NULL, newbook);
01079         PINFO ("new book=%p", newbook);
01080         
01081         qof_session_clear_error (session);
01082         
01083         /* This code should be sufficient to initialize *any* backend,
01084         * whether http, postgres, or anything else that might come along.
01085         * Basically, the idea is that by now, a backend has already been
01086         * created & set up.  At this point, we only need to get the
01087         * top-level account group out of the backend, and that is a
01088         * generic, backend-independent operation.
01089         */
01090         be = session->backend;
01091         qof_book_set_backend(newbook, be);
01092         
01093         /* Starting the session should result in a bunch of accounts
01094         * and currencies being downloaded, but probably no transactions;
01095         * The GUI will need to do a query for that.
01096         */
01097         if (be)
01098         {
01099                 be->percentage = percentage_func;
01100                 
01101                 if (be->load) 
01102                 {
01103                         be->load (be, newbook);

QofSession* qof_session_get_current_session void   ) 
 

Deprecated:
Each application should keep their own session context.

Definition at line 213 of file qofsession.c.

00214 {
00215   if (!current_session)
00216   {
00217     qof_event_suspend ();
00218     current_session = qof_session_new ();
00219     qof_event_resume ();
00220   }
00221 
00222   return current_session;
00223 }

void qof_session_set_current_session QofSession session  ) 
 

Deprecated:
Each application should keep their own session context.

Definition at line 228 of file qofsession.c.

00229 {
00230   current_session = session;
00231 }

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 1424 of file qofsession.c.


Generated on Fri May 12 18:00:36 2006 for QOF by  doxygen 1.4.4