* created the backend-api document

* removed the old Query code (bug #94318)


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@7290 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Derek Atkins 2002-10-08 04:01:54 +00:00
parent 123d988ed1
commit a56e6be18b
4 changed files with 165 additions and 3212 deletions

View File

@ -1,6 +1,11 @@
2002-10-07 Derek Atkins <derek@ihtfp.com> 2002-10-07 Derek Atkins <derek@ihtfp.com>
* Rename "print invoice" to "printable invoice" * Rename "print invoice" to "printable invoice"
* Applied Matt Martin's advanced-portfolio patch.
* created the backend-api document
* removed the old Query code (bug #94318)
2002-10-07 Christian Stimming <stimming@tuhh.de> 2002-10-07 Christian Stimming <stimming@tuhh.de>
* src/import-export/hbci/druid-hbci-initial.c: Fix and activate * src/import-export/hbci/druid-hbci-initial.c: Fix and activate

160
src/doc/backend-api.txt Normal file
View File

@ -0,0 +1,160 @@
GnuCash Backend API (v2)
Derek Atkins
<derek@ihtfp.com>
Created: 2002-10-07
Problem:
--------
The current Backend API is hardcoded to dealing with Accounts,
Transactions, and Splits. The Backend Query API does not allow
caching of a Query (meaning the Backend has to recompile the Query
every time the query is executed). With the inclusion of a multitude
of new datatypes and plugable architure, the Backend API requires
modification to handle the new data.
"Dynamic" Data Types:
---------------------
The engine has a set of APIs to load new data types into the engine.
The Backends need this as well. Currently the engine supplies a set
of registration functions to register Backend handlers for new data
types. Each Backend defines a plug-in API and then data types can
register themselves. This is how extensibility works.
For example, the "file" Backend defines the API for plug-in data
types. It requires data types to implement four functions:
create_parser(), add_item(), get_count(), and write().
A new data-type, the GncFOO type, implements the required APIs and
registers itself with gncObjectRegisterBackend(). The file backend
can then either lookup the GncFOO object by name by calling
gncObjectLookupBackend(), or can iterate over all the registered
objects by using gncObjectForeachBackend(), depending on the
particular backend operation.
By using these functions, new data types can be registered and new
types of data stored using generic Backend API functions. The backend
implementing generic *_edit() or session_load() APIs could then lookup
data types by name or iterate over all known data types to determine
how to load or save data. Each backend can define the set of
interfaces it requires data-types to implement.
Handling Queries:
-----------------
The version-1 Backend provides a single run-query method that returns
a list of splits. This has proven to be limiting, and recompiling the
query into the backend format each time can be time consuming. To fix
this the backend query API needs to be broken into three pieces:
gpointer (*query_compile)(Backend* be, Query* query);
compiles a Query* into whatever Backend Language is necessary.
void (*query_free)(Backend* be, gpointer query);
frees the compiled Query (obtained from the query_compile method).
void (*query_run)(Backend* be, gpointer query);
executes the compiled Query and inserts the responses into the
engine. It will search for the type corresponding to the
Query search_for type: gncQueryGetSearchFor(). Note that the
search type CANNOT change between a compile and the execute,
but the query infrastructure maintains that invariant.
In this manner, a Backend (e.g. the Postgres backend) can compile the
Query into its own format (e.g. a SQL expression) and then use the
pre-compiled expression every run instead of rebuilding the
expression.
There is an implementation issue in the case of Queries across
multiple Books. Each book could theoretically be in a different
backend, which means we need to tie the compiled query to the book's
Backend for which it was compiled. This is an implementation detail,
and not even a challenging one, but it needs to be clearly
acknowledged up front.
Also note that this API can usurp the price_lookup() method, assuming
the GNCPriceLookup can be subsumed by the Query.
Handling Multiple Datatypes:
----------------------------
The current API specifically defines "edit" functions for Accounts and
Transactions. This rather rigid API does not allow for adding new
data types to the Backend. A better approach is to generalize the
begin_edit, rollback_edit, and commit_edit APIs into a general API
which is dynamically sub-typed at runtime:
void (*begin_edit)(Backend* be, GNCIdTypeConst data_type, gpointer object);
void (*rollback_edit)(Backend* be, GNCIdTypeConst data_type, gpointer object);
void (*commit_edit)(Backend* be, GNCIdTypeConst data_type, gpointer object);
This API looks just like the existing API for Accounts, Periods, and
Price entries, although it quite obviously does not match the
Transaction commit. Note that not all data-types need to implement
all three types (there is no rollback on Accounts, Prices, or
Periods). Note that certain data-types can _still_ be special (e.g.
the Period handling).
Question: why does the transaction commit have two transactions? In
particular, can't the backend "know" that the "original" transaction
is in "trans->orig". Besides, if the Backend is truly in charge of
the data, then the engine can make changes to the local copy and can
"back out" by accessing the backend (or commit by sending it to the
backend). Can't one assume that the "backend" knows how the engine is
implementing the rollback caching?
When to load data?
------------------
Data loads into the engine at two times, at start time and at query
time. Loading data during queries is discussed above. This section
discusses data loaded at startup.
Currently the API has book_load() and price_load(). That's nice for
the book and price DB, but there may be other items that need to be
loaded at "start" time. A better approach would be to combine all
the _load() APIs into a single API:
void session_load(Backend*, GNCBook*);
This one API would load all the necessary "start-time" data, including
the Chart of Accounts, the Commodity Table, the Scheduled Transaction
List, the Pricedb, etc. There is no need to have multiple APIs for
each of the data types loaded at start-time. Dynamic data-types that
require data to be loaded at start-time can register a specific API
for the backend to execute the load.
Usefulness of sync_*()?
-----------------------
What is the point of sync_all(), sync_group(), and sync_price()?
Obviously one of them is necessary to implement "save-as", but there
is no need for multiple versions. New datatypes can just be plugged
in by the dynamic API. There is no reason to differentiate the book
from the pricedb, as they are still attached to each other.
Therefore, sync_all() should be left and sync_group() and sync_price()
should be removed.
Usefulness of export()?
-----------------------
The export() method is used to export a Chart of Accounts to a file.
Is it really necessary that this be in the backend? What does it mean
to "export" in anything else? Note that only the file backend even
IMPLEMENTS this method... How general is export?
============================== END OF DOCUMENT =====================

File diff suppressed because it is too large Load Diff

View File

@ -34,7 +34,6 @@
#include "guid.h" #include "guid.h"
#include "kvp_frame.h" #include "kvp_frame.h"
#if 1
#include "QueryNew.h" #include "QueryNew.h"
#include "QueryCore.h" #include "QueryCore.h"
@ -218,241 +217,4 @@ void xaccQuerySetSortOrder(Query *q, GList *p1, GList *p2, GList *p3);
time_t xaccQueryGetEarliestDateFound(Query * q); time_t xaccQueryGetEarliestDateFound(Query * q);
time_t xaccQueryGetLatestDateFound(Query * q); time_t xaccQueryGetLatestDateFound(Query * q);
#else
typedef struct query_s Query;
/* Protect us if QueryNew.h is included first */
#ifndef GNC_QUERYNEW_H
typedef enum {
QUERY_AND=1,
QUERY_OR,
QUERY_NAND,
QUERY_NOR,
QUERY_XOR
} QueryOp;
#endif
typedef enum {
BY_STANDARD=1,
BY_DATE,
BY_DATE_ROUNDED,
BY_DATE_ENTERED,
BY_DATE_ENTERED_ROUNDED,
BY_DATE_RECONCILED,
BY_DATE_RECONCILED_ROUNDED,
BY_NUM,
BY_AMOUNT,
BY_MEMO,
BY_DESC,
BY_RECONCILE,
BY_ACCOUNT_FULL_NAME,
BY_ACCOUNT_CODE,
BY_CORR_ACCOUNT_FULL_NAME,
BY_CORR_ACCOUNT_CODE,
BY_NONE
} sort_type_t;
typedef enum {
PD_ACCOUNT=1,
PD_AMOUNT,
PD_BALANCE,
PD_BOOK,
PD_CLEARED,
PD_DATE,
PD_GUID,
PD_KVP,
PD_STRING,
PD_MISC
} pd_type_t;
typedef enum {
PR_ACCOUNT=1,
PR_ACTION,
PR_BALANCE,
PR_BOOK,
PR_CLEARED,
PR_DATE,
PR_DESC,
PR_GUID,
PR_KVP,
PR_MEMO,
PR_NUM,
PR_PRICE,
PR_SHRS, /* FIXME: misnamed, should be PR_QUANT or PR_AMOUNT */
PR_VALUE,
PR_MISC,
} pr_type_t;
typedef enum {
ACCT_MATCH_ALL=1,
ACCT_MATCH_ANY,
ACCT_MATCH_NONE
} acct_match_t;
typedef enum
{
AMT_MATCH_ATLEAST=1,
AMT_MATCH_ATMOST,
AMT_MATCH_EXACTLY
} amt_match_t;
typedef enum {
AMT_SGN_MATCH_EITHER=1,
AMT_SGN_MATCH_CREDIT,
AMT_SGN_MATCH_DEBIT
} amt_match_sgn_t;
typedef enum {
BOOK_MATCH_ANY=1,
BOOK_MATCH_NONE
} book_match_t;
typedef enum {
CLEARED_NO = 1 << 0,
CLEARED_CLEARED = 1 << 1,
CLEARED_RECONCILED = 1 << 2,
CLEARED_FROZEN = 1 << 3,
CLEARED_VOIDED = 1 << 4
} cleared_match_t;
enum {
STRING_MATCH_CASE = 1 << 0,
STRING_MATCH_REGEXP = 1 << 1
};
typedef enum {
BALANCE_BALANCED = 1 << 0,
BALANCE_UNBALANCED = 1 << 1
} balance_match_t;
typedef enum {
KVP_MATCH_LT=1,
KVP_MATCH_LTE,
KVP_MATCH_EQ,
KVP_MATCH_GTE,
KVP_MATCH_GT
} kvp_match_t;
typedef enum {
KVP_MATCH_SPLIT = 1 << 0,
KVP_MATCH_TRANS = 1 << 1,
KVP_MATCH_ACCOUNT = 1 << 2
} kvp_match_where_t;
typedef enum {
QUERY_MATCH_ALL=1, /* match all accounts */
QUERY_MATCH_ANY=2 /* match any account */
} query_run_t;
/* compare two queries for equality. this is a simplistic
* implementation -- logical equivalences between different
* and/or trees are ignored. */
gboolean gncQueryEqual(Query *q1, Query *q2);
/* handy for debugging */
void xaccQueryPrint(Query *q);
/* (Un-)set the Query's search to look at the template account group used by
* scheduled transactions, as well. */
void xaccQuerySearchTemplateGroup( Query *, gboolean );
/*******************************************************************
* match-adding API
*******************************************************************/
void xaccQueryAddAccountMatch(Query *, AccountList *,
acct_match_t how, QueryOp op);
void xaccQueryAddAccountGUIDMatch(Query *, AccountGUIDList *,
acct_match_t, QueryOp);
void xaccQueryAddSingleAccountMatch(Query *, Account *, QueryOp);
void xaccQueryAddBookMatch(Query * q, BookList *,
acct_match_t how, QueryOp op);
void xaccQueryAddBookGUIDMatch(Query *, BookGUIDList *,
acct_match_t how, QueryOp op);
void xaccQueryAddSingleBookMatch(Query *, GNCBook *, QueryOp);
void xaccQueryAddDescriptionMatch(Query * q, const char * matchstring,
int case_sens, int use_regexp, QueryOp op);
void xaccQueryAddNumberMatch(Query * q, const char * matchstring,
int case_sens, int use_regexp, QueryOp op);
void xaccQueryAddActionMatch(Query * q, const char * matchstring,
int case_sens, int use_regexp, QueryOp op);
/* ?????????? why are these depricated ??????????? */
void DxaccQueryAddValueMatch(Query * q, double amount,
amt_match_sgn_t amt_sgn,
amt_match_t how, QueryOp op);
void DxaccQueryAddSharePriceMatch(Query * q, double amount,
amt_match_t how, QueryOp op);
void DxaccQueryAddSharesMatch(Query * q, double amount,
amt_match_t how, QueryOp op);
/* The DateMatch queries match transactions whose posted date
* is in a date range. If use_start is TRUE, then a matching
* posted date will be greater than the start date. If
* use_end is TRUE, then a match occurs for posted dates earlier
* than the end date. If both flags are set, then *both*
* conditions must hold ('and'). If neither flag is set, then
* all transactions are matched.
*/
void xaccQueryAddDateMatch(Query * q,
int use_start, int sday, int smonth, int syear,
int use_end, int eday, int emonth, int eyear,
QueryOp op);
void xaccQueryAddDateMatchTS(Query * q,
int use_start, Timespec sts,
int use_end, Timespec ets,
QueryOp op);
void xaccQueryAddDateMatchTT(Query * q,
int use_start, time_t stt,
int use_end, time_t ett,
QueryOp op);
void xaccQueryAddMemoMatch(Query * q, const char * matchstring,
int case_sens, int use_regexp, QueryOp op);
void xaccQueryAddClearedMatch(Query * q, cleared_match_t how, QueryOp op);
void xaccQueryAddBalanceMatch(Query * q, balance_match_t how, QueryOp op);
void xaccQueryAddGUIDMatch(Query * q, const GUID *guid,
GNCIdType id_type, QueryOp op);
/* given kvp value is on right side of comparison */
void xaccQueryAddKVPMatch(Query *q, GSList *path, const kvp_value *value,
kvp_match_t how, kvp_match_where_t where,
QueryOp op);
/*******************************************************************
* sort-related functions
*******************************************************************/
void xaccQuerySetSortOrder(Query * q, sort_type_t primary,
sort_type_t secondary, sort_type_t tertiary);
sort_type_t xaccQueryGetPrimarySortOrder(Query * q);
sort_type_t xaccQueryGetSecondarySortOrder(Query * q);
sort_type_t xaccQueryGetTertiarySortOrder(Query * q);
void xaccQuerySetSortIncreasing(Query * q,
gboolean prim_increasing,
gboolean sec_increasing,
gboolean tert_increasing);
gboolean xaccQueryGetSortPrimaryIncreasing (Query *q);
gboolean xaccQueryGetSortSecondaryIncreasing (Query *q);
gboolean xaccQueryGetSortTertiaryIncreasing (Query *q);
void xaccQuerySetMaxSplits(Query * q, int n);
int xaccQueryGetMaxSplits(Query * q);
/*******************************************************************
* compatibility interface with old Query API
*******************************************************************/
time_t xaccQueryGetEarliestDateFound(Query * q);
time_t xaccQueryGetLatestDateFound(Query * q);
#endif /* 0 */
#endif #endif