qofquery.c

00001 /********************************************************************\
00002  * qof_query.c -- Implement predicate API for searching for objects *
00003  * Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>                *
00004  *                                                                  *
00005  * This program is free software; you can redistribute it and/or    *
00006  * modify it under the terms of the GNU General Public License as   *
00007  * published by the Free Software Foundation; either version 2 of   *
00008  * the License, or (at your option) any later version.              *
00009  *                                                                  *
00010  * This program is distributed in the hope that it will be useful,  *
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00013  * GNU General Public License for more details.                     *
00014  *                                                                  *
00015  * You should have received a copy of the GNU General Public License*
00016  * along with this program; if not, contact:                        *
00017  *                                                                  *
00018  * Free Software Foundation           Voice:  +1-617-542-5942       *
00019  * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
00020  * Boston, MA  02111-1307,  USA       gnu@gnu.org                   *
00021  *                                                                  *
00022 \********************************************************************/
00023 
00024 #include "config.h"
00025 
00026 #include <sys/types.h>
00027 #include <time.h>
00028 #include <glib.h>
00029 #include <regex.h>
00030 #include <string.h>
00031 
00032 #include "gnc-trace.h"
00033 #include "gnc-engine-util.h"
00034 
00035 #include "qofbackend-p.h"
00036 #include "qofbook.h"
00037 #include "qofbook-p.h"
00038 #include "qofclass.h"
00039 #include "qofclass-p.h"
00040 #include "qofobject.h"
00041 #include "qofquery.h"
00042 #include "qofquery-p.h"
00043 #include "qofquerycore.h"
00044 #include "qofquerycore-p.h"
00045 
00046 static QofLogModule log_module = QOF_MOD_QUERY;
00047 
00048 struct _QofQueryTerm 
00049 {
00050   GSList *                param_list;
00051   QofQueryPredData        *pdata;
00052   gboolean                invert;
00053 
00054   /* These values are filled in during "compilation" of the query
00055    * term, based upon the obj_name, param_name, and searched-for
00056    * object type.  If conv_fcn is NULL, then we don't know how to
00057    * convert types.
00058    */
00059   GSList *                param_fcns;
00060   QofQueryPredicateFunc   pred_fcn;
00061 };
00062 
00063 struct _QofQuerySort 
00064 {
00065   GSList *            param_list;
00066   gint                options;
00067   gboolean            increasing;
00068 
00069   /* These values are filled in during "compilation" of the query
00070    * term, based upon the obj_name, param_name, and searched-for
00071    * object type.  If conv_fcn is NULL, then we don't know how to
00072    * convert types.
00073    */
00074   gboolean            use_default;
00075   GSList *            param_fcns;     /* Chain of paramters to walk */
00076   QofSortFunc         obj_cmp;        /* In case you are comparing objects */
00077   QofCompareFunc      comp_fcn;       /* When you are comparing core types */
00078 };
00079 
00080 /* The QUERY structure */
00081 struct _QofQuery 
00082 {
00083   /* The object type that we're searching for */
00084   QofIdType         search_for;
00085 
00086   /* terms is a list of the OR-terms in a sum-of-products 
00087    * logical expression. */
00088   GList *           terms;  
00089 
00090   /* sorting and chopping is independent of the search filter */
00091 
00092   QofQuerySort      primary_sort;
00093   QofQuerySort      secondary_sort;
00094   QofQuerySort      tertiary_sort;
00095   QofSortFunc       defaultSort;        /* <- Computed from search_for */
00096 
00097   /* The maximum number of results to return */
00098   int               max_results;
00099 
00100   /* list of books that will be participating in the query */
00101   GList *           books;
00102 
00103   /* a map of book to backend-compiled queries */
00104   GHashTable*       be_compiled;
00105 
00106   /* cache the results so we don't have to run the whole search 
00107    * again until it's really necessary */
00108   int               changed;
00109 
00110   GList *           results;
00111 };
00112 
00113 typedef struct _QofQueryCB 
00114 {
00115   QofQuery *        query;
00116   GList *           list;
00117   int               count;
00118 } QofQueryCB;
00119 
00120 /* initial_term will be owned by the new Query */
00121 static void query_init (QofQuery *q, QofQueryTerm *initial_term)
00122 {
00123   GList * or = NULL;
00124   GList *and = NULL;
00125   GHashTable *ht;
00126 
00127   if (initial_term) {
00128     or = g_list_alloc ();
00129     and = g_list_alloc ();
00130     and->data = initial_term;
00131     or->data = and;
00132   }
00133 
00134   if(q->terms)
00135     qof_query_clear (q);
00136 
00137   g_list_free (q->results);
00138   g_list_free (q->books);
00139 
00140   g_slist_free (q->primary_sort.param_list);
00141   g_slist_free (q->secondary_sort.param_list);
00142   g_slist_free (q->tertiary_sort.param_list);
00143 
00144   g_slist_free (q->primary_sort.param_fcns);
00145   g_slist_free (q->secondary_sort.param_fcns);
00146   g_slist_free (q->tertiary_sort.param_fcns);
00147 
00148   ht = q->be_compiled;
00149   memset (q, 0, sizeof (*q));
00150   q->be_compiled = ht;
00151 
00152   q->terms = or;
00153   q->changed = 1;
00154   q->max_results = -1;
00155 
00156   q->primary_sort.param_list = g_slist_prepend (NULL, QUERY_DEFAULT_SORT);
00157   q->primary_sort.increasing = TRUE;
00158   q->secondary_sort.increasing = TRUE;
00159   q->tertiary_sort.increasing = TRUE;
00160 }
00161 
00162 static void swap_terms (QofQuery *q1, QofQuery *q2)
00163 {
00164   GList *g;
00165 
00166   if (!q1 || !q2) return;
00167 
00168   g = q1->terms;
00169   q1->terms = q2->terms;
00170   q2->terms = g;
00171 
00172   g = q1->books;
00173   q1->books = q2->books;
00174   q2->books = g;
00175 
00176   q1->changed = 1;
00177   q2->changed = 1;
00178 }
00179 
00180 static void free_query_term (QofQueryTerm *qt)
00181 {
00182   if (!qt) return;
00183 
00184   qof_query_core_predicate_free (qt->pdata);
00185   g_slist_free (qt->param_list);
00186   g_slist_free (qt->param_fcns);
00187   g_free (qt);
00188 }
00189 
00190 static QofQueryTerm * copy_query_term (QofQueryTerm *qt)
00191 {
00192   QofQueryTerm *new_qt;
00193   if (!qt) return NULL;
00194 
00195   new_qt = g_new0 (QofQueryTerm, 1);
00196   memcpy (new_qt, qt, sizeof(QofQueryTerm));
00197   new_qt->param_list = g_slist_copy (qt->param_list);
00198   new_qt->param_fcns = g_slist_copy (qt->param_fcns);
00199   new_qt->pdata = qof_query_core_predicate_copy (qt->pdata);
00200   return new_qt;
00201 }
00202 
00203 static GList * copy_and_terms (GList *and_terms)
00204 {
00205   GList *and = NULL;
00206   GList *cur_and;
00207 
00208   for(cur_and = and_terms; cur_and; cur_and = cur_and->next)
00209   {
00210     and = g_list_prepend(and, copy_query_term (cur_and->data));
00211   }
00212 
00213   return g_list_reverse(and);
00214 }
00215 
00216 static GList * 
00217 copy_or_terms(GList * or_terms) 
00218 {
00219   GList * or = NULL;
00220   GList * cur_or;
00221 
00222   for(cur_or = or_terms; cur_or; cur_or = cur_or->next)
00223   {
00224     or = g_list_prepend(or, copy_and_terms(cur_or->data));
00225   }
00226 
00227   return g_list_reverse(or);
00228 }
00229 
00230 static void copy_sort (QofQuerySort *dst, const QofQuerySort *src)
00231 {
00232   memcpy (dst, src, sizeof (*dst));
00233   dst->param_list = g_slist_copy (src->param_list);
00234   dst->param_fcns = g_slist_copy (src->param_fcns);
00235 }
00236 
00237 static void free_sort (QofQuerySort *s)
00238 {
00239   g_slist_free (s->param_list);
00240   s->param_list = NULL;
00241 
00242   g_slist_free (s->param_fcns);
00243   s->param_fcns = NULL;
00244 }
00245 
00246 static void free_members (QofQuery *q)
00247 {
00248   GList * cur_or;
00249 
00250   if (q == NULL) return;
00251 
00252   for(cur_or = q->terms; cur_or; cur_or = cur_or->next) 
00253   {
00254     GList * cur_and;
00255 
00256     for(cur_and = cur_or->data; cur_and; cur_and = cur_and->next) 
00257     {
00258       free_query_term(cur_and->data);
00259       cur_and->data = NULL;
00260     }
00261 
00262     g_list_free(cur_or->data);
00263     cur_or->data = NULL;
00264   }
00265 
00266   free_sort (&(q->primary_sort));
00267   free_sort (&(q->secondary_sort));
00268   free_sort (&(q->tertiary_sort));
00269 
00270   g_list_free(q->terms);
00271   q->terms = NULL;
00272 
00273   g_list_free(q->books);
00274   q->books = NULL;
00275 
00276   g_list_free(q->results);
00277   q->results = NULL;
00278 }
00279 
00280 static int cmp_func (QofQuerySort *sort, QofSortFunc default_sort,
00281                      gconstpointer a, gconstpointer b)
00282 {
00283   QofParam *param = NULL;
00284   GSList *node;
00285   gpointer conva, convb;
00286 
00287   g_return_val_if_fail (sort, 0);
00288 
00289   /* See if this is a default sort */
00290   if (sort->use_default) 
00291   {
00292     if (default_sort) return default_sort ((gpointer)a, (gpointer)b);
00293     return 0;
00294   }
00295 
00296   /* If no parameters, consider them equal */
00297   if (!sort->param_fcns) return 0;
00298 
00299   /* no compare function, consider the two objects equal */
00300   if (!sort->comp_fcn && !sort->obj_cmp) return 0;
00301   
00302   /* Do the list of conversions */
00303   conva = (gpointer)a;
00304   convb = (gpointer)b;
00305   for (node = sort->param_fcns; node; node = node->next) 
00306   {
00307     param = node->data;
00308 
00309     /* The last term is really the "parameter getter",
00310      * unless we're comparing objects ;) */
00311     if (!node->next && !sort->obj_cmp)
00312       break;
00313 
00314     /* Do the converstions */
00315     conva = (param->param_getfcn) (conva, param);
00316     convb = (param->param_getfcn) (convb, param);
00317   }
00318 
00319   /* And now return the (appropriate) compare */
00320   if (sort->comp_fcn)
00321   {
00322     int rc = sort->comp_fcn (conva, convb, sort->options, param);
00323     return rc;
00324   }
00325 
00326   return sort->obj_cmp (conva, convb);
00327 }
00328 
00329 static QofQuery * sortQuery = NULL;
00330 
00331 static int sort_func (gconstpointer a, gconstpointer b)
00332 {
00333   int retval;
00334 
00335   g_return_val_if_fail (sortQuery, 0);
00336 
00337   retval = cmp_func (&(sortQuery->primary_sort), sortQuery->defaultSort, a, b);
00338   if (retval == 0) 
00339   {
00340     retval = cmp_func (&(sortQuery->secondary_sort), sortQuery->defaultSort,
00341                        a, b);
00342     if (retval == 0) 
00343     {
00344       retval = cmp_func (&(sortQuery->tertiary_sort), sortQuery->defaultSort,
00345                          a, b);
00346       return sortQuery->tertiary_sort.increasing ? retval : -retval;
00347     } 
00348     else 
00349     {
00350       return sortQuery->secondary_sort.increasing ? retval : -retval;
00351     }
00352   } 
00353   else 
00354   {
00355     return sortQuery->primary_sort.increasing ? retval : -retval;
00356   }
00357 }
00358 
00359 /* ==================================================================== */ 
00360 /* This is the main workhorse for performing the query.  For each
00361  * object, it walks over all of the query terms to see if the 
00362  * object passes the seive.
00363  */
00364 
00365 static int 
00366 check_object (QofQuery *q, gpointer object)
00367 {
00368   GList     * and_ptr;
00369   GList     * or_ptr;
00370   QofQueryTerm * qt;
00371   int       and_terms_ok=1;
00372   
00373   ENTER (" object=%p terms=%p name=%s", 
00374           object, q->terms, qof_object_printable (q->search_for, object));
00375 
00376   for(or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next) 
00377   {
00378     and_terms_ok = 1;
00379     for(and_ptr = or_ptr->data; and_ptr; and_ptr = and_ptr->next) 
00380     {
00381       qt = (QofQueryTerm *)(and_ptr->data);
00382       if (qt->param_fcns && qt->pred_fcn) 
00383       {
00384         GSList *node;
00385         QofParam *param = NULL;
00386         gpointer conv_obj = object;
00387 
00388         /* iterate through the conversions */
00389         for (node = qt->param_fcns; node; node = node->next) 
00390         {
00391           param = node->data;
00392 
00393           /* The last term is the actual parameter getter */
00394           if (!node->next) break;
00395 
00396           conv_obj = param->param_getfcn (conv_obj, param);
00397         }
00398 
00399         if (((qt->pred_fcn)(conv_obj, param, qt->pdata)) == qt->invert) 
00400         {
00401           and_terms_ok = 0;
00402           break;
00403         }
00404       } 
00405       else 
00406       {
00407         /* XXX: Don't know how to do this conversion -- do we care? */
00408       }
00409     }
00410     if (and_terms_ok) 
00411     {
00412       LEAVE (" (terms are OK)");
00413       return 1;
00414     }
00415   }
00416 
00417   /* If there are no terms, assume a "match any" applies.
00418    * A query with no terms is still meaningful, since the user
00419    * may want to get all objects, but in a particular sorted 
00420    * order.
00421    */
00422   LEAVE (" ");
00423   if (NULL == q->terms) return 1;
00424   return 0;
00425 }
00426 
00427 /* walk the list of parameters, starting with the given object, and
00428  * compile the list of parameter get-functions.  Save the last valid
00429  * parameter definition in "final" and return the list of functions.
00430  *
00431  * returns NULL if the first parameter is bad (and final is unchanged).
00432  */
00433 static GSList * 
00434 compile_params (GSList *param_list, QofIdType start_obj, 
00435                 QofParam const **final)
00436 {
00437   const QofParam *objDef = NULL;
00438   GSList *fcns = NULL;
00439 
00440   ENTER ("param_list=%p id=%s", param_list, start_obj);
00441   g_return_val_if_fail (param_list, NULL);
00442   g_return_val_if_fail (start_obj, NULL);
00443   g_return_val_if_fail (final, NULL);
00444 
00445   for (; param_list; param_list = param_list->next) 
00446   {
00447     QofIdType param_name = param_list->data;
00448     objDef = qof_class_get_parameter (start_obj, param_name);
00449 
00450     /* If it doesn't exist, then we've reached the end */
00451     if (!objDef) break;
00452 
00453     /* Save off this parameter */
00454     fcns = g_slist_prepend (fcns, (gpointer) objDef);
00455 
00456     /* Save this off, just in case */
00457     *final = objDef;
00458 
00459     /* And reset for the next parameter */
00460     start_obj = (QofIdType) objDef->param_type;
00461   }
00462 
00463   LEAVE ("fcns=%p", fcns);
00464   return (g_slist_reverse (fcns));
00465 }
00466 
00467 static void 
00468 compile_sort (QofQuerySort *sort, QofIdType obj)
00469 {
00470   const QofParam *resObj = NULL;
00471 
00472   ENTER ("sort=%p id=%s params=%p", sort, obj, sort->param_list);
00473   sort->use_default = FALSE;
00474 
00475   g_slist_free (sort->param_fcns);
00476   sort->param_fcns = NULL;
00477   sort->comp_fcn = NULL;
00478   sort->obj_cmp = NULL;
00479 
00480   /* An empty param_list implies "no sort" */
00481   if (!sort->param_list) { LEAVE (" "); return; }
00482 
00483   /* Walk the parameter list of obtain the parameter functions */
00484   sort->param_fcns = compile_params (sort->param_list, obj, &resObj);
00485 
00486   /* If we have valid parameters, grab the compare function,
00487    * If not, check if this is the default sort.
00488    */
00489   if (sort->param_fcns) 
00490   {
00491     sort->comp_fcn = qof_query_core_get_compare (resObj->param_type);
00492 
00493     /* Hrm, perhaps this is an object compare, not a core compare? */
00494     if (sort->comp_fcn == NULL)
00495     {
00496       sort->obj_cmp = qof_class_get_default_sort (resObj->param_type);
00497     }
00498   } 
00499   else if (!safe_strcmp (sort->param_list->data, QUERY_DEFAULT_SORT))
00500   {
00501     sort->use_default = TRUE;
00502   }
00503   LEAVE ("sort=%p id=%s", sort, obj);
00504 }
00505 
00506 static void compile_terms (QofQuery *q)
00507 {
00508   GList *or_ptr, *and_ptr, *node;
00509 
00510   ENTER (" query=%p", q);
00511   /* Find the specific functions for this Query.  Note that the
00512    * Query's search_for should now be set to the new type.
00513    */
00514   for (or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next) {
00515     for (and_ptr = or_ptr->data; and_ptr; and_ptr = and_ptr->next) {
00516       QofQueryTerm *qt = and_ptr->data;
00517       const QofParam *resObj = NULL;
00518       
00519       g_slist_free (qt->param_fcns);
00520       qt->param_fcns = NULL;
00521 
00522       /* Walk the parameter list of obtain the parameter functions */
00523       qt->param_fcns = compile_params (qt->param_list, q->search_for,
00524                                        &resObj);
00525 
00526       /* If we have valid parameters, grab the predicate function,
00527        * If not, see if this is the default sort.
00528        */
00529 
00530       if (qt->param_fcns)
00531         qt->pred_fcn = qof_query_core_get_predicate (resObj->param_type);
00532       else
00533         qt->pred_fcn = NULL;
00534     }
00535   }
00536 
00537   /* Update the sort functions */
00538   compile_sort (&(q->primary_sort), q->search_for);
00539   compile_sort (&(q->secondary_sort), q->search_for);
00540   compile_sort (&(q->tertiary_sort), q->search_for);
00541 
00542   q->defaultSort = qof_class_get_default_sort (q->search_for);
00543 
00544   /* Now compile the backend instances */
00545   for (node = q->books; node; node = node->next) {
00546     QofBook *book = node->data;
00547     QofBackend *be = book->backend;
00548 
00549     if (be && be->compile_query) {
00550       gpointer result = (be->compile_query)(be, q);
00551       if (result)
00552         g_hash_table_insert (q->be_compiled, book, result);
00553     }
00554   }
00555   LEAVE (" query=%p", q);
00556 }
00557 
00558 static void check_item_cb (gpointer object, gpointer user_data)
00559 {
00560   QofQueryCB *ql = user_data;
00561 
00562   if (!object || !ql) return;
00563 
00564   if (check_object (ql->query, object)) {
00565     ql->list = g_list_prepend (ql->list, object);
00566     ql->count++;
00567   }
00568   return;
00569 }
00570 
00571 static int param_list_cmp (GSList *l1, GSList *l2)
00572 {
00573   while (1) {
00574     int ret;
00575 
00576     /* Check the easy stuff */
00577     if (!l1 && !l2) return 0;
00578     if (!l1 && l2) return -1;
00579     if (l1 && !l2) return 1;
00580 
00581     ret = safe_strcmp (l1->data, l2->data);
00582     if (ret)
00583       return ret;
00584 
00585     l1 = l1->next;
00586     l2 = l2->next;
00587   }
00588 }
00589 
00590 static GList * merge_books (GList *l1, GList *l2)
00591 {
00592   GList *res = NULL;
00593   GList *node;
00594 
00595   res = g_list_copy (l1);
00596 
00597   for (node = l2; node; node = node->next) {
00598     if (g_list_index (res, node->data) == -1)
00599       res = g_list_prepend (res, node->data);
00600   }
00601 
00602   return res;
00603 }
00604 
00605 static gboolean
00606 query_free_compiled (gpointer key, gpointer value, gpointer not_used)
00607 {
00608   QofBook* book = key;
00609   QofBackend* be = book->backend;
00610 
00611   if (be && be->free_query)
00612     (be->free_query)(be, value);
00613 
00614   return TRUE;
00615 }
00616 
00617 /* clear out any cached query_compilations */
00618 static void query_clear_compiles (QofQuery *q)
00619 {
00620   g_hash_table_foreach_remove (q->be_compiled, query_free_compiled, NULL);
00621 }
00622 
00623 /********************************************************************/
00624 /* PUBLISHED API FUNCTIONS */
00625 
00626 GSList *
00627 qof_query_build_param_list (char const *param, ...)
00628 {
00629   GSList *param_list = NULL;
00630   char const *this_param;
00631   va_list ap;
00632 
00633   if (!param)
00634     return NULL;
00635 
00636   va_start (ap, param);
00637 
00638   for (this_param = param; this_param; this_param = va_arg (ap, const char *))
00639     param_list = g_slist_prepend (param_list, (gpointer)this_param);
00640 
00641   va_end (ap);
00642 
00643   return g_slist_reverse (param_list);
00644 }
00645 
00646 void qof_query_add_term (QofQuery *q, GSList *param_list,                      
00647                       QofQueryPredData *pred_data, QofQueryOp op)
00648 {
00649   QofQueryTerm *qt;
00650   QofQuery *qr, *qs;
00651 
00652   if (!q || !param_list || !pred_data) return;
00653 
00654   qt = g_new0 (QofQueryTerm, 1);
00655   qt->param_list = param_list;
00656   qt->pdata = pred_data;
00657   qs = qof_query_create ();
00658   query_init (qs, qt);
00659 
00660   if (qof_query_has_terms (q))
00661     qr = qof_query_merge (q, qs, op);
00662   else
00663     qr = qof_query_merge (q, qs, QOF_QUERY_OR);
00664 
00665   swap_terms (q, qr);
00666   qof_query_destroy (qs);
00667   qof_query_destroy (qr);
00668 }
00669 
00670 void qof_query_purge_terms (QofQuery *q, GSList *param_list)
00671 {
00672   QofQueryTerm *qt;
00673   GList *or, *and;
00674 
00675   if (!q || !param_list) return;
00676 
00677   for (or = q->terms; or; or = or->next) {
00678     for (and = or->data; and; and = and->next) {
00679       qt = and->data;
00680       if (!param_list_cmp (qt->param_list, param_list)) {
00681         if (g_list_length (or->data) == 1) {
00682           q->terms = g_list_remove_link (q->terms, or);
00683           g_list_free_1 (or);
00684           or = q->terms;
00685           break;
00686         } else {
00687           or->data = g_list_remove_link (or->data, and);
00688           g_list_free_1 (and);
00689           and = or->data;
00690           if (!and) break;
00691         }
00692         q->changed = 1;
00693         free_query_term (qt);
00694       }
00695     }
00696     if (!or) break;
00697   }
00698 }
00699 
00700 GList * qof_query_run (QofQuery *q)
00701 {
00702   GList *matching_objects = NULL;
00703   GList *node;
00704   int        object_count = 0;
00705 
00706   if (!q) return NULL;
00707   g_return_val_if_fail (q->search_for, NULL);
00708   g_return_val_if_fail (q->books, NULL);
00709   ENTER (" q=%p", q);
00710 
00711   /* XXX: Prioritize the query terms? */
00712 
00713   /* prepare the Query for processing */
00714   if (q->changed) 
00715   {
00716     query_clear_compiles (q);
00717     compile_terms (q);
00718   }
00719 
00720   /* Maybe log this sucker */
00721   if (gnc_should_log (log_module, GNC_LOG_DETAIL)) qof_query_print (q);
00722 
00723   /* Now run the query over all the objects and save the results */
00724   {
00725     QofQueryCB qcb;
00726 
00727     memset (&qcb, 0, sizeof (qcb));
00728     qcb.query = q;
00729 
00730     /* For each book */
00731     for (node=q->books; node; node=node->next) 
00732     {
00733       QofBook *book = node->data;
00734       QofBackend *be = book->backend;
00735 
00736       /* run the query in the backend */
00737       if (be) 
00738       {
00739         gpointer compiled_query = g_hash_table_lookup (q->be_compiled, book);
00740 
00741         if (compiled_query && be->run_query)
00742         {
00743           (be->run_query) (be, compiled_query);
00744         }
00745       }
00746 
00747       /* And then iterate over all the objects */
00748       qof_object_foreach (q->search_for, book, (QofEntityForeachCB) check_item_cb, &qcb);
00749     }
00750 
00751     matching_objects = qcb.list;
00752     object_count = qcb.count;
00753   }
00754   PINFO ("matching objects=%p count=%d", matching_objects, object_count);
00755 
00756   /* There is no absolute need to reverse this list, since it's being
00757    * sorted below. However, in the common case, we will be searching
00758    * in a confined location where the objects are already in order,
00759    * thus reversing will put us in the correct order we want and make
00760    * the sorting go much faster.
00761    */
00762   matching_objects = g_list_reverse(matching_objects);
00763 
00764   /* Now sort the matching objects based on the search criteria
00765    * sortQuery is an unforgivable use of static global data...  
00766    * I just can't figure out how else to do this sanely.
00767    */
00768   if (q->primary_sort.comp_fcn || q->primary_sort.obj_cmp ||
00769       (q->primary_sort.use_default && q->defaultSort))
00770   {
00771     sortQuery = q;
00772     matching_objects = g_list_sort(matching_objects, sort_func);
00773     sortQuery = NULL;
00774   }
00775 
00776   /* Crop the list to limit the number of splits. */
00777   if((object_count > q->max_results) && (q->max_results > -1)) 
00778   {
00779     if(q->max_results > 0) 
00780     {
00781       GList *mptr;
00782 
00783       /* mptr is set to the first node of what will be the new list */
00784       mptr = g_list_nth(matching_objects, object_count - q->max_results);
00785       /* mptr should not be NULL, but let's be safe */
00786       if (mptr != NULL) 
00787       {
00788         if (mptr->prev != NULL) mptr->prev->next = NULL;
00789         mptr->prev = NULL;
00790       }
00791       g_list_free(matching_objects);
00792       matching_objects = mptr;
00793     }
00794     else 
00795     { 
00796       /* q->max_results == 0 */
00797       g_list_free(matching_objects);
00798       matching_objects = NULL;
00799     }
00800     object_count = q->max_results;
00801   }
00802   
00803   q->changed = 0;
00804   
00805   g_list_free(q->results);
00806   q->results = matching_objects;
00807   
00808   LEAVE (" q=%p", q);
00809   return matching_objects;
00810 }
00811 
00812 GList *
00813 qof_query_last_run (QofQuery *query)
00814 {
00815   if (!query)
00816     return NULL;
00817 
00818   return query->results;
00819 }
00820 
00821 void qof_query_clear (QofQuery *query)
00822 {
00823   QofQuery *q2 = qof_query_create ();
00824   swap_terms (query, q2);
00825   qof_query_destroy (q2);
00826 
00827   g_list_free (query->books);
00828   query->books = NULL;
00829   g_list_free (query->results);
00830   query->results = NULL;
00831   query->changed = 1;
00832 }
00833 
00834 QofQuery * qof_query_create (void)
00835 {
00836   QofQuery *qp = g_new0 (QofQuery, 1);
00837   qp->be_compiled = g_hash_table_new (g_direct_hash, g_direct_equal);
00838   query_init (qp, NULL);
00839   return qp;
00840 }
00841 
00842 void qof_query_search_for (QofQuery *q, QofIdTypeConst obj_type)
00843 {
00844   if (!q || !obj_type)
00845     return;
00846 
00847   if (safe_strcmp (q->search_for, obj_type)) {
00848     q->search_for = (QofIdType) obj_type;
00849     q->changed = 1;
00850   }
00851 }
00852 
00853 QofQuery * qof_query_create_for (QofIdTypeConst obj_type)
00854 {
00855   QofQuery *q;
00856   if (!obj_type)
00857     return NULL;
00858   q = qof_query_create ();
00859   qof_query_search_for (q, obj_type);
00860   return q;
00861 }
00862 
00863 int qof_query_has_terms (QofQuery *q)
00864 {
00865   if (!q) return 0;
00866   return g_list_length (q->terms);
00867 }
00868 
00869 int qof_query_num_terms (QofQuery *q)
00870 {
00871   GList *o;
00872   int n = 0;
00873   if (!q) return 0;
00874   for (o = q->terms; o; o=o->next)
00875     n += g_list_length(o->data);
00876   return n;
00877 }
00878 
00879 gboolean qof_query_has_term_type (QofQuery *q, GSList *term_param)
00880 {
00881   GList *or;
00882   GList *and;
00883 
00884   if (!q || !term_param)
00885     return FALSE;
00886 
00887   for(or = q->terms; or; or = or->next) {
00888     for(and = or->data; and; and = and->next) {
00889       QofQueryTerm *qt = and->data;
00890       if (!param_list_cmp (term_param, qt->param_list))
00891         return TRUE;
00892     }
00893   }
00894 
00895   return FALSE;
00896 }
00897 
00898 GSList * qof_query_get_term_type (QofQuery *q, GSList *term_param)
00899 {
00900   GList *or;
00901   GList *and;
00902   GSList *results = NULL;
00903 
00904   if (!q || !term_param)
00905     return FALSE;
00906 
00907   for(or = q->terms; or; or = or->next) {
00908     for(and = or->data; and; and = and->next) {
00909       QofQueryTerm *qt = and->data;
00910       if (!param_list_cmp (term_param, qt->param_list))
00911         results = g_slist_append(results, qt->pdata);
00912     }
00913   }
00914 
00915   return results;
00916 }
00917 
00918 void qof_query_destroy (QofQuery *q)
00919 {
00920   if (!q) return;
00921   free_members (q);
00922   query_clear_compiles (q);
00923   g_hash_table_destroy (q->be_compiled);
00924   g_free (q);
00925 }
00926 
00927 QofQuery * qof_query_copy (QofQuery *q)
00928 {
00929   QofQuery *copy;
00930   GHashTable *ht;
00931 
00932   if (!q) return NULL;
00933   copy = qof_query_create ();
00934   ht = copy->be_compiled;
00935   free_members (copy);
00936 
00937   memcpy (copy, q, sizeof (QofQuery));
00938 
00939   copy->be_compiled = ht;
00940   copy->terms = copy_or_terms (q->terms);
00941   copy->books = g_list_copy (q->books);
00942   copy->results = g_list_copy (q->results);
00943 
00944   copy_sort (&(copy->primary_sort), &(q->primary_sort));
00945   copy_sort (&(copy->secondary_sort), &(q->secondary_sort));
00946   copy_sort (&(copy->tertiary_sort), &(q->tertiary_sort));
00947 
00948   copy->changed = 1;
00949 
00950   return copy;
00951 }
00952 
00953 /* *******************************************************************
00954  * qof_query_invert 
00955  * return a newly-allocated Query object which is the 
00956  * logical inverse of the original.
00957  ********************************************************************/
00958 
00959 QofQuery * qof_query_invert (QofQuery *q)
00960 {
00961   QofQuery  * retval;
00962   QofQuery  * right, * left, * iright, * ileft;
00963   QofQueryTerm * qt;
00964   GList  * aterms;
00965   GList  * cur;
00966   GList  * new_oterm;
00967   int    num_or_terms;
00968 
00969   if (!q)
00970     return NULL;
00971 
00972   num_or_terms = g_list_length(q->terms);
00973 
00974   switch(num_or_terms) 
00975   {
00976   case 0:
00977     retval = qof_query_create();
00978     retval->max_results = q->max_results;
00979     break;
00980 
00981     /* This is the DeMorgan expansion for a single AND expression. */
00982     /* !(abc) = !a + !b + !c */
00983   case 1:
00984     retval = qof_query_create();
00985     retval->max_results = q->max_results;
00986     retval->books = g_list_copy (q->books);
00987     retval->search_for = q->search_for;
00988     retval->changed = 1;
00989 
00990     aterms = g_list_nth_data(q->terms, 0);
00991     new_oterm = NULL;
00992     for(cur=aterms; cur; cur=cur->next) {
00993       qt = copy_query_term(cur->data);
00994       qt->invert = !(qt->invert);
00995       new_oterm = g_list_append(NULL, qt);
00996 
00997       /* g_list_append() can take forever, so let's do this for speed
00998        * in "large" queries.
00999        */
01000       retval->terms = g_list_reverse(retval->terms);
01001       retval->terms = g_list_prepend(retval->terms, new_oterm);
01002       retval->terms = g_list_reverse(retval->terms);
01003     }
01004     break;
01005 
01006     /* If there are multiple OR-terms, we just recurse by 
01007      * breaking it down to !(a + b + c) = 
01008      * !a * !(b + c) = !a * !b * !c.  */
01009   default:
01010     right        = qof_query_create();
01011     right->terms = copy_or_terms(g_list_nth(q->terms, 1));
01012 
01013     left         = qof_query_create();
01014     left->terms  = g_list_append(NULL, 
01015                                  copy_and_terms(g_list_nth_data(q->terms, 0)));
01016 
01017     iright       = qof_query_invert(right);
01018     ileft        = qof_query_invert(left);
01019 
01020     retval = qof_query_merge(iright, ileft, QOF_QUERY_AND);
01021     retval->books          = g_list_copy (q->books);
01022     retval->max_results    = q->max_results;
01023     retval->search_for     = q->search_for;
01024     retval->changed        = 1;
01025 
01026     qof_query_destroy(iright);
01027     qof_query_destroy(ileft);
01028     qof_query_destroy(right);
01029     qof_query_destroy(left);
01030     break;
01031   }
01032 
01033   return retval;
01034 }
01035 
01036 /* *******************************************************************
01037  * qof_query_merge
01038  * combine 2 Query objects by the logical operation in "op".
01039  ********************************************************************/
01040 
01041 QofQuery * 
01042 qof_query_merge(QofQuery *q1, QofQuery *q2, QofQueryOp op)
01043 {
01044   
01045   QofQuery * retval = NULL;
01046   QofQuery * i1, * i2;
01047   QofQuery * t1, * t2;
01048   GList * i, * j;
01049   QofIdType search_for;
01050 
01051   if(!q1) return q2;
01052   if(!q2) return q1;
01053 
01054   if (q1->search_for && q2->search_for)
01055     g_return_val_if_fail (safe_strcmp (q1->search_for, q2->search_for) == 0,
01056                           NULL);
01057 
01058   search_for = (q1->search_for ? q1->search_for : q2->search_for);
01059 
01060   /* Avoid merge surprises if op==QOF_QUERY_AND but q1 is empty.
01061    * The goal of this tweak is to all the user to start with
01062    * an empty q1 and then append to it recursively
01063    * (and q1 (and q2 (and q3 (and q4 ....))))
01064    * without bombing out because the append started with an 
01065    * empty list.
01066    * We do essentially the same check in qof_query_add_term()
01067    * so that the first term added to an empty query doesn't screw up.
01068    */
01069   if ((QOF_QUERY_AND == op) && (0 == qof_query_has_terms (q1)))
01070   {
01071     op = QOF_QUERY_OR;
01072   }
01073 
01074   switch(op) 
01075   {
01076   case QOF_QUERY_OR:
01077     retval = qof_query_create();
01078     retval->terms = 
01079       g_list_concat(copy_or_terms(q1->terms), copy_or_terms(q2->terms));
01080     retval->books           = merge_books (q1->books, q2->books);
01081     retval->max_results    = q1->max_results;
01082     retval->changed        = 1;
01083     break;
01084 
01085   case QOF_QUERY_AND:
01086     retval = qof_query_create();
01087     retval->books          = merge_books (q1->books, q2->books);
01088     retval->max_results    = q1->max_results;
01089     retval->changed        = 1;
01090 
01091     /* g_list_append() can take forever, so let's build the list in
01092      * reverse and then reverse it at the end, to deal better with
01093      * "large" queries.
01094      */
01095     for(i=q1->terms; i; i=i->next) 
01096     {
01097       for(j=q2->terms; j; j=j->next) 
01098       {
01099         retval->terms = 
01100           g_list_prepend(retval->terms, 
01101                         g_list_concat
01102                         (copy_and_terms(i->data),
01103                          copy_and_terms(j->data)));
01104       }
01105     }
01106     retval->terms = g_list_reverse(retval->terms);
01107     break;
01108 
01109   case QOF_QUERY_NAND:
01110     /* !(a*b) = (!a + !b) */
01111     i1     = qof_query_invert(q1);
01112     i2     = qof_query_invert(q2);
01113     retval = qof_query_merge(i1, i2, QOF_QUERY_OR);
01114     qof_query_destroy(i1);
01115     qof_query_destroy(i2);
01116     break;
01117 
01118   case QOF_QUERY_NOR:
01119     /* !(a+b) = (!a*!b) */
01120     i1     = qof_query_invert(q1);
01121     i2     = qof_query_invert(q2);
01122     retval = qof_query_merge(i1, i2, QOF_QUERY_AND);
01123     qof_query_destroy(i1);
01124     qof_query_destroy(i2);
01125     break;
01126 
01127   case QOF_QUERY_XOR:
01128     /* a xor b = (a * !b) + (!a * b) */
01129     i1     = qof_query_invert(q1);
01130     i2     = qof_query_invert(q2);
01131     t1     = qof_query_merge(q1, i2, QOF_QUERY_AND);
01132     t2     = qof_query_merge(i1, q2, QOF_QUERY_AND);
01133     retval = qof_query_merge(t1, t2, QOF_QUERY_OR);
01134 
01135     qof_query_destroy(i1);
01136     qof_query_destroy(i2);
01137     qof_query_destroy(t1);
01138     qof_query_destroy(t2);     
01139     break;
01140   }
01141 
01142   retval->search_for = search_for;
01143   return retval;
01144 }
01145 
01146 void
01147 qof_query_merge_in_place(QofQuery *q1, QofQuery *q2, QofQueryOp op)
01148 {
01149   QofQuery *tmp_q;
01150 
01151   if (!q1 || !q2)
01152     return;
01153 
01154   tmp_q = qof_query_merge (q1, q2, op);
01155   swap_terms (q1, tmp_q);
01156   qof_query_destroy (tmp_q);
01157 }
01158 
01159 void
01160 qof_query_set_sort_order (QofQuery *q,
01161                       GSList *params1, GSList *params2, GSList *params3)
01162 {
01163   if (!q) return;
01164   if (q->primary_sort.param_list)
01165     g_slist_free (q->primary_sort.param_list);
01166   q->primary_sort.param_list = params1;
01167   q->primary_sort.options = 0;
01168 
01169   if (q->secondary_sort.param_list)
01170     g_slist_free (q->secondary_sort.param_list);
01171   q->secondary_sort.param_list = params2;
01172   q->secondary_sort.options = 0;
01173 
01174   if (q->tertiary_sort.param_list)
01175     g_slist_free (q->tertiary_sort.param_list);
01176   q->tertiary_sort.param_list = params3;
01177   q->tertiary_sort.options = 0;
01178 
01179   q->changed = 1;
01180 }
01181 
01182 void qof_query_set_sort_options (QofQuery *q, gint prim_op, gint sec_op,
01183                              gint tert_op)
01184 {
01185   if (!q) return;
01186   q->primary_sort.options = prim_op;
01187   q->secondary_sort.options = sec_op;
01188   q->tertiary_sort.options = tert_op;
01189 }
01190 
01191 void qof_query_set_sort_increasing (QofQuery *q, gboolean prim_inc,
01192                                 gboolean sec_inc, gboolean tert_inc)
01193 {
01194   if (!q) return;
01195   q->primary_sort.increasing = prim_inc;
01196   q->secondary_sort.increasing = sec_inc;
01197   q->tertiary_sort.increasing = tert_inc;
01198 }
01199 
01200 void qof_query_set_max_results (QofQuery *q, int n)
01201 {
01202   if (!q) return;
01203   q->max_results = n;
01204 }
01205 
01206 void qof_query_add_guid_list_match (QofQuery *q, GSList *param_list,
01207                                GList *guid_list, QofGuidMatch options,
01208                                QofQueryOp op)
01209 {
01210   QofQueryPredData *pdata;
01211 
01212   if (!q || !param_list) return;
01213 
01214   if (!guid_list)
01215     g_return_if_fail (options == QOF_GUID_MATCH_NULL);
01216 
01217   pdata = qof_query_guid_predicate (options, guid_list);
01218   qof_query_add_term (q, param_list, pdata, op);
01219 }
01220 
01221 void qof_query_add_guid_match (QofQuery *q, GSList *param_list,
01222                            const GUID *guid, QofQueryOp op)
01223 {
01224   GList *g = NULL;
01225 
01226   if (!q || !param_list) return;
01227 
01228   if (guid)
01229     g = g_list_prepend (g, (gpointer)guid);
01230 
01231   qof_query_add_guid_list_match (q, param_list, g,
01232                             g ? QOF_GUID_MATCH_ANY : QOF_GUID_MATCH_NULL, op);
01233 
01234   g_list_free (g);
01235 }
01236 
01237 void qof_query_set_book (QofQuery *q, QofBook *book)
01238 {
01239   GSList *slist = NULL;
01240   if (!q || !book) return;
01241 
01242   /* Make sure this book is only in the list once */
01243   if (g_list_index (q->books, book) == -1)
01244     q->books = g_list_prepend (q->books, book);
01245 
01246   g_slist_prepend (slist, QOF_PARAM_GUID);
01247   g_slist_prepend (slist, QOF_PARAM_BOOK);
01248   qof_query_add_guid_match (q, slist,
01249                         qof_book_get_guid(book), QOF_QUERY_AND);
01250 }
01251 
01252 GList * qof_query_get_books (QofQuery *q)
01253 {
01254   if (!q) return NULL;
01255   return q->books;
01256 }
01257 
01258 void qof_query_add_boolean_match (QofQuery *q, GSList *param_list, gboolean value,
01259                               QofQueryOp op)
01260 {
01261   QofQueryPredData *pdata;
01262   if (!q || !param_list) return;
01263 
01264   pdata = qof_query_boolean_predicate (QOF_COMPARE_EQUAL, value);
01265   qof_query_add_term (q, param_list, pdata, op);
01266 }
01267 
01268 /**********************************************************************/
01269 /* PRIVATE PUBLISHED API FUNCTIONS                                    */
01270 
01271 void qof_query_init (void)
01272 {
01273   ENTER (" ");
01274   qof_query_core_init ();
01275   qof_class_init ();
01276 }
01277 
01278 void qof_query_shutdown (void)
01279 {
01280   qof_class_shutdown ();
01281   qof_query_core_shutdown ();
01282 }
01283 
01284 int qof_query_get_max_results (QofQuery *q)
01285 {
01286   if (!q) return 0;
01287   return q->max_results;
01288 }
01289 
01290 QofIdType qof_query_get_search_for (QofQuery *q)
01291 {
01292   if (!q) return NULL;
01293   return q->search_for;
01294 }
01295 
01296 GList * qof_query_get_terms (QofQuery *q)
01297 {
01298   if (!q) return NULL;
01299   return q->terms;
01300 }
01301 
01302 GSList * qof_query_term_get_param_path (QofQueryTerm *qt)
01303 {
01304   if (!qt)
01305     return NULL;
01306   return qt->param_list;
01307 }
01308 
01309 QofQueryPredData *qof_query_term_get_pred_data (QofQueryTerm *qt)
01310 {
01311   if (!qt)
01312     return NULL;
01313   return qt->pdata;
01314 }
01315 
01316 gboolean qof_query_term_is_inverted (QofQueryTerm *qt)
01317 {
01318   if (!qt)
01319     return FALSE;
01320   return qt->invert;
01321 }
01322 
01323 void qof_query_get_sorts (QofQuery *q, QofQuerySort **primary,
01324                        QofQuerySort **secondary, QofQuerySort **tertiary)
01325 {
01326   if (!q)
01327     return;
01328   if (primary)
01329     *primary = &(q->primary_sort);
01330   if (secondary)
01331     *secondary = &(q->secondary_sort);
01332   if (tertiary)
01333     *tertiary = &(q->tertiary_sort);
01334 }
01335 
01336 GSList * qof_query_sort_get_param_path (QofQuerySort *qs)
01337 {
01338   if (!qs)
01339     return NULL;
01340   return qs->param_list;
01341 }
01342 
01343 gint qof_query_sort_get_sort_options (QofQuerySort *qs)
01344 {
01345   if (!qs)
01346     return 0;
01347   return qs->options;
01348 }
01349 
01350 gboolean qof_query_sort_get_increasing (QofQuerySort *qs)
01351 {
01352   if (!qs)
01353     return FALSE;
01354   return qs->increasing;
01355 }
01356 
01357 static gboolean 
01358 qof_query_term_equal (QofQueryTerm *qt1, QofQueryTerm *qt2)
01359 {
01360   if (qt1 == qt2) return TRUE;
01361   if (!qt1 || !qt2) return FALSE;
01362 
01363   if (qt1->invert != qt2->invert) return FALSE;
01364   if (param_list_cmp (qt1->param_list, qt2->param_list)) return FALSE;
01365   return qof_query_core_predicate_equal (qt1->pdata, qt2->pdata);
01366 }
01367 
01368 static gboolean 
01369 qof_query_sort_equal (QofQuerySort* qs1, QofQuerySort* qs2)
01370 {
01371   if (qs1 == qs2) return TRUE;
01372   if (!qs1 || !qs2) return FALSE;
01373 
01374   /* "Empty" sorts are equivalent, regardless of the flags */
01375   if (!qs1->param_list && !qs2->param_list) return TRUE;
01376 
01377   if (qs1->options != qs2->options) return FALSE;
01378   if (qs1->increasing != qs2->increasing) return FALSE;
01379   return (param_list_cmp (qs1->param_list, qs2->param_list) == 0);
01380 }
01381 
01382 gboolean qof_query_equal (QofQuery *q1, QofQuery *q2)
01383 {
01384   GList *or1, *or2;
01385 
01386   if (q1 == q2) return TRUE;
01387   if (!q1 || !q2) return FALSE;
01388 
01389   if (g_list_length (q1->terms) != g_list_length (q2->terms)) return FALSE;
01390   if (q1->max_results != q2->max_results) return FALSE;
01391 
01392   for (or1 = q1->terms, or2 = q2->terms; or1;
01393        or1 = or1->next, or2 = or2->next)
01394   {
01395     GList *and1, *and2;
01396 
01397     and1 = or1->data;
01398     and2 = or2->data;
01399 
01400     if (g_list_length (and1) != g_list_length (and2)) return FALSE;
01401 
01402     for ( ; and1; and1 = and1->next, and2 = and2->next)
01403       if (!qof_query_term_equal (and1->data, and2->data))
01404         return FALSE;
01405   }
01406 
01407   if (!qof_query_sort_equal (&(q1->primary_sort), &(q2->primary_sort)))
01408     return FALSE;
01409   if (!qof_query_sort_equal (&(q1->secondary_sort), &(q2->secondary_sort)))
01410     return FALSE;
01411   if (!qof_query_sort_equal (&(q1->tertiary_sort), &(q2->tertiary_sort)))
01412     return FALSE;
01413 
01414   return TRUE;
01415 }
01416 
01417 /* **************************************************************************/
01418 /* Query Print functions.  qof_query_print is public; everthing else supports
01419  * that.
01420  * Just call qof_query_print(QofQuery *q), and it will print out the query 
01421  * contents to stderr.
01422 */
01423 
01424 /* Static prototypes */
01425 static GList *qof_query_printSearchFor (QofQuery * query, GList * output);
01426 static GList *qof_query_printTerms (QofQuery * query, GList * output);
01427 static GList *qof_query_printSorts (QofQuerySort *s[], const gint numSorts,
01428                                   GList * output);
01429 static GList *qof_query_printAndTerms (GList * terms, GList * output);
01430 static gchar *qof_query_printStringForHow (QofQueryCompare how);
01431 static gchar *qof_query_printStringMatch (QofStringMatch s);
01432 static gchar *qof_query_printDateMatch (QofDateMatch d);
01433 static gchar *qof_query_printNumericMatch (QofNumericMatch n);
01434 static gchar *qof_query_printGuidMatch (QofGuidMatch g);
01435 static gchar *qof_query_printCharMatch (QofCharMatch c);
01436 static GString *qof_query_printPredData (QofQueryPredData *pd);
01437 static GString *qof_query_printParamPath (GSList * parmList);
01438 static void qof_query_printValueForParam (QofQueryPredData *pd, GString * gs);
01439 static void qof_query_printOutput (GList * output);
01440 
01441 /*
01442         This function cycles through a QofQuery object, and
01443         prints out the values of the various members of the query
01444 */
01445 void
01446 qof_query_print (QofQuery * query)
01447 {
01448   GList *output;
01449   GString *str;
01450   QofQuerySort *s[3];
01451   gint maxResults = 0, numSorts = 3;
01452 
01453   ENTER (" ");
01454 
01455   if (!query) {
01456       LEAVE("query is (null)");
01457       return;
01458   }
01459 
01460   output = NULL;
01461   str = NULL;
01462   maxResults = qof_query_get_max_results (query);
01463 
01464   output = qof_query_printSearchFor (query, output);
01465   output = qof_query_printTerms (query, output);
01466 
01467   qof_query_get_sorts (query, &s[0], &s[1], &s[2]);
01468 
01469   if (s[0])
01470   {
01471     output = qof_query_printSorts (s, numSorts, output);
01472   }
01473 
01474   str = g_string_new (" ");
01475   g_string_sprintf (str, "Maximum number of results: %d", maxResults);
01476   output = g_list_append (output, str);
01477 
01478   qof_query_printOutput (output);
01479   LEAVE (" ");
01480 }
01481 
01482 static void
01483 qof_query_printOutput (GList * output)
01484 {
01485   GList *lst;
01486 
01487   for (lst = output; lst; lst = lst->next)
01488   {
01489     GString *line = (GString *) lst->data;
01490 
01491     fprintf (stderr, "%s\n", line->str);
01492     g_string_free (line, TRUE);
01493     line = NULL;
01494   }
01495 }
01496 
01497 /*
01498         Get the search_for type--This is the type of Object
01499         we are searching for (SPLIT, TRANS, etc)
01500 */
01501 static GList *
01502 qof_query_printSearchFor (QofQuery * query, GList * output)
01503 {
01504   QofIdType searchFor;
01505   GString *gs;
01506 
01507   searchFor = qof_query_get_search_for (query);
01508   gs = g_string_new ("Query Object Type: ");
01509   g_string_append (gs, (NULL == searchFor)? "(null)" : searchFor);
01510   output = g_list_append (output, gs);
01511 
01512   return output;
01513 }                               /* qof_query_printSearchFor */
01514 
01515 /*
01516         Run through the terms of the query.  This is a outer-inner
01517         loop.  The elements of the outer loop are ORed, and the
01518         elements of the inner loop are ANDed.
01519 */
01520 static GList *
01521 qof_query_printTerms (QofQuery * query, GList * output)
01522 {
01523 
01524   GList *terms, *lst;
01525 
01526   terms = qof_query_get_terms (query);
01527 
01528   for (lst = terms; lst; lst = lst->next)
01529   {
01530     output = g_list_append (output, g_string_new ("OR and AND Terms:"));
01531 
01532     if (lst->data)
01533     {
01534       output = qof_query_printAndTerms (lst->data, output);
01535     }
01536     else
01537     {
01538       output =
01539         g_list_append (output, g_string_new ("  No data for AND terms"));
01540     }
01541   }
01542 
01543   return output;
01544 }                               /* qof_query_printTerms */
01545 
01546 /*
01547         Process the sort parameters
01548         If this function is called, the assumption is that the first sort
01549         not null.
01550 */
01551 static GList *
01552 qof_query_printSorts (QofQuerySort *s[], const gint numSorts, GList * output)
01553 {
01554   GSList *gsl, *n = NULL;
01555   gint curSort;
01556   GString *gs = g_string_new ("  Sort Parameters:\n");
01557 
01558   for (curSort = 0; curSort < numSorts; curSort++)
01559   {
01560     gboolean increasing;
01561     if (!s[curSort])
01562     {
01563       break;
01564     }
01565     increasing = qof_query_sort_get_increasing (s[curSort]);
01566 
01567     gsl = qof_query_sort_get_param_path (s[curSort]); 
01568     if (gsl) g_string_sprintfa (gs, "    Param: ");
01569     for (n=gsl; n; n = n->next)
01570     {
01571       QofIdType param_name = n->data;
01572       if (gsl != n)g_string_sprintfa (gs, "\n           ");
01573       g_string_sprintfa (gs, "%s", param_name);
01574     }
01575     if (gsl) 
01576     {
01577       g_string_sprintfa (gs, " %s\n", increasing ? "DESC" : "ASC");
01578       g_string_sprintfa (gs, "    Options: 0x%x\n", s[curSort]->options);
01579     }
01580   }
01581 
01582   output = g_list_append (output, gs);
01583   return output;
01584 
01585 }                               /* qof_query_printSorts */
01586 
01587 /*
01588         Process the AND terms of the query.  This is a GList
01589         of WHERE terms that will be ANDed
01590 */
01591 static GList *
01592 qof_query_printAndTerms (GList * terms, GList * output)
01593 {
01594   const char *prefix = "  AND Terms:";
01595   QofQueryTerm *qt;
01596   QofQueryPredData *pd;
01597   GSList *path;
01598   GList *lst;
01599   gboolean invert;
01600 
01601   output = g_list_append (output, g_string_new (prefix));
01602   for (lst = terms; lst; lst = lst->next)
01603   {
01604     qt = (QofQueryTerm *) lst->data;
01605     pd = qof_query_term_get_pred_data (qt);
01606     path = qof_query_term_get_param_path (qt);
01607     invert = qof_query_term_is_inverted (qt);
01608 
01609     if (invert) output = g_list_append (output, 
01610                                      g_string_new("    INVERT SENSE "));
01611     output = g_list_append (output, qof_query_printParamPath (path));
01612     output = g_list_append (output, qof_query_printPredData (pd));
01613     output = g_list_append (output, g_string_new("\n"));
01614   }
01615 
01616   return output;
01617 }                               /* qof_query_printAndTerms */
01618 
01619 /*
01620         Process the parameter types of the predicate data
01621 */
01622 static GString *
01623 qof_query_printParamPath (GSList * parmList)
01624 {
01625   GSList *list = NULL;
01626   GString *gs = g_string_new ("    Param List:\n");
01627   g_string_append (gs, "      ");
01628   for (list = parmList; list; list = list->next)
01629   {
01630     g_string_append (gs, (gchar *) list->data);
01631     if (list->next)
01632       g_string_append (gs, "->");
01633   }
01634 
01635   return gs;
01636 }                               /* qof_query_printParamPath */
01637 
01638 /*
01639         Process the PredData of the AND terms
01640 */
01641 static GString *
01642 qof_query_printPredData (QofQueryPredData *pd)
01643 {
01644   GString *gs;
01645 
01646   gs = g_string_new ("    Pred Data:\n      ");
01647   g_string_append (gs, (gchar *) pd->type_name);
01648 
01649   /* Char Predicate and GUID predicate don't use the 'how' field. */
01650   if (safe_strcmp (pd->type_name, QOF_TYPE_CHAR) &&
01651       safe_strcmp (pd->type_name, QOF_TYPE_GUID))
01652   {
01653     g_string_sprintfa (gs, "\n      how: %s",
01654                        qof_query_printStringForHow (pd->how));
01655   }
01656 
01657   qof_query_printValueForParam (pd, gs);
01658 
01659   return gs;
01660 }                               /* qof_query_printPredData */
01661 
01662 /*
01663         Get a string representation for the
01664         QofCompareFunc enum type.
01665 */
01666 static gchar *
01667 qof_query_printStringForHow (QofQueryCompare how)
01668 {
01669 
01670   switch (how)
01671   {
01672     case QOF_COMPARE_LT:
01673       return "QOF_COMPARE_LT";
01674     case QOF_COMPARE_LTE:
01675       return "QOF_COMPARE_LTE";
01676     case QOF_COMPARE_EQUAL:
01677       return "QOF_COMPARE_EQUAL";
01678     case QOF_COMPARE_GT:
01679       return "QOF_COMPARE_GT";
01680     case QOF_COMPARE_GTE:
01681       return "QOF_COMPARE_GTE";
01682     case QOF_COMPARE_NEQ:
01683       return "QOF_COMPARE_NEQ";
01684   }
01685 
01686   return "INVALID HOW";
01687 }                               /* qncQueryPrintStringForHow */
01688 
01689 
01690 static void
01691 qof_query_printValueForParam (QofQueryPredData *pd, GString * gs)
01692 {
01693 
01694   if (!safe_strcmp (pd->type_name, QOF_TYPE_GUID))
01695   {
01696     GList *node;
01697     query_guid_t pdata = (query_guid_t) pd;
01698     g_string_sprintfa (gs, "\n      Match type %s",
01699                        qof_query_printGuidMatch (pdata->options));
01700     for (node = pdata->guids; node; node = node->next)
01701     {
01702           /* THREAD-UNSAFE */
01703       g_string_sprintfa (gs, ", guids: %s",
01704                          guid_to_string ((GUID *) node->data));
01705     }
01706     return;
01707   }
01708   if (!safe_strcmp (pd->type_name, QOF_TYPE_STRING))
01709   {
01710     query_string_t pdata = (query_string_t) pd;
01711     g_string_sprintfa (gs, "\n      Match type %s",
01712                        qof_query_printStringMatch (pdata->options));
01713     g_string_sprintfa (gs, " %s string: %s",
01714                        pdata->is_regex ? "Regex" : "Not regex",
01715                        pdata->matchstring);
01716     return;
01717   }
01718   if (!safe_strcmp (pd->type_name, QOF_TYPE_NUMERIC))
01719   {
01720     query_numeric_t pdata = (query_numeric_t) pd;
01721     g_string_sprintfa (gs, "\n      Match type %s",
01722                        qof_query_printNumericMatch (pdata->options));
01723     g_string_sprintfa (gs, " gnc_numeric: %s",
01724                        gnc_num_dbg_to_string (pdata->amount));
01725     return;
01726   }
01727   if (!safe_strcmp (pd->type_name, QOF_TYPE_KVP))
01728   {
01729     GSList *node;
01730     query_kvp_t pdata = (query_kvp_t) pd;
01731     g_string_sprintfa (gs, "\n      kvp path: ");
01732     for (node = pdata->path; node; node = node->next)
01733     {
01734       g_string_sprintfa (gs, "/%s", (gchar *) node->data);
01735     }
01736     g_string_sprintfa (gs, "\n");
01737     g_string_sprintfa (gs, "      kvp value: %s\n", 
01738                          kvp_value_to_string (pdata->value));
01739     return;
01740   }
01741   if (!safe_strcmp (pd->type_name, QOF_TYPE_INT64))
01742   {
01743     query_int64_t pdata = (query_int64_t) pd;
01744     g_string_sprintfa (gs, " int64: %" G_GINT64_FORMAT, pdata->val);
01745     return;
01746   }
01747   if (!safe_strcmp (pd->type_name, QOF_TYPE_INT32))
01748   {
01749     query_int32_t pdata = (query_int32_t) pd;
01750     g_string_sprintfa (gs, " int32: %d", pdata->val);
01751     return;
01752   }
01753   if (!safe_strcmp (pd->type_name, QOF_TYPE_DOUBLE))
01754   {
01755     query_double_t pdata = (query_double_t) pd;
01756     g_string_sprintfa (gs, " double: %.18g", pdata->val);
01757     return;
01758   }
01759   if (!safe_strcmp (pd->type_name, QOF_TYPE_DATE))
01760   {
01761     query_date_t pdata = (query_date_t) pd;
01762     g_string_sprintfa (gs, "\n      Match type %s",
01763                        qof_query_printDateMatch (pdata->options));
01764     g_string_sprintfa (gs, " query_date: %s", gnc_print_date (pdata->date));
01765     return;
01766   }
01767   if (!safe_strcmp (pd->type_name, QOF_TYPE_CHAR))
01768   {
01769     query_char_t pdata = (query_char_t) pd;
01770     g_string_sprintfa (gs, "\n      Match type %s",
01771                        qof_query_printCharMatch (pdata->options));
01772     g_string_sprintfa (gs, " char list: %s", pdata->char_list);
01773     return;
01774   }
01775   if (!safe_strcmp (pd->type_name, QOF_TYPE_BOOLEAN))
01776   {
01777     query_boolean_t pdata = (query_boolean_t) pd;
01778     g_string_sprintfa (gs, " boolean: %s", pdata->val?"TRUE":"FALSE");
01779     return;
01780   }
01782   return;
01783 }                               /* qof_query_printValueForParam */
01784 
01785 /*
01786  * Print out a string representation of the
01787  * QofStringMatch enum
01788  */
01789 static gchar *
01790 qof_query_printStringMatch (QofStringMatch s)
01791 {
01792   switch (s)
01793   {
01794     case QOF_STRING_MATCH_NORMAL:
01795       return "QOF_STRING_MATCH_NORMAL";
01796     case QOF_STRING_MATCH_CASEINSENSITIVE:
01797       return "QOF_STRING_MATCH_CASEINSENSITIVE";
01798   }
01799   return "UNKNOWN MATCH TYPE";
01800 }                               /* qof_query_printStringMatch */
01801 
01802 /*
01803  * Print out a string representation of the
01804  * QofDateMatch enum
01805  */
01806 static gchar *
01807 qof_query_printDateMatch (QofDateMatch d)
01808 {
01809   switch (d)
01810   {
01811     case QOF_DATE_MATCH_NORMAL:
01812       return "QOF_DATE_MATCH_NORMAL";
01813     case QOF_DATE_MATCH_DAY:
01814       return "QOF_DATE_MATCH_DAY";
01815   }
01816   return "UNKNOWN MATCH TYPE";
01817 }                               /* qof_query_printDateMatch */
01818 
01819 /*
01820  * Print out a string representation of the
01821  * QofNumericMatch enum
01822  */
01823 static gchar *
01824 qof_query_printNumericMatch (QofNumericMatch n)
01825 {
01826   switch (n)
01827   {
01828     case QOF_NUMERIC_MATCH_DEBIT:
01829       return "QOF_NUMERIC_MATCH_DEBIT";
01830     case QOF_NUMERIC_MATCH_CREDIT:
01831       return "QOF_NUMERIC_MATCH_CREDIT";
01832     case QOF_NUMERIC_MATCH_ANY:
01833       return "QOF_NUMERIC_MATCH_ANY";
01834   }
01835   return "UNKNOWN MATCH TYPE";
01836 }                               /* qof_query_printNumericMatch */
01837 
01838 /*
01839  * Print out a string representation of the
01840  * QofGuidMatch enum
01841  */
01842 static gchar *
01843 qof_query_printGuidMatch (QofGuidMatch g)
01844 {
01845   switch (g)
01846   {
01847     case QOF_GUID_MATCH_ANY:
01848       return "QOF_GUID_MATCH_ANY";
01849     case QOF_GUID_MATCH_ALL:
01850       return "QOF_GUID_MATCH_ALL";
01851     case QOF_GUID_MATCH_NONE:
01852       return "QOF_GUID_MATCH_NONE";
01853     case QOF_GUID_MATCH_NULL:
01854       return "QOF_GUID_MATCH_NULL";
01855     case QOF_GUID_MATCH_LIST_ANY:
01856       return "QOF_GUID_MATCH_LIST_ANY";
01857   }
01858 
01859   return "UNKNOWN MATCH TYPE";
01860 }                               /* qof_query_printGuidMatch */
01861 
01862 /*
01863  * Print out a string representation of the
01864  * QofCharMatch enum
01865  */
01866 static gchar *
01867 qof_query_printCharMatch (QofCharMatch c)
01868 {
01869   switch (c)
01870   {
01871     case QOF_CHAR_MATCH_ANY:
01872       return "QOF_CHAR_MATCH_ANY";
01873     case QOF_CHAR_MATCH_NONE:
01874       return "QOF_CHAR_MATCH_NONE";
01875   }
01876   return "UNKNOWN MATCH TYPE";
01877 }                               /* qof_query_printGuidMatch */
01878 
01879 /* ======================== END OF FILE =================== */

Generated on Fri Oct 21 15:49:55 2005 for QOF by  doxygen 1.4.5