Query: Querying for Objects
[Query Object Framework]


Detailed Description

BASIC QUERY API: With this API you can create arbitrary logical queries to find sets of arbitrary object. To make simple queries (1 term, such as a search for a parameter with one value), create the appropriate QueryTerm structure and stick it in a Query object using xaccInitQuery. The QueryTerm should be malloced but the Query object will handle freeing it. To make compound queries, make multiple simple queries and combine them using qof_query_merge() and the logical operations of your choice.

SQL QUERY API: As an alternative to building queries one predicate at a time, you can use the SQL query interface. This interface will accept a string containing an SQL query, parse it, convert it into the core representation, and execute it.

STRUCTURE OF A QUERY: A Query is a logical function of any number of QueryTerms. A QueryTerm consists of a C function pointer (the Predicate) and a PredicateData structure containing data passed to the predicate funtion. The PredicateData structure is a constant associated with the Term and is identical for every object that is tested.

The terms of the Query may represent any logical function and are stored in canonical form, i.e. the function is expressed as a logical sum of logical products. So if you have QueryTerms a, b, c, d, e and you have the logical function a(b+c) + !(c(d+e)), it gets stored as ab + ac + !c + !c!e +!d!c + !d!e. This may not be optimal for evaluation of some functions but it's easy to store, easy to manipulate, and it doesn't require a complete algebra system to deal with.

The representation is of a GList of GLists of QueryTerms. The "backbone" GList q->terms represents the OR-chain, and every item on the backbone is a GList of QueryTerms representing an AND-chain corresponding to a single product-term in the canonical representation. QueryTerms are duplicated when necessary to fill out the canonical form, and the same predicate may be evaluated multiple times per split for complex queries. This is a place where we could probably optimize.

Evaluation of a Query (see qof_query_run()) is optimized as much as possible by short-circuited evaluation. The predicates in each AND-chain are sorted by predicate type, with Account queries sorted first to allow the evaluator to completely eliminate accounts from the search if there's no chance of them having splits that match. (XXX above no longer applies)


Files

file  qofquery.h
 find objects that match a certain expression.
file  qofquerycore.h
 API for providing core Query data types.
file  qofsql.h
 QOF client-side SQL parser, interfacing with libgda.

Modules

 SQL Interface to Query

Data Structures

struct  _QofQueryPredData

Query Subsystem Initialization and Shudown

void qof_query_init (void)
void qof_query_shutdown (void)

Low-Level API Functions

GSList * qof_query_build_param_list (char const *param,...)
QofQueryqof_query_create (void)
QofQueryqof_query_create_for (QofIdTypeConst obj_type)
void qof_query_destroy (QofQuery *q)
void qof_query_search_for (QofQuery *query, QofIdTypeConst obj_type)
void qof_query_set_book (QofQuery *q, QofBook *book)
void qof_query_add_term (QofQuery *query, GSList *param_list, QofQueryPredData *pred_data, QofQueryOp op)
void qof_query_add_guid_match (QofQuery *q, GSList *param_list, const GUID *guid, QofQueryOp op)
void qof_query_add_guid_list_match (QofQuery *q, GSList *param_list, GList *guid_list, QofGuidMatch options, QofQueryOp op)
void qof_query_add_boolean_match (QofQuery *q, GSList *param_list, gboolean value, QofQueryOp op)
GList * qof_query_run (QofQuery *query)
GList * qof_query_last_run (QofQuery *query)
void qof_query_clear (QofQuery *query)
void qof_query_purge_terms (QofQuery *q, GSList *param_list)
int qof_query_has_terms (QofQuery *q)
int qof_query_num_terms (QofQuery *q)
gboolean qof_query_has_term_type (QofQuery *q, GSList *term_param)
GSList * qof_query_get_term_type (QofQuery *q, GSList *term_param)
QofQueryqof_query_copy (QofQuery *q)
QofQueryqof_query_invert (QofQuery *q)
QofQueryqof_query_merge (QofQuery *q1, QofQuery *q2, QofQueryOp op)
void qof_query_merge_in_place (QofQuery *q1, QofQuery *q2, QofQueryOp op)
void qof_query_set_sort_order (QofQuery *q, GSList *primary_sort_params, GSList *secondary_sort_params, GSList *tertiary_sort_params)
void qof_query_set_sort_options (QofQuery *q, gint prim_op, gint sec_op, gint tert_op)
void qof_query_set_sort_increasing (QofQuery *q, gboolean prim_inc, gboolean sec_inc, gboolean tert_inc)
void qof_query_set_max_results (QofQuery *q, int n)
gboolean qof_query_equal (QofQuery *q1, QofQuery *q2)
void qof_query_print (QofQuery *query)
QofIdType qof_query_get_search_for (QofQuery *q)
GList * qof_query_get_books (QofQuery *q)

Core Data Type Predicates

QofQueryPredDataqof_query_string_predicate (QofQueryCompare how, const char *str, QofStringMatch options, gboolean is_regex)
QofQueryPredDataqof_query_date_predicate (QofQueryCompare how, QofDateMatch options, Timespec date)
QofQueryPredDataqof_query_numeric_predicate (QofQueryCompare how, QofNumericMatch options, gnc_numeric value)
QofQueryPredDataqof_query_guid_predicate (QofGuidMatch options, GList *guids)
QofQueryPredDataqof_query_int32_predicate (QofQueryCompare how, gint32 val)
QofQueryPredDataqof_query_int64_predicate (QofQueryCompare how, gint64 val)
QofQueryPredDataqof_query_double_predicate (QofQueryCompare how, double val)
QofQueryPredDataqof_query_boolean_predicate (QofQueryCompare how, gboolean val)
QofQueryPredDataqof_query_char_predicate (QofCharMatch options, const char *chars)
QofQueryPredDataqof_query_collect_predicate (QofGuidMatch options, QofCollection *coll)
QofQueryPredDataqof_query_choice_predicate (QofGuidMatch options, GList *guids)
QofQueryPredDataqof_query_kvp_predicate (QofQueryCompare how, GSList *path, const KvpValue *value)
QofQueryPredDataqof_query_kvp_predicate_path (QofQueryCompare how, const char *path, const KvpValue *value)
QofQueryPredDataqof_query_core_predicate_copy (QofQueryPredData *pdata)
void qof_query_core_predicate_free (QofQueryPredData *pdata)
gboolean qof_query_date_predicate_get_date (QofQueryPredData *pd, Timespec *date)
char * qof_query_core_to_string (QofType, gpointer object, QofParam *getter)

