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

Generated on Fri May 12 17:57:20 2006 for QOF by  doxygen 1.4.4