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 function. 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.


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 (gchar 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)
gint qof_query_has_terms (QofQuery *q)
gint 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, gint n)
gboolean qof_query_equal (QofQuery *q1, QofQuery *q2)
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 gchar *str, QofStringMatch options, gboolean is_regex)
QofQueryPredDataqof_query_time_predicate (QofQueryCompare how, QofDateMatch options, QofTime *qt)
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 gchar *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 gchar *path, const KvpValue *value)
QofQueryPredDataqof_query_core_predicate_copy (QofQueryPredData *pdata)
void qof_query_core_predicate_free (QofQueryPredData *pdata)
gboolean qof_query_time_predicate_get_time (QofQueryPredData *pd, QofTime *qt)
gchar * 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 104 of file qofquery.h.

#define QOF_PARAM_KVP   "kvp"

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

Definition at line 108 of file qofquery.h.

#define QOF_QUERY_FIRST_TERM   QOF_QUERY_AND

First/only term is same as 'and'

Definition at line 98 of file qofquery.h.

#define QUERY_DEFAULT_SORT   "QofQueryDefaultSort"

Default sort object type

Definition at line 101 of file qofquery.h.


Typedef Documentation

typedef struct _QofQuery QofQuery

A Query

Definition at line 85 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 46 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 127 of file qofquerycore.h.

00128 {
00129     QOF_CHAR_MATCH_ANY = 1,
00130     QOF_CHAR_MATCH_NONE
00131 } 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 78 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 104 of file qofquerycore.h.

00105 {
00108     QOF_GUID_MATCH_ANY = 1,
00109     QOF_GUID_MATCH_NONE,
00110     QOF_GUID_MATCH_NULL,
00113     QOF_GUID_MATCH_ALL,
00116     QOF_GUID_MATCH_LIST_ANY,
00117 } 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 96 of file qofquerycore.h.

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

00089 {
00090     QOF_QUERY_AND = 1,
00091     QOF_QUERY_OR,
00092     QOF_QUERY_NAND,
00093     QOF_QUERY_NOR,
00094     QOF_QUERY_XOR
00095 } 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.

00066 {
00067     QOF_STRING_MATCH_NORMAL = 1,
00068     QOF_STRING_MATCH_CASEINSENSITIVE
00069 } 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 1359 of file qofquery.c.

01361 {
01362     QofQueryPredData *pdata;
01363     if (!q || !param_list)
01364         return;
01365 
01366     pdata = qof_query_boolean_predicate (QOF_COMPARE_EQUAL, value);
01367     qof_query_add_term (q, param_list, pdata, op);
01368 }

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

DOCUMENT ME !!

Definition at line 1300 of file qofquery.c.

01302 {
01303     QofQueryPredData *pdata;
01304 
01305     if (!q || !param_list)
01306         return;
01307 
01308     if (!guid_list)
01309         g_return_if_fail (options == QOF_GUID_MATCH_NULL);
01310 
01311     pdata = qof_query_guid_predicate (options, guid_list);
01312     qof_query_add_term (q, param_list, pdata, op);
01313 }

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

DOCUMENT ME !!

Definition at line 1316 of file qofquery.c.

01318 {
01319     GList *g = NULL;
01320 
01321     if (!q || !param_list)
01322         return;
01323 
01324     if (guid)
01325         g = g_list_prepend (g, (gpointer) guid);
01326 
01327     qof_query_add_guid_list_match (q, param_list, g,
01328         g ? QOF_GUID_MATCH_ANY : QOF_GUID_MATCH_NULL, op);
01329 
01330     g_list_free (g);
01331 }

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 comparator. The param_list is a recursive list of parameters. The list becomes the property of the Query.

QofQueryPredData *time_pred_data;
QofQuery *query;
QofParam *param;
QofTime *qoftime;

time_pred_data = qof_query_time_predicate (QOF_COMPARE_GTE,
	QOF_DATE_MATCH_DAY, qoftime);
qof_query_add_term (query, 
	qof_query_build_param_list ((gchar*)param->param_name, 
	NULL), time_pred_data, QOF_QUERY_AND);

Note:
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 You can simulate a join by using recursive or sequential queries.

Definition at line 690 of file qofquery.c.