Defines

#define QOF_MOD_QUERY   "qof-query"
#define QOF_QUERY_FIRST_TERM   QOF_QUERY_AND
#define QUERY_DEFAULT_SORT   "QofQueryDefaultSort"
#define QOF_PARAM_BOOK   "book"
#define QOF_PARAM_GUID   "guid"
#define QOF_PARAM_KVP   "kvp"
#define QOF_PARAM_ACTIVE   "active"
#define QOF_PARAM_VERSION   "version"

Typedefs

typedef _QofQuery QofQuery
typedef _QofQueryPredData QofQueryPredData

Enumerations

enum  QofQueryOp {
  QOF_QUERY_AND = 1, QOF_QUERY_OR, QOF_QUERY_NAND, QOF_QUERY_NOR,
  QOF_QUERY_XOR
}
enum  QofQueryCompare {
  QOF_COMPARE_LT = 1, QOF_COMPARE_LTE, QOF_COMPARE_EQUAL, QOF_COMPARE_GT,
  QOF_COMPARE_GTE, QOF_COMPARE_NEQ
}
enum  QofStringMatch { QOF_STRING_MATCH_NORMAL = 1, QOF_STRING_MATCH_CASEINSENSITIVE }
enum  QofDateMatch { QOF_DATE_MATCH_NORMAL = 1, QOF_DATE_MATCH_DAY }
enum  QofNumericMatch { QOF_NUMERIC_MATCH_DEBIT = 1, QOF_NUMERIC_MATCH_CREDIT, QOF_NUMERIC_MATCH_ANY }
enum  QofGuidMatch {
  QOF_GUID_MATCH_ANY = 1, QOF_GUID_MATCH_NONE, QOF_GUID_MATCH_NULL, QOF_GUID_MATCH_ALL,
  QOF_GUID_MATCH_LIST_ANY
}
enum  QofCharMatch { QOF_CHAR_MATCH_ANY = 1, QOF_CHAR_MATCH_NONE }


Define Documentation

#define QOF_PARAM_BOOK   "book"
 

"Known" Object Parameters -- all objects must support these

Definition at line 111 of file qofquery.h.

#define QOF_PARAM_KVP   "kvp"
 

"Known" Object Parameters -- some objects might support these

Definition at line 115 of file qofquery.h.

#define QOF_QUERY_FIRST_TERM   QOF_QUERY_AND
 

First/only term is same as 'and'

Definition at line 105 of file qofquery.h.

#define QUERY_DEFAULT_SORT   "QofQueryDefaultSort"
 

Default sort object type

Definition at line 108 of file qofquery.h.


Typedef Documentation

typedef struct _QofQuery QofQuery
 

A Query

Definition at line 93 of file qofquery.h.

typedef struct _QofQueryPredData QofQueryPredData
 

PREDICATE DATA TYPES: All the predicate data types are rolled up into the union type PredicateData. The "type" field specifies which type the union is.

Definition at line 47 of file qofquerycore.h.


Enumeration Type Documentation

enum QofCharMatch
 

A CHAR type is for a RECNCell, Comparisons for QOF_TYPE_CHAR 'ANY' will match any charagter in the string.

Match 'ANY' is a convenience/performance-enhanced predicate for the compound statement (value==char1) || (value==char2) || etc. Match 'NONE' is equivalent to (value != char1) && (value != char2) && etc.

Definition at line 123 of file qofquerycore.h.

00123              {
00124   QOF_CHAR_MATCH_ANY = 1,
00125   QOF_CHAR_MATCH_NONE
00126 } QofCharMatch;

enum QofDateMatch
 

Comparisons for QOF_TYPE_DATE The QOF_DATE_MATCH_DAY comparison rounds the two time values to mid-day and then compares these rounded values. The QOF_DATE_MATCH_NORMAL comparison matches the time values, down to the second.

Definition at line 77 of file qofquerycore.h.

00077              {
00078   QOF_DATE_MATCH_NORMAL = 1,
00079   QOF_DATE_MATCH_DAY
00080 } QofDateMatch;

enum QofGuidMatch
 

Enumerator:
QOF_GUID_MATCH_ANY  These expect a single object and expect the QofAccessFunc returns GUID*
QOF_GUID_MATCH_ALL  These expect a GList* of objects and calls the QofAccessFunc routine on each item in the list to obtain a GUID* for each object
QOF_GUID_MATCH_LIST_ANY  These expect a single object and expect the QofAccessFunc function to return a GList* of GUID* (the list is the property of the caller)

Definition at line 101 of file qofquerycore.h.

00101              {
00104   QOF_GUID_MATCH_ANY = 1,
00105   QOF_GUID_MATCH_NONE,
00106   QOF_GUID_MATCH_NULL,
00109   QOF_GUID_MATCH_ALL,
00112   QOF_GUID_MATCH_LIST_ANY,
00113 } QofGuidMatch;

enum QofNumericMatch
 

Comparisons for QOF_TYPE_NUMERIC, QOF_TYPE_DEBCRED

XXX Should be deprecated, or at least wrapped up as a convenience function, this is based on the old bill gribble code, which assumed the amount was always positive, and then specified a funds-flow direction (credit, debit, or either).

