GncBusiness v. GNCSession

Derek Atkins warlord@MIT.EDU
21 Nov 2001 20:17:18 -0500


linas@linas.org (Linas Vepstas) writes:

> off-topic .. anyone looked at the gnome-2.0 'gobj' library?  Any
> opinions?  Should we start using it?  I suspect is provides at least some
> of the mechanism that we are fishing for, and if we're lucky, Ariel Rios
> will write the scheme bindings for it ....

No, I haven't looked at it, but we should DEFINITELY not use it.
In fact, we shouldn't even consider it until well after gnome-2.0
is both releases AND incorporated into the major Linux distributions.

> I don't understand two things:
> 1) why a generic gui wrapper is needed. What services/value-add
>    does it provide?
> 2) The mechanism of returning a hook back to the 'actual' gui code.

Take a look at src/gnome-utils/gnc-general-select.[ch] for an example.
A generic gui selector is necessary because otherwise you'd end up
with N widgets that basically do exactly the same thing.
Generalization is a good thing.

> > An Invoice has dual meanings.  There is the invoice which is something
> > that is printed/viewed/mailed/etc.  Then there is the GncInvoice which
> > links a bunch of order entries to the posted transaction.
> 
> I guess I'll need to think about what an 'order entry' is.

An order entry is a line-item on an Order (either a purchase order
or sales order).  For example, a customer orders:
	Object	Qty	Price
	Widget1 1	1.00
	Widget2 1	2.00
	Widget3	1	1.50

Each of these is an Entry.  Each of these could be invoiced
separately.

> off-topic again, but what happens when you fill half an order, and
> back-order the rest?  Is the posted transactin modified? (hope not).
> Are multiple invoiced linked ?  ??

There would be multiple invoices, and each invoice would be tied
to a transaction.

> There are days when I feel I'm reinventing SQL, badly ...

It would be nice if we could have just used SQL directly ;)

> I'm not sue what you are saying.  The GUI builds a query, using 
> the engine query api.  It then 'executes' the query, which causes
> the the query to be passed to the engine.  The backend walks the 
> tree of boolean joins, and converts it to a single SQL query.
> It sends that to the server, and finishes by poking data back into the
> engine.
> 
> The backend doesn't call back into the engine to discover what the query
> is, it doesn't need to, it has full access to all engine structs.

My proposal is to change that.  I don't want to require the backend
to know about all engine structs, because I want to dynamically add
new structs to the engine.

> > By the process, the Account* query-method would give:
> > 	all accounts
> > 
> > the Split* query-method would give:
> > 	where split-memo='some string'
> 
> The point is that you want to do the join inside the sql server, and
> not in the backend.  You want to say 
> 
> SELECT * FROM gncSplit WHERE gncSplit.memo='asdf' AND
> gncAccount.type='bank'.  
> 
> Because there might be only one split that matches this join, whereas
> there might be millions of splits with ths memo (beloning to type
> 'creditcard').  SQL servers are highly optimized to do this, we 
> couldn't begin to compete by doing it ourselves.
> 
> If every core-object has a (*query)() method  on it, then how do I 
> do boolean joins, and nesting?
> 
> (account='xxx' AND invoice='yyy') OR (transaction.amount >333 AND 
> account.balance < 222)

The query isn't EXECUTED in the core-object; the query is BUILT in the
core-object (i.e. a char* is returned).  The core-object builds up
it's part of the query, which is eventually executed in the "Central
Backend".

> > > > Who initializes this business-object backend, and how?
> > > 
> > > This is based on the URL. If the URL is postgres://asdf.com then
> > > the postgres backend is loaded.
> > 
> > That's nice in the abstract.  Show me the code.  
> 
> This is exactly what gnc-session does.  Its almost the only
> thing that gnc-session does.

Which implies we need a generic method to tell gnc-session all of the
core-objects...  Which is my suggested "core-object registration" :)

> > Somewhere this
> > URL has to be passed to the business objects.  Where and how
> > is that done?
> 
> Quite the reverse.  In the current scheme of thngs,  gnc-session 
> parses the URL and determines which backend to load.  It then 
> tells the backend 'giddyap', and the backend creates all the 
> objects, and anchors them into the book.
> 
> Although the association of url-type to backend is currently
> hard-coded, we could get it out of a config file if needed.
> 
> What I do *not* want to do is to load each backend, and ask it 
> if it knows how to handle this particular URL.  That would blow
> out the RAM.

Fair enough.  What I really meant was:

	user calls 
		gnc_open_session("url:/path/to/data");

	how does some the backend know how to load some random core-object?

> > > Please stop calling it 'save'.  Save is a meaningless concept for 
> > > the sql backend.  Save is peculirat to the file backend.
> > 
> > Indeed, but until we stop supporting the file backend it's an
> > important concept.. That's why I keep bringing it up.  I acknowledge
> > that for SQL, RPC, HTTP, etc it has no meaning.  That's fine, and a
> > good thing.  However unless file support goes away it's the
> > "odd-problem" that needs to be solved.
> 
> OK, point taken.  But we need to call it 'sync' not save. 
> The sql backend may be abhorent of 'save', but it does need 
> a 'sync', if, for example, the engine was loaded from a file,
> and now needs to be merged into the (presumably larger) sql db.
> 'save' implies 'clobber', which is wrong.   Maybe I'm being picky.

Ok.  Conceeded.  "Sync" :)

