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