The point being that 'match credit' is equivalent to the compound predicate (amount >= 0) && (amount 'op' value) while the 'match debit' predicate is equivalent to (amount <= 0) && (abs(amount) 'op' value)

Definition at line 94 of file qofquerycore.h.

00094              {
00095   QOF_NUMERIC_MATCH_DEBIT = 1,
00096   QOF_NUMERIC_MATCH_CREDIT,
00097   QOF_NUMERIC_MATCH_ANY
00098 } QofNumericMatch;

enum QofQueryCompare
 

Standard Query comparitors, for how to compare objects in a predicate. Note that not all core types implement all comparitors

Definition at line 52 of file qofquerycore.h.

00052              {
00053   QOF_COMPARE_LT = 1,
00054   QOF_COMPARE_LTE,
00055   QOF_COMPARE_EQUAL,
00056   QOF_COMPARE_GT,
00057   QOF_COMPARE_GTE,
00058   QOF_COMPARE_NEQ
00059 } QofQueryCompare;

enum QofQueryOp
 

Query Term Operators, for combining Query Terms

Definition at line 96 of file qofquery.h.

00096              {
00097   QOF_QUERY_AND=1,
00098   QOF_QUERY_OR,
00099   QOF_QUERY_NAND,
00100   QOF_QUERY_NOR,
00101   QOF_QUERY_XOR
00102 } QofQueryOp;

enum QofStringMatch
 

List of known core query data-types... Each core query type defines it's set of optional "comparitor qualifiers".

Definition at line 65 of file qofquerycore.h.

00065              {
00066   QOF_STRING_MATCH_NORMAL = 1,
00067   QOF_STRING_MATCH_CASEINSENSITIVE
00068 } QofStringMatch;


Function Documentation

void qof_query_add_boolean_match QofQuery q,
GSList *  param_list,
gboolean  value,
QofQueryOp  op
 

Handy-dandy convenience routines, avoids having to create a separate predicate for boolean matches. We might want to create handy-dandy sugar routines for the other predicate types as well.

Definition at line 1258 of file qofquery.c.

01260 {
01261   QofQueryPredData *pdata;
01262   if (!q || !param_list) return;
01263 
01264   pdata = qof_query_boolean_predicate (QOF_COMPARE_EQUAL, value);
01265   qof_query_add_term (q, param_list, pdata, op);
01266 }

void qof_query_add_guid_list_match QofQuery q,
GSList *  param_list,
GList *  guid_list,
QofGuidMatch  options,
QofQueryOp  op
 

DOCUMENT ME !!

Definition at line 1206 of file qofquery.c.

01209 {
01210   QofQueryPredData *pdata;
01211 
01212   if (!q || !param_list) return;
01213 
01214   if (!guid_list)
01215     g_return_if_fail (options == QOF_GUID_MATCH_NULL);
01216 
01217   pdata = qof_query_guid_predicate (options, guid_list);
01218   qof_query_add_term (q, param_list, pdata, op);
01219 }

void qof_query_add_guid_match QofQuery q,
GSList *  param_list,
const GUID guid,
QofQueryOp  op
 

DOCUMENT ME !!

Definition at line 1221 of file qofquery.c.

01223 {
01224   GList *g = NULL;
01225 
01226   if (!q || !param_list) return;
01227 
01228   if (guid)
01229     g = g_list_prepend (g, (gpointer)guid);
01230 
01231   qof_query_add_guid_list_match (q, param_list, g,
01232                             g ? QOF_GUID_MATCH_ANY : QOF_GUID_MATCH_NULL, op);
01233 
01234   g_list_free (g);
01235 }

void qof_query_add_term QofQuery query,
GSList *  param_list,
QofQueryPredData pred_data,
QofQueryOp  op
 

This is the general function that adds a new Query Term to a query. It will find the 'obj_type' object of the search item and compare the 'param_list' parameter to the predicate data via the comparitor.

The param_list is a recursive list of parameters. For example, you can say 'split->memo' by creating a list of one element, "SPLIT_MEMO". You can say 'split->account->name' by creating a list of two elements, "SPLIT_ACCOUNT" and "ACCOUNT_NAME". The list becomes the property of the Query.

For example:

acct_name_pred_data = make_string_pred_data(QOF_STRING_MATCH_CASEINSENSITIVE, account_name); param_list = make_list (SPLIT_ACCOUNT, ACCOUNT_NAME, NULL); qof_query_add_term (query, param_list, QOF_COMPARE_EQUAL, acct_name_pred_data, QOF_QUERY_AND);

Please note that QofQuery does not, at this time, support joins. That is, one cannot specify a predicate that is a parameter list. Put another way, one cannot search for objects where obja->thingy == objb->stuff

Definition at line 646 of file qofquery.c.

00648 {
00649   QofQueryTerm *qt;
00650   QofQuery *qr, *qs;
00651 
00652   if (!q || !param_list || !pred_data) return;
00653 
00654   qt = g_new0 (QofQueryTerm, 1);
00655   qt->param_list = param_list;
00656   qt->pdata = pred_data;
00657   qs = qof_query_create ();
00658   query_init (qs, qt);
00659 
00660   if (qof_query_has_terms (q))
00661     qr = qof_query_merge (q, qs, op);
00662   else
00663     qr = qof_query_merge (q, qs, QOF_QUERY_OR);
00664 
00665   swap_terms (q, qr);
00666   qof_query_destroy (qs);
00667   qof_query_destroy (qr);
00668 }

void qof_query_clear QofQuery query  ) 
 

Remove all query terms from query. query matches nothing after qof_query_clear().

Definition at line 821 of file qofquery.c.

00822 {
00823   QofQuery *q2 = qof_query_create ();
00824   swap_terms (query, q2);
00825   qof_query_destroy (q2);
00826 
00827   g_list_free (query->books);
00828   query->books = NULL;
00829   g_list_free (query->results);
00830   query->results = NULL;
00831   query->changed = 1;
00832 }

