qofquery.c

00001 /********************************************************************\
00002  * qof_query.c -- Implement predicate API for searching for objects *
00003  * Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>                *
00004  * Copyright (C) 2006 Neil Williams <linux@codehelp.co.uk>          *
00005  *                                                                  *
00006  * This program is free software; you can redistribute it and/or    *
00007  * modify it under the terms of the GNU General Public License as   *
00008  * published by the Free Software Foundation; either version 2 of   *
00009  * the License, or (at your option) any later version.              *
00010  *                                                                  *
00011  * This program is distributed in the hope that it will be useful,  *
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00014  * GNU General Public License for more details.                     *
00015  *                                                                  *
00016  * You should have received a copy of the GNU General Public License*
00017  * along with this program; if not, contact:                        *
00018  *                                                                  *
00019  * Free Software Foundation           Voice:  +1-617-542-5942       *
00020  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00021  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00022  *                                                                  *
00023 \********************************************************************/
00024 
00025 #include "config.h"
00026 
00027 #include <sys/types.h>
00028 #include <time.h>
00029 #include <glib.h>
00030 #include <regex.h>
00031 #include <string.h>
00032 
00033 #include "qof.h"
00034 #include "qofbackend-p.h"
00035 #include "qofbook-p.h"
00036 #include "qofclass-p.h"
00037 #include "qofquery-p.h"
00038 #include "qofquerycore-p.h"
00039 
00040 static QofLogModule log_module = QOF_MOD_QUERY;
00041 
00042 struct _QofQueryTerm
00043 {
00044     GSList *param_list;
00045     QofQueryPredData *pdata;
00046     gboolean invert;
00047 
00048     /* These values are filled in during "compilation" of the query
00049      * term, based upon the obj_name, param_name, and searched-for
00050      * object type.  If conv_fcn is NULL, then we don't know how to
00051      * convert types.
00052      */
00053     GSList *param_fcns;
00054     QofQueryPredicateFunc pred_fcn;
00055 };
00056 
00057 struct _QofQuerySort
00058 {
00059     GSList *param_list;
00060     gint options;
00061     gboolean increasing;
00062 
00063     /* These values are filled in during "compilation" of the query
00064      * term, based upon the obj_name, param_name, and searched-for
00065      * object type.  If conv_fcn is NULL, then we don't know how to
00066      * convert types.
00067      */
00068     gboolean use_default;
00069     GSList *param_fcns;         /* Chain of paramters to walk */
00070     QofSortFunc obj_cmp;        /* In case you are comparing objects */
00071     QofCompareFunc comp_fcn;    /* When you are comparing core types */
00072 };
00073 
00074 /* The QUERY structure */
00075 struct _QofQuery
00076 {
00077     /* The object type that we're searching for */
00078     QofIdType search_for;
00079 
00080     /* terms is a list of the OR-terms in a sum-of-products 
00081      * logical expression. */
00082     GList *terms;
00083 
00084     /* sorting and chopping is independent of the search filter */
00085 
00086     QofQuerySort primary_sort;
00087     QofQuerySort secondary_sort;
00088     QofQuerySort tertiary_sort;
00089     QofSortFunc defaultSort;    /* <- Computed from search_for */
00090 
00091     /* The maximum number of results to return */
00092     gint max_results;
00093 
00094     /* list of books that will be participating in the query */
00095     GList *books;
00096 
00097     /* a map of book to backend-compiled queries */
00098     GHashTable *be_compiled;
00099 
00100     /* cache the results so we don't have to run the whole search 
00101      * again until it's really necessary */
00102     gint changed;
00103 
00104     GList *results;
00105 };
00106 
00107 typedef struct _QofQueryCB
00108 {
00109     QofQuery *query;
00110     GList *list;
00111     gint count;
00112 } QofQueryCB;
00113 
00114 /* initial_term will be owned by the new Query */
00115 static void
00116 query_init (QofQuery * q, QofQueryTerm * initial_term)
00117 {
00118     GList *or = NULL;
00119     GList *and = NULL;
00120     GHashTable *ht;
00121 
00122     if (initial_term)
00123     {
00124         or = g_list_alloc ();
00125         and = g_list_alloc ();
00126         and->data = initial_term;
00127         or->data = and;
00128     }
00129 
00130     if (q->terms)
00131         qof_query_clear (q);
00132 
00133     g_list_free (q->results);
00134     g_list_free (q->books);
00135 
00136     g_slist_free (q->primary_sort.param_list);
00137     g_slist_free (q->secondary_sort.param_list);
00138     g_slist_free (q->tertiary_sort.param_list);
00139 
00140     g_slist_free (q->primary_sort.param_fcns);
00141     g_slist_free (q->secondary_sort.param_fcns);
00142     g_slist_free (q->tertiary_sort.param_fcns);
00143 
00144     ht = q->be_compiled;
00145     memset (q, 0, sizeof (*q));
00146     q->be_compiled = ht;
00147 
00148     q->terms = or;
00149     q->changed = 1;
00150     q->max_results = -1;
00151 
00152     q->primary_sort.param_list =
00153         g_slist_prepend (NULL, QUERY_DEFAULT_SORT);
00154     q->primary_sort.increasing = TRUE;
00155     q->secondary_sort.increasing = TRUE;
00156     q->tertiary_sort.increasing = TRUE;
00157 }
00158 
00159 static void
00160 swap_terms (QofQuery * q1, QofQuery * q2)
00161 {
00162     GList *g;
00163 
00164     if (!q1 || !q2)
00165         return;
00166 
00167     g = q1->terms;
00168     q1->terms = q2->terms;
00169     q2->terms = g;
00170 
00171     g = q1->books;
00172     q1->books = q2->books;
00173     q2->books = g;
00174 
00175     q1->changed = 1;
00176     q2->changed = 1;
00177 }
00178 
00179 static void
00180 free_query_term (QofQueryTerm * qt)
00181 {
00182     if (!qt)
00183         return;
00184 
00185     qof_query_core_predicate_free (qt->pdata);
00186     g_slist_free (qt->param_list);
00187     g_slist_free (qt->param_fcns);
00188     g_free (qt);
00189 }
00190 
00191 static QofQueryTerm *
00192 copy_query_term (QofQueryTerm * qt)
00193 {
00194     QofQueryTerm *new_qt;
00195     if (!qt)
00196         return NULL;
00197 
00198     new_qt = g_new0 (QofQueryTerm, 1);
00199     memcpy (new_qt, qt, sizeof (QofQueryTerm));
00200     new_qt->param_list = g_slist_copy (qt->param_list);
00201     new_qt->param_fcns = g_slist_copy (qt->param_fcns);
00202     new_qt->pdata = qof_query_core_predicate_copy (qt->pdata);
00203     return new_qt;
00204 }
00205 
00206 static GList *
00207 copy_and_terms (GList * and_terms)
00208 {
00209     GList *and = NULL;
00210     GList *cur_and;
00211 
00212     for (cur_and = and_terms; cur_and; cur_and = cur_and->next)
00213     {
00214         and = g_list_prepend (and, copy_query_term (cur_and->data));
00215     }
00216 
00217     return g_list_reverse (and);
00218 }
00219 
00220 static GList *
00221 copy_or_terms (GList * or_terms)
00222 {
00223     GList *or = NULL;
00224     GList *cur_or;
00225 
00226     for (cur_or = or_terms; cur_or; cur_or = cur_or->next)
00227     {
00228         or = g_list_prepend (or, copy_and_terms (cur_or->data));
00229     }
00230 
00231     return g_list_reverse (or);
00232 }
00233 
00234 static void
00235 copy_sort (QofQuerySort * dst, const QofQuerySort * src)
00236 {
00237     memcpy (dst, src, sizeof (*dst));
00238     dst->param_list = g_slist_copy (src->param_list);
00239     dst->param_fcns = g_slist_copy (src->param_fcns);
00240 }
00241 
00242 static void
00243 free_sort (QofQuerySort * s)
00244 {
00245     g_slist_free (s->param_list);
00246     s->param_list = NULL;
00247 
00248     g_slist_free (s->param_fcns);
00249     s->param_fcns = NULL;
00250 }
00251 
00252 static void
00253 free_members (QofQuery * q)
00254 {
00255     GList *cur_or;
00256 
00257     if (q == NULL)
00258         return;
00259 
00260     for (cur_or = q->terms; cur_or; cur_or = cur_or->next)
00261     {
00262         GList *cur_and;
00263 
00264         for (cur_and = cur_or->data; cur_and; cur_and = cur_and->next)
00265         {
00266             free_query_term (cur_and->data);
00267             cur_and->data = NULL;
00268         }
00269 
00270         g_list_free (cur_or->data);
00271         cur_or->data = NULL;
00272     }
00273 
00274     free_sort (&(q->primary_sort));
00275     free_sort (&(q->secondary_sort));
00276     free_sort (&(q->tertiary_sort));
00277 
00278     g_list_free (q->terms);
00279     q->terms = NULL;
00280 
00281     g_list_free (q->books);
00282     q->books = NULL;
00283 
00284     g_list_free (q->results);
00285     q->results = NULL;
00286 }
00287 
00288 static gint
00289 cmp_func (QofQuerySort * sort, QofSortFunc default_sort,
00290     gconstpointer a, gconstpointer b)
00291 {
00292     QofParam *param = NULL;
00293     GSList *node;
00294     gpointer conva, convb;
00295 
00296     g_return_val_if_fail (sort, 0);
00297 
00298     /* See if this is a default sort */
00299     if (sort->use_default)
00300     {
00301         if (default_sort)
00302             return default_sort (a, b);
00303         return 0;
00304     }
00305 
00306     /* If no parameters, consider them equal */
00307     if (!sort->param_fcns)
00308         return 0;
00309 
00310     /* no compare function, consider the two objects equal */
00311     if (!sort->comp_fcn && !sort->obj_cmp)
00312         return 0;
00313 
00314     /* Do the list of conversions */
00315     conva = (gpointer) a;
00316     convb = (gpointer) b;
00317     for (node = sort->param_fcns; node; node = node->next)
00318     {
00319         param = node->data;
00320 
00321         /* The last term is really the "parameter getter",
00322          * unless we're comparing objects ;) */
00323         if (!node->next && !sort->obj_cmp)
00324             break;
00325 
00326         /* Do the converstions */
00327         conva = (param->param_getfcn) (conva, param);
00328         convb = (param->param_getfcn) (convb, param);
00329     }
00330 
00331     /* And now return the (appropriate) compare */
00332     if (sort->comp_fcn)
00333     {
00334         gint rc = sort->comp_fcn (conva, convb, sort->options, param);
00335         return rc;
00336     }
00337 
00338     return sort->obj_cmp (conva, convb);
00339 }
00340 
00341 static QofQuery *sortQuery = NULL;
00342 
00343 static gint
00344 sort_func (gconstpointer a, gconstpointer b)
00345 {
00346     gint retval;
00347 
00348     g_return_val_if_fail (sortQuery, 0);
00349 
00350     retval =
00351         cmp_func (&(sortQuery->primary_sort), sortQuery->defaultSort, a,
00352         b);
00353     if (retval == 0)
00354     {
00355         retval =
00356             cmp_func (&(sortQuery->secondary_sort), 
00357             sortQuery->defaultSort,
00358             a, b);
00359         if (retval == 0)
00360         {
00361             retval =
00362                 cmp_func (&(sortQuery->tertiary_sort),
00363                 sortQuery->defaultSort, a, b);
00364             return sortQuery->tertiary_sort.increasing ? 
00365                 retval : -retval;
00366         }
00367         else
00368         {
00369             return sortQuery->secondary_sort.increasing ? 
00370                 retval : -retval;
00371         }
00372     }
00373     else
00374     {
00375         return sortQuery->primary_sort.increasing ? retval : -retval;
00376     }
00377 }
00378 
00379 /* ==================================================================== */
00380 /* This is the main workhorse for performing the query.  For each
00381  * object, it walks over all of the query terms to see if the 
00382  * object passes the seive.
00383  */
00384 
00385 static gint
00386 check_object (QofQuery * q, gpointer object)
00387 {
00388     GList *and_ptr;
00389     GList *or_ptr;
00390     QofQueryTerm *qt;
00391     gint and_terms_ok = 1;
00392 
00393     for (or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next)
00394     {
00395         and_terms_ok = 1;
00396         for (and_ptr = or_ptr->data; and_ptr; and_ptr = and_ptr->next)
00397         {
00398             qt = (QofQueryTerm *) (and_ptr->data);
00399             if (qt->param_fcns && qt->pred_fcn)
00400             {
00401                 GSList *node;
00402                 QofParam *param = NULL;
00403                 gpointer conv_obj = object;
00404 
00405                 /* iterate through the conversions */
00406                 for (node = qt->param_fcns; node; node = node->next)
00407                 {
00408                     param = node->data;
00409 
00410                     /* The last term is the actual parameter getter */
00411                     if (!node->next)
00412                         break;
00413 
00414                     conv_obj = param->param_getfcn (conv_obj, param);
00415                 }
00416 
00417                 if (((qt->pred_fcn) (conv_obj, param,
00418                             qt->pdata)) == qt->invert)
00419                 {
00420                     and_terms_ok = 0;
00421                     break;
00422                 }
00423             }
00424             else
00425             {
00426                 /* XXX: Don't know how to do this conversion -- do we care? */
00427             }
00428         }
00429         if (and_terms_ok)
00430         {
00431             return 1;
00432         }
00433     }
00434 
00435     /* If there are no terms, assume a "match any" applies.
00436      * A query with no terms is still meaningful, since the user
00437      * may want to get all objects, but in a particular sorted 
00438      * order.
00439      */
00440     if (NULL == q->terms)
00441         return 1;
00442     return 0;
00443 }
00444 
00445 /* walk the list of parameters, starting with the given object, and
00446  * compile the list of parameter get-functions.  Save the last valid
00447  * parameter definition in "final" and return the list of functions.
00448  *
00449  * returns NULL if the first parameter is bad (and final is unchanged).
00450  */
00451 static GSList *
00452 compile_params (GSList * param_list, QofIdType start_obj,
00453     QofParam const **final)
00454 {
00455     const QofParam *objDef = NULL;
00456     GSList *fcns = NULL;
00457 
00458     ENTER ("param_list=%p id=%s", param_list, start_obj);
00459     g_return_val_if_fail (param_list, NULL);
00460     g_return_val_if_fail (start_obj, NULL);
00461     g_return_val_if_fail (final, NULL);
00462 
00463     for (; param_list; param_list = param_list->next)
00464     {
00465         QofIdType param_name = param_list->data;
00466         objDef = qof_class_get_parameter (start_obj, param_name);
00467 
00468         /* If it doesn't exist, then we've reached the end */
00469         if (!objDef)
00470             break;
00471 
00472         /* Save off this parameter */
00473         fcns = g_slist_prepend (fcns, (gpointer) objDef);
00474 
00475         /* Save this off, just in case */
00476         *final = objDef;
00477 
00478         /* And reset for the next parameter */
00479         start_obj = (QofIdType) objDef->param_type;
00480     }
00481 
00482     LEAVE ("fcns=%p", fcns);
00483     return (g_slist_reverse (fcns));
00484 }
00485 
00486 static void
00487 compile_sort (QofQuerySort * sort, QofIdType obj)
00488 {
00489     const QofParam *resObj = NULL;
00490 
00491     ENTER ("sort=%p id=%s params=%p", sort, obj, sort->param_list);
00492     sort->use_default = FALSE;
00493 
00494     g_slist_free (sort->param_fcns);
00495     sort->param_fcns = NULL;
00496     sort->comp_fcn = NULL;
00497     sort->obj_cmp = NULL;
00498 
00499     /* An empty param_list implies "no sort" */
00500     if (!sort->param_list)
00501     {
00502         LEAVE (" ");
00503         return;
00504     }
00505 
00506     /* Walk the parameter list of obtain the parameter functions */
00507     sort->param_fcns = compile_params (sort->param_list, obj, &resObj);
00508 
00509     /* If we have valid parameters, grab the compare function,
00510      * If not, check if this is the default sort.
00511      */
00512     if (sort->param_fcns)
00513     {
00514         sort->comp_fcn = qof_query_core_get_compare (resObj->param_type);
00515 
00516         /* Hrm, perhaps this is an object compare, not a core compare? */
00517         if (sort->comp_fcn == NULL)
00518         {
00519             sort->obj_cmp =
00520                 qof_class_get_default_sort (resObj->param_type);
00521         }
00522     }
00523     else if (!safe_strcmp (sort->param_list->data, QUERY_DEFAULT_SORT))
00524     {
00525         sort->use_default = TRUE;
00526     }
00527     LEAVE ("sort=%p id=%s", sort, obj);
00528 }
00529 
00530 static void
00531 compile_terms (QofQuery * q)
00532 {
00533     GList *or_ptr, *and_ptr, *node;
00534 
00535     ENTER (" query=%p", q);
00536     /* Find the specific functions for this Query.  Note that the
00537      * Query's search_for should now be set to the new type.
00538      */
00539     for (or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next)
00540     {
00541         for (and_ptr = or_ptr->data; and_ptr; and_ptr = and_ptr->next)
00542         {
00543             QofQueryTerm *qt = and_ptr->data;
00544             const QofParam *resObj = NULL;
00545 
00546             g_slist_free (qt->param_fcns);
00547             qt->param_fcns = NULL;
00548 
00549             /* Walk the parameter list of obtain the parameter functions */
00550             qt->param_fcns = compile_params (qt->param_list, q->search_for,
00551                 &resObj);
00552 
00553             /* If we have valid parameters, grab the predicate function,
00554              * If not, see if this is the default sort.
00555              */
00556 
00557             if (qt->param_fcns)
00558                 qt->pred_fcn =
00559                     qof_query_core_get_predicate (resObj->param_type);
00560             else
00561                 qt->pred_fcn = NULL;
00562         }
00563     }
00564 
00565     /* Update the sort functions */
00566     compile_sort (&(q->primary_sort), q->search_for);
00567     compile_sort (&(q->secondary_sort), q->search_for);
00568     compile_sort (&(q->tertiary_sort), q->search_for);
00569 
00570     q->defaultSort = qof_class_get_default_sort (q->search_for);
00571 
00572     /* Now compile the backend instances */
00573     for (node = q->books; node; node = node->next)
00574     {
00575         QofBook *book = node->data;
00576         QofBackend *be = book->backend;
00577 
00578         if (be && be->compile_query)
00579         {
00580             gpointer result = (be->compile_query) (be, q);
00581             if (result)
00582                 g_hash_table_insert (q->be_compiled, book, result);
00583         }
00584     }
00585     LEAVE (" query=%p", q);
00586 }
00587 
00588 static void
00589 check_item_cb (gpointer object, gpointer user_data)
00590 {
00591     QofQueryCB *ql = user_data;
00592 
00593     if (!object || !ql)
00594         return;
00595 
00596     if (check_object (ql->query, object))
00597     {
00598         ql->list = g_list_prepend (ql->list, object);
00599         ql->count++;
00600     }
00601     return;
00602 }
00603 
00604 static int
00605 param_list_cmp (GSList * l1, GSList * l2)
00606 {
00607     while (1)
00608     {
00609         int ret;
00610 
00611         /* Check the easy stuff */
00612         if (!l1 && !l2)
00613             return 0;
00614         if (!l1 && l2)
00615             return -1;
00616         if (l1 && !l2)
00617             return 1;
00618 
00619         ret = safe_strcmp (l1->data, l2->data);
00620         if (ret)
00621             return ret;
00622 
00623         l1 = l1->next;
00624         l2 = l2->next;
00625     }
00626 }
00627 
00628 static GList *
00629 merge_books (GList * l1, GList * l2)
00630 {
00631     GList *res = NULL;
00632     GList *node;
00633 
00634     res = g_list_copy (l1);
00635 
00636     for (node = l2; node; node = node->next)
00637     {
00638         if (g_list_index (res, node->data) == -1)
00639             res = g_list_prepend (res, node->data);
00640     }
00641 
00642     return res;
00643 }
00644 
00645 static gboolean
00646 query_free_compiled (gpointer key, gpointer value, gpointer not_used)
00647 {
00648     QofBook *book = key;
00649     QofBackend *be = book->backend;
00650 
00651     if (be && be->free_query)
00652         (be->free_query) (be, value);
00653 
00654     return TRUE;
00655 }
00656 
00657 /* clear out any cached query_compilations */
00658 static void
00659 query_clear_compiles (QofQuery * q)
00660 {
00661     g_hash_table_foreach_remove (q->be_compiled, query_free_compiled,
00662         NULL);
00663 }
00664 
00665 /********************************************************************/
00666 /* PUBLISHED API FUNCTIONS */
00667 
00668 GSList *
00669 qof_query_build_param_list (gchar const *param, ...)
00670 {
00671     GSList *param_list = NULL;
00672     gchar const *this_param;
00673     va_list ap;
00674 
00675     if (!param)
00676         return NULL;
00677 
00678     va_start (ap, param);
00679 
00680     for (this_param = param; this_param;
00681         this_param = va_arg (ap, const gchar *))
00682           param_list = g_slist_prepend (param_list, (gpointer) this_param);
00683 
00684     va_end (ap);
00685 
00686     return g_slist_reverse (param_list);
00687 }
00688 
00689 void
00690 qof_query_add_term (QofQuery * q, GSList * param_list,
00691     QofQueryPredData * pred_data, QofQueryOp op)
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 }
00714 
00715 void
00716 qof_query_purge_terms (QofQuery * q, GSList * param_list)
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 }
00754 
00755 GList *
00756 qof_query_run (QofQuery * q)
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 }
00874 
00875 GList *
00876 qof_query_last_run (QofQuery * query)
00877 {
00878     if (!query)
00879         return NULL;
00880 
00881     return query->results;
00882 }
00883 
00884 void
00885 qof_query_clear (QofQuery * query)
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 }
00897 
00898 QofQuery *
00899 qof_query_create (void)
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 }
00906 
00907 void
00908 qof_query_search_for (QofQuery * q, QofIdTypeConst obj_type)
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 }
00919 
00920 QofQuery *
00921 qof_query_create_for (QofIdTypeConst obj_type)
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 }
00930 
00931 gint
00932 qof_query_has_terms (QofQuery * q)
00933 {
00934     if (!q)
00935         return 0;
00936     return g_list_length (q->terms);
00937 }
00938 
00939 gint
00940 qof_query_num_terms (QofQuery * q)
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 }
00950 
00951 gboolean
00952 qof_query_has_term_type (QofQuery * q, GSList * term_param)
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 }
00972 
00973 GSList *
00974 qof_query_get_term_type (QofQuery * q, GSList * term_param)
00975 {
00976     GList *or;
00977     GList *and;
00978     GSList *results = NULL;
00979 
00980     if (!q || !term_param)
00981         return FALSE;
00982 
00983     for (or = q->terms; or; or = or->next)
00984     {
00985         for (and = or->data; and; and = and->next)
00986         {
00987             QofQueryTerm *qt = and->data;
00988             if (!param_list_cmp (term_param, qt->param_list))
00989                 results = g_slist_append (results, qt->pdata);
00990         }
00991     }
00992 
00993     return results;
00994 }
00995 
00996 void
00997 qof_query_destroy (QofQuery * q)
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 }
01006 
01007 QofQuery *
01008 qof_query_copy (QofQuery * q)
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 }
01034 
01035 /* *******************************************************************
01036  * qof_query_invert 
01037  * return a newly-allocated Query object which is the 
01038  * logical inverse of the original.
01039  ********************************************************************/
01040 
01041 QofQuery *
01042 qof_query_invert (QofQuery * q)
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 }
01119 
01120 /* *******************************************************************
01121  * qof_query_merge
01122  * combine 2 Query objects by the logical operation in "op".
01123  ********************************************************************/
01124 
01125 QofQuery *
01126 qof_query_merge (QofQuery * q1, QofQuery * q2, QofQueryOp op)
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 }
01231 
01232 void
01233 qof_query_merge_in_place (QofQuery * q1, QofQuery * q2, QofQueryOp op)
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 }
01244 
01245 void
01246 qof_query_set_sort_order (QofQuery * q,
01247     GSList * params1, GSList * params2, GSList * params3)
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 }
01268 
01269 void
01270 qof_query_set_sort_options (QofQuery * q, gint prim_op, gint sec_op,
01271     gint tert_op)
01272 {
01273     if (!q)
01274         return;
01275     q->primary_sort.options = prim_op;
01276     q->secondary_sort.options = sec_op;
01277     q->tertiary_sort.options = tert_op;
01278 }
01279 
01280 void
01281 qof_query_set_sort_increasing (QofQuery * q, gboolean prim_inc,
01282     gboolean sec_inc, gboolean tert_inc)
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 }
01290 
01291 void
01292 qof_query_set_max_results (QofQuery * q, gint n)
01293 {
01294     if (!q)
01295         return;
01296     q->max_results = n;
01297 }
01298 
01299 void
01300 qof_query_add_guid_list_match (QofQuery * q, GSList * param_list,
01301     GList * guid_list, QofGuidMatch options, QofQueryOp op)
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 }
01314 
01315 void
01316 qof_query_add_guid_match (QofQuery * q, GSList * param_list,
01317     const GUID * guid, QofQueryOp op)
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 }
01332 
01333 void
01334 qof_query_set_book (QofQuery * q, QofBook * book)
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 }
01349 
01350 GList *
01351 qof_query_get_books (QofQuery * q)
01352 {
01353     if (!q)
01354         return NULL;
01355     return q->books;
01356 }
01357 
01358 void
01359 qof_query_add_boolean_match (QofQuery * q, GSList * param_list,
01360     gboolean value, QofQueryOp op)
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 }
01369 
01370 /**********************************************************************/
01371 /* PRIVATE PUBLISHED API FUNCTIONS                                    */
01372 
01373 void
01374 qof_query_init (void)
01375 {
01376     ENTER (" ");
01377     qof_query_core_init ();
01378     qof_class_init ();
01379     LEAVE ("Completed initialization of QofQuery");
01380 }
01381 
01382 void
01383 qof_query_shutdown (void)
01384 {
01385     qof_class_shutdown ();
01386     qof_query_core_shutdown ();
01387 }
01388 
01389 gint
01390 qof_query_get_max_results (QofQuery * q)
01391 {
01392     if (!q)
01393         return 0;
01394     return q->max_results;
01395 }
01396 
01397 QofIdType
01398 qof_query_get_search_for (QofQuery * q)
01399 {
01400     if (!q)
01401         return NULL;
01402     return q->search_for;
01403 }
01404 
01405 GList *
01406 qof_query_get_terms (QofQuery * q)
01407 {
01408     if (!q)
01409         return NULL;
01410     return q->terms;
01411 }
01412 
01413 GSList *
01414 qof_query_term_get_param_path (QofQueryTerm * qt)
01415 {
01416     if (!qt)
01417         return NULL;
01418     return qt->param_list;
01419 }
01420 
01421 QofQueryPredData *
01422 qof_query_term_get_pred_data (QofQueryTerm * qt)
01423 {
01424     if (!qt)
01425         return NULL;
01426     return qt->pdata;
01427 }
01428 
01429 gboolean
01430 qof_query_term_is_inverted (QofQueryTerm * qt)
01431 {
01432     if (!qt)
01433         return FALSE;
01434     return qt->invert;
01435 }
01436 
01437 void
01438 qof_query_get_sorts (QofQuery * q, QofQuerySort ** primary,
01439     QofQuerySort ** secondary, QofQuerySort ** tertiary)
01440 {
01441     if (!q)
01442         return;
01443     if (primary)
01444         *primary = &(q->primary_sort);
01445     if (secondary)
01446         *secondary = &(q->secondary_sort);
01447     if (tertiary)
01448         *tertiary = &(q->tertiary_sort);
01449 }
01450 
01451 GSList *
01452 qof_query_sort_get_param_path (QofQuerySort * qs)
01453 {
01454     if (!qs)
01455         return NULL;
01456     return qs->param_list;
01457 }
01458 
01459 gint
01460 qof_query_sort_get_sort_options (QofQuerySort * qs)
01461 {
01462     if (!qs)
01463         return 0;
01464     return qs->options;
01465 }
01466 
01467 gboolean
01468 qof_query_sort_get_increasing (QofQuerySort * qs)
01469 {
01470     if (!qs)
01471         return FALSE;
01472     return qs->increasing;
01473 }
01474 
01475 static gboolean
01476 qof_query_term_equal (QofQueryTerm * qt1, QofQueryTerm * qt2)
01477 {
01478     if (qt1 == qt2)
01479         return TRUE;
01480     if (!qt1 || !qt2)
01481         return FALSE;
01482 
01483     if (qt1->invert != qt2->invert)
01484         return FALSE;
01485     if (param_list_cmp (qt1->param_list, qt2->param_list))
01486         return FALSE;
01487     return qof_query_core_predicate_equal (qt1->pdata, qt2->pdata);
01488 }
01489 
01490 static gboolean
01491 qof_query_sort_equal (QofQuerySort * qs1, QofQuerySort * qs2)
01492 {
01493     if (qs1 == qs2)
01494         return TRUE;
01495     if (!qs1 || !qs2)
01496         return FALSE;
01497 
01498     /* "Empty" sorts are equivalent, regardless of the flags */
01499     if (!qs1->param_list && !qs2->param_list)
01500         return TRUE;
01501 
01502     if (qs1->options != qs2->options)
01503         return FALSE;
01504     if (qs1->increasing != qs2->increasing)
01505         return FALSE;
01506     return (param_list_cmp (qs1->param_list, qs2->param_list) == 0);
01507 }
01508 
01509 gboolean
01510 qof_query_equal (QofQuery * q1, QofQuery * q2)
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 }
01550 
01551 /* **************************************************************************/
01552 /* Query Print functions for use with qof_log_set_level.
01553 */
01554 
01555 /* Static prototypes */
01556 static GList *qof_query_printSearchFor (QofQuery * query, GList * output);
01557 static GList *qof_query_printTerms (QofQuery * query, GList * output);
01558 static GList *qof_query_printSorts (QofQuerySort * s[],
01559     const gint numSorts, GList * output);
01560 static GList *qof_query_printAndTerms (GList * terms, GList * output);
01561 static gchar *qof_query_printStringForHow (QofQueryCompare how);
01562 static gchar *qof_query_printStringMatch (QofStringMatch s);
01563 static gchar *qof_query_printDateMatch (QofDateMatch d);
01564 static gchar *qof_query_printNumericMatch (QofNumericMatch n);
01565 static gchar *qof_query_printGuidMatch (QofGuidMatch g);
01566 static gchar *qof_query_printCharMatch (QofCharMatch c);
01567 static GList *qof_query_printPredData (QofQueryPredData * pd, GList * lst);
01568 static GString *qof_query_printParamPath (GSList * parmList);
01569 static void qof_query_printValueForParam (QofQueryPredData * pd,
01570     GString * gs);
01571 static void qof_query_printOutput (GList * output);
01572 
01574 void
01575 qof_query_print (QofQuery * query)
01576 {
01577     GList *output;
01578     GString *str;
01579     QofQuerySort *s[3];
01580     gint maxResults = 0, numSorts = 3;
01581 
01582     ENTER (" ");
01583 
01584     if (!query)
01585     {
01586         LEAVE ("query is (null)");
01587         return;
01588     }
01589 
01590     output = NULL;
01591     str = NULL;
01592     maxResults = qof_query_get_max_results (query);
01593 
01594     output = qof_query_printSearchFor (query, output);
01595     output = qof_query_printTerms (query, output);
01596 
01597     qof_query_get_sorts (query, &s[0], &s[1], &s[2]);
01598 
01599     if (s[0])
01600     {
01601         output = qof_query_printSorts (s, numSorts, output);
01602     }
01603 
01604     str = g_string_new (" ");
01605     g_string_printf (str, "Maximum number of results: %d", maxResults);
01606     output = g_list_append (output, str);
01607 
01608     qof_query_printOutput (output);
01609     LEAVE (" ");
01610 }
01611 
01612 static void
01613 qof_query_printOutput (GList * output)
01614 {
01615     GList *lst;
01616 
01617     for (lst = output; lst; lst = lst->next)
01618     {
01619         GString *line = (GString *) lst->data;
01620 
01621         DEBUG (" %s", line->str);
01622         g_string_free (line, TRUE);
01623         line = NULL;
01624     }
01625 }
01626 
01627 /*
01628     Get the search_for type--This is the type of Object
01629     we are searching for (SPLIT, TRANS, etc)
01630 */
01631 static GList *
01632 qof_query_printSearchFor (QofQuery * query, GList * output)
01633 {
01634     QofIdType searchFor;
01635     GString *gs;
01636 
01637     searchFor = qof_query_get_search_for (query);
01638     gs = g_string_new ("Query Object Type: ");
01639     g_string_append (gs, (NULL == searchFor) ? "(null)" : searchFor);
01640     output = g_list_append (output, gs);
01641 
01642     return output;
01643 }   /* qof_query_printSearchFor */
01644 
01645 /*
01646     Run through the terms of the query.  This is a outer-inner
01647     loop.  The elements of the outer loop are ORed, and the
01648     elements of the inner loop are ANDed.
01649 */
01650 static GList *
01651 qof_query_printTerms (QofQuery * query, GList * output)
01652 {
01653 
01654     GList *terms, *lst;
01655 
01656     terms = qof_query_get_terms (query);
01657 
01658     for (lst = terms; lst; lst = lst->next)
01659     {
01660         output =
01661             g_list_append (output, g_string_new ("OR Terms:"));
01662 
01663         if (lst->data)
01664         {
01665             output = qof_query_printAndTerms (lst->data, output);
01666         }
01667         else
01668         {
01669             output =
01670                 g_list_append (output,
01671                 g_string_new ("  No data for AND terms"));
01672         }
01673     }
01674 
01675     return output;
01676 }   /* qof_query_printTerms */
01677 
01678 /*
01679     Process the sort parameters
01680     If this function is called, the assumption is that the first sort
01681     not null.
01682 */
01683 static GList *
01684 qof_query_printSorts (QofQuerySort * s[], const gint numSorts,
01685     GList * output)
01686 {
01687     GSList *gsl, *n = NULL;
01688     gint curSort;
01689     GString *gs = g_string_new ("Sort Parameters:   ");
01690 
01691     for (curSort = 0; curSort < numSorts; curSort++)
01692     {
01693         gboolean increasing;
01694         if (!s[curSort])
01695         {
01696             break;
01697         }
01698         increasing = qof_query_sort_get_increasing (s[curSort]);
01699 
01700         gsl = qof_query_sort_get_param_path (s[curSort]);
01701         if (gsl)
01702             g_string_append_printf (gs, " Param: ");
01703         for (n = gsl; n; n = n->next)
01704         {
01705             QofIdType param_name = n->data;
01706             if (gsl != n)
01707                 g_string_append_printf (gs, " ");
01708             g_string_append_printf (gs, "%s", param_name);
01709         }
01710         if (gsl)
01711         {
01712             g_string_append_printf (gs, " %s ",
01713                 increasing ? "DESC" : "ASC");
01714             g_string_append_printf (gs, " Options: 0x%x ",
01715                 s[curSort]->options);
01716         }
01717     }
01718 
01719     output = g_list_append (output, gs);
01720     return output;
01721 
01722 }       /* qof_query_printSorts */
01723 
01724 /*
01725     Process the AND terms of the query.  This is a GList
01726     of WHERE terms that will be ANDed
01727 */
01728 static GList *
01729 qof_query_printAndTerms (GList * terms, GList * output)
01730 {
01731     const gchar *prefix = "AND Terms:";
01732     QofQueryTerm *qt;
01733     QofQueryPredData *pd;
01734     GSList *path;
01735     GList *lst;
01736     gboolean invert;
01737 
01738     output = g_list_append (output, g_string_new (prefix));
01739     for (lst = terms; lst; lst = lst->next)
01740     {
01741         qt = (QofQueryTerm *) lst->data;
01742         pd = qof_query_term_get_pred_data (qt);
01743         path = qof_query_term_get_param_path (qt);
01744         invert = qof_query_term_is_inverted (qt);
01745 
01746         if (invert)
01747             output =
01748                 g_list_append (output, g_string_new (" INVERT SENSE "));
01749         output = g_list_append (output, qof_query_printParamPath (path));
01750         output = qof_query_printPredData (pd, output);
01751 //    output = g_list_append (output, g_string_new(" "));
01752     }
01753 
01754     return output;
01755 }   /* qof_query_printAndTerms */
01756 
01757 /*
01758     Process the parameter types of the predicate data
01759 */
01760 static GString *
01761 qof_query_printParamPath (GSList * parmList)
01762 {
01763     GSList *list = NULL;
01764     GString *gs = g_string_new ("Param List: ");
01765     g_string_append (gs, " ");
01766     for (list = parmList; list; list = list->next)
01767     {
01768         g_string_append (gs, (gchar *) list->data);
01769         if (list->next)
01770             g_string_append (gs, ", ");
01771     }
01772 
01773     return gs;
01774 }   /* qof_query_printParamPath */
01775 
01776 /*
01777     Process the PredData of the AND terms
01778 */
01779 static GList *
01780 qof_query_printPredData (QofQueryPredData * pd, GList * lst)
01781 {
01782     GString *gs;
01783 
01784     gs = g_string_new ("Pred Data: ");
01785     g_string_append (gs, (gchar *) pd->type_name);
01786 
01787     /* Char Predicate and GUID predicate don't use the 'how' field. */
01788     if (safe_strcmp (pd->type_name, QOF_TYPE_CHAR) &&
01789         safe_strcmp (pd->type_name, QOF_TYPE_GUID))
01790     {
01791         g_string_append_printf (gs, " how: %s",
01792             qof_query_printStringForHow (pd->how));
01793     }
01794     lst = g_list_append (lst, gs);
01795     gs = g_string_new ("");
01796     qof_query_printValueForParam (pd, gs);
01797     lst = g_list_append (lst, gs);
01798     return lst;
01799 }   /* qof_query_printPredData */
01800 
01801 /*
01802     Get a string representation for the
01803     QofCompareFunc enum type.
01804 */
01805 static gchar *
01806 qof_query_printStringForHow (QofQueryCompare how)
01807 {
01808 
01809     switch (how)
01810     {
01811         AS_STRING_CASE(QOF_COMPARE_LT,)
01812         AS_STRING_CASE(QOF_COMPARE_LTE,)
01813         AS_STRING_CASE(QOF_COMPARE_EQUAL,)
01814         AS_STRING_CASE(QOF_COMPARE_GT,)
01815         AS_STRING_CASE(QOF_COMPARE_GTE,)
01816         AS_STRING_CASE(QOF_COMPARE_NEQ,)
01817     }
01818     return "INVALID HOW";
01819 }   /* qncQueryPrintStringForHow */
01820 
01821 
01822 static void
01823 qof_query_printValueForParam (QofQueryPredData * pd, GString * gs)
01824 {
01825 
01826     if (!safe_strcmp (pd->type_name, QOF_TYPE_GUID))
01827     {
01828         GList *node;
01829         query_guid_t pdata = (query_guid_t) pd;
01830         g_string_append_printf (gs, "Match type %s ",
01831             qof_query_printGuidMatch (pdata->options));
01832         for (node = pdata->guids; node; node = node->next)
01833         {
01834             /* THREAD-UNSAFE */
01835             g_string_append_printf (gs, ", guids: %s",
01836                 guid_to_string ((GUID *) node->data));
01837         }
01838         return;
01839     }
01840     if (!safe_strcmp (pd->type_name, QOF_TYPE_STRING))
01841     {
01842         query_string_t pdata = (query_string_t) pd;
01843         g_string_append_printf (gs, "Match type %s ",
01844             qof_query_printStringMatch (pdata->options));
01845         g_string_append_printf (gs, " %s string: %s",
01846             pdata->is_regex ? "Regex" : "Not regex", pdata->matchstring);
01847         return;
01848     }
01849     if (!safe_strcmp (pd->type_name, QOF_TYPE_NUMERIC))
01850     {
01851         query_numeric_t pdata = (query_numeric_t) pd;
01852         g_string_append_printf (gs, "Match type %s ",
01853             qof_query_printNumericMatch (pdata->options));
01854         g_string_append_printf (gs, " gnc_numeric: %s",
01855             gnc_num_dbg_to_string (pdata->amount));
01856         return;
01857     }
01858     if (!safe_strcmp (pd->type_name, QOF_TYPE_KVP))
01859     {
01860         GSList *node;
01861         query_kvp_t pdata = (query_kvp_t) pd;
01862         g_string_append_printf (gs, " kvp path: ");
01863         for (node = pdata->path; node; node = node->next)
01864         {
01865             g_string_append_printf (gs, "/%s", (gchar *) node->data);
01866         }
01867         g_string_append_printf (gs, " kvp value: %s ",
01868             kvp_value_to_string (pdata->value));
01869         return;
01870     }
01871     if (!safe_strcmp (pd->type_name, QOF_TYPE_INT64))
01872     {
01873         query_int64_t pdata = (query_int64_t) pd;
01874         g_string_append_printf (gs, " int64: %" G_GINT64_FORMAT,
01875             pdata->val);
01876         return;
01877     }
01878     if (!safe_strcmp (pd->type_name, QOF_TYPE_INT32))
01879     {
01880         query_int32_t pdata = (query_int32_t) pd;
01881         g_string_append_printf (gs, " int32: %d", pdata->val);
01882         return;
01883     }
01884     if (!safe_strcmp (pd->type_name, QOF_TYPE_DOUBLE))
01885     {
01886         query_double_t pdata = (query_double_t) pd;
01887         g_string_append_printf (gs, " double: %.18g", pdata->val);
01888         return;
01889     }
01890     if (!safe_strcmp (pd->type_name, QOF_TYPE_TIME))
01891     {
01892         query_time_t pdata;
01893         QofDate *qd;
01894 
01895         pdata = (query_time_t) pd;
01896         qd = qof_date_from_qtime (pdata->qt);
01897         g_string_append_printf (gs, "Match type %s " ,
01898             qof_query_printDateMatch (pdata->options));
01899         g_string_append_printf (gs, "query date: %s",
01900             qof_date_print (qd, QOF_DATE_FORMAT_UTC));
01901         qof_date_free (qd);
01902     }
01903 #ifndef QOF_DISABLE_DEPRECATED
01904     if (!safe_strcmp (pd->type_name, QOF_TYPE_DATE))
01905     {
01906         query_date_t pdata;
01907 
01908         pdata = (query_date_t) pd;
01909         g_string_append_printf (gs, "Match type %s ",
01910             qof_query_printDateMatch (pdata->options));
01911         g_string_append_printf (gs, " query_date: %s", 
01912             gnc_print_date (pdata->date));
01913         return;
01914     }
01915 #endif
01916     if (!safe_strcmp (pd->type_name, QOF_TYPE_CHAR))
01917     {
01918         query_char_t pdata = (query_char_t) pd;
01919         g_string_append_printf (gs, "Match type %s ",
01920             qof_query_printCharMatch (pdata->options));
01921         g_string_append_printf (gs, " char list: %s", 
01922             pdata->char_list);
01923         return;
01924     }
01925     if (!safe_strcmp (pd->type_name, QOF_TYPE_BOOLEAN))
01926     {
01927         query_boolean_t pdata = (query_boolean_t) pd;
01928         g_string_append_printf (gs, " boolean: %s",
01929             pdata->val ? "TRUE" : "FALSE");
01930         return;
01931     }
01933     return;
01934 }   /* qof_query_printValueForParam */
01935 
01936 /*
01937  * Print out a string representation of the
01938  * QofStringMatch enum
01939  */
01940 static gchar *
01941 qof_query_printStringMatch (QofStringMatch s)
01942 {
01943     switch (s)
01944     {
01945         AS_STRING_CASE(QOF_STRING_MATCH_NORMAL,)
01946         AS_STRING_CASE(QOF_STRING_MATCH_CASEINSENSITIVE,)
01947     }
01948     return "UNKNOWN MATCH TYPE";
01949 }   /* qof_query_printStringMatch */
01950 
01951 /*
01952  * Print out a string representation of the
01953  * QofDateMatch enum
01954  */
01955 static gchar *
01956 qof_query_printDateMatch (QofDateMatch d)
01957 {
01958     switch (d)
01959     {
01960         AS_STRING_CASE(QOF_DATE_MATCH_NORMAL,)
01961         AS_STRING_CASE(QOF_DATE_MATCH_DAY,)
01962     }
01963     return "UNKNOWN MATCH TYPE";
01964 }   /* qof_query_printDateMatch */
01965 
01966 /*
01967  * Print out a string representation of the
01968  * QofNumericMatch enum
01969  */
01970 static gchar *
01971 qof_query_printNumericMatch (QofNumericMatch n)
01972 {
01973     switch (n)
01974     {
01975         AS_STRING_CASE(QOF_NUMERIC_MATCH_DEBIT,)
01976         AS_STRING_CASE(QOF_NUMERIC_MATCH_CREDIT,)
01977         AS_STRING_CASE(QOF_NUMERIC_MATCH_ANY,)
01978     }
01979     return "UNKNOWN MATCH TYPE";
01980 }   /* qof_query_printNumericMatch */
01981 
01982 /*
01983  * Print out a string representation of the
01984  * QofGuidMatch enum
01985  */
01986 static gchar *
01987 qof_query_printGuidMatch (QofGuidMatch g)
01988 {
01989     switch (g)
01990     {
01991         AS_STRING_CASE(QOF_GUID_MATCH_ANY,)
01992         AS_STRING_CASE(QOF_GUID_MATCH_ALL,)
01993         AS_STRING_CASE(QOF_GUID_MATCH_NONE,)
01994         AS_STRING_CASE(QOF_GUID_MATCH_NULL,)
01995         AS_STRING_CASE(QOF_GUID_MATCH_LIST_ANY,)
01996     }
01997 
01998     return "UNKNOWN MATCH TYPE";
01999 }   /* qof_query_printGuidMatch */
02000 
02001 /*
02002  * Print out a string representation of the
02003  * QofCharMatch enum
02004  */
02005 static gchar *
02006 qof_query_printCharMatch (QofCharMatch c)
02007 {
02008     switch (c)
02009     {
02010         AS_STRING_CASE(QOF_CHAR_MATCH_ANY,)
02011         AS_STRING_CASE(QOF_CHAR_MATCH_NONE,)
02012     }
02013     return "UNKNOWN MATCH TYPE";
02014 }   /* qof_query_printGuidMatch */
02015 
02016 /* ======================== END OF FILE =================== */

Generated on Tue Sep 19 17:05:29 2006 for QOF by  doxygen 1.4.7