> > Ok.  I think that the GncBusinessObject structure is one place
> > to start.  My list of methods from my previous mail is a second.
> 
> well, lets just crawl through this field by field.
> 
> 'version'. Why is this needed? How will it be used?  How will it be
> documented so that some joker doesn't mis-use it?

The version is to make sure that the module and 'core' are
compiled against the same API.

> 'description'.  Why is this needed?  Is it supposed to be human-readable,
> i18n gettext'ed?  Who does the gettext?  Or is this a 'machine readable
> string', listing some kind of config info?

The description is used for generic searches, to present a printable
string to the user when the generic widget says "Choose FOO".

> 'destroy()'  OK, sounds reasonable... is it OK to call other methods
> on this class after destroy ahs been called? If not, then when do we
> call finalize?

This destroys any 'local state' in the session created during the
'gnc_session_create()' function (which calls the 'create()' method).
After destroy(), there is no more data.  No methods can be called
against this GNCSession after destroy().

> finalize() ... what does this do? is it OK to call this before calling
> destroy() ?  

Yes.  finalize() means "this session is about to go away -- clean
up while you have the chance".  It's a warning, and should be called
before destroy().

> 'save()' OK, as long as it has sync not clobber semantics.  I'm
> wishy-washy about whether there should be independent 'sync-from'
> and 'syncto' routines.

Sure. I'm flexible on this one.

> 'the_session_is_going_away()' -- I think we need one of these,
> although none of the objects/backends currently have this.  
> I think that this is supposed to mean 'close down the back end'.

That's finalize(), isn't it?

> 'begin_edit() && commit_edit()'.  Debatable.  I think its a
> straightjacket. I think this overpsecifies the problem.

What exactly is the problem?  I thought that begin/commit we there to
"lock" and "commit" the data structure?  Or, at least, it was there to
allow multiple 'changes' to be done with only one write-behind in the
backend.

> query() -- highly debatable, because of the join issue.

Yea.. I'm not sure about this, either..  But there needs to be some
way to extend the backend (which includes queries) without requiring
the backend to know about all data structures.

It we could assume SQL, then the query() method could be used to build
a char* query string and then the real backend would do that.

> I think I can go for:
> 
>  	Account && AccountGroup
>  	Transaction && Split
>  	SchedXAction
>  	Commodity (yes!)
>  	Prices	
> 
> > I may be missing something here.  I'm also not 100% convinced that a
> > Commodity is a core object.  
> 
> it should be.
> 
> > For one thing, it doesn't have a GUID, so
> > perhaps it is a "special" core object?
> 
> That's because commodities have thier own way of getting a unique
> special name.   No matter, if guid is a show-stopper, we should issue
> them guid's.

:)

> > Aren't there more SQL tables than this?
> Yes, but they are subsidiary tables, for KVP, versioning, auditing.
> 
> > How programatic is SQL?  Isn't it basically of the form:
> > 
> > 	SELECT <what you want to see> FROM <where you want to find it>
> > 		WHERE <what you want to search on>
> 
> Yes, except as dave points out, you mostly don't need the FROM,
> and WHERE can be not only a boolean tree of nested parens, AND, OR,
> NOT, but can also be the results of nested sub-selects, expressions
> containing +, -, *, /, < > = !, full-blown regexp's for strings, 
> system-defined functions like sum(), avg(), count(), or any user 
> defined pattern-matching 'functions'.  (written in sql, or C, or 
> perl, or python, or...)
> 
> I use some of the fancier forms to compute the running balances.

Well, you already build a SQL query from the GNCQuery..  Why couldn't
you build it in a distributed manner?

> > Hrm, I wasn't thinking of tying it in quite like this.  This is partly
> > where the GncInvoice() comes in.  When a Receipt is posted to A/R, the
> > receipt transaction is tagged with the GncInvoice that is being paid
> > (and the Invoice is marked paid).
> 
> which means that you have to lock the transaction and the invoice tables
> which means that the invoice backend needs to know about the transaction
> table, which pretty much means that the invoice backend will be 
> integrated with the transaction backend.

Huh?  I don't understand this.

> > I still think you want to be able to lock/commit the backend on a
> > per-object basis.  That way if I'm going to be editing, say, a
> > Customer record, you don't over-write my changes.
> 
> No what I mean is that sometimes I have to lock the split transactino
> and account tables at the same time, because I have to modify all of
> them atomically.  I cant just do one, and then do the next one.
> They have to happen simultaneously.
> 
> (in sql, locks define what happens 'simulatneously' from the point of
> view of other observers.)

Oooh, I think I see the problem -- you can cause deadlock if you try
to lock multiple tables in different orders.  Processes A and B want
tables X and Y, but if A tries to lock X then Y, and B tries to lock Y
then X, then you could cause a deadlock where A holds X and B holds Y
and neither can continue.

The obvious fix for this is a "locking lock" which you must hold in
order to ask for any locks, and you must hold it until you have all
the locks you want.  Only when you have all your locks can you release
the locking lock.  Therefore:

A would obtain L, then obtain X and Y, then release L
B would obtain L, then obtain Y and X, then release L

Nothing says you can't lock multiple tables at once.

> --linas

-derek

-- 
       Derek Atkins, SB '93 MIT EE, SM '95 MIT Media Laboratory
       Member, MIT Student Information Processing Board  (SIPB)
       URL: http://web.mit.edu/warlord/    PP-ASEL-IA     N1NWH
       warlord@MIT.EDU                        PGP key available