QofQuery* qof_query_copy QofQuery q  ) 
 

Make a copy of the indicated query

Definition at line 927 of file qofquery.c.

00928 {
00929   QofQuery *copy;
00930   GHashTable *ht;
00931 
00932   if (!q) return NULL;
00933   copy = qof_query_create ();
00934   ht = copy->be_compiled;
00935   free_members (copy);
00936 
00937   memcpy (copy, q, sizeof (QofQuery));
00938 
00939   copy->be_compiled = ht;
00940   copy->terms = copy_or_terms (q->terms);
00941   copy->books = g_list_copy (q->books);
00942   copy->results = g_list_copy (q->results);
00943 
00944   copy_sort (&(copy->primary_sort), &(q->primary_sort));
00945   copy_sort (&(copy->secondary_sort), &(q->secondary_sort));
00946   copy_sort (&(copy->tertiary_sort), &(q->tertiary_sort));
00947 
00948   copy->changed = 1;
00949 
00950   return copy;
00951 }

QofQueryPredData* qof_query_core_predicate_copy QofQueryPredData pdata  ) 
 

Copy a predicate.

Definition at line 1792 of file qofquerycore.c.

01793 {
01794   QueryPredicateCopyFunc copy;
01795 
01796   g_return_val_if_fail (pdata, NULL);
01797   g_return_val_if_fail (pdata->type_name, NULL);
01798 
01799   copy = qof_query_copy_predicate (pdata->type_name);
01800   return (copy (pdata));
01801 }

void qof_query_core_predicate_free QofQueryPredData pdata  ) 
 

Destroy a predicate.

Definition at line 1780 of file qofquerycore.c.

01781 {
01782   QueryPredDataFree free_fcn;
01783 
01784   g_return_if_fail (pdata);
01785   g_return_if_fail (pdata->type_name);
01786 
01787   free_fcn = qof_query_predicate_free (pdata->type_name);
01788   free_fcn (pdata);
01789 }

char* qof_query_core_to_string QofType  ,
gpointer  object,
QofParam getter
 

Return a printable string for a core data object. Caller needs to g_free() the returned string.

Definition at line 1804 of file qofquerycore.c.

01806 {
01807   QueryToString toString;
01808 
01809   g_return_val_if_fail (type, NULL);
01810   g_return_val_if_fail (object, NULL);
01811   g_return_val_if_fail (getter, NULL);
01812 
01813   toString = g_hash_table_lookup (toStringTable, type);
01814   g_return_val_if_fail (toString, NULL);
01815 
01816   return toString (object, getter);
01817 }

QofQuery* qof_query_create void   ) 
 

Create a new query. Before running the query, a 'search-for' type must be set otherwise nothing will be returned. The results of the query is a list of the indicated search-for type.

Allocates and initializes a Query structure which must be freed by the user with qof_query_destroy(). A newly-allocated QofQuery object matches nothing (qof_query_run() will return NULL).

Definition at line 834 of file qofquery.c.

00835 {
00836   QofQuery *qp = g_new0 (QofQuery, 1);
00837   qp->be_compiled = g_hash_table_new (g_direct_hash, g_direct_equal);
00838   query_init (qp, NULL);
00839   return qp;
00840 }

gboolean qof_query_date_predicate_get_date QofQueryPredData pd,
Timespec date
 

Retrieve a predicate.

Definition at line 379 of file qofquerycore.c.

00380 {
00381   query_date_t pdata = (query_date_t)pd;
00382 
00383   if (pdata->pd.type_name != query_date_type)
00384     return FALSE;
00385   *date = pdata->date;
00386   return TRUE;
00387 }

void qof_query_destroy QofQuery q  ) 
 

Frees the resources associate with a Query object.

Definition at line 918 of file qofquery.c.

00919 {
00920   if (!q) return;
00921   free_members (q);
00922   query_clear_compiles (q);
00923   g_hash_table_destroy (q->be_compiled);
00924   g_free (q);
00925 }

gboolean qof_query_equal QofQuery q1,
QofQuery q2
 

Compare two queries for equality. Query terms are compared each to each. This is a simplistic implementation -- logical equivalences between different and/or trees are ignored.

Definition at line 1382 of file qofquery.c.

01383 {
01384   GList *or1, *or2;
01385 
01386   if (q1 == q2) return TRUE;
01387   if (!q1 || !q2) return FALSE;
01388 
01389   if (g_list_length (q1->terms) != g_list_length (q2->terms)) return FALSE;
01390   if (q1->max_results != q2->max_results) return FALSE;
01391 
01392   for (or1 = q1->terms, or2 = q2->terms; or1;
01393        or1 = or1->next, or2 = or2->next)
01394   {
01395     GList *and1, *and2;
01396 
01397     and1 = or1->data;
01398     and2 = or2->data;
01399 
01400     if (g_list_length (and1) != g_list_length (and2)) return FALSE;
01401 
01402     for ( ; and1; and1 = and1->next, and2 = and2->next)
01403       if (!qof_query_term_equal (and1->data, and2->data))
01404         return FALSE;
01405   }
01406 
01407   if (!qof_query_sort_equal (&(q1->primary_sort), &(q2->primary_sort)))
01408     return FALSE;
01409   if (!qof_query_sort_equal (&(q1->secondary_sort), &(q2->secondary_sort)))
01410     return FALSE;
01411   if (!qof_query_sort_equal (&(q1->tertiary_sort), &(q2->tertiary_sort)))
01412     return FALSE;
01413 
01414   return TRUE;
01415 }

GList* qof_query_get_books QofQuery q  ) 
 

Return the list of books we're using

Definition at line 1252 of file qofquery.c.

01253 {
01254   if (!q) return NULL;
01255   return q->books;
01256 }

