New Hacker and QOF instance modification / undo

Neil Williams linux at
Fri Sep 2 05:55:43 EDT 2005

On Thursday 01 September 2005 11:19 pm, Mike B wrote:
> Hey everyone,


> I've just been playing around with the 
> GNUCash source for the first time, and I'm having a
> little trouble.  I haven't seen anything in the design
> documents about how account data is organized and how
> the object mapping interfaces with that, so I'm having
> trouble making the queries that I want to make.

As already mentioned, each object describes itself to the query object 
framework. A static object description outlines what data the object can 
accept and provide, how to iterate over each instance of that object and how 
to mark an instance as dirty.

The mapping from the object data to a specific backend layout is full of 
indirection because the object neither knows nor cares about the detail of 
the backend and vice versa. Each application using QOF can provide it's own 
objects and / or it's own backends.

> I think it's interesting that when someone like me is
> first getting into the GNUCash source, what I really
> want is a database schema and access to SQL queries.

(repeat ten times: gnucash has no database).

If you want to, you could download CashUtil (it needs CVS QOF to build 
currently) as this does provide command line SQL queries. It's far from 
complete but it's easier than compiling new SQL queries via the gnucash tree.

> I'm sure that I'll learn to appreciate the OR mapping
> layer, but right now I'm just confused.

That would be because you are looking for something that does not exist!

I was just as confused when I started with the gnucash source tree.

Josh wrote:
> > You should take a good look at QOF, however.  It's not an OR mapping
> > layer so much as a mappable object model ... i.e., queries expressed
> > over in-memory QOF object-graphs can be translated into SQL.  I'm still
> > not sure how it handles data-modificiation, though.

In QOF, object descriptions provide the object model map using a list of 
parameters that have a name, data type, get function and set function. This 
list is static within the application and referenced via a GHashTable in QOF. 
There is no need to copy QofParam - it's always static and sufficient to keep 
just a pointer.

The SQL translation is actually the reverse of how Josh described it. Incoming 
SQL is converted to the QOF predicates (which can also be manipulated 
directly) where the search is actually achieved. QOF can accept limited SQL 
commands but only by converting them to a QofQuery that it understands. i.e. 
SQL support is not native (and will be implemented via the GDA external 
library in due course). There is currently no way to convert a series of 
predicates into SQL but I'm working on that for CashUtil so that it can have 
some rudimentary macro functions. 

Data-modification is done via QofParam and param_setfcn - a pointer to the 
actual object function that modifies the instance.

QofParam *param; // assuming a string param_type in this case
param->param_setfcn(QofEntity *ent, const char *string);
If this was the Account name parameter (from Account.c):
(QofSetterFunc) xaccAccountSetName },

This would actually call:
void 	xaccAccountSetName (Account *account, const char *name)

The Account* is cast from the QofEntity*.

param->param_getfcn works in a similar way. There's also an important 
distinction between parameters that are "calculated" and those that can be 
"set". Parameters like the Account balance can be queried (using get) but 
should never be set, so the param_setfcn is NULL. This rule is fundamental to 
QOF and it is vital that all objects implement the distinction in a logical 

Function pointers are required to set certain parameters (like gnc_numeric, 
boolean and dates) that are not passed as pointers themselves:

Timespec    cli_date;
void (*date_setter)     (QofEntity*, Timespec);
date_setter = (void(*)(QofEntity*, Timespec))param->param_setfcn;
if(date_setter != NULL) { date_setter(ent, cli_date); }

(That took me ages to understand but thanks to Derek's patience, I got it 


All data modification should occur between a begin_edit and commit_edit pair.

Once QOF undo is implemented, there'll be a further wrapper for a series of 
data modifications that form a single "operation" from the user perspective.

(For those who are interested, the limited undo support for QOF as currently 
implemented in CashUtil is documented using (a buggy) Doxygen output here:

> I think what I really need is an example that I don't
> have to follow through layers of indirection.

If you want to experiment with your own objects, try QOF-gen:

Or PilotQOF (which uses simpler objects):


Neil Williams

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url :

More information about the gnucash-devel mailing list