From a56e6be18b214b6e4102e4e348348061e42234ca Mon Sep 17 00:00:00 2001 From: Derek Atkins Date: Tue, 8 Oct 2002 04:01:54 +0000 Subject: [PATCH] * 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 --- ChangeLog | 5 + src/doc/backend-api.txt | 160 +++ src/engine/Query.c | 2974 --------------------------------------- src/engine/Query.h | 238 ---- 4 files changed, 165 insertions(+), 3212 deletions(-) create mode 100644 src/doc/backend-api.txt diff --git a/ChangeLog b/ChangeLog index 4cd425b620..80f83c80b9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,11 @@ 2002-10-07 Derek Atkins * 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 * src/import-export/hbci/druid-hbci-initial.c: Fix and activate diff --git a/src/doc/backend-api.txt b/src/doc/backend-api.txt new file mode 100644 index 0000000000..38874a9b6f --- /dev/null +++ b/src/doc/backend-api.txt @@ -0,0 +1,160 @@ + GnuCash Backend API (v2) + + Derek Atkins + + +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 ===================== diff --git a/src/engine/Query.c b/src/engine/Query.c index a8ce5c728e..3db19fb6b4 100644 --- a/src/engine/Query.c +++ b/src/engine/Query.c @@ -45,8 +45,6 @@ static short module = MOD_QUERY; -#if 1 - static GSList * build_param_list_internal (const char *first, va_list rest) { @@ -631,2976 +629,4 @@ xaccQuerySetSortOrder(Query *q, GList *p1, GList *p2, GList *p3) gncQuerySetSortOrder (q, l1, l2, l3); } -#else - -/* the Query makes a subset of all splits based on 3 things: - * - an AND-OR tree of predicates which combine to make a - * split filter - * - a sorting order for the matched splits - * - a chop limit which gives the maximum number of sorted - * splits to return. */ - -struct query_s -{ - /* terms is a list of the OR-terms in a sum-of-products - * logical expression. */ - GList * terms; - - /* sorting and chopping is independent of the search filter */ - sort_type_t primary_sort; - sort_type_t secondary_sort; - sort_type_t tertiary_sort; - gboolean primary_increasing; - gboolean secondary_increasing; - gboolean tertiary_increasing; - int max_splits; - - /* list of books that will be participating in the query */ - BookList *books; - - /* Flag to indicate if we're searching over template-transactions, too. */ - gboolean templates_too; - - /* cache the results so we don't have to run the whole search - * again until it's really necessary */ - int changed; - query_run_t last_run_type; - - SplitList * split_list; - TransList * xtn_list; -}; - -/******************************************************************* - * predicates for standard match types - *******************************************************************/ - -static int xaccAccountMatchPredicate(Split * s, PredicateData * pd); -static int xaccActionMatchPredicate(Split * s, PredicateData * pd); -static int xaccBalanceMatchPredicate(Split * s, PredicateData * pd); -static int xaccBookMatchPredicate(Split * s, PredicateData * pd); -static int xaccClearedMatchPredicate(Split * s, PredicateData * pd); -static int xaccDateMatchPredicate(Split * s, PredicateData * pd); -static int xaccDescriptionMatchPredicate(Split * s, PredicateData * pd); -static int xaccGUIDMatchPredicate(Split * s, PredicateData * pd); -static int xaccKVPMatchPredicate(Split * s, PredicateData * pd); -static int xaccMemoMatchPredicate(Split * s, PredicateData * pd); -static int xaccNumberMatchPredicate(Split * s, PredicateData * pd); -static int xaccSharePriceMatchPredicate(Split * s, PredicateData * pd); -static int xaccSharesMatchPredicate(Split * s, PredicateData * pd); -static int xaccValueMatchPredicate(Split * s, PredicateData * pd); - -/******************************************************************** - ********************************************************************/ - -void -xaccQueryPrint(Query * q) -{ - GList * aterms; - GList * i, * j; - QueryTerm * qt; - - if (!q) - { - printf("Query: null\n"); - return; - } - - printf("Query: max splits = %d\n", q->max_splits); - - /* print and & or terms */ - for(i=q->terms; i; i=i->next) { - aterms = i->data; - printf("("); - for(j=aterms; j; j=j->next) { - qt = (QueryTerm *)j->data; - if(!qt->data.base.sense) printf("~"); - printf("%d ", qt->data.type); - } - printf(")"); - if(i->next) { - printf(" | "); - } - } - printf("\n"); - - /* print the node contents */ - for(i=q->terms; i; i=i->next) - { - aterms = i->data; - printf("aterm=%p\n", aterms); - for(j=aterms; j; j=j->next) - { - qt = (QueryTerm *)j->data; - switch (qt->data.base.term_type) - { - case PR_ACCOUNT: { - GList *p; - char buff[40]; - printf ("account sense=%d how=%d\n", - qt->data.base.sense, - qt->data.acct.how); - for (p=qt->data.acct.account_guids; p; p=p->next) { - guid_to_string_buff (p->data, buff); - printf ("\tguid=%s\n", buff); - } - for (p=qt->data.acct.accounts; p; p=p->next) { - printf ("\tacct ptr=%p\n", p->data); - } - break; - } - case PR_ACTION: - printf ("action sense=%d case sensitive=%d\n", qt->data.str.sense, - qt->data.str.case_sens); - printf ("\tmatch string=%s \n", qt->data.str.matchstring); - break; - - case PR_BALANCE: - printf ("balance sense=%d how=%d\n", qt->data.balance.sense, - qt->data.balance.how); - break; - - case PR_BOOK: { - GList *p; - char buff[40]; - printf ("book sense=%d how=%d\n", - qt->data.base.sense, - qt->data.book.how); - for (p=qt->data.book.book_guids; p; p=p->next) { - guid_to_string_buff (p->data, buff); - printf ("\tguid=%s\n", buff); - } - for (p=qt->data.book.books; p; p=p->next) { - printf ("\tbook ptr=%p\n", p->data); - } - break; - } - case PR_CLEARED: - printf ("cleared sense=%d how=%d\n", qt->data.cleared.sense, - qt->data.cleared.how); - break; - - case PR_DATE: { - char buff[40]; - printf ("date sense=%d use_start=%d use_end=%d\n", - qt->data.base.sense, - qt->data.date.use_start, - qt->data.date.use_end - ); - if (qt->data.date.use_start) { - gnc_timespec_to_iso8601_buff (qt->data.date.start, buff); - printf ("\tstart date=%s\n", buff); - } - if (qt->data.date.use_end) { - gnc_timespec_to_iso8601_buff (qt->data.date.end, buff); - printf ("\tend date=%s\n", buff); - } - break; - } - case PR_DESC: - printf ("desc sense=%d case sensitive=%d\n", qt->data.str.sense, - qt->data.str.case_sens); - printf ("\tmatch string=%s \n", qt->data.str.matchstring); - break; - - case PR_GUID: { - char buff[40]; - printf ("guid sense=%d\n", qt->data.guid.sense); - guid_to_string_buff (&qt->data.guid.guid, buff); - printf ("\tguid=%s\n", buff); - printf ("\tid type=%s\n", qt->data.guid.id_type); - break; - } - case PR_KVP: { - GSList *node; - char *str; - printf ("kvp sense=%d how=%d where=%d\n", qt->data.kvp.sense, - qt->data.kvp.how, qt->data.kvp.where); - printf ("path:"); - for (node = qt->data.kvp.path; node; node = node->next) - { - printf (node->data); - if (node->next) - printf ("/"); - } - printf ("\n"); - str = kvp_value_to_string (qt->data.kvp.value); - printf ("value: %s\n", str); - g_free (str); - break; - } - case PR_MEMO: - printf ("memo sense=%d case sensitive=%d\n", qt->data.str.sense, - qt->data.str.case_sens); - printf ("\tmatch string=%s \n", qt->data.str.matchstring); - break; - - case PR_MISC: - printf ("misc\n"); - break; - - case PR_NUM: - printf ("num sense=%d case sensitive=%d\n", qt->data.str.sense, - qt->data.str.case_sens); - printf ("\tmatch string=%s \n", qt->data.str.matchstring); - break; - - case PR_PRICE: - printf ("price sense=%d how=%d\n", qt->data.amount.sense, - qt->data.amount.how); - printf ("\tsign=%d amount=%f\n", qt->data.amount.amt_sgn, - qt->data.amount.amount); - break; - - case PR_SHRS: - printf ("shrs sense=%d how=%d\n", qt->data.amount.sense, - qt->data.amount.how); - printf ("\tsign=%d amount=%f\n", qt->data.amount.amt_sgn, - qt->data.amount.amount); - break; - - case PR_VALUE: - printf ("value sense=%d how=%d\n", qt->data.amount.sense, - qt->data.amount.how); - printf ("\tsign=%d amount=%f\n", qt->data.amount.amt_sgn, - qt->data.amount.amount); - break; - - default: - printf ("unkown term type=%d \n", qt->data.base.term_type); - } - } - printf("\n"); - if(i->next) { - printf("\n"); - } - } -} - -void -xaccQuerySearchTemplateGroup( Query *q, gboolean newState ) -{ - if (!q) - return; - q->templates_too = newState; -} - - -/******************************************************************** - * xaccInitQuery - ********************************************************************/ - -/* initial_term has hand-over semantics! Thus, initial_term must point - * to newly allocated memory or be NULL. */ - -static void -xaccInitQuery(Query * q, QueryTerm * initial_term) -{ - GList * or = NULL; - GList * and = NULL; - - if(initial_term) - { - or = g_list_alloc(); - and = g_list_alloc(); - and->data = initial_term; - or->data = and; - } - - if(q->terms) xaccQueryClear(q); - - q->books = NULL; - - q->terms = or; - q->split_list = NULL; - q->xtn_list = NULL; - q->changed = 1; - - q->max_splits = -1; - - q->primary_sort = BY_STANDARD; - q->secondary_sort = BY_NONE; - q->tertiary_sort = BY_NONE; - - q->primary_increasing = TRUE; - q->secondary_increasing = TRUE; - q->tertiary_increasing = TRUE; - - q->templates_too = FALSE; -} - - -/******************************************************************** - * xaccMallocQuery - ********************************************************************/ - -Query * -xaccMallocQuery(void) -{ - Query * qp = g_new0(Query, 1); - xaccInitQuery(qp, NULL); - return qp; -} - - -/******************************************************************** - * xaccQuerySwapTerms - * swaps the terms fields of two queries, mostly to - * allow quick pass-off of results. - ********************************************************************/ - -static void -xaccQuerySwapTerms(Query * q1, Query * q2) -{ - GList * g; - - if (!q1 || !q2) - return; - - g = q1->terms; - q1->terms = q2->terms; - q2->terms = g; - - q1->changed = 1; - q2->changed = 1; -} - - -/******************************************************************** - * xaccQueryHasTerms - * returns the number of 'OR' terms in the query, which is generally - * used as a truth test. - ********************************************************************/ - -int -xaccQueryHasTerms(Query * q) -{ - if (!q) - return 0; - - return g_list_length(q->terms); -} - -int -xaccQueryNumTerms(Query * q) -{ - GList *o; - int n=0; - if (!q) - return 0; - - for(o=q->terms; o; o=o->next) - { - n += g_list_length(o->data); - } - return n; -} - -GList * -xaccQueryGetTerms (Query *q) -{ - if (!q) return NULL; - return q->terms; -} - - -/******************************************************************** - * xaccQueryHasTermType - * returns TRUE if the query has any terms of the given type - ********************************************************************/ -gboolean -xaccQueryHasTermType(Query * q, pd_type_t type) -{ - GList *or; - GList *and; - - if (!q) - return FALSE; - - for(or = q->terms; or; or = or->next) { - for(and = or->data; and; and = and->next) { - QueryTerm *qt = and->data; - if(qt->data.type == type) - return TRUE; - } - } - - return FALSE; -} - -static void -free_query_term(QueryTerm *qt) -{ - GList *node; - - if (qt == NULL) - return; - - switch (qt->data.type) - { - case PD_ACCOUNT: - g_list_free (qt->data.acct.accounts); - qt->data.acct.accounts = NULL; - - for (node = qt->data.acct.account_guids; node; node = node->next) - xaccGUIDFree (node->data); - - g_list_free (qt->data.acct.account_guids); - qt->data.acct.account_guids = NULL; - break; - - case PD_BOOK: - g_list_free (qt->data.book.books); - qt->data.book.books = NULL; - - for (node = qt->data.book.book_guids; node; node = node->next) - xaccGUIDFree (node->data); - - g_list_free (qt->data.book.book_guids); - qt->data.book.book_guids = NULL; - break; - - case PD_KVP: - g_slist_free (qt->data.kvp.path); - qt->data.kvp.path = NULL; - kvp_value_delete (qt->data.kvp.value); - qt->data.kvp.value = NULL; - break; - - case PD_STRING: - g_free(qt->data.str.matchstring); - qt->data.str.matchstring = NULL; - break; - - default: - break; - } - - g_free(qt); -} - -static QueryTerm * -copy_query_term(QueryTerm * qt) -{ - QueryTerm * nqt; - GList *node; - - if (qt == NULL) - return NULL; - - nqt = g_new0(QueryTerm, 1); - - memcpy(nqt, qt, sizeof(QueryTerm)); - - switch (nqt->data.type) - { - case PD_ACCOUNT: - nqt->data.acct.accounts = g_list_copy (nqt->data.acct.accounts); - nqt->data.acct.account_guids = - g_list_copy (nqt->data.acct.account_guids); - for (node = nqt->data.acct.account_guids; node; node = node->next) - { - GUID *old = node->data; - GUID *new = xaccGUIDMalloc (); - - *new = *old; - node->data = new; - } - break; - - case PD_BOOK: - nqt->data.book.books = g_list_copy (nqt->data.book.books); - nqt->data.book.book_guids = - g_list_copy (nqt->data.book.book_guids); - for (node = nqt->data.book.book_guids; node; node = node->next) - { - GUID *old = node->data; - GUID *new = xaccGUIDMalloc (); - - *new = *old; - node->data = new; - } - break; - - case PD_KVP: { - GSList *node; - - nqt->data.kvp.path = g_slist_copy (nqt->data.kvp.path); - nqt->data.kvp.value = kvp_value_copy (nqt->data.kvp.value); - - for (node = nqt->data.kvp.path; node; node = node->next) - node->data = g_strdup (node->data); - - break; - } - - case PD_STRING: - nqt->data.str.matchstring = g_strdup(nqt->data.str.matchstring); - break; - - default: - break; - } - - return nqt; -} - -static GList * -copy_and_terms(GList *and_terms) -{ - GList *and = NULL; - GList *cur_and; - - for(cur_and = and_terms; cur_and; cur_and = cur_and->next) - { - and = g_list_prepend(and, copy_query_term (cur_and->data)); - } - - return g_list_reverse(and); -} - -static GList * -copy_or_terms(GList * or_terms) -{ - GList * or = NULL; - GList * cur_or; - - for(cur_or = or_terms; cur_or; cur_or = cur_or->next) - { - or = g_list_prepend(or, copy_and_terms(cur_or->data)); - } - - return g_list_reverse(or); -} - - -/******************************************************************** - * xaccFreeQuery - * note that the terms list is freed, so you must have newly - * allocated it - ********************************************************************/ - -static void -xaccFreeQueryMembers(Query *q) -{ - GList * cur_or; - - if (q == NULL) return; - - for(cur_or = q->terms; cur_or; cur_or = cur_or->next) - { - GList * cur_and; - - for(cur_and = cur_or->data; cur_and; cur_and = cur_and->next) - { - free_query_term(cur_and->data); - cur_and->data = NULL; - } - - g_list_free(cur_or->data); - cur_or->data = NULL; - } - - g_list_free(q->terms); - q->terms = NULL; - - g_list_free(q->split_list); - q->split_list = NULL; - - g_list_free(q->xtn_list); - q->xtn_list = NULL; -} - -void -xaccFreeQuery(Query * q) -{ - if (q == NULL) return; - - xaccFreeQueryMembers (q); - - g_free(q); -} - -Query * -xaccQueryCopy(Query *q) -{ - Query *copy; - - if (q == NULL) return NULL; - - copy = xaccMallocQuery (); - xaccFreeQueryMembers (copy); - - copy->terms = copy_or_terms (q->terms); - - copy->primary_sort = q->primary_sort; - copy->secondary_sort = q->secondary_sort; - copy->tertiary_sort = q->tertiary_sort; - - copy->primary_increasing = q->primary_increasing; - copy->secondary_increasing = q->secondary_increasing; - copy->tertiary_increasing = q->tertiary_increasing; - copy->max_splits = q->max_splits; - - copy->changed = q->changed; - copy->split_list = g_list_copy (q->split_list); - copy->xtn_list = g_list_copy (q->xtn_list); - - copy->templates_too = q->templates_too; - - return copy; -} - -/******************************************************************** - * xaccQueryInvert - * return a newly-allocated Query object which is the - * logical inverse of the original. - ********************************************************************/ - -Query * -xaccQueryInvert(Query * q) -{ - Query * retval; - Query * right, * left, * iright, * ileft; - QueryTerm * qt; - GList * aterms; - GList * cur; - GList * new_oterm; - int num_or_terms; - - num_or_terms = g_list_length(q->terms); - - switch(num_or_terms) - { - case 0: - retval = xaccMallocQuery(); - retval->max_splits = q->max_splits; - break; - - /* this is demorgan expansion for a single AND expression. */ - /* !(abc) = !a + !b + !c */ - case 1: - retval = xaccMallocQuery(); - retval->max_splits = q->max_splits; - - aterms = g_list_nth_data(q->terms, 0); - new_oterm = NULL; - for(cur=aterms; cur; cur=cur->next) { - qt = copy_query_term(cur->data); - qt->data.base.sense = !(qt->data.base.sense); - new_oterm = g_list_append(NULL, qt); - retval->terms = g_list_append(retval->terms, new_oterm); - } - break; - - /* if there are multiple OR-terms, we just recurse by - * breaking it down to !(a + b + c) = - * !a * !(b + c) = !a * !b * !c. */ - default: - right = xaccMallocQuery(); - right->terms = copy_or_terms(g_list_nth(q->terms, 1)); - - left = xaccMallocQuery(); - left->terms = g_list_append(NULL, - copy_and_terms(g_list_nth_data(q->terms, 0))); - - iright = xaccQueryInvert(right); - ileft = xaccQueryInvert(left); - - retval = xaccQueryMerge(iright, ileft, QUERY_AND); - retval->max_splits = q->max_splits; - retval->changed = 1; - - xaccFreeQuery(iright); - xaccFreeQuery(ileft); - xaccFreeQuery(right); - xaccFreeQuery(left); - break; - } - - return retval; -} - - -/******************************************************************** - * xaccQueryMerge - * combine 2 Query objects by the logical operation in "op". - ********************************************************************/ - -Query * -xaccQueryMerge(Query * q1, Query * q2, QueryOp op) -{ - - Query * retval = NULL; - Query * i1, * i2; - Query * t1, * t2; - GList * i, * j; - - if(!q1 || !q2 ) return NULL; - - switch(op) - { - case QUERY_OR: - retval = xaccMallocQuery(); - retval->terms = - g_list_concat(copy_or_terms(q1->terms), copy_or_terms(q2->terms)); - retval->max_splits = q1->max_splits; - retval->split_list = NULL; - retval->xtn_list = NULL; - retval->changed = 1; - break; - - case QUERY_AND: - retval = xaccMallocQuery(); - retval->max_splits = q1->max_splits; - retval->split_list = NULL; - retval->xtn_list = NULL; - retval->changed = 1; - - for(i=q1->terms; i; i=i->next) - { - for(j=q2->terms; j; j=j->next) - { - retval->terms = - g_list_append(retval->terms, - g_list_concat - (copy_and_terms(i->data), - copy_and_terms(j->data))); - } - } - break; - - case QUERY_NAND: - /* !(a*b) = (!a + !b) */ - i1 = xaccQueryInvert(q1); - i2 = xaccQueryInvert(q2); - retval = xaccQueryMerge(i1, i2, QUERY_OR); - xaccFreeQuery(i1); - xaccFreeQuery(i2); - break; - - case QUERY_NOR: - /* !(a+b) = (!a*!b) */ - i1 = xaccQueryInvert(q1); - i2 = xaccQueryInvert(q2); - retval = xaccQueryMerge(i1, i2, QUERY_AND); - xaccFreeQuery(i1); - xaccFreeQuery(i2); - break; - - case QUERY_XOR: - /* a xor b = (a * !b) + (!a * b) */ - i1 = xaccQueryInvert(q1); - i2 = xaccQueryInvert(q2); - t1 = xaccQueryMerge(q1, i2, QUERY_AND); - t2 = xaccQueryMerge(i1, q2, QUERY_AND); - retval = xaccQueryMerge(t1, t2, QUERY_OR); - - xaccFreeQuery(i1); - xaccFreeQuery(i2); - xaccFreeQuery(t1); - xaccFreeQuery(t2); - break; - } - - return retval; -} - - -/* this sort function just puts account queries at the top of the - * list. this lets us skip accounts that have no chance. */ -static gint -query_sort_func(gconstpointer pa, gconstpointer pb) -{ - const QueryTerm * a = pa; - const QueryTerm * b = pb; - if(a->data.type == PD_ACCOUNT) { - return -1; - } - else if(b->data.type == PD_ACCOUNT) { - return 1; - } - else if(a->data.type == PD_AMOUNT) { - return -1; - } - else if(b->data.type == PD_AMOUNT) { - return 1; - } - else { - return 0; - } -} - -static int -acct_query_matches(QueryTerm * qt, Account * acct) -{ - GList *node; - gboolean account_in_set = FALSE; - - g_return_val_if_fail(qt && acct, FALSE); - g_return_val_if_fail(qt->data.type == PD_ACCOUNT, FALSE); - - for(node = qt->data.acct.accounts; node ; node = node->next) - { - if(acct == node->data) - { - account_in_set = TRUE; - break; - } - } - - /* If we need the query to match "ALL" accounts, we only return - * true for the first acct in the set. */ - switch(qt->data.acct.how) - { - case ACCT_MATCH_ALL: - return account_in_set; - break; - - case ACCT_MATCH_ANY: - return account_in_set; - break; - - case ACCT_MATCH_NONE: - return !account_in_set; - break; - } - - return FALSE; -} - -static Query * split_sort_query = NULL; - -static int -date_cmp_func(Timespec *t1, Timespec *t2) -{ - /* check seconds */ - if (t1->tv_sec < t2->tv_sec) { - return -1; - } - else if (t1->tv_sec > t2->tv_sec) { - return +1; - } - - /* else, seconds match. check nanoseconds */ - if (t1->tv_nsec < t2->tv_nsec) { - return -1; - } - else if (t1->tv_nsec > t2->tv_nsec) { - return +1; - } - - return 0; -} - -/* compared by dates but return 0 if on same day */ - -static int -date_rounded_cmp_func(Timespec *t1, Timespec *t2) -{ - Timespec canon_t1, canon_t2; - canon_t1 = timespecCanonicalDayTime(*t1); - canon_t2 = timespecCanonicalDayTime(*t2); - return date_cmp_func(&canon_t1, &canon_t2); -} - - -static int -split_cmp_func(sort_type_t how, gconstpointer ga, gconstpointer gb) -{ - Split * sa = (Split *)ga; - Split * sb = (Split *)gb; - Transaction * ta; - Transaction * tb; - unsigned long n1; - unsigned long n2; - const char *da, *db; - gnc_numeric fa, fb; - - if (sa && !sb) return -1; - if (!sa && sb) return 1; - if (!sa && !sb) return 0; - - ta = sa->parent; - tb = sb->parent; - - if (ta->orig) ta = ta->orig; - if (tb->orig) tb = tb->orig; - - if ( (ta) && !(tb) ) return -1; - if ( !(ta) && (tb) ) return +1; - if ( !(ta) && !(tb) ) return 0; - - - switch(how) - { - case BY_STANDARD: - return xaccSplitDateOrder(sa, sb); - break; - - case BY_DATE: - return date_cmp_func(&(ta->date_posted), &(tb->date_posted)); - - break; - - case BY_DATE_ROUNDED: - return date_rounded_cmp_func(&(ta->date_posted), &(tb->date_posted)); - - break; - - - case BY_DATE_ENTERED: - return date_cmp_func(&(ta->date_entered), &(tb->date_entered)); - - break; - - case BY_DATE_ENTERED_ROUNDED: - return date_rounded_cmp_func(&(ta->date_entered), &(tb->date_entered)); - - break; - - case BY_DATE_RECONCILED: - return date_cmp_func(&(sa->date_reconciled), &(sb->date_reconciled)); - - break; - - case BY_DATE_RECONCILED_ROUNDED: - return date_rounded_cmp_func(&(sa->date_reconciled), &(sb->date_reconciled)); - - break; - - case BY_NUM: - /* sort on transaction number */ - da = ta->num; - db = tb->num; - if (gnc_strisnum(da)) { - if (!gnc_strisnum(db)) { - return -1; - } - sscanf(da, "%lu", &n1); - sscanf(db, "%lu", &n2); - if (n1 < n2) { - return -1; - } - if (n1 == n2) { - return 0; - } - return +1; - } - if (gnc_strisnum(db)) { - return +1; - } - return safe_strcmp (da, db); - break; - - case BY_MEMO: - /* sort on memo strings */ - return safe_strcmp (sa->memo, sb->memo); - break; - - case BY_DESC: - /* sort on transaction strings */ - return safe_strcmp (ta->description, tb->description); - break; - - case BY_AMOUNT: - fa = sa->value; - fb = sb->value; - return gnc_numeric_compare(fa, fb); - break; - - case BY_RECONCILE: - /* Reconcile flags are sorted as: FREC = YREC < CREC = NREC */ - switch (sa->reconciled) { - case YREC: - case FREC: - if (sb->reconciled == YREC) - return 0; - if (sb->reconciled == FREC) - return 0; - return -1; - break; - - case CREC: - case NREC: - if (sb->reconciled == CREC) - return 0; - if (sb->reconciled == NREC) - return 0; - return 1; - break; - } - break; - - case BY_ACCOUNT_FULL_NAME: - return xaccSplitCompareAccountFullNames(sa, sb); - - case BY_ACCOUNT_CODE: - return xaccSplitCompareAccountCodes(sa, sb); - break; - - case BY_CORR_ACCOUNT_FULL_NAME: - return xaccSplitCompareOtherAccountFullNames(sa, sb); - - case BY_CORR_ACCOUNT_CODE: - return xaccSplitCompareOtherAccountCodes(sa, sb); - - case BY_NONE: - return 0; - break; - } - - return 0; -} - -static int -split_sort_func(gconstpointer a, gconstpointer b) -{ - int retval; - - g_return_val_if_fail (split_sort_query, 0); - - retval = split_cmp_func(split_sort_query->primary_sort, a, b); - if((retval == 0) && - (split_sort_query->secondary_sort != BY_NONE)) { - retval = split_cmp_func(split_sort_query->secondary_sort, a, b); - if((retval == 0) && - (split_sort_query->tertiary_sort != BY_NONE)) { - retval = split_cmp_func(split_sort_query->tertiary_sort, a, b); - return split_sort_query->tertiary_increasing ? retval : - retval; - } - else { - return split_sort_query->secondary_increasing ? retval : - retval; - } - } - else { - return split_sort_query->primary_increasing ? retval : - retval; - } -} - - -/******************************************************************** - * xaccQueryCheckSplit - * check a single split against the query. This is a short-circuited - * and-or check, so sorting it with best criteria first is a win. - ********************************************************************/ - -static int -xaccQueryCheckSplit(Query * q, Split * s) -{ - GList * and_ptr; - GList * or_ptr; - QueryTerm * qt; - int and_terms_ok=1; - - for(or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next) { - and_terms_ok = 1; - for(and_ptr = or_ptr->data; and_ptr; and_ptr = and_ptr->next) { - qt = (QueryTerm *)(and_ptr->data); - if(((qt->p)(s, &(qt->data))) != qt->data.base.sense) { - and_terms_ok = 0; - break; - } - } - if(and_terms_ok) { - return 1; - } - } - return 0; -} - -static AccountGUIDList * -account_list_to_guid_list (AccountList *accounts) -{ - GList *guids = NULL; - GList *node; - - for (node = accounts; node; node = node->next) - { - Account *account = node->data; - GUID *guid; - - if (!account) continue; - - guid = xaccGUIDMalloc (); - *guid = *xaccAccountGetGUID (account); - - guids = g_list_prepend (guids, guid); - } - - return g_list_reverse (guids); -} - -static BookGUIDList * -book_list_to_guid_list (BookList *books) -{ - GList *guids = NULL; - GList *node; - - for (node = books; node; node = node->next) - { - GNCBook *book = node->data; - GUID *guid; - - if (!book) continue; - - guid = xaccGUIDMalloc (); - *guid = *gnc_book_get_guid (book); - - guids = g_list_prepend (guids, guid); - } - - return g_list_reverse (guids); -} - -static GList * -copy_guid_list (GList *guids) -{ - GList *new_guids = NULL; - GList *node; - - for (node = guids; node; node = node->next) - { - GUID *guid = node->data; - GUID *new_guid; - - if (!guid) - continue; - - new_guid = xaccGUIDMalloc (); - *new_guid = *guid; - - new_guids = g_list_prepend (new_guids, new_guid); - } - - return g_list_reverse (new_guids); -} - -static AccountList * -guid_list_to_account_list (Query * q, AccountGUIDList *guids) -{ - GList *accounts = NULL; - GList *node, *bn; - - if (!q) return NULL; - - for (node = guids; node; node = node->next) - { - GUID *guid = node->data; - Account *account = NULL; - - if (!guid) continue; - - for (bn = q->books; bn; bn=bn->next) - { - GNCBook *book = bn->data; - account = xaccAccountLookup (guid, book); - if (account) break; - } - if (!account) continue; - - accounts = g_list_prepend (accounts, account); - } - - return g_list_reverse (accounts); -} - -/******************************************************************** - * xaccQueryCompileTerms - * Prepare terms for processing by xaccQueryGetSplits - ********************************************************************/ - -static void -xaccQueryCompileTerms (Query *q) -{ - GList * or_ptr, * and_ptr; - - /* Find all of the books involved; well need these for the - * entity tables and account groups they contain. */ - if (q->books) g_list_free (q->books); - q->books = NULL; - - for(or_ptr = q->terms; or_ptr ; or_ptr = or_ptr->next) - { - for(and_ptr = or_ptr->data; and_ptr; and_ptr = and_ptr->next) - { - QueryTerm *qt = and_ptr->data; - switch (qt->data.type) - { - case PD_BOOK: { - GList *node; - for (node = qt->data.book.books; node; node=node->next) - { - q->books = g_list_prepend (q->books, node->data); - } - - break; - } - default: - break; - } - } - } - - /* convert account guid lists to lists of accounts */ - for(or_ptr = q->terms; or_ptr ; or_ptr = or_ptr->next) - { - for(and_ptr = or_ptr->data; and_ptr; and_ptr = and_ptr->next) - { - QueryTerm *qt = and_ptr->data; - switch (qt->data.type) - { - case PD_ACCOUNT: - g_list_free (qt->data.acct.accounts); - qt->data.acct.accounts = - guid_list_to_account_list (q, qt->data.acct.account_guids); - break; - - default: - break; - } - } - } -} - -/******************************************************************** - * xaccQueryGetSplits - * Run the search. - ********************************************************************/ - -SplitList * -xaccQueryGetSplits(Query * q) -{ - GList * matching_splits=NULL; - GList * or_ptr, * and_ptr, * mptr; - GList * all_accts, * node; - Account * current; - QueryTerm * qt; - - int total_splits_checked = 0; - int split_count = 0; - int acct_ok; - - if (!q) return NULL; - ENTER("query=%p", q); - - /* tmp hack alert */ - q->changed = 1; - - if(q->changed == 0) - { - return q->split_list; - } - - /* prioritize the query terms for a faster search. This - * will put account queries at the top of the AND chains. */ - - /* FIXME : sort for securities queries and eliminate non- - * security accounts ??? huh why ??? oh, because if looking - * for prices, then only security accounts will have these ... - */ - for(or_ptr = q->terms; or_ptr ; or_ptr = or_ptr->next) - { - and_ptr = or_ptr->data; - or_ptr->data = g_list_sort(and_ptr, query_sort_func); - } - - /* prepare the terms for processing */ - xaccQueryCompileTerms (q); - - /* If there is a backend, query the backend, let it fetch the data */ - /* FIXME: we should avoid submitting the query more than once to the - * same backend ... */ - for (node=q->books; node; node=node->next) - { - GNCBook *book = node->data; - Backend *be = book->backend; - - if (be && be->run_query) - { - (be->run_query) (be, q); - } - } - - /* iterate over accounts */ - all_accts = NULL; - for (node=q->books; node; node=node->next) - { - GNCBook *book = node->data; - all_accts = g_list_concat (all_accts, - xaccGroupGetSubAccounts (gnc_book_get_group(book))); - /* If we're searching template accounts, then add them. */ - if ( q->templates_too ) { - all_accts = - g_list_concat( all_accts, - xaccGroupGetSubAccounts( gnc_book_get_template_group(book) ) ); - } - } - - for (node = all_accts; node; node = node->next) - { - current = node->data; - - if (!current) - continue; - - /* check this account to see if we need to look at it at all */ - acct_ok = 0; - for(or_ptr = q->terms; or_ptr ; or_ptr = or_ptr->next) - { - and_ptr = or_ptr->data; - qt = and_ptr->data; - - if(qt->data.type == PD_ACCOUNT) - { - if(acct_query_matches(qt, current)) - { - acct_ok = 1; - break; - } - } - else - { - /* if the first query term isn't an account, then we have to - * look at every split in every account. */ - - /* FIXME : security accounts can be ruled out ??? huh ??? */ - acct_ok = 1; - break; - } - } - - if(acct_ok) - { - GList *lp; - - /* iterate over splits */ - for(lp = xaccAccountGetSplitList(current); lp; lp = lp->next) - { - Split *s = (Split *) lp->data; - if(xaccQueryCheckSplit(q, s)) - { - matching_splits = g_list_prepend(matching_splits, s); - split_count++; - } - total_splits_checked++; - } - } - } - - g_list_free (all_accts); - - /* There is no absolute need to reverse this list, since it's - * being sorted below. However, in the common case, we will be - * searching in a single account and returning in the account - * order, thus reversing will put us in the correct order we - * want and make the sorting go much faster. */ - matching_splits = g_list_reverse(matching_splits); - - /* now sort the matching splits based on the search criteria - * split_sort_query is an unforgivable use of static global data... - * I just can't figure out how else to do this sanely. */ - split_sort_query = q; - matching_splits = g_list_sort(matching_splits, split_sort_func); - - /* crop the list to limit the number of splits */ - if((split_count > q->max_splits) && (q->max_splits > -1)) - { - if(q->max_splits > 0) - { - /* mptr is set to the first node of what will be the new list */ - mptr = g_list_nth(matching_splits, split_count - q->max_splits); - /* mptr should not be NULL, but let's be safe */ - if (mptr != NULL) - { - if (mptr->prev != NULL) mptr->prev->next = NULL; - mptr->prev = NULL; - } - g_list_free(matching_splits); - matching_splits = mptr; - } - else - { - /* q->max_splits == 0 */ - g_list_free(matching_splits); - matching_splits = NULL; - } - split_count = q->max_splits; - } - - q->changed = 0; - - g_list_free(q->split_list); - q->split_list = matching_splits; - - return matching_splits; -} - -/******************************************************************** - * xaccQueryGetSplitsUniqueTrans - * Get splits but no more than one from a given transaction. - ********************************************************************/ - -SplitList * -xaccQueryGetSplitsUniqueTrans(Query *q) -{ - GList * splits = xaccQueryGetSplits(q); - GList * current; - GList * result = NULL; - GHashTable * trans_hash = g_hash_table_new(g_direct_hash, g_direct_equal); - - for (current = splits; current; current = current->next) - { - Split *split = current->data; - Transaction *trans = xaccSplitGetParent (split); - - if (!g_hash_table_lookup (trans_hash, trans)) - { - g_hash_table_insert (trans_hash, trans, trans); - result = g_list_prepend (result, split); - } - } - - g_hash_table_destroy (trans_hash); - - return g_list_reverse (result); -} - -/******************************************************************** - * xaccQueryGetTransactions - * Get transactions matching the query terms, specifying whether - * we require some or all splits to match - ********************************************************************/ - -static void -query_match_all_filter_func(gpointer key, gpointer value, gpointer user_data) -{ - Transaction * t = key; - int num_matches = GPOINTER_TO_INT(value); - GList ** matches = user_data; - - if(num_matches == xaccTransCountSplits(t)) { - *matches = g_list_prepend(*matches, t); - } -} - -static void -query_match_any_filter_func(gpointer key, gpointer value, gpointer user_data) -{ - Transaction * t = key; - GList ** matches = user_data; - *matches = g_list_prepend(*matches, t); -} - -TransList * -xaccQueryGetTransactions (Query * q, query_run_t runtype) -{ - GList * splits = xaccQueryGetSplits(q); - GList * current = NULL; - GList * retval = NULL; - GHashTable * trans_hash = g_hash_table_new(g_direct_hash, g_direct_equal); - Transaction * trans = NULL; - gpointer val = NULL; - int count = 0; - - /* iterate over matching splits, incrementing a match-count in - * the hash table */ - for(current = splits; current; current=current->next) { - trans = xaccSplitGetParent((Split *)(current->data)); - - /* don't waste time looking up unless we need the count - * information */ - if(runtype == QUERY_MATCH_ALL) { - val = g_hash_table_lookup(trans_hash, trans); - count = GPOINTER_TO_INT(val); - } - g_hash_table_insert(trans_hash, trans, GINT_TO_POINTER(count + 1)); - } - - /* now pick out the transactions that match */ - if(runtype == QUERY_MATCH_ALL) { - g_hash_table_foreach(trans_hash, query_match_all_filter_func, - &retval); - } - else { - g_hash_table_foreach(trans_hash, query_match_any_filter_func, - &retval); - } - - /* ATM we rerun the xtn filter every time. Need to add checks and - * updates for using cached results. */ - q->last_run_type = runtype; - - g_list_free (q->xtn_list); - q->xtn_list = retval; - - g_hash_table_destroy(trans_hash); - - return retval; -} - -/******************************************************************** - * xaccQueryEqual - * Compare two queries for equality - ********************************************************************/ - -static gboolean -xaccQueryTermEqual (QueryTerm *qt1, QueryTerm *qt2) -{ - GList *l1, *l2; - - if (qt1 == qt2) return TRUE; - if (!qt1 || !qt2) return FALSE; - - if (qt1->p != qt2->p) return FALSE; - if (qt1->data.type != qt2->data.type) return FALSE; - if (qt1->data.base.term_type != qt2->data.base.term_type) return FALSE; - if (qt1->data.base.sense != qt2->data.base.sense) return FALSE; - - switch (qt1->data.type) - { - case PD_DATE: - if (qt1->data.date.use_start != qt2->data.date.use_start) return FALSE; - if (qt1->data.date.use_end != qt2->data.date.use_end) return FALSE; - if (!timespec_equal (&qt1->data.date.start, &qt2->data.date.start)) - return FALSE; - if (!timespec_equal (&qt1->data.date.end, &qt2->data.date.end)) - return FALSE; - break; - - case PD_AMOUNT: - if (qt1->data.amount.how != qt2->data.amount.how) return FALSE; - if (qt1->data.amount.amt_sgn != qt2->data.amount.amt_sgn) return FALSE; - if (qt1->data.amount.amount != qt2->data.amount.amount) - return FALSE; - break; - - case PD_ACCOUNT: - if (qt1->data.acct.how != qt2->data.acct.how) return FALSE; - l1 = qt1->data.acct.account_guids; - l2 = qt2->data.acct.account_guids; - if (g_list_length (l1) != g_list_length (l2)) return FALSE; - for ( ; l1; l1 = l1->next, l2 = l2->next) - if (!guid_equal (l1->data, l2->data)) - return FALSE; - break; - - case PD_STRING: - if (qt1->data.str.case_sens != qt2->data.str.case_sens) - return FALSE; - if (qt1->data.str.use_regexp != qt2->data.str.use_regexp) - return FALSE; - if (strcmp (qt1->data.str.matchstring, qt2->data.str.matchstring) != 0) - return FALSE; - break; - - case PD_CLEARED: - if (qt1->data.cleared.how != qt2->data.cleared.how) return FALSE; - break; - - case PD_BALANCE: - if (qt1->data.balance.how != qt2->data.balance.how) return FALSE; - break; - - case PD_GUID: - if (!guid_equal (&qt1->data.guid.guid, &qt2->data.guid.guid)) - return FALSE; - if (safe_strcmp (qt1->data.guid.id_type, qt2->data.guid.id_type)) - return FALSE; - break; - - case PD_KVP: { - GSList *n1, *n2; - - n1 = qt1->data.kvp.path; - n2 = qt2->data.kvp.path; - - for ( ; n1 && n2; n1 = n1->next, n2 = n2->next) - if (safe_strcmp (n1->data, n2->data) != 0) - return FALSE; - - if (n1 || n2) - return FALSE; - - if (kvp_value_compare (qt1->data.kvp.value, qt2->data.kvp.value) != 0) - return FALSE; - - break; - } - - case PD_MISC: - if (qt1->data.misc.how != qt2->data.misc.how) return FALSE; - if (qt1->data.misc.data != qt2->data.misc.data) return FALSE; - break; - - default: - PERR ("bad query term type"); - return FALSE; - } - - return TRUE; -} - -gboolean -xaccQueryEqual (Query *q1, Query *q2) -{ - GList *or1, *or2; - - if (q1 == q2) return TRUE; - if (!q1 || !q2) return FALSE; - - if (g_list_length (q1->terms) != g_list_length (q2->terms)) return FALSE; - - for (or1 = q1->terms, or2 = q2->terms; or1; - or1 = or1->next, or2 = or2->next) - { - GList *and1, *and2; - - and1 = or1->data; - and2 = or2->data; - - if (g_list_length (and1) != g_list_length (and2)) return FALSE; - - for ( ; and1; and1 = and1->next, and2 = and2->next) - if (!xaccQueryTermEqual (and1->data, and2->data)) - return FALSE; - } - - if (q1->primary_sort != q2->primary_sort) return FALSE; - if (q1->secondary_sort != q2->secondary_sort) return FALSE; - if (q1->tertiary_sort != q2->tertiary_sort) return FALSE; - - if (q1->primary_increasing != q2->primary_increasing) return FALSE; - if (q1->secondary_increasing != q2->secondary_increasing) return FALSE; - if (q1->tertiary_increasing != q2->tertiary_increasing) return FALSE; - - if (q1->max_splits != q2->max_splits) return FALSE; - - return TRUE; -} - -Predicate -xaccQueryGetPredicate (pr_type_t term_type) -{ - Predicate p = NULL; - - /* the predicates are only known in the local - * address space, which is why we have to set them - * from the abstract type here. - */ - switch (term_type) - { - case PR_ACCOUNT: - p = & xaccAccountMatchPredicate; - break; - case PR_ACTION: - p = & xaccActionMatchPredicate; - break; - case PR_BALANCE: - p = & xaccBalanceMatchPredicate; - break; - case PR_BOOK: - p = & xaccBookMatchPredicate; - break; - case PR_CLEARED: - p = & xaccClearedMatchPredicate; - break; - case PR_DATE: - p = & xaccDateMatchPredicate; - break; - case PR_DESC: - p = & xaccDescriptionMatchPredicate; - break; - case PR_GUID: - p = & xaccGUIDMatchPredicate; - break; - case PR_KVP: - p = & xaccKVPMatchPredicate; - break; - case PR_MEMO: - p = & xaccMemoMatchPredicate; - break; - case PR_NUM: - p = & xaccNumberMatchPredicate; - break; - case PR_PRICE: - p = & xaccSharePriceMatchPredicate; - break; - case PR_SHRS: - p = & xaccSharesMatchPredicate; - break; - case PR_VALUE: - p = & xaccValueMatchPredicate; - break; - case PR_MISC: - PERR ("misc term must not appear"); - break; - } - return p; -} - -/* =========================================================== */ - -#define ADD_TERM(qt, op) \ -{ \ - Query * qs = xaccMallocQuery(); \ - Query * qr; \ - \ - xaccInitQuery(qs, qt); \ - \ - if(xaccQueryHasTerms(q)) \ - { \ - qr = xaccQueryMerge(q, qs, op); \ - } \ - else \ - { \ - qr = xaccQueryMerge(q, qs, QUERY_OR); \ - } \ - xaccQuerySwapTerms(q, qr); \ - xaccFreeQuery(qs); \ - xaccFreeQuery(qr); \ -} - -#define STRING_TERM(case_sens, use_regexp) \ -{ \ - int flags = REG_EXTENDED; \ - \ - if(!case_sens) \ - { \ - flags |= REG_ICASE; \ - } \ - \ - if(use_regexp) \ - { \ - regcomp(&qt->data.str.compiled, matchstring, flags); \ - } \ - else if(!case_sens) \ - { \ - char *s; \ - \ - for(s = qt->data.str.matchstring; *s; s++) \ - { \ - *s = tolower(*s); \ - } \ - } \ -} - -/******************************************************************** - * xaccQueryAddPredicate - * Add a predicate an existing query. - ********************************************************************/ - -void -xaccQueryAddPredicate (Query * q, - PredicateData *pred, - QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->data = *pred; - qt->p = xaccQueryGetPredicate (qt->data.base.term_type); - - ADD_TERM(qt, op); -} - -/******************************************************************** - * xaccQueryAddAccountMatch - * Add an account filter to an existing query. - ********************************************************************/ - -void -xaccQueryAddAccountMatch(Query * q, AccountList * accounts, acct_match_t how, - QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->p = & xaccAccountMatchPredicate; - qt->data.type = PD_ACCOUNT; - qt->data.base.term_type = PR_ACCOUNT; - qt->data.base.sense = 1; - qt->data.acct.how = how; - qt->data.acct.accounts = NULL; - qt->data.acct.account_guids = account_list_to_guid_list (accounts); - - ADD_TERM(qt, op); -} - -/******************************************************************** - * xaccQueryAddAccountGUIDMatch - * Add an account filter to an existing query. - ********************************************************************/ - -void -xaccQueryAddAccountGUIDMatch(Query * q, AccountGUIDList * account_guids, - acct_match_t how, QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->p = & xaccAccountMatchPredicate; - qt->data.type = PD_ACCOUNT; - qt->data.base.term_type = PR_ACCOUNT; - qt->data.base.sense = 1; - qt->data.acct.how = how; - qt->data.acct.accounts = NULL; - qt->data.acct.account_guids = copy_guid_list (account_guids); - - ADD_TERM(qt, op); -} - -/******************************************************************** - * xaccQueryAddSingleAccountMatch - * Add an account filter to an existing query. - ********************************************************************/ - -void -xaccQueryAddSingleAccountMatch(Query * q, Account * acct, - QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->p = & xaccAccountMatchPredicate; - qt->data.type = PD_ACCOUNT; - qt->data.base.term_type = PR_ACCOUNT; - qt->data.base.sense = 1; - qt->data.acct.how = ACCT_MATCH_ANY; - qt->data.acct.accounts = g_list_prepend(NULL, acct); - qt->data.acct.account_guids - = account_list_to_guid_list (qt->data.acct.accounts); - - ADD_TERM(qt, op); -} - -/******************************************************************** - * xaccQueryAddBookMatch - * Add an book filter to an existing query. - ********************************************************************/ - -void -xaccQueryAddBookMatch(Query * q, BookList * books, book_match_t how, - QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - PERR ("multiple book support is not implemented"); - qt->p = & xaccBookMatchPredicate; - qt->data.type = PD_BOOK; - qt->data.base.term_type = PR_BOOK; - qt->data.base.sense = 1; - qt->data.book.how = how; - qt->data.book.books = NULL; - qt->data.book.book_guids = book_list_to_guid_list (books); - - ADD_TERM(qt, op); -} - -/******************************************************************** - * xaccQueryAddBookGUIDMatch - * Add an book filter to an existing query. - ********************************************************************/ - -void -xaccQueryAddBookGUIDMatch(Query * q, BookGUIDList * book_guids, - book_match_t how, QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - PERR ("multiple book support is not implemented"); - qt->p = & xaccBookMatchPredicate; - qt->data.type = PD_BOOK; - qt->data.base.term_type = PR_BOOK; - qt->data.base.sense = 1; - qt->data.book.how = how; - qt->data.book.books = NULL; - qt->data.book.book_guids = copy_guid_list (book_guids); - - ADD_TERM(qt, op); -} - -/******************************************************************** - * xaccQueryAddSingleBookMatch - * Add an book filter to an existing query. - ********************************************************************/ - -void -xaccQueryAddSingleBookMatch(Query * q, GNCBook * book, QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->p = & xaccBookMatchPredicate; - qt->data.type = PD_BOOK; - qt->data.base.term_type = PR_BOOK; - qt->data.base.sense = 1; - qt->data.book.how = BOOK_MATCH_ANY; - qt->data.book.books = g_list_prepend(NULL, book); - qt->data.book.book_guids - = book_list_to_guid_list (qt->data.book.books); - - ADD_TERM(qt, op); -} - - -/******************************************************************** - * xaccQueryAddDescriptionMatch - * Add a description filter to an existing query - ********************************************************************/ - -void -xaccQueryAddDescriptionMatch(Query * q, const char * matchstring, - int case_sens, int use_regexp, - QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->p = & xaccDescriptionMatchPredicate; - qt->data.type = PD_STRING; - qt->data.base.term_type = PR_DESC; - qt->data.base.sense = 1; - qt->data.str.case_sens = case_sens; - qt->data.str.use_regexp = use_regexp; - qt->data.str.matchstring = g_strdup(matchstring); - - STRING_TERM (case_sens, use_regexp); - ADD_TERM(qt, op); -} - - -/******************************************************************** - * xaccQueryAddMemoMatch - * Add a memo conparison to an existing query - ********************************************************************/ - -void -xaccQueryAddMemoMatch(Query * q, const char * matchstring, - int case_sens, int use_regexp, - QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->p = & xaccMemoMatchPredicate; - qt->data.type = PD_STRING; - qt->data.base.term_type = PR_MEMO; - qt->data.base.sense = 1; - qt->data.str.case_sens = case_sens; - qt->data.str.use_regexp = use_regexp; - qt->data.str.matchstring = g_strdup(matchstring); - - STRING_TERM (case_sens, use_regexp); - ADD_TERM(qt, op); -} - - -/******************************************************************** - * xaccQueryAddDateMatchTS - * Add a date filter to an existing query. - ********************************************************************/ - -void -xaccQueryAddDateMatchTS(Query * q, - int use_start, - Timespec sts, - int use_end, - Timespec ets, - QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->p = & xaccDateMatchPredicate; - qt->data.type = PD_DATE; - qt->data.base.term_type = PR_DATE; - qt->data.base.sense = 1; - qt->data.date.use_start = use_start; - qt->data.date.use_end = use_end; - qt->data.date.start = sts; - qt->data.date.end = ets; - - ADD_TERM(qt, op); -} - -/******************************************************************** - * xaccQueryAddDateMatch - * Add a date filter to an existing query. - ********************************************************************/ - -void -xaccQueryAddDateMatch(Query * q, - int use_start, int sday, int smonth, int syear, - int use_end, int eday, int emonth, int eyear, - QueryOp op) -{ - /* gcc -O3 will auto-inline this function, avoiding a call overhead */ - xaccQueryAddDateMatchTS (q, use_start, - gnc_dmy2timespec(sday, smonth, syear), - use_end, - gnc_dmy2timespec_end(eday, emonth, eyear), - op); -} - -/******************************************************************** - * xaccQueryAddDateMatchTT - * Add a date filter to an existing query. - ********************************************************************/ - -void -xaccQueryAddDateMatchTT(Query * q, - int use_start, - time_t stt, - int use_end, - time_t ett, - QueryOp op) -{ - Timespec sts; - Timespec ets; - - sts.tv_sec = (long long)stt; - sts.tv_nsec = 0; - - ets.tv_sec = (long long)ett; - ets.tv_nsec = 0; - - /* gcc -O3 will auto-inline this function, avoiding a call overhead */ - xaccQueryAddDateMatchTS (q, use_start, sts, - use_end, ets, op); - -} - -/******************************************************************** - * xaccQueryAddNumberMatch - * Add a number-field filter - ********************************************************************/ - -void -xaccQueryAddNumberMatch(Query * q, const char * matchstring, int case_sens, - int use_regexp, QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->p = & xaccNumberMatchPredicate; - qt->data.type = PD_STRING; - qt->data.base.term_type = PR_NUM; - qt->data.base.sense = 1; - qt->data.str.case_sens = case_sens; - qt->data.str.use_regexp = use_regexp; - qt->data.str.matchstring = g_strdup(matchstring); - - STRING_TERM (case_sens, use_regexp); - ADD_TERM(qt, op); -} - - -/******************************************************************** - * xaccQueryAddActionMatch - * Add a action-field filter - ********************************************************************/ - -void -xaccQueryAddActionMatch(Query * q, const char * matchstring, int case_sens, - int use_regexp, QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->p = & xaccActionMatchPredicate; - qt->data.type = PD_STRING; - qt->data.base.term_type = PR_ACTION; - qt->data.base.sense = 1; - qt->data.str.case_sens = case_sens; - qt->data.str.use_regexp = use_regexp; - qt->data.str.matchstring = g_strdup(matchstring); - - STRING_TERM (case_sens, use_regexp); - ADD_TERM(qt, op); -} - - -/******************************************************************** - * DxaccQueryAddValueMatch - * Add a value filter to an existing query. - * FIXME ?? fix what ?? - ********************************************************************/ - -void -DxaccQueryAddValueMatch(Query * q, double amt, - amt_match_sgn_t amt_sgn, - amt_match_t how, - QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->p = & xaccValueMatchPredicate; - qt->data.type = PD_AMOUNT; - qt->data.base.term_type = PR_VALUE; - qt->data.base.sense = 1; - qt->data.amount.how = how; - qt->data.amount.amt_sgn = amt_sgn; - qt->data.amount.amount = amt; - - ADD_TERM(qt, op); -} - - -/******************************************************************** - * DxaccQueryAddSharePriceMatch - * Add a share-price filter to an existing query. - * FIXME ?? fix what ?? - ********************************************************************/ - -void -DxaccQueryAddSharePriceMatch(Query * q, double amt, - amt_match_t how, - QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->p = & xaccSharePriceMatchPredicate; - qt->data.type = PD_AMOUNT; - qt->data.base.term_type = PR_PRICE; - qt->data.base.sense = 1; - qt->data.amount.how = how; - qt->data.amount.amt_sgn = AMT_SGN_MATCH_EITHER; - qt->data.amount.amount = amt; - - ADD_TERM(qt, op); -} - - -/******************************************************************** - * DxaccQueryAddSharesMatch - * Add a quantity filter to an existing query. - * FIXME ?? fix what ?? - ********************************************************************/ - -void -DxaccQueryAddSharesMatch(Query * q, double amt, - amt_match_t how, - QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->p = & xaccSharesMatchPredicate; - qt->data.type = PD_AMOUNT; - qt->data.base.term_type = PR_SHRS; - qt->data.base.sense = 1; - qt->data.amount.how = how; - qt->data.amount.amt_sgn = AMT_SGN_MATCH_EITHER; - qt->data.amount.amount = amt; - - ADD_TERM(qt, op); -} - - -/******************************************************************** - * xaccQueryAddMiscMatch - * Add an arbitrary predicate to a query. You really shouldn't - * do this. - ********************************************************************/ - -void -xaccQueryAddMiscMatch(Query * q, Predicate p, int how, int data, - QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->p = p; - qt->data.type = PD_MISC; - qt->data.base.term_type = PR_MISC; - qt->data.base.sense = 1; - qt->data.misc.how = how; - qt->data.misc.data = data; - - ADD_TERM(qt, op); -} - -/******************************************************************** - * xaccQueryAddClearedMatch - * Add a 'cleared' filter to an existing query. - ********************************************************************/ - -void -xaccQueryAddClearedMatch(Query * q, cleared_match_t how, - QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->p = & xaccClearedMatchPredicate; - qt->data.type = PD_CLEARED; - qt->data.base.term_type = PR_CLEARED; - qt->data.base.sense = 1; - qt->data.cleared.how = how; - - ADD_TERM(qt, op); -} - -/******************************************************************** - * xaccQueryAddBalanceMatch - * Add a 'balance' filter to an existing query. - ********************************************************************/ - -void -xaccQueryAddBalanceMatch(Query * q, balance_match_t how, QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->p = & xaccBalanceMatchPredicate; - qt->data.type = PD_BALANCE; - qt->data.base.term_type = PR_BALANCE; - qt->data.base.sense = 1; - qt->data.balance.how = how; - - ADD_TERM(qt, op); -} - -/******************************************************************** - * xaccQueryAddGUIDMatch - * Add a 'guid' filter to an existing query. - ********************************************************************/ - -void -xaccQueryAddGUIDMatch(Query * q, const GUID *guid, - GNCIdType id_type, QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - - qt->p = & xaccGUIDMatchPredicate; - qt->data.type = PD_GUID; - qt->data.base.term_type = PR_GUID; - qt->data.base.sense = 1; - qt->data.guid.guid = guid ? *guid : *xaccGUIDNULL (); - qt->data.guid.id_type = id_type; - - ADD_TERM(qt, op); -} - -/******************************************************************** - * xaccQueryAddKVPMatch - * Add a 'kvp' filter to an existing query. - ********************************************************************/ - -void -xaccQueryAddKVPMatch(Query *q, GSList *path, const kvp_value *value, - kvp_match_t how, kvp_match_where_t where, QueryOp op) -{ - QueryTerm * qt = g_new0(QueryTerm, 1); - GSList * node; - - qt->p = &xaccKVPMatchPredicate; - qt->data.type = PD_KVP; - qt->data.base.term_type = PR_KVP; - qt->data.base.sense = 1; - qt->data.kvp.how = how; - qt->data.kvp.where = where; - qt->data.kvp.path = g_slist_copy (path); - qt->data.kvp.value = kvp_value_copy (value); - - for (node = qt->data.kvp.path; node; node = node->next) - { - node->data = g_strdup (node->data); - } - - ADD_TERM(qt, op); -} - -/******************************************************************* - * xaccQueryPurgeTerms - * delete any terms of a particular type - *******************************************************************/ - -void -xaccQueryPurgeTerms(Query * q, pd_type_t type) -{ - QueryTerm * qt; - GList * or; - GList * and; - - if (!q) - return; - - for(or = q->terms; or; or = or->next) { - for(and = or->data; and; and = and->next) { - qt = and->data; - if(qt->data.type == type) { - if(g_list_length(or->data) == 1) { - q->terms = g_list_remove_link(q->terms, or); - g_list_free_1(or); - or = q->terms; - break; - } - else { - or->data = g_list_remove_link(or->data, and); - g_list_free_1(and); - and = or->data; - if(!and) break; - } - q->changed = 1; - free_query_term(qt); - } - } - if(!or) break; - } -} - - -/******************************************************************* - * xaccQueryClear - * remove all terms from a query. - *******************************************************************/ - -void -xaccQueryClear(Query * q) -{ - Query * q2 = xaccMallocQuery(); - xaccQuerySwapTerms(q, q2); - q->changed = 1; - xaccFreeQuery(q2); -} - - -/******************************************************************* - * string_match_predicate - * internal subroutine for description and memo matching - *******************************************************************/ - -static int -string_match_predicate(const char * s, PredicateData * pd) -{ - regmatch_t match; - - g_return_val_if_fail(s && pd && (pd->type == PD_STRING), FALSE); - - if(!pd->str.matchstring) return 0; - - if(pd->str.use_regexp) { - if(!regexec(&pd->str.compiled, s, 1, &match, 0)) { - return 1; - } - else { - return 0; - } - } - else if(pd->str.case_sens) { - if(strstr(s, pd->str.matchstring)) return 1; - else return 0; - } - else { - /* use case-insensitive compare */ - if(strcasestr(s, pd->str.matchstring)) return 1; - else return 0; - } - -} - - -/******************************************************************* - * value_match_predicate - *******************************************************************/ -static int -value_match_predicate(double splitamt, PredicateData * pd) -{ - switch(pd->amount.how) - { - case AMT_MATCH_ATLEAST: - return fabs(splitamt) >= pd->amount.amount; - break; - case AMT_MATCH_ATMOST: - return fabs(splitamt) <= pd->amount.amount; - break; - case AMT_MATCH_EXACTLY: - return fabs(fabs(splitamt) - fabs(pd->amount.amount)) < .0001; - break; - } - - return 0; -} - -/******************************************************************* - * xaccBookMatchPredicate - *******************************************************************/ - -static int -xaccBookMatchPredicate(Split * s, PredicateData * pd) -{ - g_return_val_if_fail(s && pd, FALSE); - g_return_val_if_fail(pd->type == PD_BOOK, FALSE); - - switch(pd->book.how) - { - case BOOK_MATCH_ANY: - /* s must match some book in pd */ - return (g_list_find(pd->book.books, s->book) != NULL); - break; - - case BOOK_MATCH_NONE: - /* s must match none of the books in pd */ - return (g_list_find(pd->book.books, s->book) == NULL); - break; - } - return 0; -} - -/******************************************************************* - * xaccAccountMatchPredicate - *******************************************************************/ - -static int -xaccAccountMatchPredicate(Split * s, PredicateData * pd) -{ - Transaction * parent; - Split * split; - Account * split_acct; - GList * acct_node; - - g_return_val_if_fail(s && pd, FALSE); - g_return_val_if_fail(pd->type == PD_ACCOUNT, FALSE); - - switch(pd->acct.how) - { - case ACCT_MATCH_ALL: - /* There must be a split in parent transaction that matches - * each of the accounts listed in pd. */ - parent = xaccSplitGetParent(s); - g_return_val_if_fail(parent, FALSE); - - for (acct_node=pd->acct.accounts; acct_node; acct_node=acct_node->next) - { - GList * split_node = xaccTransGetSplitList (parent); - - for ( ; split_node; split_node = split_node->next) - { - split = split_node->data; - - if (acct_node->data == xaccSplitGetAccount(split)) - { - /* break here means we found a match before running out - * of splits (success) */ - break; - } - } - - if (!split_node) - { - /* break here means we ran out of splits before finding - * an account (failure) */ - break; - } - } - - if (acct_node) return 0; - else return 1; - - break; - - case ACCT_MATCH_ANY: - /* s must match some account in pd */ - split_acct = xaccSplitGetAccount(s); - return (g_list_find(pd->acct.accounts, split_acct) != NULL); - break; - - case ACCT_MATCH_NONE: - /* s must match none of the accounts in pd */ - split_acct = xaccSplitGetAccount(s); - return (g_list_find(pd->acct.accounts, split_acct) == NULL); - break; - } - - return 0; -} - - -/******************************************************************* - * xaccDescriptionMatchPredicate - *******************************************************************/ - -static int -xaccDescriptionMatchPredicate(Split * s, PredicateData * pd) -{ - Transaction * parent; - const char * descript; - - g_return_val_if_fail(s && pd, FALSE); - g_return_val_if_fail(pd->type == PD_STRING, FALSE); - - parent = xaccSplitGetParent(s); - g_return_val_if_fail(parent, FALSE); - - descript = xaccTransGetDescription(parent); - return string_match_predicate(descript, pd); -} - -/******************************************************************* - * xaccGUIDMatchPredicate - *******************************************************************/ - -static int -xaccGUIDMatchPredicate(Split * s, PredicateData * pd) -{ - GUID *guid; - - g_return_val_if_fail (s, 0); - g_return_val_if_fail (pd, 0); - g_return_val_if_fail (pd->type == PD_GUID, 0); - - guid = &pd->guid.guid; - - if (pd->guid.id_type == GNC_ID_NONE || - !safe_strcmp (pd->guid.id_type, GNC_ID_NULL)) - { - return 0; - } - else if (!safe_strcmp (pd->guid.id_type, GNC_ID_ACCOUNT)) - { - return (xaccSplitGetAccount (s) == - xaccAccountLookup (guid, s->book)); - } - else if (!safe_strcmp (pd->guid.id_type, GNC_ID_BOOK)) - { - return (guid_equal (guid, &s->book->guid)); - } - else if (!safe_strcmp (pd->guid.id_type, GNC_ID_TRANS)) - { - return (xaccSplitGetParent (s) == - xaccTransLookup (guid, s->book)); - } - else if (!safe_strcmp (pd->guid.id_type, GNC_ID_SPLIT)) - { - return (guid_equal (guid, &s->guid)); - } - else - { - return 0; - } -} - -/******************************************************************* - * xaccKVPMatchPredicate - *******************************************************************/ - -static int -kvp_match_helper (GSList *path, kvp_value *value, kvp_match_t how, - kvp_frame *frame) -{ - kvp_value *value_2; - int compare; - - if (!path || !value || !frame || !how) return 0; - - value_2 = kvp_frame_get_slot_path_gslist (frame, path); - if (!value_2) - return 0; - - if (kvp_value_get_type (value) != kvp_value_get_type (value_2)) - return 0; - - compare = kvp_value_compare (value_2, value); - - switch (how) - { - case KVP_MATCH_LT: - return (compare < 0); - case KVP_MATCH_LTE: - return (compare <= 0); - case KVP_MATCH_EQ: - return (compare == 0); - case KVP_MATCH_GTE: - return (compare >= 0); - case KVP_MATCH_GT: - return (compare > 0); - default: - PWARN ("bad match type: %d", how); - return FALSE; - } -} - -static int -xaccKVPMatchPredicate(Split * s, PredicateData * pd) -{ - KVPPredicateData *kpd; - - g_return_val_if_fail (s, FALSE); - g_return_val_if_fail (pd, FALSE); - g_return_val_if_fail (pd->type == PD_KVP, FALSE); - - kpd = &pd->kvp; - - if (kpd->where && KVP_MATCH_SPLIT) - { - kvp_frame *frame = xaccSplitGetSlots (s); - - if (kvp_match_helper (kpd->path, kpd->value, kpd->how, frame)) - return TRUE; - } - - if (kpd->where && KVP_MATCH_TRANS) - { - Transaction *trans = xaccSplitGetParent (s); - kvp_frame *frame = xaccTransGetSlots (trans); - - if (kvp_match_helper (kpd->path, kpd->value, kpd->how, frame)) - return TRUE; - } - - if (kpd->where && KVP_MATCH_ACCOUNT) - { - Account *account = xaccSplitGetAccount (s); - kvp_frame *frame = xaccAccountGetSlots (account); - - if (kvp_match_helper (kpd->path, kpd->value, kpd->how, frame)) - return TRUE; - } - - return 0; -} - -/******************************************************************* - * xaccNumberMatchPredicate - *******************************************************************/ - -static int -xaccNumberMatchPredicate(Split * s, PredicateData * pd) -{ - Transaction * parent; - const char * number; - - g_return_val_if_fail(s && pd, FALSE); - g_return_val_if_fail(pd->type == PD_STRING, FALSE); - - parent = xaccSplitGetParent(s); - g_return_val_if_fail(parent, FALSE); - - number = xaccTransGetNum(parent); - return string_match_predicate(number, pd); -} - - -/******************************************************************* - * xaccActionMatchPredicate - *******************************************************************/ - -static int -xaccActionMatchPredicate(Split * s, PredicateData * pd) -{ - const char * action; - - g_return_val_if_fail(s && pd, FALSE); - g_return_val_if_fail(pd->type == PD_STRING, FALSE); - - action = xaccSplitGetAction(s); - return string_match_predicate(action, pd); -} - - -/******************************************************************* - * xaccMemoMatchPredicate - *******************************************************************/ -static int -xaccMemoMatchPredicate(Split * s, PredicateData * pd) -{ - const char * memo; - - g_return_val_if_fail(s && pd, FALSE); - memo = xaccSplitGetMemo(s); - - return string_match_predicate(memo, pd); -} - - -/******************************************************************* - * xaccValueMatchPredicate - *******************************************************************/ - -static int -xaccValueMatchPredicate(Split * s, PredicateData * pd) -{ - double splitamt; - - g_return_val_if_fail(s && pd, FALSE); - g_return_val_if_fail(pd->type == PD_AMOUNT, FALSE); - - splitamt = DxaccSplitGetValue(s); - - switch(pd->amount.amt_sgn) { - case AMT_SGN_MATCH_CREDIT: - if(splitamt > 0.0) return 0; - break; - case AMT_SGN_MATCH_DEBIT: - if(splitamt < 0.0) return 0; - break; - default: - break; - } - - return value_match_predicate(splitamt, pd); -} - -/******************************************************************* - * xaccSharePriceMatchPredicate - *******************************************************************/ -static int -xaccSharePriceMatchPredicate(Split * s, PredicateData * pd) -{ - double splitamt; - Account * acct; - int type; - - g_return_val_if_fail(s && pd, FALSE); - g_return_val_if_fail(pd->type == PD_AMOUNT, FALSE); - - acct = xaccSplitGetAccount(s); - type = xaccAccountGetType(acct); - - splitamt = DxaccSplitGetSharePrice(s); - - return value_match_predicate(splitamt, pd); -} - - -/******************************************************************* - * xaccSharesMatchPredicate - *******************************************************************/ - -static int -xaccSharesMatchPredicate(Split * s, PredicateData * pd) -{ - double splitamt; - Account * acct; - int type; - - g_return_val_if_fail(s && pd, FALSE); - g_return_val_if_fail(pd->type == PD_AMOUNT, FALSE); - - acct = xaccSplitGetAccount(s); - type = xaccAccountGetType(acct); - - splitamt = DxaccSplitGetShareAmount(s); - - return value_match_predicate(splitamt, pd); -} - - -/******************************************************************* - * xaccDateMatchPredicate - *******************************************************************/ - -static int -xaccDateMatchPredicate(Split * s, PredicateData * pd) -{ - Timespec transtime; - - g_return_val_if_fail(s && pd, FALSE); - g_return_val_if_fail(pd->type == PD_DATE, FALSE); - - xaccTransGetDatePostedTS(xaccSplitGetParent(s), &transtime); - - if(pd->date.use_start && pd->date.use_end) { - return ((transtime.tv_sec >= pd->date.start.tv_sec) && - (transtime.tv_sec <= pd->date.end.tv_sec)); - } - else if(pd->date.use_start) { - return ((transtime.tv_sec >= pd->date.start.tv_sec)); - } - else if(pd->date.use_end) { - return ((transtime.tv_sec <= pd->date.end.tv_sec)); - } - else { - return 1; - } -} - -/******************************************************************* - * xaccClearedMatchPredicate - *******************************************************************/ - -static int -xaccClearedMatchPredicate(Split * s, PredicateData * pd) -{ - int cstate; - - g_return_val_if_fail(s && pd, FALSE); - g_return_val_if_fail(pd->type == PD_CLEARED, FALSE); - - cstate = xaccSplitGetReconcile(s); - switch(cstate) { - case CREC: - return ((pd->cleared.how & CLEARED_CLEARED) ? 1 : 0); - break; - case YREC: - return ((pd->cleared.how & CLEARED_RECONCILED) ? 1 : 0); - break; - case FREC: - return ((pd->cleared.how & CLEARED_FROZEN) ? 1 : 0); - break; - case NREC: - return ((pd->cleared.how & CLEARED_NO) ? 1 : 0); - break; - case VREC: - return ((pd->cleared.how & CLEARED_VOIDED) ? 1 : 0); - break; - - } - - return 0; -} - -/******************************************************************* - * xaccBalanceMatchPredicate - *******************************************************************/ - -static int -xaccBalanceMatchPredicate(Split * s, PredicateData * pd) -{ - gboolean balanced; - - g_return_val_if_fail(s && pd, FALSE); - g_return_val_if_fail(pd->type == PD_BALANCE, FALSE); - - if ((pd->balance.how & BALANCE_BALANCED) && - (pd->balance.how & BALANCE_UNBALANCED)) - return 1; - - balanced = - gnc_numeric_zero_p (xaccTransGetImbalance (xaccSplitGetParent (s))); - - if (balanced && (pd->balance.how & BALANCE_BALANCED)) - return 1; - - if (!balanced && (pd->balance.how & BALANCE_UNBALANCED)) - return 1; - - return 0; -} - - -/******************************************************************* - * xaccQuerySetSortOrder - *******************************************************************/ - -void -xaccQuerySetSortOrder(Query * q, sort_type_t primary, - sort_type_t secondary, sort_type_t tertiary) -{ - if (!q) return; - q->primary_sort = primary; - q->secondary_sort = secondary; - q->tertiary_sort = tertiary; - - q->changed = 1; -} - -/******************************************************************* - * xaccQueryGetPrimarySortOrder - *******************************************************************/ - -sort_type_t -xaccQueryGetPrimarySortOrder(Query * q) -{ - if (!q) return BY_NONE; - return q->primary_sort; -} - -/******************************************************************* - * xaccQueryGetSecondarySortOrder - *******************************************************************/ - -sort_type_t -xaccQueryGetSecondarySortOrder(Query * q) -{ - if (!q) return BY_NONE; - return q->secondary_sort; -} - -/******************************************************************* - * xaccQueryGetTertiarySortOrder - *******************************************************************/ - -sort_type_t -xaccQueryGetTertiarySortOrder(Query * q) -{ - if (!q) return BY_NONE; - return q->tertiary_sort; -} - -/******************************************************************* - * xaccQuerySetSortIncreasing - *******************************************************************/ - -void -xaccQuerySetSortIncreasing(Query * q, gboolean prim_increasing, - gboolean sec_increasing, - gboolean tert_increasing) -{ - if (!q) return; - q->primary_increasing = prim_increasing; - q->secondary_increasing = sec_increasing; - q->tertiary_increasing = tert_increasing; - return; -} - -/******************************************************************* - * xaccQueryGetSortPrimaryIncreasing - *******************************************************************/ - -gboolean -xaccQueryGetSortPrimaryIncreasing (Query *q) -{ - if (!q) return TRUE; - return q->primary_increasing; -} - -/******************************************************************* - * xaccQueryGetSortSecondaryIncreasing - *******************************************************************/ - -gboolean -xaccQueryGetSortSecondaryIncreasing (Query *q) -{ - if (!q) return TRUE; - return q->secondary_increasing; -} - -/******************************************************************* - * xaccQueryGetSortTertiaryIncreasing - *******************************************************************/ - -gboolean -xaccQueryGetSortTertiaryIncreasing (Query *q) -{ - if (!q) return TRUE; - return q->tertiary_increasing; -} - -/******************************************************************* - * xaccQuerySetMaxSplits - *******************************************************************/ - -void -xaccQuerySetMaxSplits(Query * q, int n) -{ - if (!q) return; - q->max_splits = n; -} - -int -xaccQueryGetMaxSplits(Query * q) -{ - if (!q) return 0; - return q->max_splits; -} - - -/******************************************************************* - * xaccQuerySetBook - *******************************************************************/ - -void -xaccQuerySetBook(Query * q, GNCBook *book) -{ - if (!q || !book) return; - xaccQueryAddSingleBookMatch(q, book, QUERY_AND); -} - -void -DxaccQuerySetGroup(Query * q, AccountGroup *grp) -{ - if (!q || !grp) return; - xaccQuerySetBook (q, xaccGroupGetBook(grp)); -} - -#endif /* 0 */ - /* ======================== END OF FILE ======================= */ diff --git a/src/engine/Query.h b/src/engine/Query.h index 29e02a5841..6462c2b83d 100644 --- a/src/engine/Query.h +++ b/src/engine/Query.h @@ -34,7 +34,6 @@ #include "guid.h" #include "kvp_frame.h" -#if 1 #include "QueryNew.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 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