QofIdType qof_query_get_search_for QofQuery q  ) 
 

Return the type of data we're querying for

Definition at line 1290 of file qofquery.c.

01291 {
01292   if (!q) return NULL;
01293   return q->search_for;
01294 }

gboolean qof_query_has_term_type QofQuery q,
GSList *  term_param
 

DOCUMENT ME !!

Definition at line 879 of file qofquery.c.

00880 {
00881   GList *or;
00882   GList *and;
00883 
00884   if (!q || !term_param)
00885     return FALSE;
00886 
00887   for(or = q->terms; or; or = or->next) {
00888     for(and = or->data; and; and = and->next) {
00889       QofQueryTerm *qt = and->data;
00890       if (!param_list_cmp (term_param, qt->param_list))
00891         return TRUE;
00892     }
00893   }
00894 
00895   return FALSE;
00896 }

int qof_query_has_terms QofQuery q  ) 
 

Return boolean FALSE if there are no terms in the query Can be used as a predicate to see if the query has been initialized (return value > 0) or is "blank" (return value == 0).

Definition at line 863 of file qofquery.c.

00864 {
00865   if (!q) return 0;
00866   return g_list_length (q->terms);
00867 }

void qof_query_init void   ) 
 

Subsystem initialization and shutdown. Call init() once to initalize the query subsytem; call shutdown() to free up any resources associated with the query subsystem. Typically called during application startup, shutdown.

Definition at line 1271 of file qofquery.c.

01272 {
01273   ENTER (" ");
01274   qof_query_core_init ();
01275   qof_class_init ();
01276 }

QofQuery* qof_query_invert QofQuery q  ) 
 

Make a copy of the indicated query, inverting the sense of the search. In other words, if the original query search for all objects with a certain condition, the inverted query will search for all object with NOT that condition. The union of the results returned by the original and inverted queries equals the set of all searched objects. These to sets are disjoint (share no members in common).

This will return a newly allocated QofQuery object, or NULL on error. Free it with qof_query_destroy() when no longer needed.

Definition at line 959 of file qofquery.c.

00960 {
00961   QofQuery  * retval;
00962   QofQuery  * right, * left, * iright, * ileft;
00963   QofQueryTerm * qt;
00964   GList  * aterms;
00965   GList  * cur;
00966   GList  * new_oterm;
00967   int    num_or_terms;
00968 
00969   if (!q)
00970     return NULL;
00971 
00972   num_or_terms = g_list_length(q->terms);
00973 
00974   switch(num_or_terms) 
00975   {
00976   case 0:
00977     retval = qof_query_create();
00978     retval->max_results = q->max_results;
00979     break;
00980 
00981     /* This is the DeMorgan expansion for a single AND expression. */
00982     /* !(abc) = !a + !b + !c */
00983   case 1:
00984     retval = qof_query_create();
00985     retval->max_results = q->max_results;
00986     retval->books = g_list_copy (q->books);
00987     retval->search_for = q->search_for;
00988     retval->changed = 1;
00989 
00990     aterms = g_list_nth_data(q->terms, 0);
00991     new_oterm = NULL;
00992     for(cur=aterms; cur; cur=cur->next) {
00993       qt = copy_query_term(cur->data);
00994       qt->invert = !(qt->invert);
00995       new_oterm = g_list_append(NULL, qt);
00996 
00997       /* g_list_append() can take forever, so let's do this for speed
00998        * in "large" queries.
00999        */
01000       retval->terms = g_list_reverse(retval->terms);
01001       retval->terms = g_list_prepend(retval->terms, new_oterm);
01002       retval->terms = g_list_reverse(retval->terms);
01003     }
01004     break;
01005 
01006     /* If there are multiple OR-terms, we just recurse by 
01007      * breaking it down to !(a + b + c) = 
01008      * !a * !(b + c) = !a * !b * !c.  */
01009   default:
01010     right        = qof_query_create();
01011     right->terms = copy_or_terms(g_list_nth(q->terms, 1));
01012 
01013     left         = qof_query_create();
01014     left->terms  = g_list_append(NULL, 
01015                                  copy_and_terms(g_list_nth_data(q->terms, 0)));
01016 
01017     iright       = qof_query_invert(right);
01018     ileft        = qof_query_invert(left);
01019 
01020     retval = qof_query_merge(iright, ileft, QOF_QUERY_AND);
01021     retval->books          = g_list_copy (q->books);
01022     retval->max_results    = q->max_results;
01023     retval->search_for     = q->search_for;
01024     retval->changed        = 1;
01025 
01026     qof_query_destroy(iright);
01027     qof_query_destroy(ileft);
01028     qof_query_destroy(right);
01029     qof_query_destroy(left);
01030     break;
01031   }
01032 
01033   return retval;
01034 }

QofQueryPredData* qof_query_kvp_predicate QofQueryCompare  how,
GSList *  path,
const KvpValue value
 

The qof_query_kvp_predicate() matches the object that has the value 'value' located at the path 'path'. In a certain sense, the 'path' is handled as if it were a paramter.

Definition at line 1235 of file qofquerycore.c.

01237 {
01238   query_kvp_t pdata;
01239   GSList *node;
01240 
01241   g_return_val_if_fail (path && value, NULL);
01242 
01243   pdata = g_new0 (query_kvp_def, 1);
01244   pdata->pd.type_name = query_kvp_type;
01245   pdata->pd.how = how;
01246   pdata->value = kvp_value_copy (value);
01247   pdata->path = g_slist_copy (path);
01248   for (node = pdata->path; node; node = node->next)
01249     node->data = g_strdup (node->data);
01250 
01251   return ((QofQueryPredData*)pdata);
01252 }

QofQueryPredData* qof_query_kvp_predicate_path QofQueryCompare  how,
const char *  path,
const KvpValue value
 