00692 {
00693     QofQueryTerm *qt;
00694     QofQuery *qr, *qs;
00695 
00696     if (!q || !param_list || !pred_data)
00697         return;
00698 
00699     qt = g_new0 (QofQueryTerm, 1);
00700     qt->param_list = param_list;
00701     qt->pdata = pred_data;
00702     qs = qof_query_create ();
00703     query_init (qs, qt);
00704 
00705     if (qof_query_has_terms (q))
00706         qr = qof_query_merge (q, qs, op);
00707     else
00708         qr = qof_query_merge (q, qs, QOF_QUERY_OR);
00709 
00710     swap_terms (q, qr);
00711     qof_query_destroy (qs);
00712     qof_query_destroy (qr);
00713 }

void qof_query_clear ( QofQuery query  ) 

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

Definition at line 885 of file qofquery.c.

00886 {
00887     QofQuery *q2 = qof_query_create ();
00888     swap_terms (query, q2);
00889     qof_query_destroy (q2);
00890 
00891     g_list_free (query->books);
00892     query->books = NULL;
00893     g_list_free (query->results);
00894     query->results = NULL;
00895     query->changed = 1;
00896 }

QofQuery* qof_query_copy ( QofQuery q  ) 

Make a copy of the indicated query

Definition at line 1008 of file qofquery.c.

01009 {
01010     QofQuery *copy;
01011     GHashTable *ht;
01012 
01013     if (!q)
01014         return NULL;
01015     copy = qof_query_create ();
01016     ht = copy->be_compiled;
01017     free_members (copy);
01018 
01019     memcpy (copy, q, sizeof (QofQuery));
01020 
01021     copy->be_compiled = ht;
01022     copy->terms = copy_or_terms (q->terms);
01023     copy->books = g_list_copy (q->books);
01024     copy->results = g_list_copy (q->results);
01025 
01026     copy_sort (&(copy->primary_sort), &(q->primary_sort));
01027     copy_sort (&(copy->secondary_sort), &(q->secondary_sort));
01028     copy_sort (&(copy->tertiary_sort), &(q->tertiary_sort));
01029 
01030     copy->changed = 1;
01031 
01032     return copy;
01033 }

QofQueryPredData* qof_query_core_predicate_copy ( QofQueryPredData pdata  ) 

Copy a predicate.

Definition at line 2105 of file qofquerycore.c.

void qof_query_core_predicate_free ( QofQueryPredData pdata  ) 

Destroy a predicate.

Definition at line 2093 of file qofquerycore.c.

gchar* 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 2117 of file qofquerycore.c.

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

00900 {
00901     QofQuery *qp = g_new0 (QofQuery, 1);
00902     qp->be_compiled = g_hash_table_new (g_direct_hash, g_direct_equal);
00903     query_init (qp, NULL);
00904     return qp;
00905 }

QofQuery* qof_query_create_for ( QofIdTypeConst  obj_type  ) 

create a query with a search type preset.

Definition at line 921 of file qofquery.c.

00922 {
00923     QofQuery *q;
00924     if (!obj_type)
00925         return NULL;
00926     q = qof_query_create ();
00927     qof_query_search_for (q, obj_type);
00928     return q;
00929 }

void qof_query_destroy ( QofQuery q  ) 

Frees the resources associated with a Query object.

Definition at line 997 of file qofquery.c.

00998 {
00999     if (!q)
01000         return;
01001     free_members (q);
01002     query_clear_compiles (q);
01003     g_hash_table_destroy (q->be_compiled);
01004     g_free (q);
01005 }

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

01511 {
01512     GList *or1, *or2;
01513 
01514     if (q1 == q2)
01515         return TRUE;
01516     if (!q1 || !q2)
01517         return FALSE;
01518 
01519     if (g_list_length (q1->terms) != g_list_length (q2->terms))
01520         return FALSE;
01521     if (q1->max_results != q2->max_results)
01522         return FALSE;
01523 
01524     for (or1 = q1->terms, or2 = q2->terms; or1;
01525         or1 = or1->next, or2 = or2->next)
01526     {
01527         GList *and1, *and2;
01528 
01529         and1 = or1->data;
01530         and2 = or2->data;
01531 
01532         if (g_list_length (and1) != g_list_length (and2))
01533             return FALSE;
01534 
01535         for (; and1; and1 = and1->next, and2 = and2->next)
01536             if (!qof_query_term_equal (and1->data, and2->data))
01537                 return FALSE;
01538     }
01539 
01540     if (!qof_query_sort_equal (&(q1->primary_sort), &(q2->primary_sort)))
01541         return FALSE;
01542     if (!qof_query_sort_equal (&(q1->secondary_sort),
01543             &(q2->secondary_sort)))
01544         return FALSE;
01545     if (!qof_query_sort_equal (&(q1->tertiary_sort), &(q2->tertiary_sort)))
01546         return FALSE;
01547 
01548     return TRUE;
01549 }

