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.

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_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 109 of file qofquery.h.

#define QOF_PARAM_KVP   "kvp"
 

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

Definition at line 113 of file qofquery.h.

#define QUERY_DEFAULT_SORT   "QofQueryDefaultSort"
 

Default sort object type

Definition at line 106 of file qofquery.h.


Typedef Documentation

typedef struct _QofQuery QofQuery
 

A Query

Definition at line 91 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 125 of file qofquerycore.h.

00125              {
00126   QOF_CHAR_MATCH_ANY = 1,
00127   QOF_CHAR_MATCH_NONE
00128 } 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 79 of file qofquerycore.h.

00079              {
00080   QOF_DATE_MATCH_NORMAL = 1,
00081   QOF_DATE_MATCH_DAY
00082 } 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 103 of file qofquerycore.h.

00103              {
00106   QOF_GUID_MATCH_ANY = 1,
00107   QOF_GUID_MATCH_NONE,
00108   QOF_GUID_MATCH_NULL,
00111   QOF_GUID_MATCH_ALL,
00114   QOF_GUID_MATCH_LIST_ANY,
00115 } QofGuidMatch;

enum QofNumericMatch
 

Comparisons for QOF_TYPE_NUMERIC, QOF_TYPE_DEBCRED

XXX Should be deprecated, or at least wrapped up as a convnience 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 96 of file qofquerycore.h.

00096              {
00097   QOF_NUMERIC_MATCH_DEBIT = 1,
00098   QOF_NUMERIC_MATCH_CREDIT,
00099   QOF_NUMERIC_MATCH_ANY
00100 } 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 94 of file qofquery.h.

00094              {
00095   QOF_QUERY_AND=1,
00096   QOF_QUERY_OR,
00097   QOF_QUERY_NAND,
00098   QOF_QUERY_NOR,
00099   QOF_QUERY_XOR
00100 } 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 1257 of file qofquery.c.

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

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

DOCUMENT ME !!

Definition at line 1205 of file qofquery.c.

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

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

DOCUMENT ME !!

Definition at line 1220 of file qofquery.c.

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

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 645 of file qofquery.c.

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

void qof_query_clear QofQuery query  ) 
 

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

Definition at line 820 of file qofquery.c.

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

QofQuery* qof_query_copy QofQuery q  ) 
 

Make a copy of the indicated query

Definition at line 926 of file qofquery.c.

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

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 833 of file qofquery.c.

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

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 917 of file qofquery.c.

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

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 1381 of file qofquery.c.

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

GList* qof_query_get_books QofQuery q  ) 
 

Return the list of books we're using

Definition at line 1251 of file qofquery.c.

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

QofIdType qof_query_get_search_for QofQuery q  ) 
 

Return the type of data we're querying for

Definition at line 1289 of file qofquery.c.

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

gboolean qof_query_has_term_type QofQuery q,
GSList *  term_param
 

DOCUMENT ME !!

Definition at line 878 of file qofquery.c.

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

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 862 of file qofquery.c.

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

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 1270 of file qofquery.c.

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

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 958 of file qofquery.c.

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

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 812 of file qofquery.c.

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

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 1041 of file qofquery.c.

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

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 1146 of file qofquery.c.

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

int qof_query_num_terms QofQuery q  ) 
 

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

Definition at line 868 of file qofquery.c.

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

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 669 of file qofquery.c.

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

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 699 of file qofquery.c.

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

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 841 of file qofquery.c.

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

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 1236 of file qofquery.c.

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

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 1199 of file qofquery.c.

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

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 1190 of file qofquery.c.

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

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 1159 of file qofquery.c.

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


Generated on Sun Sep 4 18:09:15 2005 for GnuCash by  doxygen 1.4.3-20050530