Same predicate as above, except that 'path' is assumed to be a string containing slash-separated pathname.

Definition at line 1255 of file qofquerycore.c.

01257 {
01258   QofQueryPredData *pd;
01259   GSList *spath = NULL;
01260   char *str, *p;
01261 
01262   if (!path) return NULL;
01263 
01264   str = g_strdup (path);
01265   p = str;
01266   if (0 == *p) return NULL;
01267   if ('/' == *p) p++;
01268 
01269   while (p)
01270   {
01271     spath = g_slist_append (spath, p);
01272     p = strchr (p, '/');
01273     if (p) { *p = 0; p++; }
01274   }
01275 
01276   pd = qof_query_kvp_predicate (how, spath, value);
01277   g_free (str);
01278   return pd;
01279 }

GList* qof_query_last_run QofQuery query  ) 
 

Return the results of the last query, without causing the query to be re-run. Do NOT free the resulting list. This list is managed internally by QofQuery.

Definition at line 813 of file qofquery.c.

00814 {
00815   if (!query)
00816     return NULL;
00817 
00818   return query->results;
00819 }

QofQuery* qof_query_merge QofQuery q1,
QofQuery q2,
QofQueryOp  op
 

Combine two queries together using the Boolean set (logical) operator 'op'. For example, if the operator 'op' is set to QUERY_AND, then the set of results returned by the query will will be the Boolean set intersection of the results returned by q1 and q2. Similarly, QUERY_OR maps to set union, etc.

Both queries must have compatible search-types. If both queries are set, they must search for the same object type. If only one is set, the resulting query will search for the set type. If neither query has the search-type set, the result will be unset as well.

This will return a newly allocated QofQuery object, or NULL on error. Free it with qof_query_destroy() when no longer needed.

Definition at line 1042 of file qofquery.c.

01043 {
01044   
01045   QofQuery * retval = NULL;
01046   QofQuery * i1, * i2;
01047   QofQuery * t1, * t2;
01048   GList * i, * j;
01049   QofIdType search_for;
01050 
01051   if(!q1) return q2;
01052   if(!q2) return q1;
01053 
01054   if (q1->search_for && q2->search_for)
01055     g_return_val_if_fail (safe_strcmp (q1->search_for, q2->search_for) == 0,
01056                           NULL);
01057 
01058   search_for = (q1->search_for ? q1->search_for : q2->search_for);
01059 
01060   /* Avoid merge surprises if op==QOF_QUERY_AND but q1 is empty.
01061    * The goal of this tweak is to all the user to start with
01062    * an empty q1 and then append to it recursively
01063    * (and q1 (and q2 (and q3 (and q4 ....))))
01064    * without bombing out because the append started with an 
01065    * empty list.
01066    * We do essentially the same check in qof_query_add_term()
01067    * so that the first term added to an empty query doesn't screw up.
01068    */
01069   if ((QOF_QUERY_AND == op) && (0 == qof_query_has_terms (q1)))
01070   {
01071     op = QOF_QUERY_OR;
01072   }
01073 
01074   switch(op) 
01075   {
01076   case QOF_QUERY_OR:
01077     retval = qof_query_create();
01078     retval->terms = 
01079       g_list_concat(copy_or_terms(q1->terms), copy_or_terms(q2->terms));
01080     retval->books           = merge_books (q1->books, q2->books);
01081     retval->max_results    = q1->max_results;
01082     retval->changed        = 1;
01083     break;
01084 
01085   case QOF_QUERY_AND:
01086     retval = qof_query_create();
01087     retval->books          = merge_books (q1->books, q2->books);
01088     retval->max_results    = q1->max_results;
01089     retval->changed        = 1;
01090 
01091     /* g_list_append() can take forever, so let's build the list in
01092      * reverse and then reverse it at the end, to deal better with
01093      * "large" queries.
01094      */
01095     for(i=q1->terms; i; i=i->next) 
01096     {
01097       for(j=q2->terms; j; j=j->next) 
01098       {
01099         retval->terms = 
01100           g_list_prepend(retval->terms, 
01101                         g_list_concat
01102                         (copy_and_terms(i->data),
01103                          copy_and_terms(j->data)));
01104       }
01105     }
01106     retval->terms = g_list_reverse(retval->terms);
01107     break;
01108 
01109   case QOF_QUERY_NAND:
01110     /* !(a*b) = (!a + !b) */
01111     i1     = qof_query_invert(q1);
01112     i2     = qof_query_invert(q2);
01113     retval = qof_query_merge(i1, i2, QOF_QUERY_OR);
01114     qof_query_destroy(i1);
01115     qof_query_destroy(i2);
01116     break;
01117 
01118   case QOF_QUERY_NOR:
01119     /* !(a+b) = (!a*!b) */
01120     i1     = qof_query_invert(q1);
01121     i2     = qof_query_invert(q2);
01122     retval = qof_query_merge(i1, i2, QOF_QUERY_AND);
01123     qof_query_destroy(i1);
01124     qof_query_destroy(i2);
01125     break;
01126 
01127   case QOF_QUERY_XOR:
01128     /* a xor b = (a * !b) + (!a * b) */
01129     i1     = qof_query_invert(q1);
01130     i2     = qof_query_invert(q2);
01131     t1     = qof_query_merge(q1, i2, QOF_QUERY_AND);
01132     t2     = qof_query_merge(i1, q2, QOF_QUERY_AND);
01133     retval = qof_query_merge(t1, t2, QOF_QUERY_OR);
01134 
01135     qof_query_destroy(i1);
01136     qof_query_destroy(i2);
01137     qof_query_destroy(t1);
01138     qof_query_destroy(t2);     
01139     break;
01140   }
01141 
01142   retval->search_for = search_for;
01143   return retval;
01144 }

void qof_query_merge_in_place QofQuery q1,
QofQuery q2,
QofQueryOp  op
 