GList* qof_query_get_books ( QofQuery q  ) 

Return the list of books we're using

Definition at line 1351 of file qofquery.c.

01352 {
01353     if (!q)
01354         return NULL;
01355     return q->books;
01356 }

QofIdType qof_query_get_search_for ( QofQuery q  ) 

Return the type of data we're querying for

Definition at line 1398 of file qofquery.c.

01399 {
01400     if (!q)
01401         return NULL;
01402     return q->search_for;
01403 }

gboolean qof_query_has_term_type ( QofQuery q,
GSList *  term_param 
)

DOCUMENT ME !!

Definition at line 952 of file qofquery.c.

00953 {
00954     GList *or;
00955     GList *and;
00956 
00957     if (!q || !term_param)
00958         return FALSE;
00959 
00960     for (or = q->terms; or; or = or->next)
00961     {
00962         for (and = or->data; and; and = and->next)
00963         {
00964             QofQueryTerm *qt = and->data;
00965             if (!param_list_cmp (term_param, qt->param_list))
00966                 return TRUE;
00967         }
00968     }
00969 
00970     return FALSE;
00971 }

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

00933 {
00934     if (!q)
00935         return 0;
00936     return g_list_length (q->terms);
00937 }

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

01375 {
01376     ENTER (" ");
01377     qof_query_core_init ();
01378     qof_class_init ();
01379     LEAVE ("Completed initialization of QofQuery");
01380 }

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

01043 {
01044     QofQuery *retval;
01045     QofQuery *right, *left, *iright, *ileft;
01046     QofQueryTerm *qt;
01047     GList *aterms;
01048     GList *cur;
01049     GList *new_oterm;
01050     gint num_or_terms;
01051 
01052     if (!q)
01053         return NULL;
01054 
01055     num_or_terms = g_list_length (q->terms);
01056 
01057     switch (num_or_terms)
01058     {
01059     case 0:
01060         retval = qof_query_create ();
01061         retval->max_results = q->max_results;
01062         break;
01063 
01064         /* This is the DeMorgan expansion for a single AND expression. */
01065         /* !(abc) = !a + !b + !c */
01066     case 1:
01067         retval = qof_query_create ();
01068         retval->max_results = q->max_results;
01069         retval->books = g_list_copy (q->books);
01070         retval->search_for = q->search_for;
01071         retval->changed = 1;
01072 
01073         aterms = g_list_nth_data (q->terms, 0);
01074         new_oterm = NULL;
01075         for (cur = aterms; cur; cur = cur->next)
01076         {
01077             qt = copy_query_term (cur->data);
01078             qt->invert = !(qt->invert);
01079             new_oterm = g_list_append (NULL, qt);
01080 
01081             /* g_list_append() can take forever, so let's do this for speed
01082              * in "large" queries.
01083              */
01084             retval->terms = g_list_reverse (retval->terms);
01085             retval->terms = g_list_prepend (retval->terms, new_oterm);
01086             retval->terms = g_list_reverse (retval->terms);
01087         }
01088         break;
01089 
01090         /* If there are multiple OR-terms, we just recurse by 
01091          * breaking it down to !(a + b + c) = 
01092          * !a * !(b + c) = !a * !b * !c.  */
01093     default:
01094         right = qof_query_create ();
01095         right->terms = copy_or_terms (g_list_nth (q->terms, 1));
01096 
01097         left = qof_query_create ();
01098         left->terms = g_list_append (NULL,
01099             copy_and_terms (g_list_nth_data (q->terms, 0)));
01100 
01101         iright = qof_query_invert (right);
01102         ileft = qof_query_invert (left);
01103 
01104         retval = qof_query_merge (iright, ileft, QOF_QUERY_AND);
01105         retval->books = g_list_copy (q->books);
01106         retval->max_results = q->max_results;
01107         retval->search_for = q->search_for;
01108         retval->changed = 1;
01109 
01110         qof_query_destroy (iright);
01111         qof_query_destroy (ileft);
01112         qof_query_destroy (right);
01113         qof_query_destroy (left);
01114         break;
01115     }
01116 
01117     return retval;
01118 }

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 1310 of file qofquerycore.c.

