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, 
00647         gpointer user_data __attribute__ ((unused)))
00648 {
00649     QofBook *book = key;
00650     QofBackend *be = book->backend;
00651 
00652     if (be && be->free_query)
00653         (be->free_query) (be, value);
00654 
00655     return TRUE;
00656 }
00657 
00658 /* clear out any cached query_compilations */
00659 static void
00660 query_clear_compiles (QofQuery * q)
00661 {
00662     g_hash_table_foreach_remove (q->be_compiled, query_free_compiled,
00663         NULL);
00664 }
00665 
00666 /********************************************************************/
00667 /* PUBLISHED API FUNCTIONS */
00668 
00669 GSList *
00670 qof_query_build_param_list (gchar const *param, ...)
00671 {
00672     GSList *param_list = NULL;
00673     gchar const *this_param;
00674     va_list ap;
00675 
00676     if (!param)
00677         return NULL;
00678 
00679     va_start (ap, param);
00680 
00681     for (this_param = param; this_param;
00682         this_param = va_arg (ap, const gchar *))
00683           param_list = g_slist_prepend (param_list, (gpointer) this_param);
00684 
00685     va_end (ap);
00686 
00687     return g_slist_reverse (param_list);
00688 }
00689 
00690 void
00691 qof_query_add_term (QofQuery * q, GSList * param_list,
00692     QofQueryPredData * pred_data, QofQueryOp op)
00693 {
00694     QofQueryTerm *qt;
00695     QofQuery *qr, *qs;
00696 
00697     if (!q || !param_list || !pred_data)
00698         return;
00699 
00700     qt = g_new0 (QofQueryTerm, 1);
00701     qt->param_list = param_list;
00702     qt->pdata = pred_data;
00703     qs = qof_query_create ();
00704     query_init (qs, qt);
00705 
00706     if (qof_query_has_terms (q))
00707         qr = qof_query_merge (q, qs, op);
00708     else
00709         qr = qof_query_merge (q, qs, QOF_QUERY_OR);
00710 
00711     swap_terms (q, qr);
00712     qof_query_destroy (qs);
00713     qof_query_destroy (qr);
00714 }
00715 
00716 void
00717 qof_query_purge_terms (QofQuery * q, GSList * param_list)
00718 {
00719     QofQueryTerm *qt;
00720     GList *or, *and;
00721 
00722     if (!q || !param_list)
00723         return;
00724 
00725     for (or = q->terms; or; or = or->next)
00726     {
00727         for (and = or->data; and; and = and->next)
00728         {
00729             qt = and->data;
00730             if (!param_list_cmp (qt->param_list, param_list))
00731             {
00732                 if (g_list_length (or->data) == 1)
00733                 {
00734                     q->terms = g_list_remove_link (q->terms, or);
00735                     g_list_free_1 (or);
00736                     or = q->terms;
00737                     break;
00738                 }
00739                 else
00740                 {
00741                     or->data = g_list_remove_link (or->data, and);
00742                     g_list_free_1 (and);
00743                     and = or->data;
00744                     if (!and)
00745                         break;
00746                 }
00747                 q->changed = 1;
00748                 free_query_term (qt);
00749             }
00750         }
00751         if (!or)
00752             break;
00753     }
00754 }
00755 
00756 GList *
00757 qof_query_run (QofQuery * q)
00758 {
00759     GList *matching_objects = NULL;
00760     GList *node;
00761     gint object_count = 0;
00762 
00763     if (!q)
00764         return NULL;
00765     g_return_val_if_fail (q->search_for, NULL);
00766     g_return_val_if_fail (q->books, NULL);
00767     ENTER (" q=%p", q);
00768 
00769     /* XXX: Prioritize the query terms? */
00770 
00771     /* prepare the Query for processing */
00772     if (q->changed)
00773     {
00774         query_clear_compiles (q);
00775         compile_terms (q);
00776     }
00777 
00778     /* Maybe log this sucker */
00779     if (qof_log_check (log_module, QOF_LOG_DETAIL))
00780         qof_query_print (q);
00781 
00782     /* Now run the query over all the objects and save the results */
00783     {
00784         QofQueryCB qcb;
00785 
00786         memset (&qcb, 0, sizeof (qcb));
00787         qcb.query = q;
00788 
00789         /* For each book */
00790         for (node = q->books; node; node = node->next)
00791         {
00792             QofBook *book = node->data;
00793             QofBackend *be = book->backend;
00794 
00795             /* run the query in the backend */
00796             if (be)
00797             {
00798                 gpointer compiled_query =
00799                     g_hash_table_lookup (q->be_compiled, book);
00800 
00801                 if (compiled_query && be->run_query)
00802                 {
00803                     (be->run_query) (be, compiled_query);
00804                 }
00805             }
00806 
00807             /* And then iterate over all the objects */
00808             qof_object_foreach (q->search_for, book,
00809                 (QofEntityForeachCB) check_item_cb, &qcb);
00810         }
00811 
00812         matching_objects = qcb.list;
00813         object_count = qcb.count;
00814     }
00815     PINFO ("matching objects=%p count=%d", matching_objects, object_count);
00816 
00817     /* There is no absolute need to reverse this list, since it's being
00818      * sorted below. However, in the common case, we will be searching
00819      * in a confined location where the objects are already in order,
00820      * thus reversing will put us in the correct order we want and make
00821      * the sorting go much faster.
00822      */
00823     matching_objects = g_list_reverse (matching_objects);
00824 
00825     /* Now sort the matching objects based on the search criteria
00826      * sortQuery is an unforgivable use of static global data...  
00827      * I just can't figure out how else to do this sanely.
00828      */
00829     if (q->primary_sort.comp_fcn || q->primary_sort.obj_cmp ||
00830         (q->primary_sort.use_default && q->defaultSort))
00831     {
00832         sortQuery = q;
00833         matching_objects = g_list_sort (matching_objects, sort_func);
00834         sortQuery = NULL;
00835     }
00836 
00837     /* Crop the list to limit the number of splits. */
00838     if ((object_count > q->max_results) && (q->max_results > -1))
00839     {
00840         if (q->max_results > 0)
00841         {
00842             GList *mptr;
00843 
00844             /* mptr is set to the first node of what will be the new list */
00845             mptr =
00846                 g_list_nth (matching_objects,
00847                 object_count - q->max_results);
00848             /* mptr should not be NULL, but let's be safe */
00849             if (mptr != NULL)
00850             {
00851                 if (mptr->prev != NULL)
00852                     mptr->prev->next = NULL;
00853                 mptr->prev = NULL;
00854             }
00855             g_list_free (matching_objects);
00856             matching_objects = mptr;
00857         }
00858         else
00859         {
00860             /* q->max_results == 0 */
00861             g_list_free (matching_objects);
00862             matching_objects = NULL;
00863         }
00864         object_count = q->max_results;
00865     }
00866 
00867     q->changed = 0;
00868 
00869     g_list_free (q->results);
00870     q->results = matching_objects;
00871 
00872     LEAVE (" q=%p", q);
00873     return matching_objects;
00874 }
00875 
00876 GList *
00877 qof_query_last_run (QofQuery * query)
00878 {
00879     if (!query)
00880         return NULL;
00881 
00882     return query->results;
00883 }
00884 
00885 void
00886 qof_query_clear (QofQuery * query)
00887 {
00888     QofQuery *q2 = qof_query_create ();
00889     swap_terms (query, q2);
00890     qof_query_destroy (q2);
00891 
00892     g_list_free (query->books);
00893     query->books = NULL;
00894     g_list_free (query->results);
00895     query->results = NULL;
00896     query->changed = 1;
00897 }
00898 
00899 QofQuery *
00900 qof_query_create (void)
00901 {
00902     QofQuery *qp = g_new0 (QofQuery, 1);
00903     qp->be_compiled = g_hash_table_new (g_direct_hash, g_direct_equal);
00904     query_init (qp, NULL);
00905     return qp;
00906 }
00907 
00908 void
00909 qof_query_search_for (QofQuery * q, QofIdTypeConst obj_type)
00910 {
00911     if (!q || !obj_type)
00912         return;
00913 
00914     if (safe_strcmp (q->search_for, obj_type))
00915     {
00916         q->search_for = (QofIdType) obj_type;
00917         q->changed = 1;
00918     }
00919 }
00920 
00921 QofQuery *
00922 qof_query_create_for (QofIdTypeConst obj_type)
00923 {
00924     QofQuery *q;
00925     if (!obj_type)
00926         return NULL;
00927     q = qof_query_create ();
00928     qof_query_search_for (q, obj_type);
00929     return q;
00930 }
00931 
00932 gint
00933 qof_query_has_terms (QofQuery * q)
00934 {
00935     if (!q)
00936         return 0;
00937     return g_list_length (q->terms);
00938 }
00939 
00940 gint
00941 qof_query_num_terms (QofQuery * q)
00942 {
00943     GList *o;
00944     gint n = 0;
00945     if (!q)
00946         return 0;
00947     for (o = q->terms; o; o = o->next)
00948         n += g_list_length (o->data);
00949     return n;
00950 }
00951 
00952 gboolean
00953 qof_query_has_term_type (QofQuery * q, GSList * term_param)
00954 {
00955     GList *or;
00956     GList *and;
00957 
00958     if (!q || !term_param)
00959         return FALSE;
00960 
00961     for (or = q->terms; or; or = or->next)
00962     {
00963         for (and = or->data; and; and = and->next)
00964         {
00965             QofQueryTerm *qt = and->data;
00966             if (!param_list_cmp (term_param, qt->param_list))
00967                 return TRUE;
00968         }
00969     }
00970 
00971     return FALSE;
00972 }
00973 
00974 GSList *
00975 qof_query_get_term_type (QofQuery * q, GSList * term_param)
00976 {
00977     GList *or;
00978     GList *and;
00979     GSList *results = NULL;
00980 
00981     if (!q || !term_param)
00982         return FALSE;
00983 
00984     for (or = q->terms; or; or = or->next)
00985     {
00986         for (and = or->data; and; and = and->next)
00987         {
00988             QofQueryTerm *qt = and->data;
00989             if (!param_list_cmp (term_param, qt->param_list))
00990                 results = g_slist_append (results, qt->pdata);
00991         }
00992     }
00993 
00994     return results;
00995 }
00996 
00997 void
00998 qof_query_destroy (QofQuery * q)
00999 {
01000     if (!q)
01001         return;
01002     free_members (q);
01003     query_clear_compiles (q);
01004     g_hash_table_destroy (q->be_compiled);
01005     g_free (q);
01006 }
01007 
01008 QofQuery *
01009 qof_query_copy (QofQuery * q)
01010 {
01011     QofQuery *copy;
01012     GHashTable *ht;
01013 
01014     if (!q)
01015         return NULL;
01016     copy = qof_query_create ();
01017     ht = copy->be_compiled;
01018     free_members (copy);
01019 
01020     memcpy (copy, q, sizeof (QofQuery));
01021 
01022     copy->be_compiled = ht;
01023     copy->terms = copy_or_terms (q->terms);
01024     copy->books = g_list_copy (q->books);
01025     copy->results = g_list_copy (q->results);
01026 
01027     copy_sort (&(copy->primary_sort), &(q->primary_sort));
01028     copy_sort (&(copy->secondary_sort), &(q->secondary_sort));
01029     copy_sort (&(copy->tertiary_sort), &(q->tertiary_sort));
01030 
01031     copy->changed = 1;
01032 
01033     return copy;
01034 }
01035 
01036 /* *******************************************************************
01037  * qof_query_invert 
01038  * return a newly-allocated Query object which is the 
01039  * logical inverse of the original.
01040  ********************************************************************/
01041 
01042 QofQuery *
01043 qof_query_invert (QofQuery * q)
01044 {
01045     QofQuery *retval;
01046     QofQuery *right, *left, *iright, *ileft;
01047     QofQueryTerm *qt;
01048     GList *aterms;
01049     GList *cur;
01050     GList *new_oterm;
01051     gint num_or_terms;
01052 
01053     if (!q)
01054         return NULL;
01055 
01056     num_or_terms = g_list_length (q->terms);
01057 
01058     switch (num_or_terms)
01059     {
01060     case 0:
01061         retval = qof_query_create ();
01062         retval->max_results = q->max_results;
01063         break;
01064 
01065         /* This is the DeMorgan expansion for a single AND expression. */
01066         /* !(abc) = !a + !b + !c */
01067     case 1:
01068         retval = qof_query_create ();
01069         retval->max_results = q->max_results;
01070         retval->books = g_list_copy (q->books);
01071         retval->search_for = q->search_for;
01072         retval->changed = 1;
01073 
01074         aterms = g_list_nth_data (q->terms, 0);
01075         new_oterm = NULL;
01076         for (cur = aterms; cur; cur = cur->next)
01077         {
01078             qt = copy_query_term (cur->data);
01079             qt->invert = !(qt->invert);
01080             new_oterm = g_list_append (NULL, qt);
01081 
01082             /* g_list_append() can take forever, so let's do this for speed
01083              * in "large" queries.
01084              */
01085             retval->terms = g_list_reverse (retval->terms);
01086             retval->terms = g_list_prepend (retval->terms, new_oterm);
01087             retval->terms = g_list_reverse (retval->terms);
01088         }
01089         break;
01090 
01091         /* If there are multiple OR-terms, we just recurse by 
01092          * breaking it down to !(a + b + c) = 
01093          * !a * !(b + c) = !a * !b * !c.  */
01094     default:
01095         right = qof_query_create ();
01096         right->terms = copy_or_terms (g_list_nth (q->terms, 1));
01097 
01098         left = qof_query_create ();
01099         left->terms = g_list_append (NULL,
01100             copy_and_terms (g_list_nth_data (q->terms, 0)));
01101 
01102         iright = qof_query_invert (right);
01103         ileft = qof_query_invert (left);
01104 
01105         retval = qof_query_merge (iright, ileft, QOF_QUERY_AND);
01106         retval->books = g_list_copy (q->books);
01107         retval->max_results = q->max_results;
01108         retval->search_for = q->search_for;
01109         retval->changed = 1;
01110 
01111         qof_query_destroy (iright);
01112         qof_query_destroy (ileft);
01113         qof_query_destroy (right);
01114         qof_query_destroy (left);
01115         break;
01116     }
01117 
01118     return retval;
01119 }
01120 
01121 /* *******************************************************************
01122  * qof_query_merge
01123  * combine 2 Query objects by the logical operation in "op".
01124  ********************************************************************/
01125 
01126 QofQuery *
01127 qof_query_merge (QofQuery * q1, QofQuery * q2, QofQueryOp op)
01128 {
01129 
01130     QofQuery *retval = NULL;
01131     QofQuery *i1, *i2;
01132     QofQuery *t1, *t2;
01133     GList *i, *j;
01134     QofIdType search_for;
01135 
01136     if (!q1)
01137         return q2;
01138     if (!q2)
01139         return q1;
01140 
01141     if (q1->search_for && q2->search_for)
01142         g_return_val_if_fail (safe_strcmp (q1->search_for,
01143                 q2->search_for) == 0, NULL);
01144 
01145     search_for = (q1->search_for ? q1->search_for : q2->search_for);
01146 
01147     /* Avoid merge surprises if op==QOF_QUERY_AND but q1 is empty.
01148      * The goal of this tweak is to all the user to start with
01149      * an empty q1 and then append to it recursively
01150      * (and q1 (and q2 (and q3 (and q4 ....))))
01151      * without bombing out because the append started with an 
01152      * empty list.
01153      * We do essentially the same check in qof_query_add_term()
01154      * so that the first term added to an empty query doesn't screw up.
01155      */
01156     if ((QOF_QUERY_AND == op) && (0 == qof_query_has_terms (q1)))
01157     {
01158         op = QOF_QUERY_OR;
01159     }
01160 
01161     switch (op)
01162     {
01163     case QOF_QUERY_OR:
01164         retval = qof_query_create ();
01165         retval->terms =
01166             g_list_concat (copy_or_terms (q1->terms),
01167             copy_or_terms (q2->terms));
01168         retval->books = merge_books (q1->books, q2->books);
01169         retval->max_results = q1->max_results;
01170         retval->changed = 1;
01171         break;
01172 
01173     case QOF_QUERY_AND:
01174         retval = qof_query_create ();
01175         retval->books = merge_books (q1->books, q2->books);
01176         retval->max_results = q1->max_results;
01177         retval->changed = 1;
01178 
01179         /* g_list_append() can take forever, so let's build the list in
01180          * reverse and then reverse it at the end, to deal better with
01181          * "large" queries.
01182          */
01183         for (i = q1->terms; i; i = i->next)
01184         {
01185             for (j = q2->terms; j; j = j->next)
01186             {
01187                 retval->terms =
01188                     g_list_prepend (retval->terms,
01189                     g_list_concat
01190                     (copy_and_terms (i->data), copy_and_terms (j->data)));
01191             }
01192         }
01193         retval->terms = g_list_reverse (retval->terms);
01194         break;
01195 
01196     case QOF_QUERY_NAND:
01197         /* !(a*b) = (!a + !b) */
01198         i1 = qof_query_invert (q1);
01199         i2 = qof_query_invert (q2);
01200         retval = qof_query_merge (i1, i2, QOF_QUERY_OR);
01201         qof_query_destroy (i1);
01202         qof_query_destroy (i2);
01203         break;
01204 
01205     case QOF_QUERY_NOR:
01206         /* !(a+b) = (!a*!b) */
01207         i1 = qof_query_invert (q1);
01208         i2 = qof_query_invert (q2);
01209         retval = qof_query_merge (i1, i2, QOF_QUERY_AND);
01210         qof_query_destroy (i1);
01211         qof_query_destroy (i2);
01212         break;
01213 
01214     case QOF_QUERY_XOR:
01215         /* a xor b = (a * !b) + (!a * b) */
01216         i1 = qof_query_invert (q1);
01217         i2 = qof_query_invert (q2);
01218         t1 = qof_query_merge (q1, i2, QOF_QUERY_AND);
01219         t2 = qof_query_merge (i1, q2, QOF_QUERY_AND);
01220         retval = qof_query_merge (t1, t2, QOF_QUERY_OR);
01221 
01222         qof_query_destroy (i1);
01223         qof_query_destroy (i2);
01224         qof_query_destroy (t1);
01225         qof_query_destroy (t2);
01226         break;
01227     }
01228 
01229     retval->search_for = search_for;
01230     return retval;
01231 }
01232 
01233 void
01234 qof_query_merge_in_place (QofQuery * q1, QofQuery * q2, QofQueryOp op)
01235 {
01236     QofQuery *tmp_q;
01237 
01238     if (!q1 || !q2)
01239         return;
01240 
01241     tmp_q = qof_query_merge (q1, q2, op);
01242     swap_terms (q1, tmp_q);
01243     qof_query_destroy (tmp_q);
01244 }
01245 
01246 void
01247 qof_query_set_sort_order (QofQuery * q,
01248     GSList * params1, GSList * params2, GSList * params3)
01249 {
01250     if (!q)
01251         return;
01252     if (q->primary_sort.param_list)
01253         g_slist_free (q->primary_sort.param_list);
01254     q->primary_sort.param_list = params1;
01255     q->primary_sort.options = 0;
01256 
01257     if (q->secondary_sort.param_list)
01258         g_slist_free (q->secondary_sort.param_list);
01259     q->secondary_sort.param_list = params2;
01260     q->secondary_sort.options = 0;
01261 
01262     if (q->tertiary_sort.param_list)
01263         g_slist_free (q->tertiary_sort.param_list);
01264     q->tertiary_sort.param_list = params3;
01265     q->tertiary_sort.options = 0;
01266 
01267     q->changed = 1;
01268 }
01269 
01270 void
01271 qof_query_set_sort_options (QofQuery * q, gint prim_op, gint sec_op,
01272     gint tert_op)
01273 {
01274     if (!q)
01275         return;
01276     q->primary_sort.options = prim_op;
01277     q->secondary_sort.options = sec_op;
01278     q->tertiary_sort.options = tert_op;
01279 }
01280 
01281 void
01282 qof_query_set_sort_increasing (QofQuery * q, gboolean prim_inc,
01283     gboolean sec_inc, gboolean tert_inc)
01284 {
01285     if (!q)
01286         return;
01287     q->primary_sort.increasing = prim_inc;
01288     q->secondary_sort.increasing = sec_inc;
01289     q->tertiary_sort.increasing = tert_inc;
01290 }
01291 
01292 void
01293 qof_query_set_max_results (QofQuery * q, gint n)
01294 {
01295     if (!q)
01296         return;
01297     q->max_results = n;
01298 }
01299 
01300 void
01301 qof_query_add_guid_list_match (QofQuery * q, GSList * param_list,
01302     GList * guid_list, QofGuidMatch options, QofQueryOp op)
01303 {
01304     QofQueryPredData *pdata;
01305 
01306     if (!q || !param_list)
01307         return;
01308 
01309     if (!guid_list)
01310         g_return_if_fail (options == QOF_GUID_MATCH_NULL);
01311 
01312     pdata = qof_query_guid_predicate (options, guid_list);
01313     qof_query_add_term (q, param_list, pdata, op);
01314 }
01315 
01316 void
01317 qof_query_add_guid_match (QofQuery * q, GSList * param_list,
01318     const GUID * guid, QofQueryOp op)
01319 {
01320     GList *g = NULL;
01321 
01322     if (!q || !param_list)
01323         return;
01324 
01325     if (guid)
01326         g = g_list_prepend (g, (gpointer) guid);
01327 
01328     qof_query_add_guid_list_match (q, param_list, g,
01329         g ? QOF_GUID_MATCH_ANY : QOF_GUID_MATCH_NULL, op);
01330 
01331     g_list_free (g);
01332 }
01333 
01334 void
01335 qof_query_set_book (QofQuery * q, QofBook * book)
01336 {
01337     GSList *slist = NULL;
01338     if (!q || !book)
01339         return;
01340 
01341     /* Make sure this book is only in the list once */
01342     if (g_list_index (q->books, book) == -1)
01343         q->books = g_list_prepend (q->books, book);
01344 
01345     slist = g_slist_prepend (slist, QOF_PARAM_GUID);
01346     slist = g_slist_prepend (slist, QOF_PARAM_BOOK);
01347     qof_query_add_guid_match (q, slist,
01348         qof_entity_get_guid ((QofEntity*)book), QOF_QUERY_AND);
01349 }
01350 
01351 GList *
01352 qof_query_get_books (QofQuery * q)
01353 {
01354     if (!q)
01355         return NULL;
01356     return q->books;
01357 }
01358 
01359 void
01360 qof_query_add_boolean_match (QofQuery * q, GSList * param_list,
01361     gboolean value, QofQueryOp op)
01362 {
01363     QofQueryPredData *pdata;
01364     if (!q || !param_list)
01365         return;
01366 
01367     pdata = qof_query_boolean_predicate (QOF_COMPARE_EQUAL, value);
01368     qof_query_add_term (q, param_list, pdata, op);
01369 }
01370 
01371 /**********************************************************************/
01372 /* PRIVATE PUBLISHED API FUNCTIONS                                    */
01373 
01374 void
01375 qof_query_init (void)
01376 {
01377     ENTER (" ");
01378     qof_query_core_init ();
01379     qof_class_init ();
01380     qof_date_init ();
01381     LEAVE ("Completed initialization of QofQuery");
01382 }
01383 
01384 void
01385 qof_query_shutdown (void)
01386 {
01387     qof_class_shutdown ();
01388     qof_query_core_shutdown ();
01389 }
01390 
01391 gint
01392 qof_query_get_max_results (QofQuery * q)
01393 {
01394     if (!q)
01395         return 0;
01396     return q->max_results;
01397 }
01398 
01399 QofIdType
01400 qof_query_get_search_for (QofQuery * q)
01401 {
01402     if (!q)
01403         return NULL;
01404     return q->search_for;
01405 }
01406 
01407 GList *
01408 qof_query_get_terms (QofQuery * q)
01409 {
01410     if (!q)
01411         return NULL;
01412     return q->terms;
01413 }
01414 
01415 GSList *
01416 qof_query_term_get_param_path (QofQueryTerm * qt)
01417 {
01418     if (!qt)
01419         return NULL;
01420     return qt->param_list;
01421 }
01422 
01423 QofQueryPredData *
01424 qof_query_term_get_pred_data (QofQueryTerm * qt)
01425 {
01426     if (!qt)
01427         return NULL;
01428     return qt->pdata;
01429 }
01430 
01431 gboolean
01432 qof_query_term_is_inverted (QofQueryTerm * qt)
01433 {
01434     if (!qt)
01435         return FALSE;
01436     return qt->invert;
01437 }
01438 
01439 void
01440 qof_query_get_sorts (QofQuery * q, QofQuerySort ** primary,
01441     QofQuerySort ** secondary, QofQuerySort ** tertiary)
01442 {
01443     if (!q)
01444         return;
01445     if (primary)
01446         *primary = &(q->primary_sort);
01447     if (secondary)
01448         *secondary = &(q->secondary_sort);
01449     if (tertiary)
01450         *tertiary = &(q->tertiary_sort);
01451 }
01452 
01453 GSList *
01454 qof_query_sort_get_param_path (QofQuerySort * qs)
01455 {
01456     if (!qs)
01457         return NULL;
01458     return qs->param_list;
01459 }
01460 
01461 gint
01462 qof_query_sort_get_sort_options (QofQuerySort * qs)
01463 {
01464     if (!qs)
01465         return 0;
01466     return qs->options;
01467 }
01468 
01469 gboolean
01470 qof_query_sort_get_increasing (QofQuerySort * qs)
01471 {
01472     if (!qs)
01473         return FALSE;
01474     return qs->increasing;
01475 }
01476 
01477 static gboolean
01478 qof_query_term_equal (QofQueryTerm * qt1, QofQueryTerm * qt2)
01479 {
01480     if (qt1 == qt2)
01481         return TRUE;
01482     if (!qt1 || !qt2)
01483         return FALSE;
01484 
01485     if (qt1->invert != qt2->invert)
01486         return FALSE;
01487     if (param_list_cmp (qt1->param_list, qt2->param_list))
01488         return FALSE;
01489     return qof_query_core_predicate_equal (qt1->pdata, qt2->pdata);
01490 }
01491 
01492 static gboolean
01493 qof_query_sort_equal (QofQuerySort * qs1, QofQuerySort * qs2)
01494 {
01495     if (qs1 == qs2)
01496         return TRUE;
01497     if (!qs1 || !qs2)
01498         return FALSE;
01499 
01500     /* "Empty" sorts are equivalent, regardless of the flags */
01501     if (!qs1->param_list && !qs2->param_list)
01502         return TRUE;
01503 
01504     if (qs1->options != qs2->options)
01505         return FALSE;
01506     if (qs1->increasing != qs2->increasing)
01507         return FALSE;
01508     return (param_list_cmp (qs1->param_list, qs2->param_list) == 0);
01509 }
01510 
01511 gboolean
01512 qof_query_equal (QofQuery * q1, QofQuery * q2)
01513 {
01514     GList *or1, *or2;
01515 
01516     if (q1 == q2)
01517         return TRUE;
01518     if (!q1 || !q2)
01519         return FALSE;
01520 
01521     if (g_list_length (q1->terms) != g_list_length (q2->terms))
01522         return FALSE;
01523     if (q1->max_results != q2->max_results)
01524         return FALSE;
01525 
01526     for (or1 = q1->terms, or2 = q2->terms; or1;
01527         or1 = or1->next, or2 = or2->next)
01528     {
01529         GList *and1, *and2;
01530 
01531         and1 = or1->data;
01532         and2 = or2->data;
01533 
01534         if (g_list_length (and1) != g_list_length (and2))
01535             return FALSE;
01536 
01537         for (; and1; and1 = and1->next, and2 = and2->next)
01538             if (!qof_query_term_equal (and1->data, and2->data))
01539                 return FALSE;
01540     }
01541 
01542     if (!qof_query_sort_equal (&(q1->primary_sort), &(q2->primary_sort)))
01543         return FALSE;
01544     if (!qof_query_sort_equal (&(q1->secondary_sort),
01545             &(q2->secondary_sort)))
01546         return FALSE;
01547     if (!qof_query_sort_equal (&(q1->tertiary_sort), &(q2->tertiary_sort)))
01548         return FALSE;
01549 
01550     return TRUE;
01551 }
01552 
01553 /* **************************************************************************/
01554 /* Query Print functions for use with qof_log_set_level.
01555 */
01556 
01557 /* Static prototypes */
01558 static GList *qof_query_printSearchFor (QofQuery * query, GList * output);
01559 static GList *qof_query_printTerms (QofQuery * query, GList * output);
01560 static GList *qof_query_printSorts (QofQuerySort * s[],
01561     const gint numSorts, GList * output);
01562 static GList *qof_query_printAndTerms (GList * terms, GList * output);
01563 static gchar *qof_query_printStringForHow (QofQueryCompare how);
01564 static gchar *qof_query_printStringMatch (QofStringMatch s);
01565 static gchar *qof_query_printDateMatch (QofDateMatch d);
01566 static gchar *qof_query_printNumericMatch (QofNumericMatch n);
01567 static gchar *qof_query_printGuidMatch (QofGuidMatch g);
01568 static gchar *qof_query_printCharMatch (QofCharMatch c);
01569 static GList *qof_query_printPredData (QofQueryPredData * pd, GList * lst);
01570 static GString *qof_query_printParamPath (GSList * parmList);
01571 static void qof_query_printValueForParam (QofQueryPredData * pd,
01572     GString * gs);
01573 static void qof_query_printOutput (GList * output);
01574 
01576 void
01577 qof_query_print (QofQuery * query)
01578 {
01579     GList *output;
01580     GString *str;
01581     QofQuerySort *s[3];
01582     gint maxResults = 0, numSorts = 3;
01583 
01584     ENTER (" ");
01585 
01586     if (!query)
01587     {
01588         LEAVE ("query is (null)");
01589         return;
01590     }
01591 
01592     output = NULL;
01593     str = NULL;
01594     maxResults = qof_query_get_max_results (query);
01595 
01596     output = qof_query_printSearchFor (query, output);
01597     output = qof_query_printTerms (query, output);
01598 
01599     qof_query_get_sorts (query, &s[0], &s[1], &s[2]);
01600 
01601     if (s[0])
01602     {
01603         output = qof_query_printSorts (s, numSorts, output);
01604     }
01605 
01606     str = g_string_new (" ");
01607     g_string_printf (str, "Maximum number of results: %d", maxResults);
01608     output = g_list_append (output, str);
01609 
01610     qof_query_printOutput (output);
01611     LEAVE (" ");
01612 }
01613 
01614 static void
01615 qof_query_printOutput (GList * output)
01616 {
01617     GList *lst;
01618 
01619     for (lst = output; lst; lst = lst->next)
01620     {
01621         GString *line = (GString *) lst->data;
01622 
01623         DEBUG (" %s", line->str);
01624         g_string_free (line, TRUE);
01625         line = NULL;
01626     }
01627 }
01628 
01629 /*
01630     Get the search_for type--This is the type of Object
01631     we are searching for (SPLIT, TRANS, etc)
01632 */
01633 static GList *
01634 qof_query_printSearchFor (QofQuery * query, GList * output)
01635 {
01636     QofIdType searchFor;
01637     GString *gs;
01638 
01639     searchFor = qof_query_get_search_for (query);
01640     gs = g_string_new ("Query Object Type: ");
01641     g_string_append (gs, (NULL == searchFor) ? "(null)" : searchFor);
01642     output = g_list_append (output, gs);
01643 
01644     return output;
01645 }   /* qof_query_printSearchFor */
01646 
01647 /*
01648     Run through the terms of the query.  This is a outer-inner
01649     loop.  The elements of the outer loop are ORed, and the
01650     elements of the inner loop are ANDed.
01651 */
01652 static GList *
01653 qof_query_printTerms (QofQuery * query, GList * output)
01654 {
01655 
01656     GList *terms, *lst;
01657 
01658     terms = qof_query_get_terms (query);
01659 
01660     for (lst = terms; lst; lst = lst->next)
01661     {
01662         output =
01663             g_list_append (output, g_string_new ("OR Terms:"));
01664 
01665         if (lst->data)
01666         {
01667             output = qof_query_printAndTerms (lst->data, output);
01668         }
01669         else
01670         {
01671             output =
01672                 g_list_append (output,
01673                 g_string_new ("  No data for AND terms"));
01674         }
01675     }
01676 
01677     return output;
01678 }   /* qof_query_printTerms */
01679 
01680 /*
01681     Process the sort parameters
01682     If this function is called, the assumption is that the first sort
01683     not null.
01684 */
01685 static GList *
01686 qof_query_printSorts (QofQuerySort * s[], const gint numSorts,
01687     GList * output)
01688 {
01689     GSList *gsl, *n = NULL;
01690     gint curSort;
01691     GString *gs = g_string_new ("Sort Parameters:   ");
01692 
01693     for (curSort = 0; curSort < numSorts; curSort++)
01694     {
01695         gboolean increasing;
01696         if (!s[curSort])
01697         {
01698             break;
01699         }
01700         increasing = qof_query_sort_get_increasing (s[curSort]);
01701 
01702         gsl = qof_query_sort_get_param_path (s[curSort]);
01703         if (gsl)
01704             g_string_append_printf (gs, " Param: ");
01705         for (n = gsl; n; n = n->next)
01706         {
01707             QofIdType param_name = n->data;
01708             if (gsl != n)
01709                 g_string_append_printf (gs, " ");
01710             g_string_append_printf (gs, "%s", param_name);
01711         }
01712         if (gsl)
01713         {
01714             g_string_append_printf (gs, " %s ",
01715                 increasing ? "DESC" : "ASC");
01716             g_string_append_printf (gs, " Options: 0x%x ",
01717                 s[curSort]->options);
01718         }
01719     }
01720 
01721     output = g_list_append (output, gs);
01722     return output;
01723 
01724 }       /* qof_query_printSorts */
01725 
01726 /*
01727     Process the AND terms of the query.  This is a GList
01728     of WHERE terms that will be ANDed
01729 */
01730 static GList *
01731 qof_query_printAndTerms (GList * terms, GList * output)
01732 {
01733     const gchar *prefix = "AND Terms:";
01734     QofQueryTerm *qt;
01735     QofQueryPredData *pd;
01736     GSList *path;
01737     GList *lst;
01738     gboolean invert;
01739 
01740     output = g_list_append (output, g_string_new (prefix));
01741     for (lst = terms; lst; lst = lst->next)
01742     {
01743         qt = (QofQueryTerm *) lst->data;
01744         pd = qof_query_term_get_pred_data (qt);
01745         path = qof_query_term_get_param_path (qt);
01746         invert = qof_query_term_is_inverted (qt);
01747 
01748         if (invert)
01749             output =
01750                 g_list_append (output, g_string_new (" INVERT SENSE "));
01751         output = g_list_append (output, qof_query_printParamPath (path));
01752         output = qof_query_printPredData (pd, output);
01753 //    output = g_list_append (output, g_string_new(" "));
01754     }
01755 
01756     return output;
01757 }   /* qof_query_printAndTerms */
01758 
01759 /*
01760     Process the parameter types of the predicate data
01761 */
01762 static GString *
01763 qof_query_printParamPath (GSList * parmList)
01764 {
01765     GSList *list = NULL;
01766     GString *gs = g_string_new ("Param List: ");
01767     g_string_append (gs, " ");
01768     for (list = parmList; list; list = list->next)
01769     {
01770         g_string_append (gs, (gchar *) list->data);
01771         if (list->next)
01772             g_string_append (gs, ", ");
01773     }
01774 
01775     return gs;
01776 }   /* qof_query_printParamPath */
01777 
01778 /*
01779     Process the PredData of the AND terms
01780 */
01781 static GList *
01782 qof_query_printPredData (QofQueryPredData * pd, GList * lst)
01783 {
01784     GString *gs;
01785 
01786     gs = g_string_new ("Pred Data: ");
01787     g_string_append (gs, (gchar *) pd->type_name);
01788 
01789     /* Char Predicate and GUID predicate don't use the 'how' field. */
01790     if (safe_strcmp (pd->type_name, QOF_TYPE_CHAR) &&
01791         safe_strcmp (pd->type_name, QOF_TYPE_GUID))
01792     {
01793         g_string_append_printf (gs, " how: %s",
01794             qof_query_printStringForHow (pd->how));
01795     }
01796     lst = g_list_append (lst, gs);
01797     gs = g_string_new ("");
01798     qof_query_printValueForParam (pd, gs);
01799     lst = g_list_append (lst, gs);
01800     return lst;
01801 }   /* qof_query_printPredData */
01802 
01803 /*
01804     Get a string representation for the
01805     QofCompareFunc enum type.
01806 */
01807 static gchar *
01808 qof_query_printStringForHow (QofQueryCompare how)
01809 {
01810 
01811     switch (how)
01812     {
01813         AS_STRING_CASE(QOF_COMPARE_LT,)
01814         AS_STRING_CASE(QOF_COMPARE_LTE,)
01815         AS_STRING_CASE(QOF_COMPARE_EQUAL,)
01816         AS_STRING_CASE(QOF_COMPARE_GT,)
01817         AS_STRING_CASE(QOF_COMPARE_GTE,)
01818         AS_STRING_CASE(QOF_COMPARE_NEQ,)
01819     }
01820     return "INVALID HOW";
01821 }   /* qncQueryPrintStringForHow */
01822 
01823 
01824 static void
01825 qof_query_printValueForParam (QofQueryPredData * pd, GString * gs)
01826 {
01827 
01828     if (!safe_strcmp (pd->type_name, QOF_TYPE_GUID))
01829     {
01830         GList *node;
01831         query_guid_t pdata = (query_guid_t) pd;
01832         g_string_append_printf (gs, "Match type %s ",
01833             qof_query_printGuidMatch (pdata->options));
01834         for (node = pdata->guids; node; node = node->next)
01835         {
01836             /* THREAD-UNSAFE */
01837             g_string_append_printf (gs, ", guids: %s",
01838                 guid_to_string ((GUID *) node->data));
01839         }
01840         return;
01841     }
01842     if (!safe_strcmp (pd->type_name, QOF_TYPE_STRING))
01843     {
01844         query_string_t pdata = (query_string_t) pd;
01845         g_string_append_printf (gs, "Match type %s ",
01846             qof_query_printStringMatch (pdata->options));
01847         g_string_append_printf (gs, " %s string: %s",
01848             pdata->is_regex ? "Regex" : "Not regex", pdata->matchstring);
01849         return;
01850     }
01851     if (!safe_strcmp (pd->type_name, QOF_TYPE_NUMERIC))
01852     {
01853         query_numeric_t pdata = (query_numeric_t) pd;
01854         g_string_append_printf (gs, "Match type %s ",
01855             qof_query_printNumericMatch (pdata->options));
01856         g_string_append_printf (gs, " numeric: %s",
01857             qof_numeric_dbg_to_string (pdata->amount));
01858         return;
01859     }
01860     if (!safe_strcmp (pd->type_name, QOF_TYPE_KVP))
01861     {
01862         GSList *node;
01863         query_kvp_t pdata = (query_kvp_t) pd;
01864         g_string_append_printf (gs, " kvp path: ");
01865         for (node = pdata->path; node; node = node->next)
01866         {
01867             g_string_append_printf (gs, "/%s", (gchar *) node->data);
01868         }
01869         g_string_append_printf (gs, " kvp value: %s ",
01870             kvp_value_to_string (pdata->value));
01871         return;
01872     }
01873     if (!safe_strcmp (pd->type_name, QOF_TYPE_INT64))
01874     {
01875         query_int64_t pdata = (query_int64_t) pd;
01876         g_string_append_printf (gs, " int64: %" G_GINT64_FORMAT,
01877             pdata->val);
01878         return;
01879     }
01880     if (!safe_strcmp (pd->type_name, QOF_TYPE_INT32))
01881     {
01882         query_int32_t pdata = (query_int32_t) pd;
01883         g_string_append_printf (gs, " int32: %d", pdata->val);
01884         return;
01885     }
01886     if (!safe_strcmp (pd->type_name, QOF_TYPE_DOUBLE))
01887     {
01888         query_double_t pdata = (query_double_t) pd;
01889         g_string_append_printf (gs, " double: %.18g", pdata->val);
01890         return;
01891     }
01892     if (!safe_strcmp (pd->type_name, QOF_TYPE_TIME))
01893     {
01894         query_time_t pdata;
01895         QofDate *qd;
01896 
01897         pdata = (query_time_t) pd;
01898         qd = qof_date_from_qtime (pdata->qt);
01899         g_string_append_printf (gs, "Match type %s " ,
01900             qof_query_printDateMatch (pdata->options));
01901         g_string_append_printf (gs, "query date: %s",
01902             qof_date_print (qd, QOF_DATE_FORMAT_UTC));
01903         qof_date_free (qd);
01904     }
01905 #ifndef QOF_DISABLE_DEPRECATED
01906     if (!safe_strcmp (pd->type_name, QOF_TYPE_DATE))
01907     {
01908         query_date_t pdata;
01909 
01910         pdata = (query_date_t) pd;
01911         g_string_append_printf (gs, "Match type %s ",
01912             qof_query_printDateMatch (pdata->options));
01913         g_string_append_printf (gs, " query_date: %s", 
01914             gnc_print_date (pdata->date));
01915         return;
01916     }
01917 #endif
01918     if (!safe_strcmp (pd->type_name, QOF_TYPE_CHAR))
01919     {
01920         query_char_t pdata = (query_char_t) pd;
01921         g_string_append_printf (gs, "Match type %s ",
01922             qof_query_printCharMatch (pdata->options));
01923         g_string_append_printf (gs, " char list: %s", 
01924             pdata->char_list);
01925         return;
01926     }
01927     if (!safe_strcmp (pd->type_name, QOF_TYPE_BOOLEAN))
01928     {
01929         query_boolean_t pdata = (query_boolean_t) pd;
01930         g_string_append_printf (gs, " boolean: %s",
01931             pdata->val ? "TRUE" : "FALSE");
01932         return;
01933     }
01935     return;
01936 }   /* qof_query_printValueForParam */
01937 
01938 /*
01939  * Print out a string representation of the
01940  * QofStringMatch enum
01941  */
01942 static gchar *
01943 qof_query_printStringMatch (QofStringMatch s)
01944 {
01945     switch (s)
01946     {
01947         AS_STRING_CASE(QOF_STRING_MATCH_NORMAL,)
01948         AS_STRING_CASE(QOF_STRING_MATCH_CASEINSENSITIVE,)
01949     }
01950     return "UNKNOWN MATCH TYPE";
01951 }   /* qof_query_printStringMatch */
01952 
01953 /*
01954  * Print out a string representation of the
01955  * QofDateMatch enum
01956  */
01957 static gchar *
01958 qof_query_printDateMatch (QofDateMatch d)
01959 {
01960     switch (d)
01961     {
01962         AS_STRING_CASE(QOF_DATE_MATCH_NORMAL,)
01963         AS_STRING_CASE(QOF_DATE_MATCH_DAY,)
01964     }
01965     return "UNKNOWN MATCH TYPE";
01966 }   /* qof_query_printDateMatch */
01967 
01968 /*
01969  * Print out a string representation of the
01970  * QofNumericMatch enum
01971  */
01972 static gchar *
01973 qof_query_printNumericMatch (QofNumericMatch n)
01974 {
01975     switch (n)
01976     {
01977         AS_STRING_CASE(QOF_NUMERIC_MATCH_DEBIT,)
01978         AS_STRING_CASE(QOF_NUMERIC_MATCH_CREDIT,)
01979         AS_STRING_CASE(QOF_NUMERIC_MATCH_ANY,)
01980     }
01981     return "UNKNOWN MATCH TYPE";
01982 }   /* qof_query_printNumericMatch */
01983 
01984 /*
01985  * Print out a string representation of the
01986  * QofGuidMatch enum
01987  */
01988 static gchar *
01989 qof_query_printGuidMatch (QofGuidMatch g)
01990 {
01991     switch (g)
01992     {
01993         AS_STRING_CASE(QOF_GUID_MATCH_ANY,)
01994         AS_STRING_CASE(QOF_GUID_MATCH_ALL,)
01995         AS_STRING_CASE(QOF_GUID_MATCH_NONE,)
01996         AS_STRING_CASE(QOF_GUID_MATCH_NULL,)
01997         AS_STRING_CASE(QOF_GUID_MATCH_LIST_ANY,)
01998     }
01999 
02000     return "UNKNOWN MATCH TYPE";
02001 }   /* qof_query_printGuidMatch */
02002 
02003 /*
02004  * Print out a string representation of the
02005  * QofCharMatch enum
02006  */
02007 static gchar *
02008 qof_query_printCharMatch (QofCharMatch c)
02009 {
02010     switch (c)
02011     {
02012         AS_STRING_CASE(QOF_CHAR_MATCH_ANY,)
02013         AS_STRING_CASE(QOF_CHAR_MATCH_NONE,)
02014     }
02015     return "UNKNOWN MATCH TYPE";
02016 }   /* qof_query_printGuidMatch */
02017 
02018 /* ======================== END OF FILE =================== */

Generated on Thu Jan 31 22:50:26 2008 for QOF by  doxygen 1.5.4