Like qof_query_merge, but this will merge a copy of q2 into q1. q2 remains unchanged.

Definition at line 1147 of file qofquery.c.

01148 {
01149   QofQuery *tmp_q;
01150 
01151   if (!q1 || !q2)
01152     return;
01153 
01154   tmp_q = qof_query_merge (q1, q2, op);
01155   swap_terms (q1, tmp_q);
01156   qof_query_destroy (tmp_q);
01157 }

int qof_query_num_terms QofQuery q  ) 
 

Return the number of terms in the canonical form of the query.

Definition at line 869 of file qofquery.c.

00870 {
00871   GList *o;
00872   int n = 0;
00873   if (!q) return 0;
00874   for (o = q->terms; o; o=o->next)
00875     n += g_list_length(o->data);
00876   return n;
00877 }

void qof_query_print QofQuery query  ) 
 

Print the Query in human-readable format. Useful for debugging and development.

Definition at line 1446 of file qofquery.c.

01447 {
01448   GList *output;
01449   GString *str;
01450   QofQuerySort *s[3];
01451   gint maxResults = 0, numSorts = 3;
01452 
01453   ENTER (" ");
01454 
01455   if (!query) {
01456       LEAVE("query is (null)");
01457       return;
01458   }
01459 
01460   output = NULL;
01461   str = NULL;
01462   maxResults = qof_query_get_max_results (query);
01463 
01464   output = qof_query_printSearchFor (query, output);
01465   output = qof_query_printTerms (query, output);
01466 
01467   qof_query_get_sorts (query, &s[0], &s[1], &s[2]);
01468 
01469   if (s[0])
01470   {
01471     output = qof_query_printSorts (s, numSorts, output);
01472   }
01473 
01474   str = g_string_new (" ");
01475   g_string_sprintf (str, "Maximum number of results: %d", maxResults);
01476   output = g_list_append (output, str);
01477 
01478   qof_query_printOutput (output);
01479   LEAVE (" ");
01480 }

void qof_query_purge_terms QofQuery q,
GSList *  param_list
 

Remove query terms of a particular type from q. The "type" of a term is determined by the type of data that gets passed to the predicate function. XXX ??? Huh? remove anything of that predicate type, or just the particular predicate ?

Definition at line 670 of file qofquery.c.

00671 {
00672   QofQueryTerm *qt;
00673   GList *or, *and;
00674 
00675   if (!q || !param_list) return;
00676 
00677   for (or = q->terms; or; or = or->next) {
00678     for (and = or->data; and; and = and->next) {
00679       qt = and->data;
00680       if (!param_list_cmp (qt->param_list, param_list)) {
00681         if (g_list_length (or->data) == 1) {
00682           q->terms = g_list_remove_link (q->terms, or);
00683           g_list_free_1 (or);
00684           or = q->terms;
00685           break;
00686         } else {
00687           or->data = g_list_remove_link (or->data, and);
00688           g_list_free_1 (and);
00689           and = or->data;
00690           if (!and) break;
00691         }
00692         q->changed = 1;
00693         free_query_term (qt);
00694       }
00695     }
00696     if (!or) break;
00697   }
00698 }

GList* qof_query_run QofQuery query  ) 
 

Perform the query, return the results. The returned list is a list of the 'search-for' type that was previously set with the qof_query_search_for() or the qof_query_create_for() routines. The returned list will have been sorted using the indicated sort order, and trimed to the max_results length.

Do NOT free the resulting list. This list is managed internally by QofQuery.

Definition at line 700 of file qofquery.c.

00701 {
00702   GList *matching_objects = NULL;
00703   GList *node;
00704   int        object_count = 0;
00705 
00706   if (!q) return NULL;
00707   g_return_val_if_fail (q->search_for, NULL);
00708   g_return_val_if_fail (q->books, NULL);
00709   ENTER (" q=%p", q);
00710 
00711   /* XXX: Prioritize the query terms? */
00712 
00713   /* prepare the Query for processing */
00714   if (q->changed) 
00715   {
00716     query_clear_compiles (q);
00717     compile_terms (q);
00718   }
00719 
00720   /* Maybe log this sucker */
00721   if (gnc_should_log (log_module, GNC_LOG_DETAIL)) qof_query_print (q);
00722 
00723   /* Now run the query over all the objects and save the results */
00724   {
00725     QofQueryCB qcb;
00726 
00727     memset (&qcb, 0, sizeof (qcb));
00728     qcb.query = q;
00729 
00730     /* For each book */
00731     for (node=q->books; node; node=node->next) 
00732     {
00733       QofBook *book = node->data;
00734       QofBackend *be = book->backend;
00735 
00736       /* run the query in the backend */
00737       if (be) 
00738       {
00739         gpointer compiled_query = g_hash_table_lookup (q->be_compiled, book);
00740 
00741         if (compiled_query && be->run_query)
00742         {
00743           (be->run_query) (be, compiled_query);
00744         }
00745       }
00746 
00747       /* And then iterate over all the objects */
00748       qof_object_foreach (q->search_for, book, (QofEntityForeachCB) check_item_cb, &qcb);
00749     }
00750 
00751     matching_objects = qcb.list;
00752     object_count = qcb.count;
00753   }
00754   PINFO ("matching objects=%p count=%d", matching_objects, object_count);
00755 
00756   /* There is no absolute need to reverse this list, since it's being
00757    * sorted below. However, in the common case, we will be searching
00758    * in a confined location where the objects are already in order,
00759    * thus reversing will put us in the correct order we want and make
00760    * the sorting go much faster.
00761    */
00762   matching_objects = g_list_reverse(matching_objects);
00763 
00764   /* Now sort the matching objects based on the search criteria
00765    * sortQuery is an unforgivable use of static global data...  
00766    * I just can't figure out how else to do this sanely.
00767    */
00768   if (q->primary_sort.comp_fcn || q->primary_sort.obj_cmp ||
00769       (q->primary_sort.use_default && q->defaultSort))
00770   {
00771     sortQuery = q;
00772     matching_objects = g_list_sort(matching_objects, sort_func);
00773     sortQuery = NULL;
00774   }
00775 
00776   /* Crop the list to limit the number of splits. */
00777   if((object_count > q->max_results) && (q->max_results > -1)) 
00778   {
00779     if(q->max_results > 0) 
00780     {
00781       GList *mptr;
00782 
00783       /* mptr is set to the first node of what will be the new list */
00784       mptr = g_list_nth(matching_objects, object_count - q->max_results);
00785       /* mptr should not be NULL, but let's be safe */
00786       if (mptr != NULL) 
00787       {
00788         if (mptr->prev != NULL) mptr->prev->next = NULL;
00789         mptr->prev = NULL;
00790       }
00791       g_list_free(matching_objects);
00792       matching_objects = mptr;
00793     }
00794     else 
00795     { 
00796       /* q->max_results == 0 */
00797       g_list_free(matching_objects);
00798       matching_objects = NULL;
00799     }
00800     object_count = q->max_results;
00801   }
00802   
00803   q->changed = 0;
00804   
00805   g_list_free(q->results);
00806   q->results = matching_objects;
00807   
00808   LEAVE (" q=%p", q);
00809   return matching_objects;
00810 }