01321     {
01322         spath = g_slist_append (spath, p);
01323         p = strchr (p, '/');
01324         if (p)
01325         {
01326             *p = 0;
01327             p++;

QofQueryPredData* qof_query_kvp_predicate_path ( QofQueryCompare  how,
const gchar *  path,
const KvpValue value 
)

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

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

00877 {
00878     if (!query)
00879         return NULL;
00880 
00881     return query->results;
00882 }

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

01127 {
01128 
01129     QofQuery *retval = NULL;
01130     QofQuery *i1, *i2;
01131     QofQuery *t1, *t2;
01132     GList *i, *j;
01133     QofIdType search_for;
01134 
01135     if (!q1)
01136         return q2;
01137     if (!q2)
01138         return q1;
01139 
01140     if (q1->search_for && q2->search_for)
01141         g_return_val_if_fail (safe_strcmp (q1->search_for,
01142                 q2->search_for) == 0, NULL);
01143 
01144     search_for = (q1->search_for ? q1->search_for : q2->search_for);
01145 
01146     /* Avoid merge surprises if op==QOF_QUERY_AND but q1 is empty.
01147      * The goal of this tweak is to all the user to start with
01148      * an empty q1 and then append to it recursively
01149      * (and q1 (and q2 (and q3 (and q4 ....))))
01150      * without bombing out because the append started with an 
01151      * empty list.
01152      * We do essentially the same check in qof_query_add_term()
01153      * so that the first term added to an empty query doesn't screw up.
01154      */
01155     if ((QOF_QUERY_AND == op) && (0 == qof_query_has_terms (q1)))
01156     {
01157         op = QOF_QUERY_OR;
01158     }
01159 
01160     switch (op)
01161     {
01162     case QOF_QUERY_OR:
01163         retval = qof_query_create ();
01164         retval->terms =
01165             g_list_concat (copy_or_terms (q1->terms),
01166             copy_or_terms (q2->terms));
01167         retval->books = merge_books (q1->books, q2->books);
01168         retval->max_results = q1->max_results;
01169         retval->changed = 1;
01170         break;
01171 
01172     case QOF_QUERY_AND:
01173         retval = qof_query_create ();
01174         retval->books = merge_books (q1->books, q2->books);
01175         retval->max_results = q1->max_results;
01176         retval->changed = 1;
01177 
01178         /* g_list_append() can take forever, so let's build the list in
01179          * reverse and then reverse it at the end, to deal better with
01180          * "large" queries.
01181          */
01182         for (i = q1->terms; i; i = i->next)
01183         {
01184             for (j = q2->terms; j; j = j->next)
01185             {
01186                 retval->terms =
01187                     g_list_prepend (retval->terms,
01188                     g_list_concat
01189                     (copy_and_terms (i->data), copy_and_terms (j->data)));
01190             }
01191         }
01192         retval->terms = g_list_reverse (retval->terms);
01193         break;
01194 
01195     case QOF_QUERY_NAND:
01196         /* !(a*b) = (!a + !b) */
01197         i1 = qof_query_invert (q1);
01198         i2 = qof_query_invert (q2);
01199         retval = qof_query_merge (i1, i2, QOF_QUERY_OR);
01200         qof_query_destroy (i1);
01201         qof_query_destroy (i2);
01202         break;
01203 
01204     case QOF_QUERY_NOR:
01205         /* !(a+b) = (!a*!b) */
01206         i1 = qof_query_invert (q1);
01207         i2 = qof_query_invert (q2);
01208         retval = qof_query_merge (i1, i2, QOF_QUERY_AND);
01209         qof_query_destroy (i1);
01210         qof_query_destroy (i2);
01211         break;
01212 
01213     case QOF_QUERY_XOR:
01214         /* a xor b = (a * !b) + (!a * b) */
01215         i1 = qof_query_invert (q1);
01216         i2 = qof_query_invert (q2);
01217         t1 = qof_query_merge (q1, i2, QOF_QUERY_AND);
01218         t2 = qof_query_merge (i1, q2, QOF_QUERY_AND);
01219         retval = qof_query_merge (t1, t2, QOF_QUERY_OR);
01220 
01221         qof_query_destroy (i1);
01222         qof_query_destroy (i2);
01223         qof_query_destroy (t1);
01224         qof_query_destroy (t2);
01225         break;
01226     }
01227 
01228     retval->search_for = search_for;
01229     return retval;
01230 }

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

01234 {
01235     QofQuery *tmp_q;
01236 
01237     if (!q1 || !q2)
01238         return;
01239 
01240     tmp_q = qof_query_merge (q1, q2, op);
01241     swap_terms (q1, tmp_q);
01242     qof_query_destroy (tmp_q);
01243 }

gint qof_query_num_terms ( QofQuery q  ) 

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

Definition at line 940 of file qofquery.c.

00941 {
00942     GList *o;
00943     gint n = 0;
00944     if (!q)
00945         return 0;
00946     for (o = q->terms; o; o = o->next)
00947         n += g_list_length (o->data);
00948     return n;
00949 }

void qof_query_purge_terms ( QofQuery q,
GSList *  param_list 
)

Remove query terms of a particular QofType from the query. The "type" of a term is determined by the QofType that gets passed to the predicate function. All query terms of this type are removed.

Definition at line 716 of file qofquery.c.

00717 {
00718     QofQueryTerm *qt;
00719     GList *or, *and;
00720 
00721     if (!q || !param_list)
00722         return;
00723 
00724     for (or = q->terms; or; or = or->next)
00725     {
00726         for (and = or->data; and; and = and->next)
00727         {
00728             qt = and->data;
00729             if (!param_list_cmp (qt->param_list, param_list))
00730             {
00731                 if (g_list_length (or->data) == 1)
00732                 {
00733                     q->terms = g_list_remove_link (q->terms, or);
00734                     g_list_free_1 (or);
00735                     or = q->terms;
00736                     break;
00737                 }
00738                 else
00739                 {
00740                     or->data = g_list_remove_link (or->data, and);
00741                     g_list_free_1 (and);
00742                     and = or->data;
00743                     if (!and)
00744                         break;
00745                 }
00746                 q->changed = 1;
00747                 free_query_term (qt);
00748             }
00749         }
00750         if (!or)
00751             break;
00752     }
00753 }

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

00757 {
00758     GList *matching_objects = NULL;
00759     GList *node;
00760     gint object_count = 0;
00761 
00762     if (!q)
00763         return NULL;
00764     g_return_val_if_fail (q->search_for, NULL);
00765     g_return_val_if_fail (q->books, NULL);
00766     ENTER (" q=%p", q);
00767 
00768     /* XXX: Prioritize the query terms? */
00769 
00770     /* prepare the Query for processing */
00771     if (q->changed)
00772     {
00773         query_clear_compiles (q);
00774         compile_terms (q);
00775     }
00776 
00777     /* Maybe log this sucker */
00778     if (qof_log_check (log_module, QOF_LOG_DETAIL))
00779         qof_query_print (q);
00780 
00781     /* Now run the query over all the objects and save the results */
00782     {
00783         QofQueryCB qcb;
00784 
00785         memset (&qcb, 0, sizeof (qcb));
00786         qcb.query = q;
00787 
00788         /* For each book */
00789         for (node = q->books; node; node = node->next)
00790         {
00791             QofBook *book = node->data;
00792             QofBackend *be = book->backend;
00793 
00794             /* run the query in the backend */
00795             if (be)
00796             {
00797                 gpointer compiled_query =
00798                     g_hash_table_lookup (q->be_compiled, book);
00799 
00800                 if (compiled_query && be->run_query)
00801                 {
00802                     (be->run_query) (be, compiled_query);
00803                 }
00804             }
00805 
00806             /* And then iterate over all the objects */
00807             qof_object_foreach (q->search_for, book,
00808                 (QofEntityForeachCB) check_item_cb, &qcb);
00809         }
00810 
00811         matching_objects = qcb.list;
00812         object_count = qcb.count;
00813     }
00814     PINFO ("matching objects=%p count=%d", matching_objects, object_count);
00815 
00816     /* There is no absolute need to reverse this list, since it's being
00817      * sorted below. However, in the common case, we will be searching
00818      * in a confined location where the objects are already in order,
00819      * thus reversing will put us in the correct order we want and make
00820      * the sorting go much faster.
00821      */
00822     matching_objects = g_list_reverse (matching_objects);
00823 
00824     /* Now sort the matching objects based on the search criteria
00825      * sortQuery is an unforgivable use of static global data...  
00826      * I just can't figure out how else to do this sanely.
00827      */
00828     if (q->primary_sort.comp_fcn || q->primary_sort.obj_cmp ||
00829         (q->primary_sort.use_default && q->defaultSort))
00830     {
00831         sortQuery = q;
00832         matching_objects = g_list_sort (matching_objects, sort_func);
00833         sortQuery = NULL;
00834     }
00835 
00836     /* Crop the list to limit the number of splits. */
00837     if ((object_count > q->max_results) && (q->max_results > -1))
00838     {
00839         if (q->max_results > 0)
00840         {
00841             GList *mptr;
00842 
00843             /* mptr is set to the first node of what will be the new list */
00844             mptr =
00845                 g_list_nth (matching_objects,
00846                 object_count - q->max_results);
00847             /* mptr should not be NULL, but let's be safe */
00848             if (mptr != NULL)
00849             {
00850                 if (mptr->prev != NULL)
00851                     mptr->prev->next = NULL;
00852                 mptr->prev = NULL;
00853             }
00854             g_list_free (matching_objects);
00855             matching_objects = mptr;
00856         }
00857         else
00858         {
00859             /* q->max_results == 0 */
00860             g_list_free (matching_objects);
00861             matching_objects = NULL;
00862         }
00863         object_count = q->max_results;
00864     }
00865 
00866     q->changed = 0;
00867 
00868     g_list_free (q->results);
00869     q->results = matching_objects;
00870 
00871     LEAVE (" q=%p", q);
00872     return matching_objects;
00873 }

void qof_query_search_for ( QofQuery query,
QofIdTypeConst  obj_type 
)

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

Definition at line 908 of file qofquery.c.

00909 {
00910     if (!q || !obj_type)
00911         return;
00912 
00913     if (safe_strcmp (q->search_for, obj_type))
00914     {
00915         q->search_for = (QofIdType) obj_type;
00916         q->changed = 1;
00917     }
00918 }

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

01335 {
01336     GSList *slist = NULL;
01337     if (!q || !book)
01338         return;
01339 
01340     /* Make sure this book is only in the list once */
01341     if (g_list_index (q->books, book) == -1)
01342         q->books = g_list_prepend (q->books, book);
01343 
01344     slist = g_slist_prepend (slist, QOF_PARAM_GUID);
01345     slist = g_slist_prepend (slist, QOF_PARAM_BOOK);
01346     qof_query_add_guid_match (q, slist,
01347         qof_entity_get_guid ((QofEntity*)book), QOF_QUERY_AND);
01348 }

void qof_query_set_max_results ( QofQuery q,
gint  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 1292 of file qofquery.c.

01293 {
01294     if (!q)
01295         return;
01296     q->max_results = n;
01297 }

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

01283 {
01284     if (!q)
01285         return;
01286     q->primary_sort.increasing = prim_inc;
01287     q->secondary_sort.increasing = sec_inc;
01288     q->tertiary_sort.increasing = tert_inc;
01289 }

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

01248 {
01249     if (!q)
01250         return;
01251     if (q->primary_sort.param_list)
01252         g_slist_free (q->primary_sort.param_list);
01253     q->primary_sort.param_list = params1;
01254     q->primary_sort.options = 0;
01255 
01256     if (q->secondary_sort.param_list)
01257         g_slist_free (q->secondary_sort.param_list);
01258     q->secondary_sort.param_list = params2;
01259     q->secondary_sort.options = 0;
01260 
01261     if (q->tertiary_sort.param_list)
01262         g_slist_free (q->tertiary_sort.param_list);
01263     q->tertiary_sort.param_list = params3;
01264     q->tertiary_sort.options = 0;
01265 
01266     q->changed = 1;
01267 }

gboolean qof_query_time_predicate_get_time ( QofQueryPredData pd,
QofTime qt 
)

Retrieve a predicate.

Definition at line 408 of file qofquerycore.c.

00408 {
00409     query_numeric_t pdata = (query_numeric_t) pd;
00410     gnc_numeric obj_val;
00411     int compare;
00412 
00413     VERIFY_PREDICATE (query_numeric_type);
00414 
00415     obj_val =
00416         ((query_numeric_getter) getter->param_getfcn) (object, getter);
00417 


Generated on Mon May 21 17:42:23 2007 for QOF by  doxygen 1.5.1