void qof_query_search_for QofQuery query,
QofIdTypeConst  obj_type
 

Set the object type to be searched for. The results of performuing the query will be a list of this obj_type.

Definition at line 842 of file qofquery.c.

00843 {
00844   if (!q || !obj_type)
00845     return;
00846 
00847   if (safe_strcmp (q->search_for, obj_type)) {
00848     q->search_for = (QofIdType) obj_type;
00849     q->changed = 1;
00850   }
00851 }

void qof_query_set_book QofQuery q,
QofBook book
 

Set the book to be searched. Books contain/identify collections of objects; the search will be performed over those books specified with this function. If no books are set, no results will be returned (since there is nothing to search over).

You can search multiple books. To specify multiple books, call this function multiple times with different arguments. XXX needed qof_query_clear_books() to reset the list ...

Definition at line 1237 of file qofquery.c.

01238 {
01239   GSList *slist = NULL;
01240   if (!q || !book) return;
01241 
01242   /* Make sure this book is only in the list once */
01243   if (g_list_index (q->books, book) == -1)
01244     q->books = g_list_prepend (q->books, book);
01245 
01246   g_slist_prepend (slist, QOF_PARAM_GUID);
01247   g_slist_prepend (slist, QOF_PARAM_BOOK);
01248   qof_query_add_guid_match (q, slist,
01249                         qof_book_get_guid(book), QOF_QUERY_AND);
01250 }

void qof_query_set_max_results QofQuery q,
int  n
 

Set the maximum number of results that should be returned. If 'max-results' is set to -1, then all of the results are returned. If there are more results than 'max-results', then the result list is trimmed. Note that there is an important interplay between 'max-results' and the sort order: only the last bit of results are returned. For example, if the sort order is set to be increasing date order, then only the objects with the most recent dates will be returned.

Definition at line 1200 of file qofquery.c.

01201 {
01202   if (!q) return;
01203   q->max_results = n;
01204 }

void qof_query_set_sort_increasing QofQuery q,
gboolean  prim_inc,
gboolean  sec_inc,
gboolean  tert_inc
 

When a query is run, the results are sorted before being returned. This routine can be used to control the direction of the ordering. A value of TRUE indicates the sort will be in increasing order, a value of FALSE will order results in decreasing order.

Note that if there are more results than the 'max-results' value, then only the *last* max-results will be returned. For example, if the sort is set to be increasing date order, then only the objects with the most recent dates will be returned.

Definition at line 1191 of file qofquery.c.

01193 {
01194   if (!q) return;
01195   q->primary_sort.increasing = prim_inc;
01196   q->secondary_sort.increasing = sec_inc;
01197   q->tertiary_sort.increasing = tert_inc;
01198 }

void qof_query_set_sort_order QofQuery q,
GSList *  primary_sort_params,
GSList *  secondary_sort_params,
GSList *  tertiary_sort_params
 

When a query is run, the results are sorted before being returned. This routine can be used to set the paramters on which the sort will be performed. Two objects in the result list will be compared using the 'primary_sort_params', and sorted based on that order. If the comparison shows that they are equal, then the 'secondary_sort_params' will be used. If still equal, then the tertiary params will be compared. Any or all of these parameter lists may be NULL. Any of these parameter lists may be set to QUERY_DEFAULT_SORT.

Note that if there are more results than the 'max-results' value, then only the *last* max-results will be returned. For example, if the sort is set to be increasing date order, then only the objects with the most recent dates will be returned.

The input lists become the property of QofQuery and are managed by it. They will be freed when the query is destroyed (or when new lists are set).

Definition at line 1160 of file qofquery.c.

01162 {
01163   if (!q) return;
01164   if (q->primary_sort.param_list)
01165     g_slist_free (q->primary_sort.param_list);
01166   q->primary_sort.param_list = params1;
01167   q->primary_sort.options = 0;
01168 
01169   if (q->secondary_sort.param_list)
01170     g_slist_free (q->secondary_sort.param_list);
01171   q->secondary_sort.param_list = params2;
01172   q->secondary_sort.options = 0;
01173 
01174   if (q->tertiary_sort.param_list)
01175     g_slist_free (q->tertiary_sort.param_list);
01176   q->tertiary_sort.param_list = params3;
01177   q->tertiary_sort.options = 0;
01178 
01179   q->changed = 1;
01180 }


Generated on Fri Oct 21 15:50:00 2005 for QOF by  doxygen 1.4.5